1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/prctl.h>
29 #include "bus-common-errors.h"
31 #include "socket-util.h"
35 #include "import-util.h"
37 typedef struct Transfer Transfer;
38 typedef struct Manager Manager;
40 typedef enum TransferType {
45 _TRANSFER_TYPE_INVALID = -1,
67 char log_message[LINE_MAX];
68 size_t log_message_size;
70 sd_event_source *pid_event_source;
71 sd_event_source *log_event_source;
74 unsigned progress_percent;
81 uint32_t current_transfer_id;
84 Hashmap *polkit_registry;
88 sd_event_source *notify_event_source;
91 #define TRANSFERS_MAX 64
93 static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
94 [TRANSFER_TAR] = "tar",
95 [TRANSFER_RAW] = "raw",
96 [TRANSFER_DKR] = "dkr",
99 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
101 static Transfer *transfer_unref(Transfer *t) {
106 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
108 sd_event_source_unref(t->pid_event_source);
109 sd_event_source_unref(t->log_event_source);
113 free(t->dkr_index_url);
114 free(t->object_path);
117 (void) kill_and_sigcont(t->pid, SIGKILL);
118 (void) wait_for_terminate(t->pid, NULL);
121 safe_close(t->log_fd);
127 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
129 static int transfer_new(Manager *m, Transfer **ret) {
130 _cleanup_(transfer_unrefp) Transfer *t = NULL;
137 if (hashmap_size(m->transfers) >= TRANSFERS_MAX)
140 r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops);
144 t = new0(Transfer, 1);
148 t->type = _TRANSFER_TYPE_INVALID;
151 id = m->current_transfer_id + 1;
153 if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0)
156 r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t);
160 m->current_transfer_id = id;
171 static void transfer_send_log_line(Transfer *t, const char *line) {
172 int r, priority = LOG_INFO;
177 syslog_parse_priority(&line, &priority, true);
179 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
181 r = sd_bus_emit_signal(
184 "org.freedesktop.import1.Transfer",
190 log_error_errno(r, "Cannot emit message: %m");
193 static void transfer_send_logs(Transfer *t, bool flush) {
196 /* Try to send out all log messages, if we can. But if we
197 * can't we remove the messages from the buffer, but don't
200 while (t->log_message_size > 0) {
201 _cleanup_free_ char *n = NULL;
204 if (t->log_message_size >= sizeof(t->log_message))
205 e = t->log_message + sizeof(t->log_message);
209 a = memchr(t->log_message, 0, t->log_message_size);
210 b = memchr(t->log_message, '\n', t->log_message_size);
224 e = t->log_message + t->log_message_size;
227 n = strndup(t->log_message, e - t->log_message);
229 /* Skip over NUL and newlines */
230 while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n'))
233 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
234 t->log_message_size -= e - t->log_message;
244 transfer_send_log_line(t, n);
248 static int transfer_finalize(Transfer *t, bool success) {
253 transfer_send_logs(t, true);
255 r = sd_bus_emit_signal(
257 "/org/freedesktop/import1",
258 "org.freedesktop.import1.Manager",
264 t->n_canceled > 0 ? "canceled" : "failed");
267 log_error_errno(r, "Cannot emit message: %m");
273 static int transfer_cancel(Transfer *t) {
278 r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
286 static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
287 Transfer *t = userdata;
288 bool success = false;
293 if (si->si_code == CLD_EXITED) {
294 if (si->si_status != 0)
295 log_error("Import process failed with exit code %i.", si->si_status);
297 log_debug("Import process succeeded.");
301 } else if (si->si_code == CLD_KILLED ||
302 si->si_code == CLD_DUMPED)
304 log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
306 log_error("Import process failed due to unknown reason.");
310 return transfer_finalize(t, success);
313 static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
314 Transfer *t = userdata;
320 l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
322 /* EOF/read error. We just close the pipe here, and
323 * close the watch, waiting for the SIGCHLD to arrive,
324 * before we do anything else. */
327 log_error_errno(errno, "Failed to read log message: %m");
329 t->log_event_source = sd_event_source_unref(t->log_event_source);
333 t->log_message_size += l;
335 transfer_send_logs(t, false);
340 static int transfer_start(Transfer *t) {
341 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
347 if (pipe2(pipefd, O_CLOEXEC) < 0)
354 const char *cmd[] = {
356 transfer_type_to_string(t->type),
358 NULL, /* verify argument */
359 NULL, /* maybe --force */
360 NULL, /* maybe --dkr-index-url */
361 NULL, /* the actual URL */
371 reset_all_signal_handlers();
373 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
375 pipefd[0] = safe_close(pipefd[0]);
377 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
378 log_error_errno(errno, "Failed to dup2() fd: %m");
382 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
383 log_error_errno(errno, "Failed to dup2() fd: %m");
387 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
388 pipefd[1] = safe_close(pipefd[1]);
390 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
392 log_error_errno(errno, "Failed to open /dev/null: %m");
396 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
397 log_error_errno(errno, "Failed to dup2() fd: %m");
401 if (null_fd != STDIN_FILENO)
404 fd_cloexec(STDIN_FILENO, false);
405 fd_cloexec(STDOUT_FILENO, false);
406 fd_cloexec(STDERR_FILENO, false);
408 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
409 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
411 cmd[k++] = import_verify_to_string(t->verify);
413 cmd[k++] = "--force";
415 if (t->dkr_index_url) {
416 cmd[k++] = "--dkr-index-url";
417 cmd[k++] = t->dkr_index_url;
420 cmd[k++] = t->remote;
425 execv(SYSTEMD_PULL_PATH, (char * const *) cmd);
426 log_error_errno(errno, "Failed to execute import tool: %m");
430 pipefd[1] = safe_close(pipefd[1]);
431 t->log_fd = pipefd[0];
434 r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
438 r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
442 /* Make sure always process logging before SIGCHLD */
443 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
447 r = sd_bus_emit_signal(
449 "/org/freedesktop/import1",
450 "org.freedesktop.import1.Manager",
461 static Manager *manager_unref(Manager *m) {
467 sd_event_source_unref(m->notify_event_source);
468 safe_close(m->notify_fd);
470 while ((t = hashmap_first(m->transfers)))
473 hashmap_free(m->transfers);
475 bus_verify_polkit_async_registry_free(m->polkit_registry);
477 sd_bus_close(m->bus);
478 sd_bus_unref(m->bus);
479 sd_event_unref(m->event);
485 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
487 static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
489 char buf[NOTIFY_BUFFER_MAX+1];
490 struct iovec iovec = {
492 .iov_len = sizeof(buf)-1,
495 struct cmsghdr cmsghdr;
496 uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
497 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
499 struct msghdr msghdr = {
502 .msg_control = &control,
503 .msg_controllen = sizeof(control),
505 struct ucred *ucred = NULL;
506 Manager *m = userdata;
507 struct cmsghdr *cmsg;
515 n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
517 if (errno == EAGAIN || errno == EINTR)
523 cmsg_close_all(&msghdr);
525 for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
526 if (cmsg->cmsg_level == SOL_SOCKET &&
527 cmsg->cmsg_type == SCM_CREDENTIALS &&
528 cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
530 ucred = (struct ucred*) CMSG_DATA(cmsg);
534 if (msghdr.msg_flags & MSG_TRUNC) {
535 log_warning("Got overly long notification datagram, ignoring.");
539 if (!ucred || ucred->pid <= 0) {
540 log_warning("Got notification datagram lacking credential information, ignoring.");
544 HASHMAP_FOREACH(t, m->transfers, i)
545 if (ucred->pid == t->pid)
549 log_warning("Got notification datagram from unexpected peer, ignoring.");
555 p = startswith(buf, "X_IMPORT_PROGRESS=");
557 p = strstr(buf, "\nX_IMPORT_PROGRESS=");
564 e = strchrnul(p, '\n');
567 r = safe_atou(p, &percent);
568 if (r < 0 || percent > 100) {
569 log_warning("Got invalid percent value, ignoring.");
573 t->progress_percent = percent;
575 log_debug("Got percentage from client: %u%%", percent);
579 static int manager_new(Manager **ret) {
580 _cleanup_(manager_unrefp) Manager *m = NULL;
581 static const union sockaddr_union sa = {
582 .un.sun_family = AF_UNIX,
583 .un.sun_path = "/run/systemd/import/notify",
585 static const int one = 1;
590 m = new0(Manager, 1);
594 r = sd_event_default(&m->event);
598 sd_event_set_watchdog(m->event, true);
600 r = sd_bus_default_system(&m->bus);
604 m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
605 if (m->notify_fd < 0)
608 (void) mkdir_parents_label(sa.un.sun_path, 0755);
609 (void) unlink(sa.un.sun_path);
611 if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
614 if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
617 r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
627 static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
633 assert(type < _TRANSFER_TYPE_MAX);
635 HASHMAP_FOREACH(t, m->transfers, i) {
637 if (t->type == type &&
638 streq_ptr(t->remote, remote) &&
639 streq_ptr(t->dkr_index_url, dkr_index_url))
646 static int check_btrfs(sd_bus_error *error) {
649 if (statfs("/var/lib/machines", &sfs) < 0) {
653 if (statfs("/var/lib", &sfs) < 0)
657 if (!F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
658 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "/var/lib/machines is not a btrfs file system. Operation is not supported on legacy file systems.");
663 static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
664 _cleanup_(transfer_unrefp) Transfer *t = NULL;
665 const char *remote, *local, *verify, *object;
666 Manager *m = userdata;
676 r = bus_verify_polkit_async(
679 "org.freedesktop.import1.pull",
687 return 1; /* Will call us back */
689 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
693 if (!http_url_is_valid(remote))
694 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
698 else if (!machine_name_is_valid(local))
699 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
702 v = IMPORT_VERIFY_SIGNATURE;
704 v = import_verify_from_string(verify);
706 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
708 r = check_btrfs(error);
712 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
714 if (manager_find(m, type, NULL, remote))
715 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
717 r = transfer_new(m, &t);
723 t->force_local = force;
725 t->remote = strdup(remote);
729 t->local = strdup(local);
733 r = transfer_start(t);
737 object = t->object_path;
741 return sd_bus_reply_method_return(msg, "uo", id, object);
744 static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
745 _cleanup_(transfer_unrefp) Transfer *t = NULL;
746 const char *index_url, *remote, *tag, *local, *verify, *object;
747 Manager *m = userdata;
756 r = bus_verify_polkit_async(
759 "org.freedesktop.import1.pull",
767 return 1; /* Will call us back */
769 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
773 if (isempty(index_url))
774 index_url = DEFAULT_DKR_INDEX_URL;
776 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
777 if (!http_url_is_valid(index_url))
778 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
780 if (!dkr_name_is_valid(remote))
781 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
785 else if (!dkr_tag_is_valid(tag))
786 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
790 else if (!machine_name_is_valid(local))
791 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
794 v = IMPORT_VERIFY_SIGNATURE;
796 v = import_verify_from_string(verify);
798 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
800 if (v != IMPORT_VERIFY_NO)
801 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
803 r = check_btrfs(error);
807 if (manager_find(m, TRANSFER_DKR, index_url, remote))
808 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
810 r = transfer_new(m, &t);
814 t->type = TRANSFER_DKR;
816 t->force_local = force;
818 t->dkr_index_url = strdup(index_url);
819 if (!t->dkr_index_url)
822 t->remote = strjoin(remote, ":", tag, NULL);
826 t->local = strdup(local);
830 r = transfer_start(t);
834 object = t->object_path;
838 return sd_bus_reply_method_return(msg, "uo", id, object);
841 static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
842 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
843 Manager *m = userdata;
852 r = sd_bus_message_new_method_return(msg, &reply);
856 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
860 HASHMAP_FOREACH(t, m->transfers, i) {
862 r = sd_bus_message_append(
866 transfer_type_to_string(t->type),
869 (double) t->progress_percent / 100.0,
875 r = sd_bus_message_close_container(reply);
879 return sd_bus_send(bus, reply, NULL);
882 static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
883 Transfer *t = userdata;
890 r = bus_verify_polkit_async(
893 "org.freedesktop.import1.pull",
896 &t->manager->polkit_registry,
901 return 1; /* Will call us back */
903 r = transfer_cancel(t);
907 return sd_bus_reply_method_return(msg, NULL);
910 static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
911 Manager *m = userdata;
920 r = bus_verify_polkit_async(
923 "org.freedesktop.import1.pull",
931 return 1; /* Will call us back */
933 r = sd_bus_message_read(msg, "u", &id);
937 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
939 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
941 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
943 r = transfer_cancel(t);
947 return sd_bus_reply_method_return(msg, NULL);
950 static int property_get_progress(
953 const char *interface,
954 const char *property,
955 sd_bus_message *reply,
957 sd_bus_error *error) {
959 Transfer *t = userdata;
965 return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
968 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
969 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
971 static const sd_bus_vtable transfer_vtable[] = {
972 SD_BUS_VTABLE_START(0),
973 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
974 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
975 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
976 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
977 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
978 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
979 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
980 SD_BUS_SIGNAL("LogMessage", "us", 0),
984 static const sd_bus_vtable manager_vtable[] = {
985 SD_BUS_VTABLE_START(0),
986 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
987 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
988 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
989 SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
990 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
991 SD_BUS_SIGNAL("TransferNew", "uo", 0),
992 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
996 static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
997 Manager *m = userdata;
1009 p = startswith(path, "/org/freedesktop/import1/transfer/_");
1013 r = safe_atou32(p, &id);
1014 if (r < 0 || id == 0)
1017 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1025 static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1026 _cleanup_strv_free_ char **l = NULL;
1027 Manager *m = userdata;
1032 l = new0(char*, hashmap_size(m->transfers) + 1);
1036 HASHMAP_FOREACH(t, m->transfers, i) {
1038 l[k] = strdup(t->object_path);
1051 static int manager_add_bus_objects(Manager *m) {
1056 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1058 return log_error_errno(r, "Failed to register object: %m");
1060 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1062 return log_error_errno(r, "Failed to register object: %m");
1064 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1066 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1068 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1070 return log_error_errno(r, "Failed to register name: %m");
1072 r = sd_bus_attach_event(m->bus, m->event, 0);
1074 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1079 static bool manager_check_idle(void *userdata) {
1080 Manager *m = userdata;
1082 return hashmap_isempty(m->transfers);
1085 static int manager_run(Manager *m) {
1088 return bus_event_loop_with_idle(
1091 "org.freedesktop.import1",
1097 int main(int argc, char *argv[]) {
1098 _cleanup_(manager_unrefp) Manager *m = NULL;
1101 log_set_target(LOG_TARGET_AUTO);
1102 log_parse_environment();
1108 log_error("This program takes no arguments.");
1113 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
1115 r = manager_new(&m);
1117 log_error_errno(r, "Failed to allocate manager object: %m");
1121 r = manager_add_bus_objects(m);
1127 log_error_errno(r, "Failed to run event loop: %m");
1132 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;