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