chiark / gitweb /
manager: free dbus error when ignoring it
[elogind.git] / src / install.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/stat.h>
23 #include <stdio.h>
24 #include <getopt.h>
25 #include <errno.h>
26 #include <unistd.h>
27
28 #include "log.h"
29 #include "path-lookup.h"
30 #include "util.h"
31 #include "macro.h"
32 #include "strv.h"
33 #include "conf-parser.h"
34 #include "dbus-common.h"
35 #include "sd-daemon.h"
36
37 static bool arg_force = false;
38
39 static enum {
40         WHERE_SYSTEM,
41         WHERE_SESSION,
42         WHERE_GLOBAL,
43 } arg_where = WHERE_SYSTEM;
44
45 static enum {
46         ACTION_INVALID,
47         ACTION_ENABLE,
48         ACTION_DISABLE,
49         ACTION_TEST
50 } arg_action = ACTION_INVALID;
51
52 static enum {
53         REALIZE_NO,        /* Don't reload/start/stop or anything */
54         REALIZE_RELOAD,    /* Only reload daemon config, don't stop/start */
55         REALIZE_MINIMAL,   /* Only shutdown/restart if running. */
56         REALIZE_MAYBE,     /* Start if WantedBy= suggests */
57         REALIZE_YES        /* Start unconditionally */
58 } arg_realize = REALIZE_NO;
59
60 typedef struct {
61         char *name;
62         char *path;
63
64         char **aliases;
65         char **wanted_by;
66 } InstallInfo;
67
68 Hashmap *will_install = NULL, *have_installed = NULL;
69
70 static int help(void) {
71
72         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
73                "Install init system units.\n\n"
74                "  -h --help           Show this help\n"
75                "     --force          Override existing links\n"
76                "     --system         Install into system\n"
77                "     --session        Install into session\n"
78                "     --global         Install into all sessions\n"
79                "     --realize[=MODE] Start/stop/restart unit after installation\n"
80                "                      Takes 'no', 'minimal', 'maybe' or 'yes'\n\n"
81                "Commands:\n"
82                "  enable [NAME...]    Enable one or more units\n"
83                "  disable [NAME...]   Disable one or more units\n"
84                "  test [NAME...]      Test whether any of the specified units are enabled\n",
85                program_invocation_short_name);
86
87         return 0;
88 }
89
90 static int parse_argv(int argc, char *argv[]) {
91
92         enum {
93                 ARG_SESSION = 0x100,
94                 ARG_SYSTEM,
95                 ARG_GLOBAL,
96                 ARG_FORCE,
97                 ARG_REALIZE
98         };
99
100         static const struct option options[] = {
101                 { "help",      no_argument,       NULL, 'h'         },
102                 { "session",   no_argument,       NULL, ARG_SESSION },
103                 { "system",    no_argument,       NULL, ARG_SYSTEM  },
104                 { "global",    no_argument,       NULL, ARG_GLOBAL  },
105                 { "force",     no_argument,       NULL, ARG_FORCE   },
106                 { "realize",   optional_argument, NULL, ARG_REALIZE },
107                 { NULL,        0,                 NULL, 0           }
108         };
109
110         int c;
111
112         assert(argc >= 1);
113         assert(argv);
114
115         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
116
117                 switch (c) {
118
119                 case 'h':
120                         help();
121                         return 0;
122
123                 case ARG_SESSION:
124                         arg_where = WHERE_SESSION;
125                         break;
126
127                 case ARG_SYSTEM:
128                         arg_where = WHERE_SYSTEM;
129                         break;
130
131                 case ARG_GLOBAL:
132                         arg_where = WHERE_GLOBAL;
133                         break;
134
135                 case ARG_FORCE:
136                         arg_force = true;
137                         break;
138
139                 case ARG_REALIZE:
140
141                         if (!optarg)
142                                 arg_realize = REALIZE_MAYBE;
143                         else if (streq(optarg, "no"))
144                                 arg_realize = REALIZE_NO;
145                         else if (streq(optarg, "minimal"))
146                                 arg_realize = REALIZE_MINIMAL;
147                         else if (streq(optarg, "maybe"))
148                                 arg_realize = REALIZE_MAYBE;
149                         else if (streq(optarg, "yes"))
150                                 arg_realize = REALIZE_YES;
151                         else if (streq(optarg, "reload"))
152                                 arg_realize = REALIZE_RELOAD;
153                         else {
154                                 log_error("Invalid --realize argument %s", optarg);
155                                 return -EINVAL;
156                         }
157
158                         break;
159
160                 case '?':
161                         return -EINVAL;
162
163                 default:
164                         log_error("Unknown option code %c", c);
165                         return -EINVAL;
166                 }
167         }
168
169         if (optind >= argc) {
170                 help();
171                 return -EINVAL;
172         }
173
174         if (streq(argv[optind], "enable"))
175                 arg_action = ACTION_ENABLE;
176         else if (streq(argv[optind], "disable"))
177                 arg_action = ACTION_DISABLE;
178         else if (streq(argv[optind], "test"))
179                 arg_action = ACTION_TEST;
180         else {
181                 log_error("Unknown verb %s.", argv[optind]);
182                 return -EINVAL;
183         }
184
185         optind++;
186
187         if (optind >= argc) {
188                 log_error("Missing unit name.");
189                 return -EINVAL;
190         }
191
192
193         return 1;
194 }
195
196 static void install_info_free(InstallInfo *i) {
197         assert(i);
198
199         free(i->name);
200         free(i->path);
201         strv_free(i->aliases);
202         strv_free(i->wanted_by);
203         free(i);
204 }
205
206 static void install_info_hashmap_free(Hashmap *m) {
207         InstallInfo *i;
208
209         while ((i = hashmap_steal_first(m)))
210                 install_info_free(i);
211
212         hashmap_free(m);
213 }
214
215 static bool unit_name_valid(const char *name) {
216
217         /* This is a minimal version of unit_name_valid() from
218          * unit-name.c */
219
220         if (!*name)
221                 return false;
222
223         if (ignore_file(name))
224                 return false;
225
226         return true;
227 }
228
229 static int install_info_add(const char *name) {
230         InstallInfo *i;
231         int r;
232
233         if (!unit_name_valid(name))
234                 return -EINVAL;
235
236         if (hashmap_get(have_installed, name) ||
237             hashmap_get(will_install, name))
238                 return 0;
239
240         if (!(i = new0(InstallInfo, 1))) {
241                 r = -ENOMEM;
242                 goto fail;
243         }
244
245         if (!(i->name = strdup(name))) {
246                 r = -ENOMEM;
247                 goto fail;
248         }
249
250         if ((r = hashmap_put(will_install, i->name, i)) < 0)
251                 goto fail;
252
253         return 0;
254
255 fail:
256         if (i)
257                 install_info_free(i);
258
259         return r;
260 }
261
262 static int daemon_reload(DBusConnection *bus) {
263         DBusMessage *m = NULL, *reply = NULL;
264         DBusError error;
265         int r;
266
267         assert(bus);
268
269         dbus_error_init(&error);
270
271         if (!(m = dbus_message_new_method_call(
272                               "org.freedesktop.systemd1",
273                               "/org/freedesktop/systemd1",
274                               "org.freedesktop.systemd1.Manager",
275                               "Reload"))) {
276                 log_error("Could not allocate message.");
277                 return -ENOMEM;
278         }
279
280         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
281                 log_error("Failed to reload configuration: %s", error.message);
282                 r = -EIO;
283                 goto finish;
284         }
285
286         r = 0;
287
288 finish:
289         if (m)
290                 dbus_message_unref(m);
291
292         if (reply)
293                 dbus_message_unref(reply);
294
295         dbus_error_free(&error);
296
297         return r;
298 }
299
300 static int install_info_run(DBusConnection *bus, InstallInfo *i) {
301         DBusMessage *m = NULL, *reply = NULL;
302         DBusError error;
303         int r;
304         const char *mode = "replace";
305
306         assert(bus);
307         assert(i);
308
309         dbus_error_init(&error);
310
311         if (arg_action == ACTION_ENABLE) {
312
313                 if (arg_realize == REALIZE_MAYBE) {
314                         char **k;
315                         bool yes_please = false;
316
317                         STRV_FOREACH(k, i->wanted_by) {
318                                 DBusMessageIter sub, iter;
319
320                                 const char *path, *state;
321                                 const char *interface = "org.freedesktop.systemd1.Unit";
322                                 const char *property = "ActiveState";
323
324                                 if (!(m = dbus_message_new_method_call(
325                                                       "org.freedesktop.systemd1",
326                                                       "/org/freedesktop/systemd1",
327                                                       "org.freedesktop.systemd1.Manager",
328                                                       "GetUnit"))) {
329                                         log_error("Could not allocate message.");
330                                         r = -ENOMEM;
331                                         goto finish;
332                                 }
333
334                                 if (!dbus_message_append_args(m,
335                                                               DBUS_TYPE_STRING, k,
336                                                               DBUS_TYPE_INVALID)) {
337                                         log_error("Could not append arguments to message.");
338                                         r = -ENOMEM;
339                                         goto finish;
340                                 }
341
342                                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
343                                         /* Hmm, this unit doesn't exist, let's try the next one */
344                                         dbus_message_unref(m);
345                                         m = NULL;
346                                         continue;
347                                 }
348
349                                 if (!dbus_message_get_args(reply, &error,
350                                                            DBUS_TYPE_OBJECT_PATH, &path,
351                                                            DBUS_TYPE_INVALID)) {
352                                         log_error("Failed to parse reply: %s", error.message);
353                                         r = -EIO;
354                                         goto finish;
355                                 }
356
357                                 dbus_message_unref(m);
358                                 if (!(m = dbus_message_new_method_call(
359                                                       "org.freedesktop.systemd1",
360                                                       path,
361                                                       "org.freedesktop.DBus.Properties",
362                                                       "Get"))) {
363                                         log_error("Could not allocate message.");
364                                         r = -ENOMEM;
365                                         goto finish;
366                                 }
367
368                                 if (!dbus_message_append_args(m,
369                                                               DBUS_TYPE_STRING, &interface,
370                                                               DBUS_TYPE_STRING, &property,
371                                                               DBUS_TYPE_INVALID)) {
372                                         log_error("Could not append arguments to message.");
373                                         r = -ENOMEM;
374                                         goto finish;
375                                 }
376
377                                 dbus_message_unref(reply);
378                                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
379                                         log_error("Failed to issue method call: %s", error.message);
380                                         r = -EIO;
381                                         goto finish;
382                                 }
383
384                                 if (!dbus_message_iter_init(reply, &iter) ||
385                                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
386                                         log_error("Failed to parse reply.");
387                                         r = -EIO;
388                                         goto finish;
389                                 }
390
391                                 dbus_message_iter_recurse(&iter, &sub);
392
393                                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
394                                         log_error("Failed to parse reply.");
395                                         r = -EIO;
396                                         goto finish;
397                                 }
398
399                                 dbus_message_iter_get_basic(&sub, &state);
400
401                                 dbus_message_unref(m);
402                                 dbus_message_unref(reply);
403                                 m = reply = NULL;
404
405                                 if (streq(state, "active") ||
406                                     startswith(state, "reloading") ||
407                                     startswith(state, "activating")) {
408                                         yes_please = true;
409                                         break;
410                                 }
411                         }
412
413                         if (!yes_please) {
414                                 r = 0;
415                                 goto finish;
416                         }
417                 }
418
419                 if (!(m = dbus_message_new_method_call(
420                                       "org.freedesktop.systemd1",
421                                       "/org/freedesktop/systemd1",
422                                       "org.freedesktop.systemd1.Manager",
423                                       arg_realize == REALIZE_MINIMAL ? "TryRestartUnit" : "RestartUnit"))) {
424                         log_error("Could not allocate message.");
425                         r = -ENOMEM;
426                         goto finish;
427                 }
428
429                 if (!dbus_message_append_args(m,
430                                               DBUS_TYPE_STRING, &i->name,
431                                               DBUS_TYPE_STRING, &mode,
432                                               DBUS_TYPE_INVALID)) {
433                         log_error("Could not append arguments to message.");
434                         r = -ENOMEM;
435                         goto finish;
436                 }
437
438
439         } else if (arg_action == ACTION_DISABLE) {
440
441                 if (!(m = dbus_message_new_method_call(
442                                       "org.freedesktop.systemd1",
443                                       "/org/freedesktop/systemd1",
444                                       "org.freedesktop.systemd1.Manager",
445                                       "StopUnit"))) {
446                         log_error("Could not allocate message.");
447                         r = -ENOMEM;
448                         goto finish;
449                 }
450
451                 if (!dbus_message_append_args(m,
452                                               DBUS_TYPE_STRING, &i->name,
453                                               DBUS_TYPE_STRING, &mode,
454                                               DBUS_TYPE_INVALID)) {
455                         log_error("Could not append arguments to message.");
456                         r = -ENOMEM;
457                         goto finish;
458                 }
459         }
460
461         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
462                 log_error("Failed to reload configuration: %s", error.message);
463                 r = -EIO;
464                 goto finish;
465         }
466
467         r = 0;
468
469 finish:
470         if (m)
471                 dbus_message_unref(m);
472
473         if (reply)
474                 dbus_message_unref(reply);
475
476         dbus_error_free(&error);
477
478         return r;
479 }
480
481 static int config_parse_also(
482                 const char *filename,
483                 unsigned line,
484                 const char *section,
485                 const char *lvalue,
486                 const char *rvalue,
487                 void *data,
488                 void *userdata) {
489
490         char *w;
491         size_t l;
492         char *state;
493
494         assert(filename);
495         assert(lvalue);
496         assert(rvalue);
497
498         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
499                 char *n;
500                 int r;
501
502                 if (!(n = strndup(w, l)))
503                         return -ENOMEM;
504
505                 r = install_info_add(n);
506                 free(n);
507
508                 if (r < 0)
509                         return r;
510         }
511
512         return 0;
513 }
514
515 static int create_symlink(const char *old_path, const char *new_path) {
516         int r;
517
518         assert(old_path);
519         assert(new_path);
520
521         if (arg_action == ACTION_ENABLE) {
522                 char *dest;
523
524                 mkdir_parents(new_path, 0755);
525
526                 if (symlink(old_path, new_path) >= 0)
527                         return 0;
528
529                 if (errno != EEXIST) {
530                         log_error("Cannot link %s to %s: %m", old_path, new_path);
531                         return -errno;
532                 }
533
534                 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
535
536                         if (errno == EINVAL) {
537                                 log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
538                                 return -EEXIST;
539                         }
540
541                         log_error("readlink() failed: %s", strerror(-r));
542                         return r;
543                 }
544
545                 if (streq(dest, old_path)) {
546                         free(dest);
547                         return 0;
548                 }
549
550                 if (!arg_force) {
551                         log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
552                         free(dest);
553                         return -EEXIST;
554                 }
555
556                 free(dest);
557                 unlink(new_path);
558
559                 if (symlink(old_path, new_path) >= 0)
560                         return 0;
561
562                 log_error("Cannot link %s to %s: %m", old_path, new_path);
563                 return -errno;
564
565         } else if (arg_action == ACTION_DISABLE) {
566                 char *dest;
567
568                 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
569                         if (errno == ENOENT)
570                                 return 0;
571
572                         if (errno == EINVAL) {
573                                 log_warning("File %s not a symlink, ignoring.", old_path);
574                                 return 0;
575                         }
576
577                         log_error("readlink() failed: %s", strerror(-r));
578                         return r;
579                 }
580
581                 if (!streq(dest, old_path)) {
582                         log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest);
583                         free(dest);
584                         return 0;
585                 }
586
587                 free(dest);
588                 if (unlink(new_path) >= 0)
589                         return 0;
590
591                 log_error("Cannot unlink %s: %m", new_path);
592                 return -errno;
593
594         } else if (arg_action == ACTION_TEST) {
595                 char *dest;
596
597                 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
598
599                         if (errno == ENOENT || errno == EINVAL)
600                                 return 0;
601
602                         log_error("readlink() failed: %s", strerror(-r));
603                         return r;
604                 }
605
606                 if (streq(dest, old_path)) {
607                         free(dest);
608                         return 1;
609                 }
610
611                 return 0;
612         }
613
614         assert_not_reached("Unknown action.");
615 }
616
617 static int install_info_symlink_alias(InstallInfo *i, const char *config_path) {
618         char **s;
619         char *alias_path = NULL;
620         int r;
621
622         assert(i);
623
624         STRV_FOREACH(s, i->aliases) {
625
626                 if (!unit_name_valid(*s)) {
627                         log_error("Invalid name %s.", *s);
628                         r = -EINVAL;
629                         goto finish;
630                 }
631
632                 free(alias_path);
633                 if (!(alias_path = path_make_absolute(*s, config_path))) {
634                         log_error("Out of memory");
635                         r = -ENOMEM;
636                         goto finish;
637                 }
638
639                 if ((r = create_symlink(i->path, alias_path)) != 0)
640                         goto finish;
641
642                 if (arg_action == ACTION_DISABLE)
643                         rmdir_parents(alias_path, config_path);
644         }
645
646         r = 0;
647
648 finish:
649         free(alias_path);
650
651         return r;
652 }
653
654 static int install_info_symlink_wants(InstallInfo *i, const char *config_path) {
655         char **s;
656         char *alias_path = NULL;
657         int r;
658
659         assert(i);
660
661         STRV_FOREACH(s, i->wanted_by) {
662                 if (!unit_name_valid(*s)) {
663                         log_error("Invalid name %s.", *s);
664                         r = -EINVAL;
665                         goto finish;
666                 }
667
668                 free(alias_path);
669                 alias_path = NULL;
670
671                 if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
672                         log_error("Out of memory");
673                         r = -ENOMEM;
674                         goto finish;
675                 }
676
677                 if ((r = create_symlink(i->path, alias_path)) != 0)
678                         goto finish;
679
680                 if (arg_action == ACTION_DISABLE)
681                         rmdir_parents(alias_path, config_path);
682         }
683
684         r = 0;
685
686 finish:
687         free(alias_path);
688
689         return r;
690 }
691
692 static int install_info_apply(LookupPaths *paths, InstallInfo *i, const char *config_path) {
693
694         const ConfigItem items[] = {
695                 { "Alias",    config_parse_strv, &i->aliases,   "Install" },
696                 { "WantedBy", config_parse_strv, &i->wanted_by, "Install" },
697                 { "Also",     config_parse_also, NULL,          "Install" },
698
699                 { NULL, NULL, NULL, NULL }
700         };
701
702         char **p;
703         char *filename = NULL;
704         FILE *f = NULL;
705         int r;
706
707         assert(paths);
708         assert(i);
709
710         STRV_FOREACH(p, paths->unit_path) {
711
712                 if (!(filename = path_make_absolute(i->name, *p))) {
713                         log_error("Out of memory");
714                         return -ENOMEM;
715                 }
716
717                 if ((f = fopen(filename, "re")))
718                         break;
719
720                 free(filename);
721                 filename = NULL;
722
723                 if (errno != ENOENT) {
724                         log_error("Failed to open %s: %m", filename);
725                         return -errno;
726                 }
727         }
728
729         if (!f) {
730                 log_error("Couldn't find %s.", i->name);
731                 return -ENOENT;
732         }
733
734         i->path = filename;
735
736         if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
737                 fclose(f);
738                 return r;
739         }
740
741         fclose(f);
742
743         if ((r = install_info_symlink_alias(i, config_path)) != 0)
744                 return r;
745
746         if ((r = install_info_symlink_wants(i, config_path)) != 0)
747                 return r;
748
749         return 0;
750 }
751
752 static char *get_config_path(void) {
753
754         switch (arg_where) {
755
756         case WHERE_SYSTEM:
757                 return strdup(SYSTEM_CONFIG_UNIT_PATH);
758
759         case WHERE_GLOBAL:
760                 return strdup(SESSION_CONFIG_UNIT_PATH);
761
762         case WHERE_SESSION: {
763                 char *p;
764
765                 if (session_config_home(&p) < 0)
766                         return NULL;
767
768                 return p;
769         }
770
771         default:
772                 assert_not_reached("Unknown config path.");
773         }
774 }
775
776 static int do_run(void) {
777         DBusConnection *bus = NULL;
778         DBusError error;
779         int r, q;
780         Iterator i;
781         InstallInfo *j;
782
783         dbus_error_init(&error);
784
785         if (arg_realize == REALIZE_NO)
786                 return 0;
787
788         if (arg_where == WHERE_GLOBAL) {
789                 log_warning("Warning: --realize has no effect with --global.");
790                 return 0;
791         }
792
793         if (arg_action != ACTION_ENABLE && arg_action != ACTION_DISABLE) {
794                 log_warning("Warning: --realize has no effect with test.");
795                 return 0;
796         }
797
798         if (arg_where == WHERE_SYSTEM && sd_booted() <= 0) {
799                 log_info("systemd is not running, --realize has not effect.");
800                 return 0;
801         }
802
803         if (arg_where == WHERE_SYSTEM && running_in_chroot() > 0) {
804                 log_info("Running in a chroot() environment, --realize has no effect.");
805                 return 0;
806         }
807
808         if ((r = bus_connect(arg_where == WHERE_SESSION ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, NULL, &error)) < 0) {
809                 log_error("Failed to get D-Bus connection: %s", error.message);
810                 goto finish;
811         }
812
813         r = 0;
814
815         if (arg_action == ACTION_ENABLE)
816                 if ((r = daemon_reload(bus)) < 0)
817                         goto finish;
818
819         if (arg_realize != REALIZE_RELOAD) {
820                 HASHMAP_FOREACH(j, have_installed, i)
821                         if ((q = install_info_run(bus, j)) < 0)
822                                 r = q;
823         }
824
825         if (arg_action == ACTION_DISABLE)
826                 if ((q = daemon_reload(bus)) < 0)
827                         r = q;
828
829 finish:
830         if (bus)
831                 dbus_connection_unref(bus);
832
833         dbus_error_free(&error);
834
835         dbus_shutdown();
836         return r;
837 }
838
839 int main(int argc, char *argv[]) {
840         int r, retval = 1, j;
841         LookupPaths paths;
842         InstallInfo *i;
843         char *config_path = NULL;
844
845         zero(paths);
846
847         log_parse_environment();
848
849         if ((r = parse_argv(argc, argv)) < 0)
850                 goto finish;
851         else if (r == 0) {
852                 retval = 0;
853                 goto finish;
854         }
855
856         if ((r = lookup_paths_init(&paths, arg_where == WHERE_SYSTEM ? MANAGER_SYSTEM : MANAGER_SESSION)) < 0) {
857                 log_error("Failed to determine lookup paths: %s", strerror(-r));
858                 goto finish;
859         }
860
861         if (!(config_path = get_config_path())) {
862                 log_error("Failed to determine config path");
863                 goto finish;
864         }
865
866         will_install = hashmap_new(string_hash_func, string_compare_func);
867         have_installed = hashmap_new(string_hash_func, string_compare_func);
868
869         if (!will_install || !have_installed) {
870                 log_error("Failed to allocate unit sets.");
871                 goto finish;
872         }
873
874         for (j = optind; j < argc; j++)
875                 if ((r = install_info_add(argv[j])) < 0)
876                         goto finish;
877
878         while ((i = hashmap_first(will_install))) {
879                 assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
880
881                 if ((r = install_info_apply(&paths, i, config_path)) != 0) {
882
883                         if (r < 0)
884                                 goto finish;
885
886                         /* In test mode and found something */
887                         retval = 0;
888                         goto finish;
889                 }
890         }
891
892         if (do_run() < 0)
893                 goto finish;
894
895         retval = arg_action == ACTION_TEST ? 1 : 0;
896
897 finish:
898         install_info_hashmap_free(will_install);
899         install_info_hashmap_free(have_installed);
900
901         lookup_paths_free(&paths);
902
903         free(config_path);
904
905         return retval;
906 }