]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/importd.c
import: make the user verficiation keyring override the vendor keyring, instead of...
[thirdparty/systemd.git] / src / import / importd.c
CommitLineData
3d7415f4
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
22#include <sys/prctl.h>
23
24#include "sd-bus.h"
25#include "util.h"
26#include "strv.h"
27#include "bus-util.h"
28#include "bus-common-errors.h"
29#include "def.h"
30#include "import-util.h"
31
32typedef struct Transfer Transfer;
33typedef struct Manager Manager;
34
35typedef enum TransferType {
36 TRANSFER_TAR,
37 TRANSFER_RAW,
38 TRANSFER_DKR,
39 _TRANSFER_TYPE_MAX,
40 _TRANSFER_TYPE_INVALID = -1,
41} TransferType;
42
43struct Transfer {
44 Manager *manager;
45
46 uint32_t id;
47 char *object_path;
48
49 TransferType type;
50 ImportVerify verify;
51
52 char *remote;
53 char *local;
54 bool force_local;
55
56 char *dkr_index_url;
57
58 pid_t pid;
59
60 int log_fd;
61
62 char log_message[LINE_MAX];
63 size_t log_message_size;
64
65 sd_event_source *pid_event_source;
66 sd_event_source *log_event_source;
67
68 unsigned n_canceled;
69};
70
71struct Manager {
72 sd_event *event;
73 sd_bus *bus;
74
75 uint32_t current_transfer_id;
76 Hashmap *transfers;
77
78 Hashmap *polkit_registry;
79};
80
81#define TRANSFERS_MAX 64
82
83static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
84 [TRANSFER_TAR] = "tar",
85 [TRANSFER_RAW] = "raw",
86 [TRANSFER_DKR] = "dkr",
87};
88
89DEFINE_PRIVATE_STRING_TABLE_LOOKUP(transfer_type, TransferType);
90
91static Transfer *transfer_unref(Transfer *t) {
92 if (!t)
93 return NULL;
94
95 if (t->manager)
96 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
97
98 sd_event_source_unref(t->pid_event_source);
99 sd_event_source_unref(t->log_event_source);
100
101 free(t->remote);
102 free(t->local);
103 free(t->dkr_index_url);
104 free(t->object_path);
105
106 if (t->pid > 0) {
107 (void) kill_and_sigcont(t->pid, SIGKILL);
108 (void) wait_for_terminate(t->pid, NULL);
109 }
110
111 safe_close(t->log_fd);
112
113 free(t);
114 return NULL;
115}
116
117DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
118
119static int transfer_new(Manager *m, Transfer **ret) {
120 _cleanup_(transfer_unrefp) Transfer *t = NULL;
121 uint32_t id;
122 int r;
123
124 assert(m);
125 assert(ret);
126
127 if (hashmap_size(m->transfers) >= TRANSFERS_MAX)
128 return -E2BIG;
129
130 r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops);
131 if (r < 0)
132 return r;
133
134 t = new0(Transfer, 1);
135 if (!t)
136 return -ENOMEM;
137
138 t->type = _TRANSFER_TYPE_INVALID;
139 t->log_fd = -1;
140
141 id = m->current_transfer_id + 1;
142
143 if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0)
144 return -ENOMEM;
145
146 r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t);
147 if (r < 0)
148 return r;
149
150 m->current_transfer_id = id;
151
152 t->manager = m;
153 t->id = id;
154
155 *ret = t;
156 t = NULL;
157
158 return 0;
159}
160
161static void transfer_send_log_line(Transfer *t, const char *line) {
162 int r, priority = LOG_INFO;
163
164 assert(t);
165 assert(line);
166
167 syslog_parse_priority(&line, &priority, true);
168
169 log_full(priority, "(transfer%" PRIu32 "): %s", t->id, line);
170
171 r = sd_bus_emit_signal(
172 t->manager->bus,
173 t->object_path,
174 "org.freedesktop.import1.Transfer",
175 "LogMessage",
176 "us",
177 priority,
178 line);
179 if (r < 0)
180 log_error_errno(r, "Cannot emit message: %m");
181 }
182
183static void transfer_send_logs(Transfer *t, bool flush) {
184 assert(t);
185
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
188 * fail */
189
190 while (t->log_message_size > 0) {
191 _cleanup_free_ char *n = NULL;
192 char *e;
193
194 if (t->log_message_size >= sizeof(t->log_message))
195 e = t->log_message + sizeof(t->log_message);
196 else {
197 char *a, *b;
198
199 a = memchr(t->log_message, 0, t->log_message_size);
200 b = memchr(t->log_message, '\n', t->log_message_size);
201
202 if (a && b)
203 e = a < b ? a : b;
204 else if (a)
205 e = a;
206 else
207 e = b;
208 }
209
210 if (!e) {
211 if (!flush)
212 return;
213
214 e = t->log_message + t->log_message_size;
215 }
216
217 n = strndup(t->log_message, e - t->log_message);
218
219 /* Skip over NUL and newlines */
220 while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n'))
221 e++;
222
223 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
224 t->log_message_size -= e - t->log_message;
225
226 if (!n) {
227 log_oom();
228 continue;
229 }
230
231 if (isempty(n))
232 continue;
233
234 transfer_send_log_line(t, n);
235 }
236}
237
238static int transfer_finalize(Transfer *t, bool success) {
239 int r;
240
241 assert(t);
242
243 transfer_send_logs(t, true);
244
245 r = sd_bus_emit_signal(
246 t->manager->bus,
247 "/org/freedesktop/import1",
248 "org.freedesktop.import1.Manager",
249 "TransferRemoved",
250 "uos",
251 t->id,
252 t->object_path,
253 success ? "done" :
254 t->n_canceled > 0 ? "canceled" : "failed");
255
256 if (r < 0)
257 log_error_errno(r, "Cannot emit message: %m");
258
259 transfer_unref(t);
260 return 0;
261}
262
263static int transfer_cancel(Transfer *t) {
264 int r;
265
266 assert(t);
267
268 r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
269 if (r < 0)
270 return r;
271
272 t->n_canceled++;
273 return 0;
274}
275
276static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
277 Transfer *t = userdata;
278 bool success = false;
279
280 assert(s);
281 assert(t);
282
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);
286 else {
287 log_debug("Import process succeeded.");
288 success = true;
289 }
290
291 } else if (si->si_code == CLD_KILLED ||
292 si->si_code == CLD_DUMPED)
293
294 log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
295 else
296 log_error("Import process failed due to unknown reason.");
297
298 t->pid = 0;
299
300 return transfer_finalize(t, success);
301}
302
303static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
304 Transfer *t = userdata;
305 ssize_t l;
306
307 assert(s);
308 assert(t);
309
310 l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
311 if (l <= 0) {
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. */
315
316 if (l < 0)
317 log_error_errno(errno, "Failed to read log message: %m");
318
319 t->log_event_source = sd_event_source_unref(t->log_event_source);
320 return 0;
321 }
322
323 t->log_message_size += l;
324
325 transfer_send_logs(t, false);
326
327 return 0;
328}
329
330static int transfer_start(Transfer *t) {
331 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
332 int r;
333
334 assert(t);
335 assert(t->pid <= 0);
336
337 if (pipe2(pipefd, O_CLOEXEC) < 0)
338 return -errno;
339
340 t->pid = fork();
341 if (t->pid < 0)
342 return -errno;
343 if (t->pid == 0) {
344 const char *cmd[] = {
345 "systemd-import",
346 t->type == TRANSFER_TAR ? "pull-tar" :
347 t->type == TRANSFER_RAW ? "pull-raw" :
348 "pull-dkr",
349 "--verify",
350 NULL, /* verify argument */
351 NULL, /* maybe --force */
352 NULL, /* maybe --dkr-index-url */
353 NULL, /* the actual URL */
354 NULL, /* remote */
355 NULL, /* local */
356 NULL
357 };
358 int null_fd;
359 unsigned k = 3;
360
361 /* Child */
362
363 reset_all_signal_handlers();
364 reset_signal_mask();
365 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
366
367 pipefd[0] = safe_close(pipefd[0]);
368
369 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
370 log_error_errno(errno, "Failed to dup2() fd: %m");
371 _exit(EXIT_FAILURE);
372 }
373
374 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
375 log_error_errno(errno, "Failed to dup2() fd: %m");
376 _exit(EXIT_FAILURE);
377 }
378
379 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
380 pipefd[1] = safe_close(pipefd[1]);
381
382 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
383 if (null_fd < 0) {
384 log_error_errno(errno, "Failed to open /dev/null: %m");
385 _exit(EXIT_FAILURE);
386 }
387
388 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
389 log_error_errno(errno, "Failed to dup2() fd: %m");
390 _exit(EXIT_FAILURE);
391 }
392
393 if (null_fd != STDIN_FILENO)
394 safe_close(null_fd);
395
396 fd_cloexec(STDIN_FILENO, false);
397 fd_cloexec(STDOUT_FILENO, false);
398 fd_cloexec(STDERR_FILENO, false);
399
400 putenv((char*) "SYSTEMD_LOG_TARGET=console-prefixed");
401
402 cmd[k++] = import_verify_to_string(t->verify);
403 if (t->force_local)
404 cmd[k++] = "--force";
405
406 if (t->dkr_index_url) {
407 cmd[k++] = "--dkr-index-url";
408 cmd[k++] = t->dkr_index_url;
409 }
410
411 cmd[k++] = t->remote;
412 if (t->local)
413 cmd[k++] = t->local;
414 cmd[k] = NULL;
415
416 execv(SYSTEMD_IMPORT_PATH, (char * const *) cmd);
417 log_error_errno(errno, "Failed to execute import tool: %m");
418 _exit(EXIT_FAILURE);
419 }
420
421 pipefd[1] = safe_close(pipefd[1]);
422 t->log_fd = pipefd[0];
423 pipefd[0] = -1;
424
425 r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
426 if (r < 0)
427 return r;
428
429 r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
430 if (r < 0)
431 return r;
432
433 /* Make sure always process logging before SIGCHLD */
434 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
435 if (r < 0)
436 return r;
437
438 r = sd_bus_emit_signal(
439 t->manager->bus,
440 "/org/freedesktop/import1",
441 "org.freedesktop.import1.Manager",
442 "TransferNew",
443 "uo",
444 t->id,
445 t->object_path);
446 if (r < 0)
447 return r;
448
449 return 0;
450}
451
452static Manager *manager_unref(Manager *m) {
453 Transfer *t;
454
455 if (!m)
456 return NULL;
457
458 while ((t = hashmap_first(m->transfers)))
459 transfer_unref(t);
460
461 hashmap_free(m->transfers);
462
463 bus_verify_polkit_async_registry_free(m->polkit_registry);
464
465 sd_bus_close(m->bus);
466 sd_bus_unref(m->bus);
467 sd_event_unref(m->event);
468
469 free(m);
470 return NULL;
471}
472
473DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
474
475static int manager_new(Manager **ret) {
476 _cleanup_(manager_unrefp) Manager *m = NULL;
477 int r;
478
479 assert(ret);
480
481 m = new0(Manager, 1);
482 if (!m)
483 return -ENOMEM;
484
485 r = sd_event_default(&m->event);
486 if (r < 0)
487 return r;
488
489 sd_event_set_watchdog(m->event, true);
490
491 r = sd_bus_default_system(&m->bus);
492 if (r < 0)
493 return r;
494
495 *ret = m;
496 m = NULL;
497
498 return 0;
499}
500
501static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
502 Transfer *t;
503 Iterator i;
504
505 assert(m);
506 assert(type >= 0);
507 assert(type < _TRANSFER_TYPE_MAX);
508
509 HASHMAP_FOREACH(t, m->transfers, i) {
510
511 if (t->type == type &&
512 streq_ptr(t->remote, remote) &&
513 streq_ptr(t->dkr_index_url, dkr_index_url))
514 return t;
515 }
516
517 return NULL;
518}
519
520static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
521 _cleanup_(transfer_unrefp) Transfer *t = NULL;
522 const char *remote, *local, *verify, *object;
523 Manager *m = userdata;
524 ImportVerify v;
525 TransferType type;
526 int force, r;
527 uint32_t id;
528
529 assert(bus);
530 assert(msg);
531 assert(m);
532
533 r = bus_verify_polkit_async(
534 msg,
535 CAP_SYS_ADMIN,
536 "org.freedesktop.import1.pull",
537 false,
538 &m->polkit_registry,
539 error);
540 if (r < 0)
541 return r;
542 if (r == 0)
543 return 1; /* Will call us back */
544
545 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
546 if (r < 0)
547 return r;
548
549 if (!http_url_is_valid(remote))
550 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
551
552 if (isempty(local))
553 local = NULL;
554 else if (!machine_name_is_valid(local))
555 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
556
557 if (isempty(verify))
558 v = IMPORT_VERIFY_SIGNATURE;
559 else
560 v = import_verify_from_string(verify);
561 if (v < 0)
562 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
563
564 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
565
566 if (manager_find(m, type, NULL, remote))
567 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
568
569 r = transfer_new(m, &t);
570 if (r < 0)
571 return r;
572
573 t->type = type;
574 t->verify = v;
575 t->force_local = force;
576
577 t->remote = strdup(remote);
578 if (!t->remote)
579 return -ENOMEM;
580
581 t->local = strdup(local);
582 if (!t->local)
583 return -ENOMEM;
584
585 r = transfer_start(t);
586 if (r < 0)
587 return r;
588
589 object = t->object_path;
590 id = t->id;
591 t = NULL;
592
593 return sd_bus_reply_method_return(msg, "uo", id, object);
594}
595
596static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
597 _cleanup_(transfer_unrefp) Transfer *t = NULL;
598 const char *index_url, *remote, *tag, *local, *verify, *object;
599 Manager *m = userdata;
600 ImportVerify v;
601 int force, r;
602 uint32_t id;
603
604 assert(bus);
605 assert(msg);
606 assert(m);
607
608 r = bus_verify_polkit_async(
609 msg,
610 CAP_SYS_ADMIN,
611 "org.freedesktop.import1.pull",
612 false,
613 &m->polkit_registry,
614 error);
615 if (r < 0)
616 return r;
617 if (r == 0)
618 return 1; /* Will call us back */
619
620 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
621 if (r < 0)
622 return r;
623
624 if (isempty(index_url))
625 index_url = DEFAULT_DKR_INDEX_URL;
626 if (!index_url)
627 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
628 if (!http_url_is_valid(index_url))
629 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
630
631 if (!dkr_name_is_valid(remote))
632 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
633
634 if (isempty(tag))
635 tag = "latest";
636 else if (!dkr_tag_is_valid(tag))
637 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
638
639 if (isempty(local))
640 local = NULL;
641 else if (!machine_name_is_valid(local))
642 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
643
644 if (isempty(verify))
645 v = IMPORT_VERIFY_SIGNATURE;
646 else
647 v = import_verify_from_string(verify);
648 if (v < 0)
649 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
650
651 if (v != IMPORT_VERIFY_NO)
652 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
653
654 if (manager_find(m, TRANSFER_DKR, index_url, remote))
655 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
656
657 r = transfer_new(m, &t);
658 if (r < 0)
659 return r;
660
661 t->type = TRANSFER_DKR;
662 t->verify = v;
663 t->force_local = force;
664
665 t->dkr_index_url = strdup(index_url);
666 if (!t->dkr_index_url)
667 return -ENOMEM;
668
669 t->remote = strjoin(remote, ":", tag, NULL);
670 if (!t->remote)
671 return -ENOMEM;
672
673 t->local = strdup(local);
674 if (!t->local)
675 return -ENOMEM;
676
677 r = transfer_start(t);
678 if (r < 0)
679 return r;
680
681 object = t->object_path;
682 id = t->id;
683 t = NULL;
684
685 return sd_bus_reply_method_return(msg, "uo", id, object);
686}
687
688static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
689 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
690 Manager *m = userdata;
691 Transfer *t;
692 Iterator i;
693 int r;
694
695 assert(bus);
696 assert(msg);
697 assert(m);
698
699 r = sd_bus_message_new_method_return(msg, &reply);
700 if (r < 0)
701 return r;
702
703 r = sd_bus_message_open_container(reply, 'a', "(ussso)");
704 if (r < 0)
705 return r;
706
707 HASHMAP_FOREACH(t, m->transfers, i) {
708
709 r = sd_bus_message_append(
710 reply,
711 "(ussso)",
712 t->id,
713 transfer_type_to_string(t->type),
714 t->remote,
715 t->local,
716 t->object_path);
717 if (r < 0)
718 return r;
719 }
720
721 r = sd_bus_message_close_container(reply);
722 if (r < 0)
723 return r;
724
725 return sd_bus_send(bus, reply, NULL);
726}
727
728static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
729 Transfer *t = userdata;
730 int r;
731
732 assert(bus);
733 assert(msg);
734 assert(t);
735
736 r = bus_verify_polkit_async(
737 msg,
738 CAP_SYS_ADMIN,
739 "org.freedesktop.import1.pull",
740 false,
741 &t->manager->polkit_registry,
742 error);
743 if (r < 0)
744 return r;
745 if (r == 0)
746 return 1; /* Will call us back */
747
748 r = transfer_cancel(t);
749 if (r < 0)
750 return r;
751
752 return sd_bus_reply_method_return(msg, NULL);
753}
754
755static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
756 Manager *m = userdata;
757 Transfer *t;
758 uint32_t id;
759 int r;
760
761 assert(bus);
762 assert(msg);
763 assert(m);
764
765 r = bus_verify_polkit_async(
766 msg,
767 CAP_SYS_ADMIN,
768 "org.freedesktop.import1.pull",
769 false,
770 &m->polkit_registry,
771 error);
772 if (r < 0)
773 return r;
774 if (r == 0)
775 return 1; /* Will call us back */
776
777 r = sd_bus_message_read(msg, "u", &id);
778 if (r < 0)
779 return r;
780 if (id <= 0)
781 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
782
783 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
784 if (!t)
785 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "Not transfer by id %" PRIu32, id);
786
787 r = transfer_cancel(t);
788 if (r < 0)
789 return r;
790
791 return sd_bus_reply_method_return(msg, NULL);
792}
793
794static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
795static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
796
797static const sd_bus_vtable transfer_vtable[] = {
798 SD_BUS_VTABLE_START(0),
799 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
800 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
801 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
802 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
803 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
804 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
805 SD_BUS_SIGNAL("LogMessage", "us", 0),
806 SD_BUS_VTABLE_END,
807};
808
809static const sd_bus_vtable manager_vtable[] = {
810 SD_BUS_VTABLE_START(0),
811 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
812 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
813 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
814 SD_BUS_METHOD("ListTransfers", NULL, "a(ussso)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
815 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
816 SD_BUS_SIGNAL("TransferNew", "uo", 0),
817 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
818 SD_BUS_VTABLE_END,
819};
820
821static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
822 Manager *m = userdata;
823 Transfer *t;
824 const char *p;
825 uint32_t id;
826 int r;
827
828 assert(bus);
829 assert(path);
830 assert(interface);
831 assert(found);
832 assert(m);
833
834 p = startswith(path, "/org/freedesktop/import1/transfer/_");
835 if (!p)
836 return 0;
837
838 r = safe_atou32(p, &id);
839 if (r < 0 || id == 0)
840 return 0;
841
842 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
843 if (!t)
844 return 0;
845
846 *found = t;
847 return 1;
848}
849
850static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
851 _cleanup_strv_free_ char **l = NULL;
852 Manager *m = userdata;
853 Transfer *t;
854 unsigned k = 0;
855 Iterator i;
856
857 l = new0(char*, hashmap_size(m->transfers) + 1);
858 if (!l)
859 return -ENOMEM;
860
861 HASHMAP_FOREACH(t, m->transfers, i) {
862
863 l[k] = strdup(t->object_path);
864 if (!l[k])
865 return -ENOMEM;
866
867 k++;
868 }
869
870 *nodes = l;
871 l = NULL;
872
873 return 1;
874}
875
876static int manager_add_bus_objects(Manager *m) {
877 int r;
878
879 assert(m);
880
881 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
882 if (r < 0)
883 return log_error_errno(r, "Failed to register object: %m");
884
885 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
886 if (r < 0)
887 return log_error_errno(r, "Failed to register object: %m");
888
889 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
890 if (r < 0)
891 return log_error_errno(r, "Failed to add transfer enumerator: %m");
892
893 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
894 if (r < 0)
895 return log_error_errno(r, "Failed to register name: %m");
896
897 r = sd_bus_attach_event(m->bus, m->event, 0);
898 if (r < 0)
899 return log_error_errno(r, "Failed to attach bus to event loop: %m");
900
901 return 0;
902}
903
904static bool manager_check_idle(void *userdata) {
905 Manager *m = userdata;
906
907 return hashmap_isempty(m->transfers);
908}
909
910static int manager_run(Manager *m) {
911 assert(m);
912
913 return bus_event_loop_with_idle(
914 m->event,
915 m->bus,
916 "org.freedesktop.import1",
917 DEFAULT_EXIT_USEC,
918 manager_check_idle,
919 m);
920}
921
922int main(int argc, char *argv[]) {
923 _cleanup_(manager_unrefp) Manager *m = NULL;
924 int r;
925
926 log_set_target(LOG_TARGET_AUTO);
927 log_parse_environment();
928 log_open();
929
930 umask(0022);
931
932 if (argc != 1) {
933 log_error("This program takes no arguments.");
934 r = -EINVAL;
935 goto finish;
936 }
937
938 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
939
940 r = manager_new(&m);
941 if (r < 0) {
942 log_error_errno(r, "Failed to allocate manager object: %m");
943 goto finish;
944 }
945
946 r = manager_add_bus_objects(m);
947 if (r < 0)
948 goto finish;
949
950 r = manager_run(m);
951 if (r < 0) {
952 log_error_errno(r, "Failed to run event loop: %m");
953 goto finish;
954 }
955
956finish:
957 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
958}