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