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/>.
29 #include "path-lookup.h"
33 #include "conf-parser.h"
34 #include "dbus-common.h"
35 #include "sd-daemon.h"
37 static bool arg_force = false;
43 } arg_where = WHERE_SYSTEM;
51 } arg_action = ACTION_INVALID;
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;
69 Hashmap *will_install = NULL, *have_installed = NULL;
71 static int help(void) {
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"
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);
93 static int parse_argv(int argc, char *argv[]) {
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 },
114 bool realize_switch = false;
119 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
128 arg_where = WHERE_SESSION;
132 arg_where = WHERE_SYSTEM;
136 arg_where = WHERE_GLOBAL;
145 realize_switch = true;
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;
160 log_error("Invalid --realize argument %s", optarg);
170 log_error("Unknown option code %c", c);
175 if (optind >= argc) {
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;
190 arg_realize = REALIZE_MAYBE;
192 log_error("Unknown verb %s.", argv[optind]);
198 if (optind >= argc) {
199 log_error("Missing unit name.");
207 static void install_info_free(InstallInfo *i) {
212 strv_free(i->aliases);
213 strv_free(i->wanted_by);
217 static void install_info_hashmap_free(Hashmap *m) {
220 while ((i = hashmap_steal_first(m)))
221 install_info_free(i);
226 static bool unit_name_valid(const char *name) {
228 /* This is a minimal version of unit_name_valid() from
234 if (ignore_file(name))
240 static int install_info_add(const char *name) {
244 if (!unit_name_valid(name))
247 if (hashmap_get(have_installed, name) ||
248 hashmap_get(will_install, name))
251 if (!(i = new0(InstallInfo, 1))) {
256 if (!(i->name = strdup(name))) {
261 if ((r = hashmap_put(will_install, i->name, i)) < 0)
268 install_info_free(i);
273 static int daemon_reload(DBusConnection *bus) {
274 DBusMessage *m = NULL, *reply = NULL;
280 dbus_error_init(&error);
282 if (!(m = dbus_message_new_method_call(
283 "org.freedesktop.systemd1",
284 "/org/freedesktop/systemd1",
285 "org.freedesktop.systemd1.Manager",
287 log_error("Could not allocate message.");
291 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
292 log_error("Failed to reload configuration: %s", error.message);
301 dbus_message_unref(m);
304 dbus_message_unref(reply);
306 dbus_error_free(&error);
311 static int install_info_run(DBusConnection *bus, InstallInfo *i, bool enabled) {
312 DBusMessage *m = NULL, *reply = NULL;
315 const char *mode = "replace";
320 dbus_error_init(&error);
322 if (arg_action == ACTION_ENABLE ||
323 (arg_action == ACTION_REALIZE && enabled)) {
325 if (arg_realize == REALIZE_MAYBE) {
327 bool yes_please = false;
329 STRV_FOREACH(k, i->wanted_by) {
330 DBusMessageIter sub, iter;
332 const char *path, *state;
333 const char *interface = "org.freedesktop.systemd1.Unit";
334 const char *property = "ActiveState";
336 if (!(m = dbus_message_new_method_call(
337 "org.freedesktop.systemd1",
338 "/org/freedesktop/systemd1",
339 "org.freedesktop.systemd1.Manager",
341 log_error("Could not allocate message.");
346 if (!dbus_message_append_args(m,
348 DBUS_TYPE_INVALID)) {
349 log_error("Could not append arguments to message.");
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);
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);
369 dbus_message_unref(m);
370 if (!(m = dbus_message_new_method_call(
371 "org.freedesktop.systemd1",
373 "org.freedesktop.DBus.Properties",
375 log_error("Could not allocate message.");
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.");
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);
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.");
403 dbus_message_iter_recurse(&iter, &sub);
405 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
406 log_error("Failed to parse reply.");
411 dbus_message_iter_get_basic(&sub, &state);
413 dbus_message_unref(m);
414 dbus_message_unref(reply);
417 if (streq(state, "active") ||
418 startswith(state, "reloading") ||
419 startswith(state, "activating")) {
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.");
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.");
451 } else if (arg_action == ACTION_DISABLE ||
452 (arg_action == ACTION_REALIZE && !enabled)) {
454 if (!(m = dbus_message_new_method_call(
455 "org.freedesktop.systemd1",
456 "/org/freedesktop/systemd1",
457 "org.freedesktop.systemd1.Manager",
459 log_error("Could not allocate message.");
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.");
473 assert_not_reached("install_info_run() called but nothing to do?");
475 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
476 log_error("Failed to realize unit: %s", error.message);
485 dbus_message_unref(m);
488 dbus_message_unref(reply);
490 dbus_error_free(&error);
495 static int config_parse_also(
496 const char *filename,
512 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
516 if (!(n = strndup(w, l)))
519 r = install_info_add(n);
529 static int create_symlink(const char *old_path, const char *new_path) {
535 if (arg_action == ACTION_ENABLE) {
538 mkdir_parents(new_path, 0755);
540 if (symlink(old_path, new_path) >= 0)
543 if (errno != EEXIST) {
544 log_error("Cannot link %s to %s: %m", old_path, new_path);
548 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
550 if (errno == EINVAL) {
551 log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
555 log_error("readlink() failed: %s", strerror(-r));
559 if (streq(dest, old_path)) {
565 log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
573 if (symlink(old_path, new_path) >= 0)
576 log_error("Cannot link %s to %s: %m", old_path, new_path);
579 } else if (arg_action == ACTION_DISABLE) {
582 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
586 if (errno == EINVAL) {
587 log_warning("File %s not a symlink, ignoring.", old_path);
591 log_error("readlink() failed: %s", strerror(-r));
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);
602 if (unlink(new_path) >= 0)
605 log_error("Cannot unlink %s: %m", new_path);
608 } else if (arg_action == ACTION_TEST || arg_action == ACTION_REALIZE) {
611 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
613 if (errno == ENOENT || errno == EINVAL)
616 log_error("readlink() failed: %s", strerror(-r));
620 if (streq(dest, old_path)) {
628 assert_not_reached("Unknown action.");
631 static int install_info_symlink_alias(InstallInfo *i, const char *config_path) {
633 char *alias_path = NULL;
638 STRV_FOREACH(s, i->aliases) {
640 if (!unit_name_valid(*s)) {
641 log_error("Invalid name %s.", *s);
647 if (!(alias_path = path_make_absolute(*s, config_path))) {
648 log_error("Out of memory");
653 if ((r = create_symlink(i->path, alias_path)) != 0)
656 if (arg_action == ACTION_DISABLE)
657 rmdir_parents(alias_path, config_path);
668 static int install_info_symlink_wants(InstallInfo *i, const char *config_path) {
670 char *alias_path = NULL;
675 STRV_FOREACH(s, i->wanted_by) {
676 if (!unit_name_valid(*s)) {
677 log_error("Invalid name %s.", *s);
685 if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
686 log_error("Out of memory");
691 if ((r = create_symlink(i->path, alias_path)) != 0)
694 if (arg_action == ACTION_DISABLE)
695 rmdir_parents(alias_path, config_path);
706 static int install_info_apply(LookupPaths *paths, InstallInfo *i, const char *config_path) {
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" },
713 { NULL, NULL, NULL, NULL }
717 char *filename = NULL;
724 STRV_FOREACH(p, paths->unit_path) {
726 if (!(filename = path_make_absolute(i->name, *p))) {
727 log_error("Out of memory");
731 if ((f = fopen(filename, "re")))
737 if (errno != ENOENT) {
738 log_error("Failed to open %s: %m", filename);
744 log_error("Couldn't find %s.", i->name);
750 if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
757 if ((r = install_info_symlink_alias(i, config_path)) != 0)
760 if ((r = install_info_symlink_wants(i, config_path)) != 0)
766 static char *get_config_path(void) {
771 return strdup(SYSTEM_CONFIG_UNIT_PATH);
774 return strdup(SESSION_CONFIG_UNIT_PATH);
776 case WHERE_SESSION: {
779 if (session_config_home(&p) < 0)
786 assert_not_reached("Unknown config path.");
790 static int do_realize(bool enabled) {
791 DBusConnection *bus = NULL;
797 dbus_error_init(&error);
799 if (arg_realize == REALIZE_NO)
802 if (arg_where == WHERE_GLOBAL) {
803 log_warning("Warning: --realize has no effect with --global.");
807 if (arg_action == ACTION_TEST) {
808 log_warning("Warning: --realize has no effect with test.");
812 if (arg_where == WHERE_SYSTEM && sd_booted() <= 0) {
813 log_info("systemd is not running, --realize has not effect.");
817 if (arg_where == WHERE_SYSTEM && running_in_chroot() > 0) {
818 log_info("Running in a chroot() environment, --realize has no effect.");
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);
829 if (arg_action == ACTION_ENABLE || arg_action == ACTION_REALIZE)
830 if ((r = daemon_reload(bus)) < 0)
833 if (arg_realize != REALIZE_RELOAD) {
834 HASHMAP_FOREACH(j, have_installed, i)
835 if ((q = install_info_run(bus, j, enabled)) < 0)
839 if (arg_action == ACTION_DISABLE)
840 if ((q = daemon_reload(bus)) < 0)
845 dbus_connection_close(bus);
846 dbus_connection_unref(bus);
849 dbus_error_free(&error);
855 int main(int argc, char *argv[]) {
856 int r, retval = 1, j;
859 char *config_path = NULL;
863 log_parse_environment();
865 if ((r = parse_argv(argc, argv)) < 0)
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));
877 if (!(config_path = get_config_path())) {
878 log_error("Failed to determine config path");
882 will_install = hashmap_new(string_hash_func, string_compare_func);
883 have_installed = hashmap_new(string_hash_func, string_compare_func);
885 if (!will_install || !have_installed) {
886 log_error("Failed to allocate unit sets.");
890 for (j = optind; j < argc; j++)
891 if ((r = install_info_add(argv[j])) < 0)
894 while ((i = hashmap_first(will_install))) {
895 assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
897 if ((r = install_info_apply(&paths, i, config_path)) != 0) {
902 /* In test mode and found something */
908 if (do_realize(!retval) < 0)
912 install_info_hashmap_free(will_install);
913 install_info_hashmap_free(have_installed);
915 lookup_paths_free(&paths);