chiark / gitweb /
systemctl: accept -p more than once
[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 #include <fcntl.h>
28 #include <dirent.h>
29
30 #include "log.h"
31 #include "path-lookup.h"
32 #include "util.h"
33 #include "macro.h"
34 #include "strv.h"
35 #include "conf-parser.h"
36 #include "dbus-common.h"
37 #include "sd-daemon.h"
38
39 static bool arg_force = false;
40 static bool arg_all = false;
41 static bool arg_verbose = false;
42
43 static enum {
44         WHERE_SYSTEM,
45         WHERE_SESSION,
46         WHERE_GLOBAL,
47 } arg_where = WHERE_SYSTEM;
48
49 static enum {
50         ACTION_INVALID,
51         ACTION_ENABLE,
52         ACTION_DISABLE,
53         ACTION_REALIZE,
54         ACTION_TEST
55 } arg_action = ACTION_INVALID;
56
57 static enum {
58         REALIZE_NO,        /* Don't reload/start/stop or anything */
59         REALIZE_RELOAD,    /* Only reload daemon config, don't stop/start */
60         REALIZE_MINIMAL,   /* Only shutdown/restart if running. */
61         REALIZE_MAYBE,     /* Start if WantedBy= suggests */
62         REALIZE_YES        /* Start unconditionally */
63 } arg_realize = REALIZE_NO;
64
65 typedef struct {
66         char *name;
67         char *path;
68
69         char **aliases;
70         char **wanted_by;
71 } InstallInfo;
72
73 static Hashmap *will_install = NULL, *have_installed = NULL;
74 static Set *remove_symlinks_to = NULL;
75
76 static int help(void) {
77
78         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
79                "Install init system units.\n\n"
80                "  -h --help           Show this help\n"
81                "     --force          Override existing links\n"
82                "     --verbose        Show what is being done as it is done\n"
83                "     --system         Install into system\n"
84                "     --session        Install into session\n"
85                "     --global         Install into all sessions\n"
86                "     --all            When disabling, remove all symlinks, not\n"
87                "                      just those listed in the [Install] section\n"
88                "     --realize[=MODE] Start/stop/restart unit after installation\n"
89                "                      Takes 'no', 'minimal', 'maybe' or 'yes'\n\n"
90                "Commands:\n"
91                "  enable [NAME...]    Enable one or more units\n"
92                "  disable [NAME...]   Disable one or more units\n"
93                "  realize [NAME...]   Test whether any of the specified units are enabled\n"
94                "                      and the start/stop/restart units accordingly\n"
95                "  test [NAME...]      Test whether any of the specified units are enabled\n",
96                program_invocation_short_name);
97
98         return 0;
99 }
100
101 static int parse_argv(int argc, char *argv[]) {
102
103         enum {
104                 ARG_SESSION = 0x100,
105                 ARG_SYSTEM,
106                 ARG_GLOBAL,
107                 ARG_FORCE,
108                 ARG_REALIZE,
109                 ARG_ALL
110         };
111
112         static const struct option options[] = {
113                 { "help",      no_argument,       NULL, 'h'         },
114                 { "session",   no_argument,       NULL, ARG_SESSION },
115                 { "system",    no_argument,       NULL, ARG_SYSTEM  },
116                 { "global",    no_argument,       NULL, ARG_GLOBAL  },
117                 { "force",     no_argument,       NULL, ARG_FORCE   },
118                 { "realize",   optional_argument, NULL, ARG_REALIZE },
119                 { "all",       no_argument,       NULL, ARG_ALL     },
120                 { "verbose",   no_argument,       NULL, 'v'         },
121                 { NULL,        0,                 NULL, 0           }
122         };
123
124         int c;
125         bool realize_switch = false;
126
127         assert(argc >= 1);
128         assert(argv);
129
130         while ((c = getopt_long(argc, argv, "hv", options, NULL)) >= 0) {
131
132                 switch (c) {
133
134                 case 'h':
135                         help();
136                         return 0;
137
138                 case ARG_SESSION:
139                         arg_where = WHERE_SESSION;
140                         break;
141
142                 case ARG_SYSTEM:
143                         arg_where = WHERE_SYSTEM;
144                         break;
145
146                 case ARG_GLOBAL:
147                         arg_where = WHERE_GLOBAL;
148                         break;
149
150                 case ARG_FORCE:
151                         arg_force = true;
152                         break;
153
154                 case ARG_REALIZE:
155
156                         realize_switch = true;
157
158                         if (!optarg)
159                                 arg_realize = REALIZE_MAYBE;
160                         else if (streq(optarg, "no"))
161                                 arg_realize = REALIZE_NO;
162                         else if (streq(optarg, "minimal"))
163                                 arg_realize = REALIZE_MINIMAL;
164                         else if (streq(optarg, "maybe"))
165                                 arg_realize = REALIZE_MAYBE;
166                         else if (streq(optarg, "yes"))
167                                 arg_realize = REALIZE_YES;
168                         else if (streq(optarg, "reload"))
169                                 arg_realize = REALIZE_RELOAD;
170                         else {
171                                 log_error("Invalid --realize argument %s", optarg);
172                                 return -EINVAL;
173                         }
174
175                         break;
176
177                 case ARG_ALL:
178                         arg_all = true;
179                         break;
180
181                 case 'v':
182                         arg_verbose = true;
183                         break;
184
185                 case '?':
186                         return -EINVAL;
187
188                 default:
189                         log_error("Unknown option code %c", c);
190                         return -EINVAL;
191                 }
192         }
193
194         if (optind >= argc) {
195                 help();
196                 return -EINVAL;
197         }
198
199         if (streq(argv[optind], "enable"))
200                 arg_action = ACTION_ENABLE;
201         else if (streq(argv[optind], "disable"))
202                 arg_action = ACTION_DISABLE;
203         else if (streq(argv[optind], "test"))
204                 arg_action = ACTION_TEST;
205         else if (streq(argv[optind], "realize")) {
206                 arg_action = ACTION_REALIZE;
207
208                 if (!realize_switch)
209                         arg_realize = REALIZE_MAYBE;
210         } else {
211                 log_error("Unknown verb %s.", argv[optind]);
212                 return -EINVAL;
213         }
214
215         optind++;
216
217         if (optind >= argc) {
218                 log_error("Missing unit name.");
219                 return -EINVAL;
220         }
221
222
223         return 1;
224 }
225
226 static void install_info_free(InstallInfo *i) {
227         assert(i);
228
229         free(i->name);
230         free(i->path);
231         strv_free(i->aliases);
232         strv_free(i->wanted_by);
233         free(i);
234 }
235
236 static void install_info_hashmap_free(Hashmap *m) {
237         InstallInfo *i;
238
239         while ((i = hashmap_steal_first(m)))
240                 install_info_free(i);
241
242         hashmap_free(m);
243 }
244
245 static bool unit_name_valid(const char *name) {
246
247         /* This is a minimal version of unit_name_valid() from
248          * unit-name.c */
249
250         if (!*name)
251                 return false;
252
253         if (ignore_file(name))
254                 return false;
255
256         return true;
257 }
258
259 static int install_info_add(const char *name) {
260         InstallInfo *i;
261         int r;
262
263         if (!unit_name_valid(name))
264                 return -EINVAL;
265
266         if (hashmap_get(have_installed, name) ||
267             hashmap_get(will_install, name))
268                 return 0;
269
270         if (!(i = new0(InstallInfo, 1))) {
271                 r = -ENOMEM;
272                 goto fail;
273         }
274
275         if (!(i->name = strdup(name))) {
276                 r = -ENOMEM;
277                 goto fail;
278         }
279
280         if ((r = hashmap_put(will_install, i->name, i)) < 0)
281                 goto fail;
282
283         return 0;
284
285 fail:
286         if (i)
287                 install_info_free(i);
288
289         return r;
290 }
291
292 static int daemon_reload(DBusConnection *bus) {
293         DBusMessage *m = NULL, *reply = NULL;
294         DBusError error;
295         int r;
296
297         assert(bus);
298
299         dbus_error_init(&error);
300
301         if (arg_verbose)
302                 log_info("Reloading daemon configuration.");
303
304         if (!(m = dbus_message_new_method_call(
305                               "org.freedesktop.systemd1",
306                               "/org/freedesktop/systemd1",
307                               "org.freedesktop.systemd1.Manager",
308                               "Reload"))) {
309                 log_error("Could not allocate message.");
310                 return -ENOMEM;
311         }
312
313         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
314                 log_error("Failed to reload configuration: %s", error.message);
315                 r = -EIO;
316                 goto finish;
317         }
318
319         r = 0;
320
321 finish:
322         if (m)
323                 dbus_message_unref(m);
324
325         if (reply)
326                 dbus_message_unref(reply);
327
328         dbus_error_free(&error);
329
330         return r;
331 }
332
333 static int install_info_run(DBusConnection *bus, InstallInfo *i, bool enabled) {
334         DBusMessage *m = NULL, *reply = NULL;
335         DBusError error;
336         int r;
337         const char *mode = "replace";
338
339         assert(bus);
340         assert(i);
341
342         dbus_error_init(&error);
343
344         if (arg_action == ACTION_ENABLE ||
345             (arg_action == ACTION_REALIZE && enabled)) {
346
347                 if (arg_realize == REALIZE_MAYBE) {
348                         char **k;
349                         bool yes_please = false;
350
351                         STRV_FOREACH(k, i->wanted_by) {
352                                 DBusMessageIter sub, iter;
353
354                                 const char *path, *state;
355                                 const char *interface = "org.freedesktop.systemd1.Unit";
356                                 const char *property = "ActiveState";
357
358                                 if (!(m = dbus_message_new_method_call(
359                                                       "org.freedesktop.systemd1",
360                                                       "/org/freedesktop/systemd1",
361                                                       "org.freedesktop.systemd1.Manager",
362                                                       "GetUnit"))) {
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, k,
370                                                               DBUS_TYPE_INVALID)) {
371                                         log_error("Could not append arguments to message.");
372                                         r = -ENOMEM;
373                                         goto finish;
374                                 }
375
376                                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
377                                         /* Hmm, this unit doesn't exist, let's try the next one */
378                                         dbus_message_unref(m);
379                                         m = NULL;
380                                         continue;
381                                 }
382
383                                 if (!dbus_message_get_args(reply, &error,
384                                                            DBUS_TYPE_OBJECT_PATH, &path,
385                                                            DBUS_TYPE_INVALID)) {
386                                         log_error("Failed to parse reply: %s", error.message);
387                                         r = -EIO;
388                                         goto finish;
389                                 }
390
391                                 dbus_message_unref(m);
392                                 if (!(m = dbus_message_new_method_call(
393                                                       "org.freedesktop.systemd1",
394                                                       path,
395                                                       "org.freedesktop.DBus.Properties",
396                                                       "Get"))) {
397                                         log_error("Could not allocate message.");
398                                         r = -ENOMEM;
399                                         goto finish;
400                                 }
401
402                                 if (!dbus_message_append_args(m,
403                                                               DBUS_TYPE_STRING, &interface,
404                                                               DBUS_TYPE_STRING, &property,
405                                                               DBUS_TYPE_INVALID)) {
406                                         log_error("Could not append arguments to message.");
407                                         r = -ENOMEM;
408                                         goto finish;
409                                 }
410
411                                 dbus_message_unref(reply);
412                                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
413                                         log_error("Failed to issue method call: %s", error.message);
414                                         r = -EIO;
415                                         goto finish;
416                                 }
417
418                                 if (!dbus_message_iter_init(reply, &iter) ||
419                                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
420                                         log_error("Failed to parse reply.");
421                                         r = -EIO;
422                                         goto finish;
423                                 }
424
425                                 dbus_message_iter_recurse(&iter, &sub);
426
427                                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
428                                         log_error("Failed to parse reply.");
429                                         r = -EIO;
430                                         goto finish;
431                                 }
432
433                                 dbus_message_iter_get_basic(&sub, &state);
434
435                                 dbus_message_unref(m);
436                                 dbus_message_unref(reply);
437                                 m = reply = NULL;
438
439                                 if (streq(state, "active") ||
440                                     startswith(state, "reloading") ||
441                                     startswith(state, "activating")) {
442                                         yes_please = true;
443                                         break;
444                                 }
445                         }
446
447                         if (!yes_please) {
448                                 r = 0;
449                                 goto finish;
450                         }
451                 }
452
453                 if (arg_verbose)
454                         log_info("Restarting %s.", i->name);
455
456                 if (!(m = dbus_message_new_method_call(
457                                       "org.freedesktop.systemd1",
458                                       "/org/freedesktop/systemd1",
459                                       "org.freedesktop.systemd1.Manager",
460                                       arg_realize == REALIZE_MINIMAL ? "TryRestartUnit" : "RestartUnit"))) {
461                         log_error("Could not allocate message.");
462                         r = -ENOMEM;
463                         goto finish;
464                 }
465
466                 if (!dbus_message_append_args(m,
467                                               DBUS_TYPE_STRING, &i->name,
468                                               DBUS_TYPE_STRING, &mode,
469                                               DBUS_TYPE_INVALID)) {
470                         log_error("Could not append arguments to message.");
471                         r = -ENOMEM;
472                         goto finish;
473                 }
474
475
476         } else if (arg_action == ACTION_DISABLE ||
477                    (arg_action == ACTION_REALIZE && !enabled)) {
478
479                 if (arg_verbose)
480                         log_info("Stopping %s.", i->name);
481
482                 if (!(m = dbus_message_new_method_call(
483                                       "org.freedesktop.systemd1",
484                                       "/org/freedesktop/systemd1",
485                                       "org.freedesktop.systemd1.Manager",
486                                       "StopUnit"))) {
487                         log_error("Could not allocate message.");
488                         r = -ENOMEM;
489                         goto finish;
490                 }
491
492                 if (!dbus_message_append_args(m,
493                                               DBUS_TYPE_STRING, &i->name,
494                                               DBUS_TYPE_STRING, &mode,
495                                               DBUS_TYPE_INVALID)) {
496                         log_error("Could not append arguments to message.");
497                         r = -ENOMEM;
498                         goto finish;
499                 }
500         } else
501                 assert_not_reached("install_info_run() called but nothing to do?");
502
503         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
504                 log_error("Failed to realize unit: %s", error.message);
505                 r = -EIO;
506                 goto finish;
507         }
508
509         r = 0;
510
511 finish:
512         if (m)
513                 dbus_message_unref(m);
514
515         if (reply)
516                 dbus_message_unref(reply);
517
518         dbus_error_free(&error);
519
520         return r;
521 }
522
523 static int config_parse_also(
524                 const char *filename,
525                 unsigned line,
526                 const char *section,
527                 const char *lvalue,
528                 const char *rvalue,
529                 void *data,
530                 void *userdata) {
531
532         char *w;
533         size_t l;
534         char *state;
535
536         assert(filename);
537         assert(lvalue);
538         assert(rvalue);
539
540         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
541                 char *n;
542                 int r;
543
544                 if (!(n = strndup(w, l)))
545                         return -ENOMEM;
546
547                 r = install_info_add(n);
548                 free(n);
549
550                 if (r < 0)
551                         return r;
552         }
553
554         return 0;
555 }
556
557 static int mark_symlink_for_removal(const char *p) {
558         char *n;
559         int r;
560
561         assert(p);
562         assert(path_is_absolute(p));
563
564         if (!remove_symlinks_to)
565                 return 0;
566
567         if (!(n = canonicalize_file_name(p)))
568                 if (!(n = strdup(p)))
569                         return -ENOMEM;
570
571         path_kill_slashes(n);
572
573         if ((r = set_put(remove_symlinks_to, n)) < 0) {
574                 free(n);
575                 return r;
576         }
577
578         return 0;
579 }
580
581 static int remove_marked_symlinks_fd(int fd, const char *config_path, const char *root, bool *deleted) {
582         int r = 0;
583         DIR *d;
584         struct dirent *de;
585
586         assert(fd >= 0);
587         assert(root);
588         assert(deleted);
589
590         if (!(d = fdopendir(fd)))
591                 return -errno;
592
593         rewinddir(d);
594
595         while ((de = readdir(d))) {
596                 bool is_dir = false, is_link = false;
597
598                 if (ignore_file(de->d_name))
599                         continue;
600
601                 if (de->d_type == DT_LNK)
602                         is_link = true;
603                 else if (de->d_type == DT_DIR)
604                         is_dir = true;
605                 else if (de->d_type == DT_UNKNOWN) {
606                         struct stat st;
607
608                         if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
609                                 log_error("Failed to stat %s/%s: %m", root, de->d_name);
610
611                                 if (r == 0)
612                                         r = -errno;
613                                 continue;
614                         }
615
616                         is_link = S_ISLNK(st.st_mode);
617                         is_dir = S_ISDIR(st.st_mode);
618                 } else
619                         continue;
620
621                 if (is_dir) {
622                         int nfd, q;
623                         char *p;
624
625                         if ((nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0) {
626                                 log_error("Failed to open %s/%s: %m", root, de->d_name);
627
628                                 if (r == 0)
629                                         r = -errno;
630                                 continue;
631                         }
632
633                         if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
634                                 log_error("Failed to allocate directory string.");
635                                 close_nointr_nofail(nfd);
636                                 r = -ENOMEM;
637                                 break;
638                         }
639
640                         q = remove_marked_symlinks_fd(nfd, config_path, p, deleted);
641                         free(p);
642                         close_nointr_nofail(nfd);
643
644                         if (r == 0)
645                                 q = r;
646
647                 } else if (is_link) {
648                         char *p, *dest, *c;
649                         int q;
650
651                         if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
652                                 log_error("Failed to allocate symlink string.");
653                                 r = -ENOMEM;
654                                 break;
655                         }
656
657                         if ((q = readlink_and_make_absolute(p, &dest)) < 0) {
658                                 log_error("Cannot read symlink %s: %s", p, strerror(-q));
659                                 free(p);
660
661                                 if (r == 0)
662                                         r = q;
663                                 continue;
664                         }
665
666                         if ((c = canonicalize_file_name(dest))) {
667                                 /* This might fail if the destination
668                                  * is already removed */
669
670                                 free(dest);
671                                 dest = c;
672                         }
673
674                         path_kill_slashes(dest);
675                         if (set_get(remove_symlinks_to, dest)) {
676
677                                 if (arg_verbose)
678                                         log_info("rm '%s'", p);
679
680                                 if (unlink(p) < 0) {
681                                         log_error("Cannot unlink symlink %s: %m", p);
682
683                                         if (r == 0)
684                                                 r = -errno;
685                                 } else {
686                                         rmdir_parents(p, config_path);
687                                         path_kill_slashes(p);
688
689                                         if (!set_get(remove_symlinks_to, p)) {
690
691                                                 if ((r = mark_symlink_for_removal(p)) < 0) {
692                                                         if (r == 0)
693                                                                 r = q;
694                                                 } else
695                                                         *deleted = true;
696                                         }
697                                 }
698                         }
699
700                         free(p);
701                         free(dest);
702                 }
703         }
704
705         return r;
706 }
707
708 static int remove_marked_symlinks(const char *config_path) {
709         int fd, r = 0;
710         bool deleted;
711
712         assert(config_path);
713
714         if (set_size(remove_symlinks_to) <= 0)
715                 return 0;
716
717         if ((fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0)
718                 return -errno;
719
720         do {
721                 int q;
722                 deleted = false;
723
724                 if ((q = remove_marked_symlinks_fd(fd, config_path, config_path, &deleted)) < 0) {
725                         if (r == 0)
726                                 r = q;
727                 }
728         } while (deleted);
729
730         close_nointr_nofail(fd);
731
732         return r;
733 }
734
735 static int create_symlink(const char *old_path, const char *new_path) {
736         int r;
737
738         assert(old_path);
739         assert(new_path);
740
741         if (arg_action == ACTION_ENABLE) {
742                 char *dest;
743
744                 mkdir_parents(new_path, 0755);
745
746                 if (symlink(old_path, new_path) >= 0)
747                         return 0;
748
749                 if (errno != EEXIST) {
750                         log_error("Cannot link %s to %s: %m", old_path, new_path);
751                         return -errno;
752                 }
753
754                 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
755
756                         if (errno == EINVAL) {
757                                 log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
758                                 return -EEXIST;
759                         }
760
761                         log_error("readlink() failed: %s", strerror(-r));
762                         return r;
763                 }
764
765                 if (streq(dest, old_path)) {
766                         free(dest);
767                         return 0;
768                 }
769
770                 if (!arg_force) {
771                         log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
772                         free(dest);
773                         return -EEXIST;
774                 }
775
776                 free(dest);
777                 unlink(new_path);
778
779                 if (arg_verbose)
780                         log_info("ln -s '%s' '%s'", old_path, new_path);
781
782                 if (symlink(old_path, new_path) >= 0)
783                         return 0;
784
785                 log_error("Cannot link %s to %s: %m", old_path, new_path);
786                 return -errno;
787
788         } else if (arg_action == ACTION_DISABLE) {
789                 char *dest;
790
791                 if ((r = mark_symlink_for_removal(old_path)) < 0)
792                         return r;
793
794                 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
795                         if (errno == ENOENT)
796                                 return 0;
797
798                         if (errno == EINVAL) {
799                                 log_warning("File %s not a symlink, ignoring.", old_path);
800                                 return 0;
801                         }
802
803                         log_error("readlink() failed: %s", strerror(-r));
804                         return r;
805                 }
806
807                 if (!streq(dest, old_path)) {
808                         log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest);
809                         free(dest);
810                         return 0;
811                 }
812
813
814                 free(dest);
815
816                 if ((r = mark_symlink_for_removal(new_path)) < 0)
817                         return r;
818
819                 if (arg_verbose)
820                         log_info("rm '%s'", new_path);
821
822                 if (unlink(new_path) >= 0)
823                         return 0;
824
825                 log_error("Cannot unlink %s: %m", new_path);
826                 return -errno;
827
828         } else if (arg_action == ACTION_TEST || arg_action == ACTION_REALIZE) {
829                 char *dest;
830
831                 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
832
833                         if (errno == ENOENT || errno == EINVAL)
834                                 return 0;
835
836                         log_error("readlink() failed: %s", strerror(-r));
837                         return r;
838                 }
839
840                 if (streq(dest, old_path)) {
841                         free(dest);
842                         return 1;
843                 }
844
845                 return 0;
846         }
847
848         assert_not_reached("Unknown action.");
849 }
850
851 static int install_info_symlink_alias(InstallInfo *i, const char *config_path) {
852         char **s;
853         char *alias_path = NULL;
854         int r;
855
856         assert(i);
857
858         STRV_FOREACH(s, i->aliases) {
859
860                 if (!unit_name_valid(*s)) {
861                         log_error("Invalid name %s.", *s);
862                         r = -EINVAL;
863                         goto finish;
864                 }
865
866                 free(alias_path);
867                 if (!(alias_path = path_make_absolute(*s, config_path))) {
868                         log_error("Out of memory");
869                         r = -ENOMEM;
870                         goto finish;
871                 }
872
873                 if ((r = create_symlink(i->path, alias_path)) != 0)
874                         goto finish;
875
876                 if (arg_action == ACTION_DISABLE)
877                         rmdir_parents(alias_path, config_path);
878         }
879
880         r = 0;
881
882 finish:
883         free(alias_path);
884
885         return r;
886 }
887
888 static int install_info_symlink_wants(InstallInfo *i, const char *config_path) {
889         char **s;
890         char *alias_path = NULL;
891         int r;
892
893         assert(i);
894
895         STRV_FOREACH(s, i->wanted_by) {
896                 if (!unit_name_valid(*s)) {
897                         log_error("Invalid name %s.", *s);
898                         r = -EINVAL;
899                         goto finish;
900                 }
901
902                 free(alias_path);
903                 alias_path = NULL;
904
905                 if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
906                         log_error("Out of memory");
907                         r = -ENOMEM;
908                         goto finish;
909                 }
910
911                 if ((r = create_symlink(i->path, alias_path)) != 0)
912                         goto finish;
913
914                 if (arg_action == ACTION_DISABLE)
915                         rmdir_parents(alias_path, config_path);
916         }
917
918         r = 0;
919
920 finish:
921         free(alias_path);
922
923         return r;
924 }
925
926 static int install_info_apply(LookupPaths *paths, InstallInfo *i, const char *config_path) {
927
928         const ConfigItem items[] = {
929                 { "Alias",    config_parse_strv, &i->aliases,   "Install" },
930                 { "WantedBy", config_parse_strv, &i->wanted_by, "Install" },
931                 { "Also",     config_parse_also, NULL,          "Install" },
932
933                 { NULL, NULL, NULL, NULL }
934         };
935
936         char **p;
937         char *filename = NULL;
938         FILE *f = NULL;
939         int r;
940
941         assert(paths);
942         assert(i);
943
944         STRV_FOREACH(p, paths->unit_path) {
945                 int fd;
946
947                 if (!(filename = path_make_absolute(i->name, *p))) {
948                         log_error("Out of memory");
949                         return -ENOMEM;
950                 }
951
952                 /* Ensure that we don't follow symlinks */
953                 if ((fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOCTTY)) >= 0)
954                         if ((f = fdopen(fd, "re")))
955                                 break;
956
957                 if (errno == ELOOP) {
958                         log_error("Refusing to operate on symlinks, please pass unit names or absolute paths to unit files.");
959                         free(filename);
960                         return -errno;
961                 }
962
963                 if (errno != ENOENT) {
964                         log_error("Failed to open %s: %m", filename);
965                         free(filename);
966                         return -errno;
967                 }
968
969                 free(filename);
970                 filename = NULL;
971         }
972
973         if (!f) {
974                 log_error("Couldn't find %s.", i->name);
975                 return -ENOENT;
976         }
977
978         i->path = filename;
979
980         if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
981                 fclose(f);
982                 return r;
983         }
984
985         fclose(f);
986
987         if ((r = install_info_symlink_alias(i, config_path)) != 0)
988                 return r;
989
990         if ((r = install_info_symlink_wants(i, config_path)) != 0)
991                 return r;
992
993         if ((r = mark_symlink_for_removal(filename)) < 0)
994                 return r;
995
996         if ((r = remove_marked_symlinks(config_path)) < 0)
997                 return r;
998
999         return 0;
1000 }
1001
1002 static char *get_config_path(void) {
1003
1004         switch (arg_where) {
1005
1006         case WHERE_SYSTEM:
1007                 return strdup(SYSTEM_CONFIG_UNIT_PATH);
1008
1009         case WHERE_GLOBAL:
1010                 return strdup(SESSION_CONFIG_UNIT_PATH);
1011
1012         case WHERE_SESSION: {
1013                 char *p;
1014
1015                 if (session_config_home(&p) < 0)
1016                         return NULL;
1017
1018                 return p;
1019         }
1020
1021         default:
1022                 assert_not_reached("Unknown config path.");
1023         }
1024 }
1025
1026 static int do_realize(bool enabled) {
1027         DBusConnection *bus = NULL;
1028         DBusError error;
1029         int r, q;
1030         Iterator i;
1031         InstallInfo *j;
1032
1033         dbus_error_init(&error);
1034
1035         if (arg_realize == REALIZE_NO)
1036                 return 0;
1037
1038         if (arg_where == WHERE_GLOBAL) {
1039                 log_warning("Warning: --realize has no effect with --global.");
1040                 return 0;
1041         }
1042
1043         if (arg_action == ACTION_TEST) {
1044                 log_warning("Warning: --realize has no effect with test.");
1045                 return 0;
1046         }
1047
1048         if (arg_where == WHERE_SYSTEM && sd_booted() <= 0) {
1049                 log_info("systemd is not running, --realize has no effect.");
1050                 return 0;
1051         }
1052
1053         if (arg_where == WHERE_SYSTEM && running_in_chroot() > 0) {
1054                 log_info("Running in a chroot() environment, --realize has no effect.");
1055                 return 0;
1056         }
1057
1058         if ((r = bus_connect(arg_where == WHERE_SESSION ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, NULL, &error)) < 0) {
1059                 log_error("Failed to get D-Bus connection: %s", error.message);
1060                 goto finish;
1061         }
1062
1063         r = 0;
1064
1065         if (arg_action == ACTION_ENABLE || arg_action == ACTION_REALIZE)
1066                 if ((r = daemon_reload(bus)) < 0)
1067                         goto finish;
1068
1069         if (arg_realize != REALIZE_RELOAD) {
1070                 HASHMAP_FOREACH(j, have_installed, i)
1071                         if ((q = install_info_run(bus, j, enabled)) < 0)
1072                                 r = q;
1073         }
1074
1075         if (arg_action == ACTION_DISABLE)
1076                 if ((q = daemon_reload(bus)) < 0)
1077                         r = q;
1078
1079 finish:
1080         if (bus) {
1081                 dbus_connection_close(bus);
1082                 dbus_connection_unref(bus);
1083         }
1084
1085         dbus_error_free(&error);
1086
1087         dbus_shutdown();
1088         return r;
1089 }
1090
1091 int main(int argc, char *argv[]) {
1092         int r, retval = 1, j;
1093         LookupPaths paths;
1094         InstallInfo *i;
1095         char *config_path = NULL;
1096
1097         zero(paths);
1098
1099         log_parse_environment();
1100
1101         if ((r = parse_argv(argc, argv)) < 0)
1102                 goto finish;
1103         else if (r == 0) {
1104                 retval = 0;
1105                 goto finish;
1106         }
1107
1108         if ((r = lookup_paths_init(&paths, arg_where == WHERE_SYSTEM ? MANAGER_SYSTEM : MANAGER_SESSION)) < 0) {
1109                 log_error("Failed to determine lookup paths: %s", strerror(-r));
1110                 goto finish;
1111         }
1112
1113         if (!(config_path = get_config_path())) {
1114                 log_error("Failed to determine config path");
1115                 goto finish;
1116         }
1117
1118         will_install = hashmap_new(string_hash_func, string_compare_func);
1119         have_installed = hashmap_new(string_hash_func, string_compare_func);
1120
1121         if (!will_install || !have_installed) {
1122                 log_error("Failed to allocate unit sets.");
1123                 goto finish;
1124         }
1125
1126         if (arg_all)
1127                 if (!(remove_symlinks_to = set_new(string_hash_func, string_compare_func))) {
1128                         log_error("Failed to allocate symlink sets.");
1129                         goto finish;
1130                 }
1131
1132         for (j = optind; j < argc; j++)
1133                 if ((r = install_info_add(argv[j])) < 0)
1134                         goto finish;
1135
1136
1137         while ((i = hashmap_first(will_install))) {
1138                 assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
1139
1140                 if ((r = install_info_apply(&paths, i, config_path)) != 0) {
1141
1142                         if (r < 0)
1143                                 goto finish;
1144
1145                         /* In test mode and found something */
1146                         retval = 0;
1147                         break;
1148                 }
1149         }
1150
1151         if (do_realize(!retval) < 0)
1152                 goto finish;
1153
1154 finish:
1155         install_info_hashmap_free(will_install);
1156         install_info_hashmap_free(have_installed);
1157
1158         set_free_free(remove_symlinks_to);
1159
1160         lookup_paths_free(&paths);
1161
1162         free(config_path);
1163
1164         return retval;
1165 }