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;
50 } arg_action = ACTION_INVALID;
53 REALIZE_NO, /* Don't reload/start/stop or anything */
54 REALIZE_RELOAD, /* Only reload daemon config, don't stop/start */
55 REALIZE_MINIMAL, /* Only shutdown/restart if running. */
56 REALIZE_MAYBE, /* Start if WantedBy= suggests */
57 REALIZE_YES /* Start unconditionally */
58 } arg_realize = REALIZE_NO;
68 Hashmap *will_install = NULL, *have_installed = NULL;
70 static int help(void) {
72 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
73 "Install init system units.\n\n"
74 " -h --help Show this help\n"
75 " --force Override existing links\n"
76 " --system Install into system\n"
77 " --session Install into session\n"
78 " --global Install into all sessions\n"
79 " --realize[=MODE] Start/stop/restart unit after installation\n"
80 " Takes 'no', 'minimal', 'maybe' or 'yes'\n\n"
82 " enable [NAME...] Enable one or more units\n"
83 " disable [NAME...] Disable one or more units\n"
84 " test [NAME...] Test whether any of the specified units are enabled\n",
85 program_invocation_short_name);
90 static int parse_argv(int argc, char *argv[]) {
100 static const struct option options[] = {
101 { "help", no_argument, NULL, 'h' },
102 { "session", no_argument, NULL, ARG_SESSION },
103 { "system", no_argument, NULL, ARG_SYSTEM },
104 { "global", no_argument, NULL, ARG_GLOBAL },
105 { "force", no_argument, NULL, ARG_FORCE },
106 { "realize", optional_argument, NULL, ARG_REALIZE },
115 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
124 arg_where = WHERE_SESSION;
128 arg_where = WHERE_SYSTEM;
132 arg_where = WHERE_GLOBAL;
142 arg_realize = REALIZE_MAYBE;
143 else if (streq(optarg, "no"))
144 arg_realize = REALIZE_NO;
145 else if (streq(optarg, "minimal"))
146 arg_realize = REALIZE_MINIMAL;
147 else if (streq(optarg, "maybe"))
148 arg_realize = REALIZE_MAYBE;
149 else if (streq(optarg, "yes"))
150 arg_realize = REALIZE_YES;
152 log_error("Invalid --realize argument %s", optarg);
162 log_error("Unknown option code %c", c);
167 if (optind >= argc) {
172 if (streq(argv[optind], "enable"))
173 arg_action = ACTION_ENABLE;
174 else if (streq(argv[optind], "disable"))
175 arg_action = ACTION_DISABLE;
176 else if (streq(argv[optind], "test"))
177 arg_action = ACTION_TEST;
179 log_error("Unknown verb %s.", argv[optind]);
185 if (optind >= argc) {
186 log_error("Missing unit name.");
194 static void install_info_free(InstallInfo *i) {
199 strv_free(i->aliases);
200 strv_free(i->wanted_by);
204 static void install_info_hashmap_free(Hashmap *m) {
207 while ((i = hashmap_steal_first(m)))
208 install_info_free(i);
213 static bool unit_name_valid(const char *name) {
215 /* This is a minimal version of unit_name_valid() from
221 if (ignore_file(name))
227 static int install_info_add(const char *name) {
231 if (!unit_name_valid(name))
234 if (hashmap_get(have_installed, name) ||
235 hashmap_get(will_install, name))
238 if (!(i = new0(InstallInfo, 1))) {
243 if (!(i->name = strdup(name))) {
248 if ((r = hashmap_put(will_install, i->name, i)) < 0)
255 install_info_free(i);
260 static int daemon_reload(DBusConnection *bus) {
261 DBusMessage *m = NULL, *reply = NULL;
267 dbus_error_init(&error);
269 if (!(m = dbus_message_new_method_call(
270 "org.freedesktop.systemd1",
271 "/org/freedesktop/systemd1",
272 "org.freedesktop.systemd1.Manager",
274 log_error("Could not allocate message.");
278 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
279 log_error("Failed to reload configuration: %s", error.message);
288 dbus_message_unref(m);
291 dbus_message_unref(reply);
293 dbus_error_free(&error);
298 static int install_info_run(DBusConnection *bus, InstallInfo *i) {
299 DBusMessage *m = NULL, *reply = NULL;
302 const char *mode = "replace";
307 dbus_error_init(&error);
309 if (arg_action == ACTION_ENABLE) {
311 if (arg_realize == REALIZE_MAYBE) {
313 bool yes_please = false;
315 STRV_FOREACH(k, i->wanted_by) {
316 DBusMessageIter sub, iter;
318 const char *path, *state;
319 const char *interface = "org.freedesktop.systemd1.Unit";
320 const char *property = "ActiveState";
322 if (!(m = dbus_message_new_method_call(
323 "org.freedesktop.systemd1",
324 "/org/freedesktop/systemd1",
325 "org.freedesktop.systemd1.Manager",
327 log_error("Could not allocate message.");
332 if (!dbus_message_append_args(m,
334 DBUS_TYPE_INVALID)) {
335 log_error("Could not append arguments to message.");
340 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
341 /* Hmm, this unit doesn't exist, let's try the next one */
342 dbus_message_unref(m);
347 if (!dbus_message_get_args(reply, &error,
348 DBUS_TYPE_OBJECT_PATH, &path,
349 DBUS_TYPE_INVALID)) {
350 log_error("Failed to parse reply: %s", error.message);
355 dbus_message_unref(m);
356 if (!(m = dbus_message_new_method_call(
357 "org.freedesktop.systemd1",
359 "org.freedesktop.DBus.Properties",
361 log_error("Could not allocate message.");
366 if (!dbus_message_append_args(m,
367 DBUS_TYPE_STRING, &interface,
368 DBUS_TYPE_STRING, &property,
369 DBUS_TYPE_INVALID)) {
370 log_error("Could not append arguments to message.");
375 dbus_message_unref(reply);
376 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
377 log_error("Failed to issue method call: %s", error.message);
382 if (!dbus_message_iter_init(reply, &iter) ||
383 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
384 log_error("Failed to parse reply.");
389 dbus_message_iter_recurse(&iter, &sub);
391 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
392 log_error("Failed to parse reply.");
397 dbus_message_iter_get_basic(&sub, &state);
399 dbus_message_unref(m);
400 dbus_message_unref(reply);
403 if (streq(state, "active") ||
404 startswith(state, "reloading") ||
405 startswith(state, "activating")) {
417 if (!(m = dbus_message_new_method_call(
418 "org.freedesktop.systemd1",
419 "/org/freedesktop/systemd1",
420 "org.freedesktop.systemd1.Manager",
421 arg_realize == REALIZE_MINIMAL ? "TryRestartUnit" : "RestartUnit"))) {
422 log_error("Could not allocate message.");
427 if (!dbus_message_append_args(m,
428 DBUS_TYPE_STRING, &i->name,
429 DBUS_TYPE_STRING, &mode,
430 DBUS_TYPE_INVALID)) {
431 log_error("Could not append arguments to message.");
437 } else if (arg_action == ACTION_DISABLE) {
439 if (!(m = dbus_message_new_method_call(
440 "org.freedesktop.systemd1",
441 "/org/freedesktop/systemd1",
442 "org.freedesktop.systemd1.Manager",
444 log_error("Could not allocate message.");
449 if (!dbus_message_append_args(m,
450 DBUS_TYPE_STRING, &i->name,
451 DBUS_TYPE_STRING, &mode,
452 DBUS_TYPE_INVALID)) {
453 log_error("Could not append arguments to message.");
459 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
460 log_error("Failed to reload configuration: %s", error.message);
469 dbus_message_unref(m);
472 dbus_message_unref(reply);
474 dbus_error_free(&error);
479 static int config_parse_also(
480 const char *filename,
496 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
500 if (!(n = strndup(w, l)))
503 r = install_info_add(n);
513 static int create_symlink(const char *old_path, const char *new_path) {
519 if (arg_action == ACTION_ENABLE) {
522 mkdir_parents(new_path, 0755);
524 if (symlink(old_path, new_path) >= 0)
527 if (errno != EEXIST) {
528 log_error("Cannot link %s to %s: %m", old_path, new_path);
532 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
534 if (errno == EINVAL) {
535 log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
539 log_error("readlink() failed: %s", strerror(-r));
543 if (streq(dest, old_path)) {
549 log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
557 if (symlink(old_path, new_path) >= 0)
560 log_error("Cannot link %s to %s: %m", old_path, new_path);
563 } else if (arg_action == ACTION_DISABLE) {
566 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
570 if (errno == EINVAL) {
571 log_warning("File %s not a symlink, ignoring.", old_path);
575 log_error("readlink() failed: %s", strerror(-r));
579 if (!streq(dest, old_path)) {
580 log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest);
586 if (unlink(new_path) >= 0)
589 log_error("Cannot unlink %s: %m", new_path);
592 } else if (arg_action == ACTION_TEST) {
595 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
597 if (errno == ENOENT || errno == EINVAL)
600 log_error("readlink() failed: %s", strerror(-r));
604 if (streq(dest, old_path)) {
612 assert_not_reached("Unknown action.");
615 static int install_info_symlink_alias(InstallInfo *i, const char *config_path) {
617 char *alias_path = NULL;
622 STRV_FOREACH(s, i->aliases) {
624 if (!unit_name_valid(*s)) {
625 log_error("Invalid name %s.", *s);
631 if (!(alias_path = path_make_absolute(*s, config_path))) {
632 log_error("Out of memory");
637 if ((r = create_symlink(i->path, alias_path)) != 0)
640 if (arg_action == ACTION_DISABLE)
641 rmdir_parents(alias_path, config_path);
652 static int install_info_symlink_wants(InstallInfo *i, const char *config_path) {
654 char *alias_path = NULL;
659 STRV_FOREACH(s, i->wanted_by) {
660 if (!unit_name_valid(*s)) {
661 log_error("Invalid name %s.", *s);
669 if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
670 log_error("Out of memory");
675 if ((r = create_symlink(i->path, alias_path)) != 0)
678 if (arg_action == ACTION_DISABLE)
679 rmdir_parents(alias_path, config_path);
690 static int install_info_apply(LookupPaths *paths, InstallInfo *i, const char *config_path) {
692 const ConfigItem items[] = {
693 { "Alias", config_parse_strv, &i->aliases, "Install" },
694 { "WantedBy", config_parse_strv, &i->wanted_by, "Install" },
695 { "Also", config_parse_also, NULL, "Install" },
697 { NULL, NULL, NULL, NULL }
701 char *filename = NULL;
708 STRV_FOREACH(p, paths->unit_path) {
710 if (!(filename = path_make_absolute(i->name, *p))) {
711 log_error("Out of memory");
715 if ((f = fopen(filename, "re")))
721 if (errno != ENOENT) {
722 log_error("Failed to open %s: %m", filename);
728 log_error("Couldn't find %s.", i->name);
734 if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
741 if ((r = install_info_symlink_alias(i, config_path)) != 0)
744 if ((r = install_info_symlink_wants(i, config_path)) != 0)
750 static char *get_config_path(void) {
755 return strdup(SYSTEM_CONFIG_UNIT_PATH);
758 return strdup(SESSION_CONFIG_UNIT_PATH);
760 case WHERE_SESSION: {
763 if (session_config_home(&p) < 0)
770 assert_not_reached("Unknown config path.");
774 static int do_run(void) {
775 DBusConnection *bus = NULL;
781 dbus_error_init(&error);
783 if (arg_realize == REALIZE_NO)
786 if (arg_where == WHERE_GLOBAL) {
787 log_warning("Warning: --realize has no effect with --global.");
791 if (arg_action != ACTION_ENABLE && arg_action != ACTION_DISABLE) {
792 log_warning("Warning: --realize has no effect with test.");
796 if (arg_where == WHERE_SYSTEM && sd_booted() <= 0) {
797 log_info("systemd is not running, --realize has not effect.");
801 if (arg_where == WHERE_SYSTEM && running_in_chroot() > 0) {
802 log_info("Running in a chroot() environment, --realize has no effect.");
806 if ((r = bus_connect(arg_where == WHERE_SESSION ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, NULL, &error)) < 0) {
807 log_error("Failed to get D-Bus connection: %s", error.message);
813 if (arg_action == ACTION_ENABLE)
814 if ((r = daemon_reload(bus)) < 0)
817 if (arg_realize != REALIZE_RELOAD) {
818 HASHMAP_FOREACH(j, have_installed, i)
819 if ((q = install_info_run(bus, j)) < 0)
823 if (arg_action == ACTION_DISABLE)
824 if ((q = daemon_reload(bus)) < 0)
829 dbus_connection_unref(bus);
831 dbus_error_free(&error);
837 int main(int argc, char *argv[]) {
838 int r, retval = 1, j;
841 char *config_path = NULL;
845 log_parse_environment();
847 if ((r = parse_argv(argc, argv)) < 0)
854 if ((r = lookup_paths_init(&paths, arg_where == WHERE_SYSTEM ? MANAGER_SYSTEM : MANAGER_SESSION)) < 0) {
855 log_error("Failed to determine lookup paths: %s", strerror(-r));
859 if (!(config_path = get_config_path())) {
860 log_error("Failed to determine config path");
864 will_install = hashmap_new(string_hash_func, string_compare_func);
865 have_installed = hashmap_new(string_hash_func, string_compare_func);
867 if (!will_install || !have_installed) {
868 log_error("Failed to allocate unit sets.");
872 for (j = optind; j < argc; j++)
873 if ((r = install_info_add(argv[j])) < 0)
876 while ((i = hashmap_first(will_install))) {
877 assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
879 if ((r = install_info_apply(&paths, i, config_path)) != 0) {
884 /* In test mode and found something */
893 retval = arg_action == ACTION_TEST ? 1 : 0;
896 install_info_hashmap_free(will_install);
897 install_info_hashmap_free(have_installed);
899 lookup_paths_free(&paths);