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