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>
28 #include "bus-common-errors.h"
30 #include "import-util.h"
32 typedef struct Transfer Transfer;
33 typedef struct Manager Manager;
35 typedef enum TransferType {
40 _TRANSFER_TYPE_INVALID = -1,
62 char log_message[LINE_MAX];
63 size_t log_message_size;
65 sd_event_source *pid_event_source;
66 sd_event_source *log_event_source;
75 uint32_t current_transfer_id;
78 Hashmap *polkit_registry;
81 #define TRANSFERS_MAX 64
83 static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
84 [TRANSFER_TAR] = "tar",
85 [TRANSFER_RAW] = "raw",
86 [TRANSFER_DKR] = "dkr",
89 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(transfer_type, TransferType);
91 static Transfer *transfer_unref(Transfer *t) {
96 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
98 sd_event_source_unref(t->pid_event_source);
99 sd_event_source_unref(t->log_event_source);
103 free(t->dkr_index_url);
104 free(t->object_path);
107 (void) kill_and_sigcont(t->pid, SIGKILL);
108 (void) wait_for_terminate(t->pid, NULL);
111 safe_close(t->log_fd);
117 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
119 static int transfer_new(Manager *m, Transfer **ret) {
120 _cleanup_(transfer_unrefp) Transfer *t = NULL;
127 if (hashmap_size(m->transfers) >= TRANSFERS_MAX)
130 r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops);
134 t = new0(Transfer, 1);
138 t->type = _TRANSFER_TYPE_INVALID;
141 id = m->current_transfer_id + 1;
143 if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0)
146 r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t);
150 m->current_transfer_id = id;
161 static void transfer_send_log_line(Transfer *t, const char *line) {
162 int r, priority = LOG_INFO;
167 syslog_parse_priority(&line, &priority, true);
169 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
171 r = sd_bus_emit_signal(
174 "org.freedesktop.import1.Transfer",
180 log_error_errno(r, "Cannot emit message: %m");
183 static void transfer_send_logs(Transfer *t, bool flush) {
186 /* Try to send out all log messages, if we can. But if we
187 * can't we remove the messages from the buffer, but don't
190 while (t->log_message_size > 0) {
191 _cleanup_free_ char *n = NULL;
194 if (t->log_message_size >= sizeof(t->log_message))
195 e = t->log_message + sizeof(t->log_message);
199 a = memchr(t->log_message, 0, t->log_message_size);
200 b = memchr(t->log_message, '\n', t->log_message_size);
214 e = t->log_message + t->log_message_size;
217 n = strndup(t->log_message, e - t->log_message);
219 /* Skip over NUL and newlines */
220 while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n'))
223 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
224 t->log_message_size -= e - t->log_message;
234 transfer_send_log_line(t, n);
238 static int transfer_finalize(Transfer *t, bool success) {
243 transfer_send_logs(t, true);
245 r = sd_bus_emit_signal(
247 "/org/freedesktop/import1",
248 "org.freedesktop.import1.Manager",
254 t->n_canceled > 0 ? "canceled" : "failed");
257 log_error_errno(r, "Cannot emit message: %m");
263 static int transfer_cancel(Transfer *t) {
268 r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
276 static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
277 Transfer *t = userdata;
278 bool success = false;
283 if (si->si_code == CLD_EXITED) {
284 if (si->si_status != 0)
285 log_error("Import process failed with exit code %i.", si->si_status);
287 log_debug("Import process succeeded.");
291 } else if (si->si_code == CLD_KILLED ||
292 si->si_code == CLD_DUMPED)
294 log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
296 log_error("Import process failed due to unknown reason.");
300 return transfer_finalize(t, success);
303 static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
304 Transfer *t = userdata;
310 l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
312 /* EOF/read error. We just close the pipe here, and
313 * close the watch, waiting for the SIGCHLD to arrive,
314 * before we do anything else. */
317 log_error_errno(errno, "Failed to read log message: %m");
319 t->log_event_source = sd_event_source_unref(t->log_event_source);
323 t->log_message_size += l;
325 transfer_send_logs(t, false);
330 static int transfer_start(Transfer *t) {
331 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
337 if (pipe2(pipefd, O_CLOEXEC) < 0)
344 const char *cmd[] = {
346 transfer_type_to_string(t->type),
348 NULL, /* verify argument */
349 NULL, /* maybe --force */
350 NULL, /* maybe --dkr-index-url */
351 NULL, /* the actual URL */
361 reset_all_signal_handlers();
363 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
365 pipefd[0] = safe_close(pipefd[0]);
367 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
368 log_error_errno(errno, "Failed to dup2() fd: %m");
372 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
373 log_error_errno(errno, "Failed to dup2() fd: %m");
377 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
378 pipefd[1] = safe_close(pipefd[1]);
380 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
382 log_error_errno(errno, "Failed to open /dev/null: %m");
386 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
387 log_error_errno(errno, "Failed to dup2() fd: %m");
391 if (null_fd != STDIN_FILENO)
394 fd_cloexec(STDIN_FILENO, false);
395 fd_cloexec(STDOUT_FILENO, false);
396 fd_cloexec(STDERR_FILENO, false);
398 putenv((char*) "SYSTEMD_LOG_TARGET=console-prefixed");
400 cmd[k++] = import_verify_to_string(t->verify);
402 cmd[k++] = "--force";
404 if (t->dkr_index_url) {
405 cmd[k++] = "--dkr-index-url";
406 cmd[k++] = t->dkr_index_url;
409 cmd[k++] = t->remote;
414 execv(SYSTEMD_PULL_PATH, (char * const *) cmd);
415 log_error_errno(errno, "Failed to execute import tool: %m");
419 pipefd[1] = safe_close(pipefd[1]);
420 t->log_fd = pipefd[0];
423 r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
427 r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
431 /* Make sure always process logging before SIGCHLD */
432 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
436 r = sd_bus_emit_signal(
438 "/org/freedesktop/import1",
439 "org.freedesktop.import1.Manager",
450 static Manager *manager_unref(Manager *m) {
456 while ((t = hashmap_first(m->transfers)))
459 hashmap_free(m->transfers);
461 bus_verify_polkit_async_registry_free(m->polkit_registry);
463 sd_bus_close(m->bus);
464 sd_bus_unref(m->bus);
465 sd_event_unref(m->event);
471 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
473 static int manager_new(Manager **ret) {
474 _cleanup_(manager_unrefp) Manager *m = NULL;
479 m = new0(Manager, 1);
483 r = sd_event_default(&m->event);
487 sd_event_set_watchdog(m->event, true);
489 r = sd_bus_default_system(&m->bus);
499 static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
505 assert(type < _TRANSFER_TYPE_MAX);
507 HASHMAP_FOREACH(t, m->transfers, i) {
509 if (t->type == type &&
510 streq_ptr(t->remote, remote) &&
511 streq_ptr(t->dkr_index_url, dkr_index_url))
518 static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
519 _cleanup_(transfer_unrefp) Transfer *t = NULL;
520 const char *remote, *local, *verify, *object;
521 Manager *m = userdata;
531 r = bus_verify_polkit_async(
534 "org.freedesktop.import1.pull",
541 return 1; /* Will call us back */
543 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
547 if (!http_url_is_valid(remote))
548 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
552 else if (!machine_name_is_valid(local))
553 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
556 v = IMPORT_VERIFY_SIGNATURE;
558 v = import_verify_from_string(verify);
560 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
562 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
564 if (manager_find(m, type, NULL, remote))
565 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
567 r = transfer_new(m, &t);
573 t->force_local = force;
575 t->remote = strdup(remote);
579 t->local = strdup(local);
583 r = transfer_start(t);
587 object = t->object_path;
591 return sd_bus_reply_method_return(msg, "uo", id, object);
594 static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
595 _cleanup_(transfer_unrefp) Transfer *t = NULL;
596 const char *index_url, *remote, *tag, *local, *verify, *object;
597 Manager *m = userdata;
606 r = bus_verify_polkit_async(
609 "org.freedesktop.import1.pull",
616 return 1; /* Will call us back */
618 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
622 if (isempty(index_url))
623 index_url = DEFAULT_DKR_INDEX_URL;
625 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
626 if (!http_url_is_valid(index_url))
627 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
629 if (!dkr_name_is_valid(remote))
630 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
634 else if (!dkr_tag_is_valid(tag))
635 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
639 else if (!machine_name_is_valid(local))
640 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
643 v = IMPORT_VERIFY_SIGNATURE;
645 v = import_verify_from_string(verify);
647 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
649 if (v != IMPORT_VERIFY_NO)
650 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
652 if (manager_find(m, TRANSFER_DKR, index_url, remote))
653 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
655 r = transfer_new(m, &t);
659 t->type = TRANSFER_DKR;
661 t->force_local = force;
663 t->dkr_index_url = strdup(index_url);
664 if (!t->dkr_index_url)
667 t->remote = strjoin(remote, ":", tag, NULL);
671 t->local = strdup(local);
675 r = transfer_start(t);
679 object = t->object_path;
683 return sd_bus_reply_method_return(msg, "uo", id, object);
686 static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
687 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
688 Manager *m = userdata;
697 r = sd_bus_message_new_method_return(msg, &reply);
701 r = sd_bus_message_open_container(reply, 'a', "(ussso)");
705 HASHMAP_FOREACH(t, m->transfers, i) {
707 r = sd_bus_message_append(
711 transfer_type_to_string(t->type),
719 r = sd_bus_message_close_container(reply);
723 return sd_bus_send(bus, reply, NULL);
726 static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
727 Transfer *t = userdata;
734 r = bus_verify_polkit_async(
737 "org.freedesktop.import1.pull",
739 &t->manager->polkit_registry,
744 return 1; /* Will call us back */
746 r = transfer_cancel(t);
750 return sd_bus_reply_method_return(msg, NULL);
753 static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
754 Manager *m = userdata;
763 r = bus_verify_polkit_async(
766 "org.freedesktop.import1.pull",
773 return 1; /* Will call us back */
775 r = sd_bus_message_read(msg, "u", &id);
779 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
781 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
783 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
785 r = transfer_cancel(t);
789 return sd_bus_reply_method_return(msg, NULL);
792 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
793 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
795 static const sd_bus_vtable transfer_vtable[] = {
796 SD_BUS_VTABLE_START(0),
797 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
798 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
799 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
800 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
801 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
802 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
803 SD_BUS_SIGNAL("LogMessage", "us", 0),
807 static const sd_bus_vtable manager_vtable[] = {
808 SD_BUS_VTABLE_START(0),
809 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
810 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
811 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
812 SD_BUS_METHOD("ListTransfers", NULL, "a(ussso)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
813 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
814 SD_BUS_SIGNAL("TransferNew", "uo", 0),
815 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
819 static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
820 Manager *m = userdata;
832 p = startswith(path, "/org/freedesktop/import1/transfer/_");
836 r = safe_atou32(p, &id);
837 if (r < 0 || id == 0)
840 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
848 static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
849 _cleanup_strv_free_ char **l = NULL;
850 Manager *m = userdata;
855 l = new0(char*, hashmap_size(m->transfers) + 1);
859 HASHMAP_FOREACH(t, m->transfers, i) {
861 l[k] = strdup(t->object_path);
874 static int manager_add_bus_objects(Manager *m) {
879 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
881 return log_error_errno(r, "Failed to register object: %m");
883 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
885 return log_error_errno(r, "Failed to register object: %m");
887 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
889 return log_error_errno(r, "Failed to add transfer enumerator: %m");
891 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
893 return log_error_errno(r, "Failed to register name: %m");
895 r = sd_bus_attach_event(m->bus, m->event, 0);
897 return log_error_errno(r, "Failed to attach bus to event loop: %m");
902 static bool manager_check_idle(void *userdata) {
903 Manager *m = userdata;
905 return hashmap_isempty(m->transfers);
908 static int manager_run(Manager *m) {
911 return bus_event_loop_with_idle(
914 "org.freedesktop.import1",
920 int main(int argc, char *argv[]) {
921 _cleanup_(manager_unrefp) Manager *m = NULL;
924 log_set_target(LOG_TARGET_AUTO);
925 log_parse_environment();
931 log_error("This program takes no arguments.");
936 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
940 log_error_errno(r, "Failed to allocate manager object: %m");
944 r = manager_add_bus_objects(m);
950 log_error_errno(r, "Failed to run event loop: %m");
955 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;