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