chiark / gitweb /
update fixme
[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 {
152                                 log_error("Invalid --realize argument %s", optarg);
153                                 return -EINVAL;
154                         }
155
156                         break;
157
158                 case '?':
159                         return -EINVAL;
160
161                 default:
162                         log_error("Unknown option code %c", c);
163                         return -EINVAL;
164                 }
165         }
166
167         if (optind >= argc) {
168                 help();
169                 return -EINVAL;
170         }
171
172         if (streq(argv[optind], "enable"))
173                 arg_action = ACTION_ENABLE;
174         else if (streq(argv[optind], "disable"))
175                 arg_action = ACTION_DISABLE;
176         else if (streq(argv[optind], "test"))
177                 arg_action = ACTION_TEST;
178         else {
179                 log_error("Unknown verb %s.", argv[optind]);
180                 return -EINVAL;
181         }
182
183         optind++;
184
185         if (optind >= argc) {
186                 log_error("Missing unit name.");
187                 return -EINVAL;
188         }
189
190
191         return 1;
192 }
193
194 static void install_info_free(InstallInfo *i) {
195         assert(i);
196
197         free(i->name);
198         free(i->path);
199         strv_free(i->aliases);
200         strv_free(i->wanted_by);
201         free(i);
202 }
203
204 static void install_info_hashmap_free(Hashmap *m) {
205         InstallInfo *i;
206
207         while ((i = hashmap_steal_first(m)))
208                 install_info_free(i);
209
210         hashmap_free(m);
211 }
212
213 static bool unit_name_valid(const char *name) {
214
215         /* This is a minimal version of unit_name_valid() from
216          * unit-name.c */
217
218         if (!*name)
219                 return false;
220
221         if (ignore_file(name))
222                 return false;
223
224         return true;
225 }
226
227 static int install_info_add(const char *name) {
228         InstallInfo *i;
229         int r;
230
231         if (!unit_name_valid(name))
232                 return -EINVAL;
233
234         if (hashmap_get(have_installed, name) ||
235             hashmap_get(will_install, name))
236                 return 0;
237
238         if (!(i = new0(InstallInfo, 1))) {
239                 r = -ENOMEM;
240                 goto fail;
241         }
242
243         if (!(i->name = strdup(name))) {
244                 r = -ENOMEM;
245                 goto fail;
246         }
247
248         if ((r = hashmap_put(will_install, i->name, i)) < 0)
249                 goto fail;
250
251         return 0;
252
253 fail:
254         if (i)
255                 install_info_free(i);
256
257         return r;
258 }
259
260 static int daemon_reload(DBusConnection *bus) {
261         DBusMessage *m = NULL, *reply = NULL;
262         DBusError error;
263         int r;
264
265         assert(bus);
266
267         dbus_error_init(&error);
268
269         if (!(m = dbus_message_new_method_call(
270                               "org.freedesktop.systemd1",
271                               "/org/freedesktop/systemd1",
272                               "org.freedesktop.systemd1.Manager",
273                               "Reload"))) {
274                 log_error("Could not allocate message.");
275                 return -ENOMEM;
276         }
277
278         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
279                 log_error("Failed to reload configuration: %s", error.message);
280                 r = -EIO;
281                 goto finish;
282         }
283
284         r = 0;
285
286 finish:
287         if (m)
288                 dbus_message_unref(m);
289
290         if (reply)
291                 dbus_message_unref(reply);
292
293         dbus_error_free(&error);
294
295         return r;
296 }
297
298 static int install_info_run(DBusConnection *bus, InstallInfo *i) {
299         DBusMessage *m = NULL, *reply = NULL;
300         DBusError error;
301         int r;
302         const char *mode = "replace";
303
304         assert(bus);
305         assert(i);
306
307         dbus_error_init(&error);
308
309         if (arg_action == ACTION_ENABLE) {
310
311                 if (arg_realize == REALIZE_MAYBE) {
312                         char **k;
313                         bool yes_please = false;
314
315                         STRV_FOREACH(k, i->wanted_by) {
316                                 DBusMessageIter sub, iter;
317
318                                 const char *path, *state;
319                                 const char *interface = "org.freedesktop.systemd1.Unit";
320                                 const char *property = "ActiveState";
321
322                                 if (!(m = dbus_message_new_method_call(
323                                                       "org.freedesktop.systemd1",
324                                                       "/org/freedesktop/systemd1",
325                                                       "org.freedesktop.systemd1.Manager",
326                                                       "GetUnit"))) {
327                                         log_error("Could not allocate message.");
328                                         r = -ENOMEM;
329                                         goto finish;
330                                 }
331
332                                 if (!dbus_message_append_args(m,
333                                                               DBUS_TYPE_STRING, k,
334                                                               DBUS_TYPE_INVALID)) {
335                                         log_error("Could not append arguments to message.");
336                                         r = -ENOMEM;
337                                         goto finish;
338                                 }
339
340                                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
341                                         /* Hmm, this unit doesn't exist, let's try the next one */
342                                         dbus_message_unref(m);
343                                         m = NULL;
344                                         continue;
345                                 }
346
347                                 if (!dbus_message_get_args(reply, &error,
348                                                            DBUS_TYPE_OBJECT_PATH, &path,
349                                                            DBUS_TYPE_INVALID)) {
350                                         log_error("Failed to parse reply: %s", error.message);
351                                         r = -EIO;
352                                         goto finish;
353                                 }
354
355                                 dbus_message_unref(m);
356                                 if (!(m = dbus_message_new_method_call(
357                                                       "org.freedesktop.systemd1",
358                                                       path,
359                                                       "org.freedesktop.DBus.Properties",
360                                                       "Get"))) {
361                                         log_error("Could not allocate message.");
362                                         r = -ENOMEM;
363                                         goto finish;
364                                 }
365
366                                 if (!dbus_message_append_args(m,
367                                                               DBUS_TYPE_STRING, &interface,
368                                                               DBUS_TYPE_STRING, &property,
369                                                               DBUS_TYPE_INVALID)) {
370                                         log_error("Could not append arguments to message.");
371                                         r = -ENOMEM;
372                                         goto finish;
373                                 }
374
375                                 dbus_message_unref(reply);
376                                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
377                                         log_error("Failed to issue method call: %s", error.message);
378                                         r = -EIO;
379                                         goto finish;
380                                 }
381
382                                 if (!dbus_message_iter_init(reply, &iter) ||
383                                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
384                                         log_error("Failed to parse reply.");
385                                         r = -EIO;
386                                         goto finish;
387                                 }
388
389                                 dbus_message_iter_recurse(&iter, &sub);
390
391                                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
392                                         log_error("Failed to parse reply.");
393                                         r = -EIO;
394                                         goto finish;
395                                 }
396
397                                 dbus_message_iter_get_basic(&sub, &state);
398
399                                 dbus_message_unref(m);
400                                 dbus_message_unref(reply);
401                                 m = reply = NULL;
402
403                                 if (streq(state, "active") ||
404                                     startswith(state, "reloading") ||
405                                     startswith(state, "activating")) {
406                                         yes_please = true;
407                                         break;
408                                 }
409                         }
410
411                         if (!yes_please) {
412                                 r = 0;
413                                 goto finish;
414                         }
415                 }
416
417                 if (!(m = dbus_message_new_method_call(
418                                       "org.freedesktop.systemd1",
419                                       "/org/freedesktop/systemd1",
420                                       "org.freedesktop.systemd1.Manager",
421                                       arg_realize == REALIZE_MINIMAL ? "TryRestartUnit" : "RestartUnit"))) {
422                         log_error("Could not allocate message.");
423                         r = -ENOMEM;
424                         goto finish;
425                 }
426
427                 if (!dbus_message_append_args(m,
428                                               DBUS_TYPE_STRING, &i->name,
429                                               DBUS_TYPE_STRING, &mode,
430                                               DBUS_TYPE_INVALID)) {
431                         log_error("Could not append arguments to message.");
432                         r = -ENOMEM;
433                         goto finish;
434                 }
435
436
437         } else if (arg_action == ACTION_DISABLE) {
438
439                 if (!(m = dbus_message_new_method_call(
440                                       "org.freedesktop.systemd1",
441                                       "/org/freedesktop/systemd1",
442                                       "org.freedesktop.systemd1.Manager",
443                                       "StopUnit"))) {
444                         log_error("Could not allocate message.");
445                         r = -ENOMEM;
446                         goto finish;
447                 }
448
449                 if (!dbus_message_append_args(m,
450                                               DBUS_TYPE_STRING, &i->name,
451                                               DBUS_TYPE_STRING, &mode,
452                                               DBUS_TYPE_INVALID)) {
453                         log_error("Could not append arguments to message.");
454                         r = -ENOMEM;
455                         goto finish;
456                 }
457         }
458
459         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
460                 log_error("Failed to reload configuration: %s", error.message);
461                 r = -EIO;
462                 goto finish;
463         }
464
465         r = 0;
466
467 finish:
468         if (m)
469                 dbus_message_unref(m);
470
471         if (reply)
472                 dbus_message_unref(reply);
473
474         dbus_error_free(&error);
475
476         return r;
477 }
478
479 static int config_parse_also(
480                 const char *filename,
481                 unsigned line,
482                 const char *section,
483                 const char *lvalue,
484                 const char *rvalue,
485                 void *data,
486                 void *userdata) {
487
488         char *w;
489         size_t l;
490         char *state;
491
492         assert(filename);
493         assert(lvalue);
494         assert(rvalue);
495
496         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
497                 char *n;
498                 int r;
499
500                 if (!(n = strndup(w, l)))
501                         return -ENOMEM;
502
503                 r = install_info_add(n);
504                 free(n);
505
506                 if (r < 0)
507                         return r;
508         }
509
510         return 0;
511 }
512
513 static int create_symlink(const char *old_path, const char *new_path) {
514         int r;
515
516         assert(old_path);
517         assert(new_path);
518
519         if (arg_action == ACTION_ENABLE) {
520                 char *dest;
521
522                 mkdir_parents(new_path, 0755);
523
524                 if (symlink(old_path, new_path) >= 0)
525                         return 0;
526
527                 if (errno != EEXIST) {
528                         log_error("Cannot link %s to %s: %m", old_path, new_path);
529                         return -errno;
530                 }
531
532                 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
533
534                         if (errno == EINVAL) {
535                                 log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
536                                 return -EEXIST;
537                         }
538
539                         log_error("readlink() failed: %s", strerror(-r));
540                         return r;
541                 }
542
543                 if (streq(dest, old_path)) {
544                         free(dest);
545                         return 0;
546                 }
547
548                 if (!arg_force) {
549                         log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
550                         free(dest);
551                         return -EEXIST;
552                 }
553
554                 free(dest);
555                 unlink(new_path);
556
557                 if (symlink(old_path, new_path) >= 0)
558                         return 0;
559
560                 log_error("Cannot link %s to %s: %m", old_path, new_path);
561                 return -errno;
562
563         } else if (arg_action == ACTION_DISABLE) {
564                 char *dest;
565
566                 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
567                         if (errno == ENOENT)
568                                 return 0;
569
570                         if (errno == EINVAL) {
571                                 log_warning("File %s not a symlink, ignoring.", old_path);
572                                 return 0;
573                         }
574
575                         log_error("readlink() failed: %s", strerror(-r));
576                         return r;
577                 }
578
579                 if (!streq(dest, old_path)) {
580                         log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest);
581                         free(dest);
582                         return 0;
583                 }
584
585                 free(dest);
586                 if (unlink(new_path) >= 0)
587                         return 0;
588
589                 log_error("Cannot unlink %s: %m", new_path);
590                 return -errno;
591
592         } else if (arg_action == ACTION_TEST) {
593                 char *dest;
594
595                 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
596
597                         if (errno == ENOENT || errno == EINVAL)
598                                 return 0;
599
600                         log_error("readlink() failed: %s", strerror(-r));
601                         return r;
602                 }
603
604                 if (streq(dest, old_path)) {
605                         free(dest);
606                         return 1;
607                 }
608
609                 return 0;
610         }
611
612         assert_not_reached("Unknown action.");
613 }
614
615 static int install_info_symlink_alias(InstallInfo *i, const char *config_path) {
616         char **s;
617         char *alias_path = NULL;
618         int r;
619
620         assert(i);
621
622         STRV_FOREACH(s, i->aliases) {
623
624                 if (!unit_name_valid(*s)) {
625                         log_error("Invalid name %s.", *s);
626                         r = -EINVAL;
627                         goto finish;
628                 }
629
630                 free(alias_path);
631                 if (!(alias_path = path_make_absolute(*s, config_path))) {
632                         log_error("Out of memory");
633                         r = -ENOMEM;
634                         goto finish;
635                 }
636
637                 if ((r = create_symlink(i->path, alias_path)) != 0)
638                         goto finish;
639
640                 if (arg_action == ACTION_DISABLE)
641                         rmdir_parents(alias_path, config_path);
642         }
643
644         r = 0;
645
646 finish:
647         free(alias_path);
648
649         return r;
650 }
651
652 static int install_info_symlink_wants(InstallInfo *i, const char *config_path) {
653         char **s;
654         char *alias_path = NULL;
655         int r;
656
657         assert(i);
658
659         STRV_FOREACH(s, i->wanted_by) {
660                 if (!unit_name_valid(*s)) {
661                         log_error("Invalid name %s.", *s);
662                         r = -EINVAL;
663                         goto finish;
664                 }
665
666                 free(alias_path);
667                 alias_path = NULL;
668
669                 if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
670                         log_error("Out of memory");
671                         r = -ENOMEM;
672                         goto finish;
673                 }
674
675                 if ((r = create_symlink(i->path, alias_path)) != 0)
676                         goto finish;
677
678                 if (arg_action == ACTION_DISABLE)
679                         rmdir_parents(alias_path, config_path);
680         }
681
682         r = 0;
683
684 finish:
685         free(alias_path);
686
687         return r;
688 }
689
690 static int install_info_apply(LookupPaths *paths, InstallInfo *i, const char *config_path) {
691
692         const ConfigItem items[] = {
693                 { "Alias",    config_parse_strv, &i->aliases,   "Install" },
694                 { "WantedBy", config_parse_strv, &i->wanted_by, "Install" },
695                 { "Also",     config_parse_also, NULL,          "Install" },
696
697                 { NULL, NULL, NULL, NULL }
698         };
699
700         char **p;
701         char *filename = NULL;
702         FILE *f = NULL;
703         int r;
704
705         assert(paths);
706         assert(i);
707
708         STRV_FOREACH(p, paths->unit_path) {
709
710                 if (!(filename = path_make_absolute(i->name, *p))) {
711                         log_error("Out of memory");
712                         return -ENOMEM;
713                 }
714
715                 if ((f = fopen(filename, "re")))
716                         break;
717
718                 free(filename);
719                 filename = NULL;
720
721                 if (errno != ENOENT) {
722                         log_error("Failed to open %s: %m", filename);
723                         return -errno;
724                 }
725         }
726
727         if (!f) {
728                 log_error("Couldn't find %s.", i->name);
729                 return -ENOENT;
730         }
731
732         i->path = filename;
733
734         if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
735                 fclose(f);
736                 return r;
737         }
738
739         fclose(f);
740
741         if ((r = install_info_symlink_alias(i, config_path)) != 0)
742                 return r;
743
744         if ((r = install_info_symlink_wants(i, config_path)) != 0)
745                 return r;
746
747         return 0;
748 }
749
750 static char *get_config_path(void) {
751
752         switch (arg_where) {
753
754         case WHERE_SYSTEM:
755                 return strdup(SYSTEM_CONFIG_UNIT_PATH);
756
757         case WHERE_GLOBAL:
758                 return strdup(SESSION_CONFIG_UNIT_PATH);
759
760         case WHERE_SESSION: {
761                 char *p;
762
763                 if (session_config_home(&p) < 0)
764                         return NULL;
765
766                 return p;
767         }
768
769         default:
770                 assert_not_reached("Unknown config path.");
771         }
772 }
773
774 static int do_run(void) {
775         DBusConnection *bus = NULL;
776         DBusError error;
777         int r, q;
778         Iterator i;
779         InstallInfo *j;
780
781         dbus_error_init(&error);
782
783         if (arg_realize == REALIZE_NO)
784                 return 0;
785
786         if (arg_where == WHERE_GLOBAL) {
787                 log_warning("Warning: --realize has no effect with --global.");
788                 return 0;
789         }
790
791         if (arg_action != ACTION_ENABLE && arg_action != ACTION_DISABLE) {
792                 log_warning("Warning: --realize has no effect with test.");
793                 return 0;
794         }
795
796         if (arg_where == WHERE_SYSTEM && sd_booted() <= 0) {
797                 log_info("systemd is not running, --realize has not effect.");
798                 return 0;
799         }
800
801         if (arg_where == WHERE_SYSTEM && running_in_chroot() > 0) {
802                 log_info("Running in a chroot() environment, --realize has no effect.");
803                 return 0;
804         }
805
806         if ((r = bus_connect(arg_where == WHERE_SESSION ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, NULL, &error)) < 0) {
807                 log_error("Failed to get D-Bus connection: %s", error.message);
808                 goto finish;
809         }
810
811         r = 0;
812
813         if (arg_action == ACTION_ENABLE)
814                 if ((r = daemon_reload(bus)) < 0)
815                         goto finish;
816
817         if (arg_realize != REALIZE_RELOAD) {
818                 HASHMAP_FOREACH(j, have_installed, i)
819                         if ((q = install_info_run(bus, j)) < 0)
820                                 r = q;
821         }
822
823         if (arg_action == ACTION_DISABLE)
824                 if ((q = daemon_reload(bus)) < 0)
825                         r = q;
826
827 finish:
828         if (bus)
829                 dbus_connection_unref(bus);
830
831         dbus_error_free(&error);
832
833         dbus_shutdown();
834         return r;
835 }
836
837 int main(int argc, char *argv[]) {
838         int r, retval = 1, j;
839         LookupPaths paths;
840         InstallInfo *i;
841         char *config_path = NULL;
842
843         zero(paths);
844
845         log_parse_environment();
846
847         if ((r = parse_argv(argc, argv)) < 0)
848                 goto finish;
849         else if (r == 0) {
850                 retval = 0;
851                 goto finish;
852         }
853
854         if ((r = lookup_paths_init(&paths, arg_where == WHERE_SYSTEM ? MANAGER_SYSTEM : MANAGER_SESSION)) < 0) {
855                 log_error("Failed to determine lookup paths: %s", strerror(-r));
856                 goto finish;
857         }
858
859         if (!(config_path = get_config_path())) {
860                 log_error("Failed to determine config path");
861                 goto finish;
862         }
863
864         will_install = hashmap_new(string_hash_func, string_compare_func);
865         have_installed = hashmap_new(string_hash_func, string_compare_func);
866
867         if (!will_install || !have_installed) {
868                 log_error("Failed to allocate unit sets.");
869                 goto finish;
870         }
871
872         for (j = optind; j < argc; j++)
873                 if ((r = install_info_add(argv[j])) < 0)
874                         goto finish;
875
876         while ((i = hashmap_first(will_install))) {
877                 assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
878
879                 if ((r = install_info_apply(&paths, i, config_path)) != 0) {
880
881                         if (r < 0)
882                                 goto finish;
883
884                         /* In test mode and found something */
885                         retval = 0;
886                         goto finish;
887                 }
888         }
889
890         if (do_run() < 0)
891                 goto finish;
892
893         retval = arg_action == ACTION_TEST ? 1 : 0;
894
895 finish:
896         install_info_hashmap_free(will_install);
897         install_info_hashmap_free(have_installed);
898
899         lookup_paths_free(&paths);
900
901         free(config_path);
902
903         return retval;
904 }