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