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