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