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