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