chiark / gitweb /
import: split out compression logic, so that we can share it with between import...
[elogind.git] / src / import / importd.c
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 "socket-util.h"
31 #include "mkdir.h"
32 #include "def.h"
33 #include "missing.h"
34 #include "machine-pool.h"
35 #include "path-util.h"
36 #include "import-util.h"
37
38 typedef struct Transfer Transfer;
39 typedef struct Manager Manager;
40
41 typedef enum TransferType {
42         TRANSFER_TAR,
43         TRANSFER_RAW,
44         TRANSFER_DKR,
45         _TRANSFER_TYPE_MAX,
46         _TRANSFER_TYPE_INVALID = -1,
47 } TransferType;
48
49 struct Transfer {
50         Manager *manager;
51
52         uint32_t id;
53         char *object_path;
54
55         TransferType type;
56         ImportVerify verify;
57
58         char *remote;
59         char *local;
60         bool force_local;
61
62         char *dkr_index_url;
63
64         pid_t pid;
65
66         int log_fd;
67
68         char log_message[LINE_MAX];
69         size_t log_message_size;
70
71         sd_event_source *pid_event_source;
72         sd_event_source *log_event_source;
73
74         unsigned n_canceled;
75         unsigned progress_percent;
76 };
77
78 struct Manager {
79         sd_event *event;
80         sd_bus *bus;
81
82         uint32_t current_transfer_id;
83         Hashmap *transfers;
84
85         Hashmap *polkit_registry;
86
87         int notify_fd;
88
89         sd_event_source *notify_event_source;
90 };
91
92 #define TRANSFERS_MAX 64
93
94 static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
95         [TRANSFER_TAR] = "tar",
96         [TRANSFER_RAW] = "raw",
97         [TRANSFER_DKR] = "dkr",
98 };
99
100 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
101
102 static Transfer *transfer_unref(Transfer *t) {
103         if (!t)
104                 return NULL;
105
106         if (t->manager)
107                 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
108
109         sd_event_source_unref(t->pid_event_source);
110         sd_event_source_unref(t->log_event_source);
111
112         free(t->remote);
113         free(t->local);
114         free(t->dkr_index_url);
115         free(t->object_path);
116
117         if (t->pid > 0) {
118                 (void) kill_and_sigcont(t->pid, SIGKILL);
119                 (void) wait_for_terminate(t->pid, NULL);
120         }
121
122         safe_close(t->log_fd);
123
124         free(t);
125         return NULL;
126 }
127
128 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
129
130 static int transfer_new(Manager *m, Transfer **ret) {
131         _cleanup_(transfer_unrefp) Transfer *t = NULL;
132         uint32_t id;
133         int r;
134
135         assert(m);
136         assert(ret);
137
138         if (hashmap_size(m->transfers) >= TRANSFERS_MAX)
139                 return -E2BIG;
140
141         r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops);
142         if (r < 0)
143                 return r;
144
145         t = new0(Transfer, 1);
146         if (!t)
147                 return -ENOMEM;
148
149         t->type = _TRANSFER_TYPE_INVALID;
150         t->log_fd = -1;
151
152         id = m->current_transfer_id + 1;
153
154         if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0)
155                 return -ENOMEM;
156
157         r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t);
158         if (r < 0)
159                 return r;
160
161         m->current_transfer_id = id;
162
163         t->manager = m;
164         t->id = id;
165
166         *ret = t;
167         t = NULL;
168
169         return 0;
170 }
171
172 static void transfer_send_log_line(Transfer *t, const char *line) {
173         int r, priority = LOG_INFO;
174
175         assert(t);
176         assert(line);
177
178         syslog_parse_priority(&line, &priority, true);
179
180         log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
181
182         r = sd_bus_emit_signal(
183                         t->manager->bus,
184                         t->object_path,
185                         "org.freedesktop.import1.Transfer",
186                         "LogMessage",
187                         "us",
188                         priority,
189                         line);
190         if (r < 0)
191                 log_error_errno(r, "Cannot emit message: %m");
192  }
193
194 static void transfer_send_logs(Transfer *t, bool flush) {
195         assert(t);
196
197         /* Try to send out all log messages, if we can. But if we
198          * can't we remove the messages from the buffer, but don't
199          * fail */
200
201         while (t->log_message_size > 0) {
202                 _cleanup_free_ char *n = NULL;
203                 char *e;
204
205                 if (t->log_message_size >= sizeof(t->log_message))
206                         e = t->log_message + sizeof(t->log_message);
207                 else {
208                         char *a, *b;
209
210                         a = memchr(t->log_message, 0, t->log_message_size);
211                         b = memchr(t->log_message, '\n', t->log_message_size);
212
213                         if (a && b)
214                                 e = a < b ? a : b;
215                         else if (a)
216                                 e = a;
217                         else
218                                 e = b;
219                 }
220
221                 if (!e) {
222                         if (!flush)
223                                 return;
224
225                         e = t->log_message + t->log_message_size;
226                 }
227
228                 n = strndup(t->log_message, e - t->log_message);
229
230                 /* Skip over NUL and newlines */
231                 while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n'))
232                         e++;
233
234                 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
235                 t->log_message_size -= e - t->log_message;
236
237                 if (!n) {
238                         log_oom();
239                         continue;
240                 }
241
242                 if (isempty(n))
243                         continue;
244
245                 transfer_send_log_line(t, n);
246         }
247 }
248
249 static int transfer_finalize(Transfer *t, bool success) {
250         int r;
251
252         assert(t);
253
254         transfer_send_logs(t, true);
255
256         r = sd_bus_emit_signal(
257                         t->manager->bus,
258                         "/org/freedesktop/import1",
259                         "org.freedesktop.import1.Manager",
260                         "TransferRemoved",
261                         "uos",
262                         t->id,
263                         t->object_path,
264                         success ? "done" :
265                         t->n_canceled > 0 ? "canceled" : "failed");
266
267         if (r < 0)
268                 log_error_errno(r, "Cannot emit message: %m");
269
270         transfer_unref(t);
271         return 0;
272 }
273
274 static int transfer_cancel(Transfer *t) {
275         int r;
276
277         assert(t);
278
279         r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
280         if (r < 0)
281                 return r;
282
283         t->n_canceled++;
284         return 0;
285 }
286
287 static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
288         Transfer *t = userdata;
289         bool success = false;
290
291         assert(s);
292         assert(t);
293
294         if (si->si_code == CLD_EXITED) {
295                 if (si->si_status != 0)
296                         log_error("Import process failed with exit code %i.", si->si_status);
297                 else {
298                         log_debug("Import process succeeded.");
299                         success = true;
300                 }
301
302         } else if (si->si_code == CLD_KILLED ||
303                    si->si_code == CLD_DUMPED)
304
305                 log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
306         else
307                 log_error("Import process failed due to unknown reason.");
308
309         t->pid = 0;
310
311         return transfer_finalize(t, success);
312 }
313
314 static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
315         Transfer *t = userdata;
316         ssize_t l;
317
318         assert(s);
319         assert(t);
320
321         l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
322         if (l <= 0) {
323                 /* EOF/read error. We just close the pipe here, and
324                  * close the watch, waiting for the SIGCHLD to arrive,
325                  * before we do anything else. */
326
327                 if (l < 0)
328                         log_error_errno(errno, "Failed to read log message: %m");
329
330                 t->log_event_source = sd_event_source_unref(t->log_event_source);
331                 return 0;
332         }
333
334         t->log_message_size += l;
335
336         transfer_send_logs(t, false);
337
338         return 0;
339 }
340
341 static int transfer_start(Transfer *t) {
342         _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
343         int r;
344
345         assert(t);
346         assert(t->pid <= 0);
347
348         if (pipe2(pipefd, O_CLOEXEC) < 0)
349                 return -errno;
350
351         t->pid = fork();
352         if (t->pid < 0)
353                 return -errno;
354         if (t->pid == 0) {
355                 const char *cmd[] = {
356                         "systemd-pull",
357                         transfer_type_to_string(t->type),
358                         "--verify",
359                         NULL, /* verify argument */
360                         NULL, /* maybe --force */
361                         NULL, /* maybe --dkr-index-url */
362                         NULL, /* the actual URL */
363                         NULL, /* remote */
364                         NULL, /* local */
365                         NULL
366                 };
367                 int null_fd;
368                 unsigned k = 3;
369
370                 /* Child */
371
372                 reset_all_signal_handlers();
373                 reset_signal_mask();
374                 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
375
376                 pipefd[0] = safe_close(pipefd[0]);
377
378                 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
379                         log_error_errno(errno, "Failed to dup2() fd: %m");
380                         _exit(EXIT_FAILURE);
381                 }
382
383                 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
384                         log_error_errno(errno, "Failed to dup2() fd: %m");
385                         _exit(EXIT_FAILURE);
386                 }
387
388                 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
389                         pipefd[1] = safe_close(pipefd[1]);
390
391                 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
392                 if (null_fd < 0) {
393                         log_error_errno(errno, "Failed to open /dev/null: %m");
394                         _exit(EXIT_FAILURE);
395                 }
396
397                 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
398                         log_error_errno(errno, "Failed to dup2() fd: %m");
399                         _exit(EXIT_FAILURE);
400                 }
401
402                 if (null_fd != STDIN_FILENO)
403                         safe_close(null_fd);
404
405                 fd_cloexec(STDIN_FILENO, false);
406                 fd_cloexec(STDOUT_FILENO, false);
407                 fd_cloexec(STDERR_FILENO, false);
408
409                 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
410                 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
411
412                 cmd[k++] = import_verify_to_string(t->verify);
413                 if (t->force_local)
414                         cmd[k++] = "--force";
415
416                 if (t->dkr_index_url) {
417                         cmd[k++] = "--dkr-index-url";
418                         cmd[k++] = t->dkr_index_url;
419                 }
420
421                 cmd[k++] = t->remote;
422                 if (t->local)
423                         cmd[k++] = t->local;
424                 cmd[k] = NULL;
425
426                 execv(SYSTEMD_PULL_PATH, (char * const *) cmd);
427                 log_error_errno(errno, "Failed to execute import tool: %m");
428                 _exit(EXIT_FAILURE);
429         }
430
431         pipefd[1] = safe_close(pipefd[1]);
432         t->log_fd = pipefd[0];
433         pipefd[0] = -1;
434
435         r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
436         if (r < 0)
437                 return r;
438
439         r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
440         if (r < 0)
441                 return r;
442
443         /* Make sure always process logging before SIGCHLD */
444         r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
445         if (r < 0)
446                 return r;
447
448         r = sd_bus_emit_signal(
449                         t->manager->bus,
450                         "/org/freedesktop/import1",
451                         "org.freedesktop.import1.Manager",
452                         "TransferNew",
453                         "uo",
454                         t->id,
455                         t->object_path);
456         if (r < 0)
457                 return r;
458
459         return 0;
460 }
461
462 static Manager *manager_unref(Manager *m) {
463         Transfer *t;
464
465         if (!m)
466                 return NULL;
467
468         sd_event_source_unref(m->notify_event_source);
469         safe_close(m->notify_fd);
470
471         while ((t = hashmap_first(m->transfers)))
472                 transfer_unref(t);
473
474         hashmap_free(m->transfers);
475
476         bus_verify_polkit_async_registry_free(m->polkit_registry);
477
478         sd_bus_close(m->bus);
479         sd_bus_unref(m->bus);
480         sd_event_unref(m->event);
481
482         free(m);
483         return NULL;
484 }
485
486 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
487
488 static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
489
490         char buf[NOTIFY_BUFFER_MAX+1];
491         struct iovec iovec = {
492                 .iov_base = buf,
493                 .iov_len = sizeof(buf)-1,
494         };
495         union {
496                 struct cmsghdr cmsghdr;
497                 uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
498                             CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
499         } control = {};
500         struct msghdr msghdr = {
501                 .msg_iov = &iovec,
502                 .msg_iovlen = 1,
503                 .msg_control = &control,
504                 .msg_controllen = sizeof(control),
505         };
506         struct ucred *ucred = NULL;
507         Manager *m = userdata;
508         struct cmsghdr *cmsg;
509         unsigned percent;
510         char *p, *e;
511         Transfer *t;
512         Iterator i;
513         ssize_t n;
514         int r;
515
516         n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
517         if (n < 0) {
518                 if (errno == EAGAIN || errno == EINTR)
519                         return 0;
520
521                 return -errno;
522         }
523
524         cmsg_close_all(&msghdr);
525
526         for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
527                 if (cmsg->cmsg_level == SOL_SOCKET &&
528                            cmsg->cmsg_type == SCM_CREDENTIALS &&
529                            cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
530
531                         ucred = (struct ucred*) CMSG_DATA(cmsg);
532                 }
533         }
534
535         if (msghdr.msg_flags & MSG_TRUNC) {
536                 log_warning("Got overly long notification datagram, ignoring.");
537                 return 0;
538         }
539
540         if (!ucred || ucred->pid <= 0) {
541                 log_warning("Got notification datagram lacking credential information, ignoring.");
542                 return 0;
543         }
544
545         HASHMAP_FOREACH(t, m->transfers, i)
546                 if (ucred->pid == t->pid)
547                         break;
548
549         if (!t) {
550                 log_warning("Got notification datagram from unexpected peer, ignoring.");
551                 return 0;
552         }
553
554         buf[n] = 0;
555
556         p = startswith(buf, "X_IMPORT_PROGRESS=");
557         if (!p) {
558                 p = strstr(buf, "\nX_IMPORT_PROGRESS=");
559                 if (!p)
560                         return 0;
561
562                 p += 19;
563         }
564
565         e = strchrnul(p, '\n');
566         *e = 0;
567
568         r = safe_atou(p, &percent);
569         if (r < 0 || percent > 100) {
570                 log_warning("Got invalid percent value, ignoring.");
571                 return 0;
572         }
573
574         t->progress_percent = percent;
575
576         log_debug("Got percentage from client: %u%%", percent);
577         return 0;
578 }
579
580 static int manager_new(Manager **ret) {
581         _cleanup_(manager_unrefp) Manager *m = NULL;
582         static const union sockaddr_union sa = {
583                 .un.sun_family = AF_UNIX,
584                 .un.sun_path = "/run/systemd/import/notify",
585         };
586         static const int one = 1;
587         int r;
588
589         assert(ret);
590
591         m = new0(Manager, 1);
592         if (!m)
593                 return -ENOMEM;
594
595         r = sd_event_default(&m->event);
596         if (r < 0)
597                 return r;
598
599         sd_event_set_watchdog(m->event, true);
600
601         r = sd_bus_default_system(&m->bus);
602         if (r < 0)
603                 return r;
604
605         m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
606         if (m->notify_fd < 0)
607                 return -errno;
608
609         (void) mkdir_parents_label(sa.un.sun_path, 0755);
610         (void) unlink(sa.un.sun_path);
611
612         if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
613                 return -errno;
614
615         if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
616                 return -errno;
617
618         r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
619         if (r < 0)
620                 return r;
621
622         *ret = m;
623         m = NULL;
624
625         return 0;
626 }
627
628 static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
629         Transfer *t;
630         Iterator i;
631
632         assert(m);
633         assert(type >= 0);
634         assert(type < _TRANSFER_TYPE_MAX);
635
636         HASHMAP_FOREACH(t, m->transfers, i) {
637
638                 if (t->type == type &&
639                     streq_ptr(t->remote, remote) &&
640                     streq_ptr(t->dkr_index_url, dkr_index_url))
641                         return t;
642         }
643
644         return NULL;
645 }
646
647 static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
648         _cleanup_(transfer_unrefp) Transfer *t = NULL;
649         const char *remote, *local, *verify, *object;
650         Manager *m = userdata;
651         ImportVerify v;
652         TransferType type;
653         int force, r;
654         uint32_t id;
655
656         assert(bus);
657         assert(msg);
658         assert(m);
659
660         r = bus_verify_polkit_async(
661                         msg,
662                         CAP_SYS_ADMIN,
663                         "org.freedesktop.import1.pull",
664                         false,
665                         UID_INVALID,
666                         &m->polkit_registry,
667                         error);
668         if (r < 0)
669                 return r;
670         if (r == 0)
671                 return 1; /* Will call us back */
672
673         r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
674         if (r < 0)
675                 return r;
676
677         if (!http_url_is_valid(remote))
678                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
679
680         if (isempty(local))
681                 local = NULL;
682         else if (!machine_name_is_valid(local))
683                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
684
685         if (isempty(verify))
686                 v = IMPORT_VERIFY_SIGNATURE;
687         else
688                 v = import_verify_from_string(verify);
689         if (v < 0)
690                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
691
692         r = setup_machine_directory((uint64_t) -1, error);
693         if (r < 0)
694                 return r;
695
696         type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
697
698         if (manager_find(m, type, NULL, remote))
699                 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
700
701         r = transfer_new(m, &t);
702         if (r < 0)
703                 return r;
704
705         t->type = type;
706         t->verify = v;
707         t->force_local = force;
708
709         t->remote = strdup(remote);
710         if (!t->remote)
711                 return -ENOMEM;
712
713         t->local = strdup(local);
714         if (!t->local)
715                 return -ENOMEM;
716
717         r = transfer_start(t);
718         if (r < 0)
719                 return r;
720
721         object = t->object_path;
722         id = t->id;
723         t = NULL;
724
725         return sd_bus_reply_method_return(msg, "uo", id, object);
726 }
727
728 static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
729         _cleanup_(transfer_unrefp) Transfer *t = NULL;
730         const char *index_url, *remote, *tag, *local, *verify, *object;
731         Manager *m = userdata;
732         ImportVerify v;
733         int force, r;
734         uint32_t id;
735
736         assert(bus);
737         assert(msg);
738         assert(m);
739
740         r = bus_verify_polkit_async(
741                         msg,
742                         CAP_SYS_ADMIN,
743                         "org.freedesktop.import1.pull",
744                         false,
745                         UID_INVALID,
746                         &m->polkit_registry,
747                         error);
748         if (r < 0)
749                 return r;
750         if (r == 0)
751                 return 1; /* Will call us back */
752
753         r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
754         if (r < 0)
755                 return r;
756
757         if (isempty(index_url))
758                 index_url = DEFAULT_DKR_INDEX_URL;
759         if (!index_url)
760                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
761         if (!http_url_is_valid(index_url))
762                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
763
764         if (!dkr_name_is_valid(remote))
765                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
766
767         if (isempty(tag))
768                 tag = "latest";
769         else if (!dkr_tag_is_valid(tag))
770                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
771
772         if (isempty(local))
773                 local = NULL;
774         else if (!machine_name_is_valid(local))
775                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
776
777         if (isempty(verify))
778                 v = IMPORT_VERIFY_SIGNATURE;
779         else
780                 v = import_verify_from_string(verify);
781         if (v < 0)
782                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
783
784         if (v != IMPORT_VERIFY_NO)
785                 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
786
787         r = setup_machine_directory((uint64_t) -1, error);
788         if (r < 0)
789                 return r;
790
791         if (manager_find(m, TRANSFER_DKR, index_url, remote))
792                 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
793
794         r = transfer_new(m, &t);
795         if (r < 0)
796                 return r;
797
798         t->type = TRANSFER_DKR;
799         t->verify = v;
800         t->force_local = force;
801
802         t->dkr_index_url = strdup(index_url);
803         if (!t->dkr_index_url)
804                 return -ENOMEM;
805
806         t->remote = strjoin(remote, ":", tag, NULL);
807         if (!t->remote)
808                 return -ENOMEM;
809
810         t->local = strdup(local);
811         if (!t->local)
812                 return -ENOMEM;
813
814         r = transfer_start(t);
815         if (r < 0)
816                 return r;
817
818         object = t->object_path;
819         id = t->id;
820         t = NULL;
821
822         return sd_bus_reply_method_return(msg, "uo", id, object);
823 }
824
825 static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
826         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
827         Manager *m = userdata;
828         Transfer *t;
829         Iterator i;
830         int r;
831
832         assert(bus);
833         assert(msg);
834         assert(m);
835
836         r = sd_bus_message_new_method_return(msg, &reply);
837         if (r < 0)
838                 return r;
839
840         r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
841         if (r < 0)
842                 return r;
843
844         HASHMAP_FOREACH(t, m->transfers, i) {
845
846                 r = sd_bus_message_append(
847                                 reply,
848                                 "(usssdo)",
849                                 t->id,
850                                 transfer_type_to_string(t->type),
851                                 t->remote,
852                                 t->local,
853                                 (double) t->progress_percent / 100.0,
854                                 t->object_path);
855                 if (r < 0)
856                         return r;
857         }
858
859         r = sd_bus_message_close_container(reply);
860         if (r < 0)
861                 return r;
862
863         return sd_bus_send(bus, reply, NULL);
864 }
865
866 static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
867         Transfer *t = userdata;
868         int r;
869
870         assert(bus);
871         assert(msg);
872         assert(t);
873
874         r = bus_verify_polkit_async(
875                         msg,
876                         CAP_SYS_ADMIN,
877                         "org.freedesktop.import1.pull",
878                         false,
879                         UID_INVALID,
880                         &t->manager->polkit_registry,
881                         error);
882         if (r < 0)
883                 return r;
884         if (r == 0)
885                 return 1; /* Will call us back */
886
887         r = transfer_cancel(t);
888         if (r < 0)
889                 return r;
890
891         return sd_bus_reply_method_return(msg, NULL);
892 }
893
894 static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
895         Manager *m = userdata;
896         Transfer *t;
897         uint32_t id;
898         int r;
899
900         assert(bus);
901         assert(msg);
902         assert(m);
903
904         r = bus_verify_polkit_async(
905                         msg,
906                         CAP_SYS_ADMIN,
907                         "org.freedesktop.import1.pull",
908                         false,
909                         UID_INVALID,
910                         &m->polkit_registry,
911                         error);
912         if (r < 0)
913                 return r;
914         if (r == 0)
915                 return 1; /* Will call us back */
916
917         r = sd_bus_message_read(msg, "u", &id);
918         if (r < 0)
919                 return r;
920         if (id <= 0)
921                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
922
923         t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
924         if (!t)
925                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
926
927         r = transfer_cancel(t);
928         if (r < 0)
929                 return r;
930
931         return sd_bus_reply_method_return(msg, NULL);
932 }
933
934 static int property_get_progress(
935                 sd_bus *bus,
936                 const char *path,
937                 const char *interface,
938                 const char *property,
939                 sd_bus_message *reply,
940                 void *userdata,
941                 sd_bus_error *error) {
942
943         Transfer *t = userdata;
944
945         assert(bus);
946         assert(reply);
947         assert(t);
948
949         return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
950 }
951
952 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
953 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
954
955 static const sd_bus_vtable transfer_vtable[] = {
956         SD_BUS_VTABLE_START(0),
957         SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
958         SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
959         SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
960         SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
961         SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
962         SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
963         SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
964         SD_BUS_SIGNAL("LogMessage", "us", 0),
965         SD_BUS_VTABLE_END,
966 };
967
968 static const sd_bus_vtable manager_vtable[] = {
969         SD_BUS_VTABLE_START(0),
970         SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
971         SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
972         SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
973         SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
974         SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
975         SD_BUS_SIGNAL("TransferNew", "uo", 0),
976         SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
977         SD_BUS_VTABLE_END,
978 };
979
980 static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
981         Manager *m = userdata;
982         Transfer *t;
983         const char *p;
984         uint32_t id;
985         int r;
986
987         assert(bus);
988         assert(path);
989         assert(interface);
990         assert(found);
991         assert(m);
992
993         p = startswith(path, "/org/freedesktop/import1/transfer/_");
994         if (!p)
995                 return 0;
996
997         r = safe_atou32(p, &id);
998         if (r < 0 || id == 0)
999                 return 0;
1000
1001         t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1002         if (!t)
1003                 return 0;
1004
1005         *found = t;
1006         return 1;
1007 }
1008
1009 static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1010         _cleanup_strv_free_ char **l = NULL;
1011         Manager *m = userdata;
1012         Transfer *t;
1013         unsigned k = 0;
1014         Iterator i;
1015
1016         l = new0(char*, hashmap_size(m->transfers) + 1);
1017         if (!l)
1018                 return -ENOMEM;
1019
1020         HASHMAP_FOREACH(t, m->transfers, i) {
1021
1022                 l[k] = strdup(t->object_path);
1023                 if (!l[k])
1024                         return -ENOMEM;
1025
1026                 k++;
1027         }
1028
1029         *nodes = l;
1030         l = NULL;
1031
1032         return 1;
1033 }
1034
1035 static int manager_add_bus_objects(Manager *m) {
1036         int r;
1037
1038         assert(m);
1039
1040         r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1041         if (r < 0)
1042                 return log_error_errno(r, "Failed to register object: %m");
1043
1044         r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1045         if (r < 0)
1046                 return log_error_errno(r, "Failed to register object: %m");
1047
1048         r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1049         if (r < 0)
1050                 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1051
1052         r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1053         if (r < 0)
1054                 return log_error_errno(r, "Failed to register name: %m");
1055
1056         r = sd_bus_attach_event(m->bus, m->event, 0);
1057         if (r < 0)
1058                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1059
1060         return 0;
1061 }
1062
1063 static bool manager_check_idle(void *userdata) {
1064         Manager *m = userdata;
1065
1066         return hashmap_isempty(m->transfers);
1067 }
1068
1069 static int manager_run(Manager *m) {
1070         assert(m);
1071
1072         return bus_event_loop_with_idle(
1073                         m->event,
1074                         m->bus,
1075                         "org.freedesktop.import1",
1076                         DEFAULT_EXIT_USEC,
1077                         manager_check_idle,
1078                         m);
1079 }
1080
1081 int main(int argc, char *argv[]) {
1082         _cleanup_(manager_unrefp) Manager *m = NULL;
1083         int r;
1084
1085         log_set_target(LOG_TARGET_AUTO);
1086         log_parse_environment();
1087         log_open();
1088
1089         umask(0022);
1090
1091         if (argc != 1) {
1092                 log_error("This program takes no arguments.");
1093                 r = -EINVAL;
1094                 goto finish;
1095         }
1096
1097         assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
1098
1099         r = manager_new(&m);
1100         if (r < 0) {
1101                 log_error_errno(r, "Failed to allocate manager object: %m");
1102                 goto finish;
1103         }
1104
1105         r = manager_add_bus_objects(m);
1106         if (r < 0)
1107                 goto finish;
1108
1109         r = manager_run(m);
1110         if (r < 0) {
1111                 log_error_errno(r, "Failed to run event loop: %m");
1112                 goto finish;
1113         }
1114
1115 finish:
1116         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1117 }