]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/importd.c
importd: fix bus policy
[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
b9a5f858 89DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
3d7415f4
LP
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
09d46cfd 169 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
3d7415f4
LP
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[] = {
ff828763 345 "systemd-pull",
aa9bd499 346 transfer_type_to_string(t->type),
3d7415f4
LP
347 "--verify",
348 NULL, /* verify argument */
349 NULL, /* maybe --force */
350 NULL, /* maybe --dkr-index-url */
351 NULL, /* the actual URL */
352 NULL, /* remote */
353 NULL, /* local */
354 NULL
355 };
356 int null_fd;
357 unsigned k = 3;
358
359 /* Child */
360
361 reset_all_signal_handlers();
362 reset_signal_mask();
363 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
364
365 pipefd[0] = safe_close(pipefd[0]);
366
367 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
368 log_error_errno(errno, "Failed to dup2() fd: %m");
369 _exit(EXIT_FAILURE);
370 }
371
372 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
373 log_error_errno(errno, "Failed to dup2() fd: %m");
374 _exit(EXIT_FAILURE);
375 }
376
377 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
378 pipefd[1] = safe_close(pipefd[1]);
379
380 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
381 if (null_fd < 0) {
382 log_error_errno(errno, "Failed to open /dev/null: %m");
383 _exit(EXIT_FAILURE);
384 }
385
386 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
387 log_error_errno(errno, "Failed to dup2() fd: %m");
388 _exit(EXIT_FAILURE);
389 }
390
391 if (null_fd != STDIN_FILENO)
392 safe_close(null_fd);
393
394 fd_cloexec(STDIN_FILENO, false);
395 fd_cloexec(STDOUT_FILENO, false);
396 fd_cloexec(STDERR_FILENO, false);
397
398 putenv((char*) "SYSTEMD_LOG_TARGET=console-prefixed");
399
400 cmd[k++] = import_verify_to_string(t->verify);
401 if (t->force_local)
402 cmd[k++] = "--force";
403
404 if (t->dkr_index_url) {
405 cmd[k++] = "--dkr-index-url";
406 cmd[k++] = t->dkr_index_url;
407 }
408
409 cmd[k++] = t->remote;
410 if (t->local)
411 cmd[k++] = t->local;
412 cmd[k] = NULL;
413
ff828763 414 execv(SYSTEMD_PULL_PATH, (char * const *) cmd);
3d7415f4
LP
415 log_error_errno(errno, "Failed to execute import tool: %m");
416 _exit(EXIT_FAILURE);
417 }
418
419 pipefd[1] = safe_close(pipefd[1]);
420 t->log_fd = pipefd[0];
421 pipefd[0] = -1;
422
423 r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
424 if (r < 0)
425 return r;
426
427 r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
428 if (r < 0)
429 return r;
430
431 /* Make sure always process logging before SIGCHLD */
432 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
433 if (r < 0)
434 return r;
435
436 r = sd_bus_emit_signal(
437 t->manager->bus,
438 "/org/freedesktop/import1",
439 "org.freedesktop.import1.Manager",
440 "TransferNew",
441 "uo",
442 t->id,
443 t->object_path);
444 if (r < 0)
445 return r;
446
447 return 0;
448}
449
450static Manager *manager_unref(Manager *m) {
451 Transfer *t;
452
453 if (!m)
454 return NULL;
455
456 while ((t = hashmap_first(m->transfers)))
457 transfer_unref(t);
458
459 hashmap_free(m->transfers);
460
461 bus_verify_polkit_async_registry_free(m->polkit_registry);
462
463 sd_bus_close(m->bus);
464 sd_bus_unref(m->bus);
465 sd_event_unref(m->event);
466
467 free(m);
468 return NULL;
469}
470
471DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
472
473static int manager_new(Manager **ret) {
474 _cleanup_(manager_unrefp) Manager *m = NULL;
475 int r;
476
477 assert(ret);
478
479 m = new0(Manager, 1);
480 if (!m)
481 return -ENOMEM;
482
483 r = sd_event_default(&m->event);
484 if (r < 0)
485 return r;
486
487 sd_event_set_watchdog(m->event, true);
488
489 r = sd_bus_default_system(&m->bus);
490 if (r < 0)
491 return r;
492
493 *ret = m;
494 m = NULL;
495
496 return 0;
497}
498
499static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
500 Transfer *t;
501 Iterator i;
502
503 assert(m);
504 assert(type >= 0);
505 assert(type < _TRANSFER_TYPE_MAX);
506
507 HASHMAP_FOREACH(t, m->transfers, i) {
508
509 if (t->type == type &&
510 streq_ptr(t->remote, remote) &&
511 streq_ptr(t->dkr_index_url, dkr_index_url))
512 return t;
513 }
514
515 return NULL;
516}
517
518static 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;
522 ImportVerify v;
523 TransferType type;
524 int force, r;
525 uint32_t id;
526
527 assert(bus);
528 assert(msg);
529 assert(m);
530
531 r = bus_verify_polkit_async(
532 msg,
533 CAP_SYS_ADMIN,
534 "org.freedesktop.import1.pull",
535 false,
536 &m->polkit_registry,
537 error);
538 if (r < 0)
539 return r;
540 if (r == 0)
541 return 1; /* Will call us back */
542
543 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
544 if (r < 0)
545 return r;
546
547 if (!http_url_is_valid(remote))
548 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
549
550 if (isempty(local))
551 local = NULL;
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);
554
555 if (isempty(verify))
556 v = IMPORT_VERIFY_SIGNATURE;
557 else
558 v = import_verify_from_string(verify);
559 if (v < 0)
560 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
561
562 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
563
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);
566
567 r = transfer_new(m, &t);
568 if (r < 0)
569 return r;
570
571 t->type = type;
572 t->verify = v;
573 t->force_local = force;
574
575 t->remote = strdup(remote);
576 if (!t->remote)
577 return -ENOMEM;
578
579 t->local = strdup(local);
580 if (!t->local)
581 return -ENOMEM;
582
583 r = transfer_start(t);
584 if (r < 0)
585 return r;
586
587 object = t->object_path;
588 id = t->id;
589 t = NULL;
590
591 return sd_bus_reply_method_return(msg, "uo", id, object);
592}
593
594static 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;
598 ImportVerify v;
599 int force, r;
600 uint32_t id;
601
602 assert(bus);
603 assert(msg);
604 assert(m);
605
606 r = bus_verify_polkit_async(
607 msg,
608 CAP_SYS_ADMIN,
609 "org.freedesktop.import1.pull",
610 false,
611 &m->polkit_registry,
612 error);
613 if (r < 0)
614 return r;
615 if (r == 0)
616 return 1; /* Will call us back */
617
618 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
619 if (r < 0)
620 return r;
621
622 if (isempty(index_url))
623 index_url = DEFAULT_DKR_INDEX_URL;
624 if (!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);
628
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);
631
632 if (isempty(tag))
633 tag = "latest";
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);
636
637 if (isempty(local))
638 local = NULL;
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);
641
642 if (isempty(verify))
643 v = IMPORT_VERIFY_SIGNATURE;
644 else
645 v = import_verify_from_string(verify);
646 if (v < 0)
647 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
648
649 if (v != IMPORT_VERIFY_NO)
650 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
651
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);
654
655 r = transfer_new(m, &t);
656 if (r < 0)
657 return r;
658
659 t->type = TRANSFER_DKR;
660 t->verify = v;
661 t->force_local = force;
662
663 t->dkr_index_url = strdup(index_url);
664 if (!t->dkr_index_url)
665 return -ENOMEM;
666
667 t->remote = strjoin(remote, ":", tag, NULL);
668 if (!t->remote)
669 return -ENOMEM;
670
671 t->local = strdup(local);
672 if (!t->local)
673 return -ENOMEM;
674
675 r = transfer_start(t);
676 if (r < 0)
677 return r;
678
679 object = t->object_path;
680 id = t->id;
681 t = NULL;
682
683 return sd_bus_reply_method_return(msg, "uo", id, object);
684}
685
686static 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;
689 Transfer *t;
690 Iterator i;
691 int r;
692
693 assert(bus);
694 assert(msg);
695 assert(m);
696
697 r = sd_bus_message_new_method_return(msg, &reply);
698 if (r < 0)
699 return r;
700
701 r = sd_bus_message_open_container(reply, 'a', "(ussso)");
702 if (r < 0)
703 return r;
704
705 HASHMAP_FOREACH(t, m->transfers, i) {
706
707 r = sd_bus_message_append(
708 reply,
709 "(ussso)",
710 t->id,
711 transfer_type_to_string(t->type),
712 t->remote,
713 t->local,
714 t->object_path);
715 if (r < 0)
716 return r;
717 }
718
719 r = sd_bus_message_close_container(reply);
720 if (r < 0)
721 return r;
722
723 return sd_bus_send(bus, reply, NULL);
724}
725
726static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
727 Transfer *t = userdata;
728 int r;
729
730 assert(bus);
731 assert(msg);
732 assert(t);
733
734 r = bus_verify_polkit_async(
735 msg,
736 CAP_SYS_ADMIN,
737 "org.freedesktop.import1.pull",
738 false,
739 &t->manager->polkit_registry,
740 error);
741 if (r < 0)
742 return r;
743 if (r == 0)
744 return 1; /* Will call us back */
745
746 r = transfer_cancel(t);
747 if (r < 0)
748 return r;
749
750 return sd_bus_reply_method_return(msg, NULL);
751}
752
753static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
754 Manager *m = userdata;
755 Transfer *t;
756 uint32_t id;
757 int r;
758
759 assert(bus);
760 assert(msg);
761 assert(m);
762
763 r = bus_verify_polkit_async(
764 msg,
765 CAP_SYS_ADMIN,
766 "org.freedesktop.import1.pull",
767 false,
768 &m->polkit_registry,
769 error);
770 if (r < 0)
771 return r;
772 if (r == 0)
773 return 1; /* Will call us back */
774
775 r = sd_bus_message_read(msg, "u", &id);
776 if (r < 0)
777 return r;
778 if (id <= 0)
779 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
780
781 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
782 if (!t)
09d46cfd 783 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
3d7415f4
LP
784
785 r = transfer_cancel(t);
786 if (r < 0)
787 return r;
788
789 return sd_bus_reply_method_return(msg, NULL);
790}
791
792static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
793static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
794
795static 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),
804 SD_BUS_VTABLE_END,
805};
806
807static 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),
816 SD_BUS_VTABLE_END,
817};
818
819static 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;
821 Transfer *t;
822 const char *p;
823 uint32_t id;
824 int r;
825
826 assert(bus);
827 assert(path);
828 assert(interface);
829 assert(found);
830 assert(m);
831
832 p = startswith(path, "/org/freedesktop/import1/transfer/_");
833 if (!p)
834 return 0;
835
836 r = safe_atou32(p, &id);
837 if (r < 0 || id == 0)
838 return 0;
839
840 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
841 if (!t)
842 return 0;
843
844 *found = t;
845 return 1;
846}
847
848static 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;
851 Transfer *t;
852 unsigned k = 0;
853 Iterator i;
854
855 l = new0(char*, hashmap_size(m->transfers) + 1);
856 if (!l)
857 return -ENOMEM;
858
859 HASHMAP_FOREACH(t, m->transfers, i) {
860
861 l[k] = strdup(t->object_path);
862 if (!l[k])
863 return -ENOMEM;
864
865 k++;
866 }
867
868 *nodes = l;
869 l = NULL;
870
871 return 1;
872}
873
874static int manager_add_bus_objects(Manager *m) {
875 int r;
876
877 assert(m);
878
879 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
880 if (r < 0)
881 return log_error_errno(r, "Failed to register object: %m");
882
883 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
884 if (r < 0)
885 return log_error_errno(r, "Failed to register object: %m");
886
887 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
888 if (r < 0)
889 return log_error_errno(r, "Failed to add transfer enumerator: %m");
890
891 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
892 if (r < 0)
893 return log_error_errno(r, "Failed to register name: %m");
894
895 r = sd_bus_attach_event(m->bus, m->event, 0);
896 if (r < 0)
897 return log_error_errno(r, "Failed to attach bus to event loop: %m");
898
899 return 0;
900}
901
902static bool manager_check_idle(void *userdata) {
903 Manager *m = userdata;
904
905 return hashmap_isempty(m->transfers);
906}
907
908static int manager_run(Manager *m) {
909 assert(m);
910
911 return bus_event_loop_with_idle(
912 m->event,
913 m->bus,
914 "org.freedesktop.import1",
915 DEFAULT_EXIT_USEC,
916 manager_check_idle,
917 m);
918}
919
920int main(int argc, char *argv[]) {
921 _cleanup_(manager_unrefp) Manager *m = NULL;
922 int r;
923
924 log_set_target(LOG_TARGET_AUTO);
925 log_parse_environment();
926 log_open();
927
928 umask(0022);
929
930 if (argc != 1) {
931 log_error("This program takes no arguments.");
932 r = -EINVAL;
933 goto finish;
934 }
935
936 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
937
938 r = manager_new(&m);
939 if (r < 0) {
940 log_error_errno(r, "Failed to allocate manager object: %m");
941 goto finish;
942 }
943
944 r = manager_add_bus_objects(m);
945 if (r < 0)
946 goto finish;
947
948 r = manager_run(m);
949 if (r < 0) {
950 log_error_errno(r, "Failed to run event loop: %m");
951 goto finish;
952 }
953
954finish:
955 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
956}