1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
31 #include "path-lookup.h"
35 #include "conf-parser.h"
36 #include "dbus-common.h"
37 #include "sd-daemon.h"
39 static bool arg_force = false;
40 static bool arg_all = false;
41 static bool arg_verbose = false;
47 } arg_where = WHERE_SYSTEM;
55 } arg_action = ACTION_INVALID;
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;
73 static Hashmap *will_install = NULL, *have_installed = NULL;
74 static Set *remove_symlinks_to = NULL;
76 static int help(void) {
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"
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);
101 static int parse_argv(int argc, char *argv[]) {
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' },
125 bool realize_switch = false;
130 while ((c = getopt_long(argc, argv, "hv", options, NULL)) >= 0) {
139 arg_where = WHERE_SESSION;
143 arg_where = WHERE_SYSTEM;
147 arg_where = WHERE_GLOBAL;
156 realize_switch = true;
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;
171 log_error("Invalid --realize argument %s", optarg);
189 log_error("Unknown option code %c", c);
194 if (optind >= argc) {
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;
209 arg_realize = REALIZE_MAYBE;
211 log_error("Unknown verb %s.", argv[optind]);
217 if (optind >= argc) {
218 log_error("Missing unit name.");
226 static void install_info_free(InstallInfo *i) {
231 strv_free(i->aliases);
232 strv_free(i->wanted_by);
236 static void install_info_hashmap_free(Hashmap *m) {
239 while ((i = hashmap_steal_first(m)))
240 install_info_free(i);
245 static bool unit_name_valid(const char *name) {
247 /* This is a minimal version of unit_name_valid() from
253 if (ignore_file(name))
259 static int install_info_add(const char *name) {
263 if (!unit_name_valid(name))
266 if (hashmap_get(have_installed, name) ||
267 hashmap_get(will_install, name))
270 if (!(i = new0(InstallInfo, 1))) {
275 if (!(i->name = strdup(name))) {
280 if ((r = hashmap_put(will_install, i->name, i)) < 0)
287 install_info_free(i);
292 static int daemon_reload(DBusConnection *bus) {
293 DBusMessage *m = NULL, *reply = NULL;
299 dbus_error_init(&error);
302 log_info("Reloading daemon configuration.");
304 if (!(m = dbus_message_new_method_call(
305 "org.freedesktop.systemd1",
306 "/org/freedesktop/systemd1",
307 "org.freedesktop.systemd1.Manager",
309 log_error("Could not allocate message.");
313 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
314 log_error("Failed to reload configuration: %s", error.message);
323 dbus_message_unref(m);
326 dbus_message_unref(reply);
328 dbus_error_free(&error);
333 static int install_info_run(DBusConnection *bus, InstallInfo *i, bool enabled) {
334 DBusMessage *m = NULL, *reply = NULL;
337 const char *mode = "replace";
342 dbus_error_init(&error);
344 if (arg_action == ACTION_ENABLE ||
345 (arg_action == ACTION_REALIZE && enabled)) {
347 if (arg_realize == REALIZE_MAYBE) {
349 bool yes_please = false;
351 STRV_FOREACH(k, i->wanted_by) {
352 DBusMessageIter sub, iter;
354 const char *path, *state;
355 const char *interface = "org.freedesktop.systemd1.Unit";
356 const char *property = "ActiveState";
358 if (!(m = dbus_message_new_method_call(
359 "org.freedesktop.systemd1",
360 "/org/freedesktop/systemd1",
361 "org.freedesktop.systemd1.Manager",
363 log_error("Could not allocate message.");
368 if (!dbus_message_append_args(m,
370 DBUS_TYPE_INVALID)) {
371 log_error("Could not append arguments to message.");
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);
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);
391 dbus_message_unref(m);
392 if (!(m = dbus_message_new_method_call(
393 "org.freedesktop.systemd1",
395 "org.freedesktop.DBus.Properties",
397 log_error("Could not allocate message.");
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.");
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);
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.");
425 dbus_message_iter_recurse(&iter, &sub);
427 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
428 log_error("Failed to parse reply.");
433 dbus_message_iter_get_basic(&sub, &state);
435 dbus_message_unref(m);
436 dbus_message_unref(reply);
439 if (streq(state, "active") ||
440 startswith(state, "reloading") ||
441 startswith(state, "activating")) {
454 log_info("Restarting %s.", i->name);
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.");
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.");
476 } else if (arg_action == ACTION_DISABLE ||
477 (arg_action == ACTION_REALIZE && !enabled)) {
480 log_info("Stopping %s.", i->name);
482 if (!(m = dbus_message_new_method_call(
483 "org.freedesktop.systemd1",
484 "/org/freedesktop/systemd1",
485 "org.freedesktop.systemd1.Manager",
487 log_error("Could not allocate message.");
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.");
501 assert_not_reached("install_info_run() called but nothing to do?");
503 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
504 log_error("Failed to realize unit: %s", error.message);
513 dbus_message_unref(m);
516 dbus_message_unref(reply);
518 dbus_error_free(&error);
523 static int config_parse_also(
524 const char *filename,
540 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
544 if (!(n = strndup(w, l)))
547 r = install_info_add(n);
557 static int mark_symlink_for_removal(const char *p) {
562 assert(path_is_absolute(p));
564 if (!remove_symlinks_to)
567 if (!(n = canonicalize_file_name(p)))
568 if (!(n = strdup(p)))
571 path_kill_slashes(n);
573 if ((r = set_put(remove_symlinks_to, n)) < 0) {
581 static int remove_marked_symlinks_fd(int fd, const char *config_path, const char *root, bool *deleted) {
590 if (!(d = fdopendir(fd)))
595 while ((de = readdir(d))) {
596 bool is_dir = false, is_link = false;
598 if (ignore_file(de->d_name))
601 if (de->d_type == DT_LNK)
603 else if (de->d_type == DT_DIR)
605 else if (de->d_type == DT_UNKNOWN) {
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);
616 is_link = S_ISLNK(st.st_mode);
617 is_dir = S_ISDIR(st.st_mode);
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);
633 if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
634 log_error("Failed to allocate directory string.");
635 close_nointr_nofail(nfd);
640 q = remove_marked_symlinks_fd(nfd, config_path, p, deleted);
642 close_nointr_nofail(nfd);
647 } else if (is_link) {
651 if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
652 log_error("Failed to allocate symlink string.");
657 if ((q = readlink_and_make_absolute(p, &dest)) < 0) {
658 log_error("Cannot read symlink %s: %s", p, strerror(-q));
666 if ((c = canonicalize_file_name(dest))) {
667 /* This might fail if the destination
668 * is already removed */
674 path_kill_slashes(dest);
675 if (set_get(remove_symlinks_to, dest)) {
678 log_info("rm '%s'", p);
681 log_error("Cannot unlink symlink %s: %m", p);
686 rmdir_parents(p, config_path);
687 path_kill_slashes(p);
689 if (!set_get(remove_symlinks_to, p)) {
691 if ((r = mark_symlink_for_removal(p)) < 0) {
708 static int remove_marked_symlinks(const char *config_path) {
714 if (set_size(remove_symlinks_to) <= 0)
717 if ((fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0)
724 if ((q = remove_marked_symlinks_fd(fd, config_path, config_path, &deleted)) < 0) {
730 close_nointr_nofail(fd);
735 static int create_symlink(const char *old_path, const char *new_path) {
741 if (arg_action == ACTION_ENABLE) {
744 mkdir_parents(new_path, 0755);
746 if (symlink(old_path, new_path) >= 0)
749 if (errno != EEXIST) {
750 log_error("Cannot link %s to %s: %m", old_path, new_path);
754 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
756 if (errno == EINVAL) {
757 log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
761 log_error("readlink() failed: %s", strerror(-r));
765 if (streq(dest, old_path)) {
771 log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
780 log_info("ln -s '%s' '%s'", old_path, new_path);
782 if (symlink(old_path, new_path) >= 0)
785 log_error("Cannot link %s to %s: %m", old_path, new_path);
788 } else if (arg_action == ACTION_DISABLE) {
791 if ((r = mark_symlink_for_removal(old_path)) < 0)
794 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
798 if (errno == EINVAL) {
799 log_warning("File %s not a symlink, ignoring.", old_path);
803 log_error("readlink() failed: %s", strerror(-r));
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);
816 if ((r = mark_symlink_for_removal(new_path)) < 0)
820 log_info("rm '%s'", new_path);
822 if (unlink(new_path) >= 0)
825 log_error("Cannot unlink %s: %m", new_path);
828 } else if (arg_action == ACTION_TEST || arg_action == ACTION_REALIZE) {
831 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
833 if (errno == ENOENT || errno == EINVAL)
836 log_error("readlink() failed: %s", strerror(-r));
840 if (streq(dest, old_path)) {
848 assert_not_reached("Unknown action.");
851 static int install_info_symlink_alias(InstallInfo *i, const char *config_path) {
853 char *alias_path = NULL;
858 STRV_FOREACH(s, i->aliases) {
860 if (!unit_name_valid(*s)) {
861 log_error("Invalid name %s.", *s);
867 if (!(alias_path = path_make_absolute(*s, config_path))) {
868 log_error("Out of memory");
873 if ((r = create_symlink(i->path, alias_path)) != 0)
876 if (arg_action == ACTION_DISABLE)
877 rmdir_parents(alias_path, config_path);
888 static int install_info_symlink_wants(InstallInfo *i, const char *config_path) {
890 char *alias_path = NULL;
895 STRV_FOREACH(s, i->wanted_by) {
896 if (!unit_name_valid(*s)) {
897 log_error("Invalid name %s.", *s);
905 if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
906 log_error("Out of memory");
911 if ((r = create_symlink(i->path, alias_path)) != 0)
914 if (arg_action == ACTION_DISABLE)
915 rmdir_parents(alias_path, config_path);
926 static int install_info_apply(LookupPaths *paths, InstallInfo *i, const char *config_path) {
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" },
933 { NULL, NULL, NULL, NULL }
937 char *filename = NULL;
944 STRV_FOREACH(p, paths->unit_path) {
947 if (!(filename = path_make_absolute(i->name, *p))) {
948 log_error("Out of memory");
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")))
957 if (errno == ELOOP) {
958 log_error("Refusing to operate on symlinks, please pass unit names or absolute paths to unit files.");
963 if (errno != ENOENT) {
964 log_error("Failed to open %s: %m", filename);
974 log_error("Couldn't find %s.", i->name);
980 if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
987 if ((r = install_info_symlink_alias(i, config_path)) != 0)
990 if ((r = install_info_symlink_wants(i, config_path)) != 0)
993 if ((r = mark_symlink_for_removal(filename)) < 0)
996 if ((r = remove_marked_symlinks(config_path)) < 0)
1002 static char *get_config_path(void) {
1004 switch (arg_where) {
1007 return strdup(SYSTEM_CONFIG_UNIT_PATH);
1010 return strdup(SESSION_CONFIG_UNIT_PATH);
1012 case WHERE_SESSION: {
1015 if (session_config_home(&p) < 0)
1022 assert_not_reached("Unknown config path.");
1026 static int do_realize(bool enabled) {
1027 DBusConnection *bus = NULL;
1033 dbus_error_init(&error);
1035 if (arg_realize == REALIZE_NO)
1038 if (arg_where == WHERE_GLOBAL) {
1039 log_warning("Warning: --realize has no effect with --global.");
1043 if (arg_action == ACTION_TEST) {
1044 log_warning("Warning: --realize has no effect with test.");
1048 if (arg_where == WHERE_SYSTEM && sd_booted() <= 0) {
1049 log_info("systemd is not running, --realize has no effect.");
1053 if (arg_where == WHERE_SYSTEM && running_in_chroot() > 0) {
1054 log_info("Running in a chroot() environment, --realize has no effect.");
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);
1065 if (arg_action == ACTION_ENABLE || arg_action == ACTION_REALIZE)
1066 if ((r = daemon_reload(bus)) < 0)
1069 if (arg_realize != REALIZE_RELOAD) {
1070 HASHMAP_FOREACH(j, have_installed, i)
1071 if ((q = install_info_run(bus, j, enabled)) < 0)
1075 if (arg_action == ACTION_DISABLE)
1076 if ((q = daemon_reload(bus)) < 0)
1081 dbus_connection_close(bus);
1082 dbus_connection_unref(bus);
1085 dbus_error_free(&error);
1091 int main(int argc, char *argv[]) {
1092 int r, retval = 1, j;
1095 char *config_path = NULL;
1099 log_parse_environment();
1101 if ((r = parse_argv(argc, argv)) < 0)
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));
1113 if (!(config_path = get_config_path())) {
1114 log_error("Failed to determine config path");
1118 will_install = hashmap_new(string_hash_func, string_compare_func);
1119 have_installed = hashmap_new(string_hash_func, string_compare_func);
1121 if (!will_install || !have_installed) {
1122 log_error("Failed to allocate unit sets.");
1127 if (!(remove_symlinks_to = set_new(string_hash_func, string_compare_func))) {
1128 log_error("Failed to allocate symlink sets.");
1132 for (j = optind; j < argc; j++)
1133 if ((r = install_info_add(argv[j])) < 0)
1137 while ((i = hashmap_first(will_install))) {
1138 assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
1140 if ((r = install_info_apply(&paths, i, config_path)) != 0) {
1145 /* In test mode and found something */
1151 if (do_realize(!retval) < 0)
1155 install_info_hashmap_free(will_install);
1156 install_info_hashmap_free(have_installed);
1158 set_free_free(remove_symlinks_to);
1160 lookup_paths_free(&paths);