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;
151 else if (streq(optarg, "reload"))
152 arg_realize = REALIZE_RELOAD;
154 log_error("Invalid --realize argument %s", optarg);
164 log_error("Unknown option code %c", c);
169 if (optind >= argc) {
174 if (streq(argv[optind], "enable"))
175 arg_action = ACTION_ENABLE;
176 else if (streq(argv[optind], "disable"))
177 arg_action = ACTION_DISABLE;
178 else if (streq(argv[optind], "test"))
179 arg_action = ACTION_TEST;
181 log_error("Unknown verb %s.", argv[optind]);
187 if (optind >= argc) {
188 log_error("Missing unit name.");
196 static void install_info_free(InstallInfo *i) {
201 strv_free(i->aliases);
202 strv_free(i->wanted_by);
206 static void install_info_hashmap_free(Hashmap *m) {
209 while ((i = hashmap_steal_first(m)))
210 install_info_free(i);
215 static bool unit_name_valid(const char *name) {
217 /* This is a minimal version of unit_name_valid() from
223 if (ignore_file(name))
229 static int install_info_add(const char *name) {
233 if (!unit_name_valid(name))
236 if (hashmap_get(have_installed, name) ||
237 hashmap_get(will_install, name))
240 if (!(i = new0(InstallInfo, 1))) {
245 if (!(i->name = strdup(name))) {
250 if ((r = hashmap_put(will_install, i->name, i)) < 0)
257 install_info_free(i);
262 static int daemon_reload(DBusConnection *bus) {
263 DBusMessage *m = NULL, *reply = NULL;
269 dbus_error_init(&error);
271 if (!(m = dbus_message_new_method_call(
272 "org.freedesktop.systemd1",
273 "/org/freedesktop/systemd1",
274 "org.freedesktop.systemd1.Manager",
276 log_error("Could not allocate message.");
280 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
281 log_error("Failed to reload configuration: %s", error.message);
290 dbus_message_unref(m);
293 dbus_message_unref(reply);
295 dbus_error_free(&error);
300 static int install_info_run(DBusConnection *bus, InstallInfo *i) {
301 DBusMessage *m = NULL, *reply = NULL;
304 const char *mode = "replace";
309 dbus_error_init(&error);
311 if (arg_action == ACTION_ENABLE) {
313 if (arg_realize == REALIZE_MAYBE) {
315 bool yes_please = false;
317 STRV_FOREACH(k, i->wanted_by) {
318 DBusMessageIter sub, iter;
320 const char *path, *state;
321 const char *interface = "org.freedesktop.systemd1.Unit";
322 const char *property = "ActiveState";
324 if (!(m = dbus_message_new_method_call(
325 "org.freedesktop.systemd1",
326 "/org/freedesktop/systemd1",
327 "org.freedesktop.systemd1.Manager",
329 log_error("Could not allocate message.");
334 if (!dbus_message_append_args(m,
336 DBUS_TYPE_INVALID)) {
337 log_error("Could not append arguments to message.");
342 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
343 /* Hmm, this unit doesn't exist, let's try the next one */
344 dbus_message_unref(m);
349 if (!dbus_message_get_args(reply, &error,
350 DBUS_TYPE_OBJECT_PATH, &path,
351 DBUS_TYPE_INVALID)) {
352 log_error("Failed to parse reply: %s", error.message);
357 dbus_message_unref(m);
358 if (!(m = dbus_message_new_method_call(
359 "org.freedesktop.systemd1",
361 "org.freedesktop.DBus.Properties",
363 log_error("Could not allocate message.");
368 if (!dbus_message_append_args(m,
369 DBUS_TYPE_STRING, &interface,
370 DBUS_TYPE_STRING, &property,
371 DBUS_TYPE_INVALID)) {
372 log_error("Could not append arguments to message.");
377 dbus_message_unref(reply);
378 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
379 log_error("Failed to issue method call: %s", error.message);
384 if (!dbus_message_iter_init(reply, &iter) ||
385 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
386 log_error("Failed to parse reply.");
391 dbus_message_iter_recurse(&iter, &sub);
393 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
394 log_error("Failed to parse reply.");
399 dbus_message_iter_get_basic(&sub, &state);
401 dbus_message_unref(m);
402 dbus_message_unref(reply);
405 if (streq(state, "active") ||
406 startswith(state, "reloading") ||
407 startswith(state, "activating")) {
419 if (!(m = dbus_message_new_method_call(
420 "org.freedesktop.systemd1",
421 "/org/freedesktop/systemd1",
422 "org.freedesktop.systemd1.Manager",
423 arg_realize == REALIZE_MINIMAL ? "TryRestartUnit" : "RestartUnit"))) {
424 log_error("Could not allocate message.");
429 if (!dbus_message_append_args(m,
430 DBUS_TYPE_STRING, &i->name,
431 DBUS_TYPE_STRING, &mode,
432 DBUS_TYPE_INVALID)) {
433 log_error("Could not append arguments to message.");
439 } else if (arg_action == ACTION_DISABLE) {
441 if (!(m = dbus_message_new_method_call(
442 "org.freedesktop.systemd1",
443 "/org/freedesktop/systemd1",
444 "org.freedesktop.systemd1.Manager",
446 log_error("Could not allocate message.");
451 if (!dbus_message_append_args(m,
452 DBUS_TYPE_STRING, &i->name,
453 DBUS_TYPE_STRING, &mode,
454 DBUS_TYPE_INVALID)) {
455 log_error("Could not append arguments to message.");
461 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
462 log_error("Failed to reload configuration: %s", error.message);
471 dbus_message_unref(m);
474 dbus_message_unref(reply);
476 dbus_error_free(&error);
481 static int config_parse_also(
482 const char *filename,
498 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
502 if (!(n = strndup(w, l)))
505 r = install_info_add(n);
515 static int create_symlink(const char *old_path, const char *new_path) {
521 if (arg_action == ACTION_ENABLE) {
524 mkdir_parents(new_path, 0755);
526 if (symlink(old_path, new_path) >= 0)
529 if (errno != EEXIST) {
530 log_error("Cannot link %s to %s: %m", old_path, new_path);
534 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
536 if (errno == EINVAL) {
537 log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
541 log_error("readlink() failed: %s", strerror(-r));
545 if (streq(dest, old_path)) {
551 log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
559 if (symlink(old_path, new_path) >= 0)
562 log_error("Cannot link %s to %s: %m", old_path, new_path);
565 } else if (arg_action == ACTION_DISABLE) {
568 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
572 if (errno == EINVAL) {
573 log_warning("File %s not a symlink, ignoring.", old_path);
577 log_error("readlink() failed: %s", strerror(-r));
581 if (!streq(dest, old_path)) {
582 log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest);
588 if (unlink(new_path) >= 0)
591 log_error("Cannot unlink %s: %m", new_path);
594 } else if (arg_action == ACTION_TEST) {
597 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
599 if (errno == ENOENT || errno == EINVAL)
602 log_error("readlink() failed: %s", strerror(-r));
606 if (streq(dest, old_path)) {
614 assert_not_reached("Unknown action.");
617 static int install_info_symlink_alias(InstallInfo *i, const char *config_path) {
619 char *alias_path = NULL;
624 STRV_FOREACH(s, i->aliases) {
626 if (!unit_name_valid(*s)) {
627 log_error("Invalid name %s.", *s);
633 if (!(alias_path = path_make_absolute(*s, config_path))) {
634 log_error("Out of memory");
639 if ((r = create_symlink(i->path, alias_path)) != 0)
642 if (arg_action == ACTION_DISABLE)
643 rmdir_parents(alias_path, config_path);
654 static int install_info_symlink_wants(InstallInfo *i, const char *config_path) {
656 char *alias_path = NULL;
661 STRV_FOREACH(s, i->wanted_by) {
662 if (!unit_name_valid(*s)) {
663 log_error("Invalid name %s.", *s);
671 if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
672 log_error("Out of memory");
677 if ((r = create_symlink(i->path, alias_path)) != 0)
680 if (arg_action == ACTION_DISABLE)
681 rmdir_parents(alias_path, config_path);
692 static int install_info_apply(LookupPaths *paths, InstallInfo *i, const char *config_path) {
694 const ConfigItem items[] = {
695 { "Alias", config_parse_strv, &i->aliases, "Install" },
696 { "WantedBy", config_parse_strv, &i->wanted_by, "Install" },
697 { "Also", config_parse_also, NULL, "Install" },
699 { NULL, NULL, NULL, NULL }
703 char *filename = NULL;
710 STRV_FOREACH(p, paths->unit_path) {
712 if (!(filename = path_make_absolute(i->name, *p))) {
713 log_error("Out of memory");
717 if ((f = fopen(filename, "re")))
723 if (errno != ENOENT) {
724 log_error("Failed to open %s: %m", filename);
730 log_error("Couldn't find %s.", i->name);
736 if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
743 if ((r = install_info_symlink_alias(i, config_path)) != 0)
746 if ((r = install_info_symlink_wants(i, config_path)) != 0)
752 static char *get_config_path(void) {
757 return strdup(SYSTEM_CONFIG_UNIT_PATH);
760 return strdup(SESSION_CONFIG_UNIT_PATH);
762 case WHERE_SESSION: {
765 if (session_config_home(&p) < 0)
772 assert_not_reached("Unknown config path.");
776 static int do_realize(void) {
777 DBusConnection *bus = NULL;
783 dbus_error_init(&error);
785 if (arg_realize == REALIZE_NO)
788 if (arg_where == WHERE_GLOBAL) {
789 log_warning("Warning: --realize has no effect with --global.");
793 if (arg_action != ACTION_ENABLE && arg_action != ACTION_DISABLE) {
794 log_warning("Warning: --realize has no effect with test.");
798 if (arg_where == WHERE_SYSTEM && sd_booted() <= 0) {
799 log_info("systemd is not running, --realize has not effect.");
803 if (arg_where == WHERE_SYSTEM && running_in_chroot() > 0) {
804 log_info("Running in a chroot() environment, --realize has no effect.");
808 if ((r = bus_connect(arg_where == WHERE_SESSION ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, NULL, &error)) < 0) {
809 log_error("Failed to get D-Bus connection: %s", error.message);
815 if (arg_action == ACTION_ENABLE)
816 if ((r = daemon_reload(bus)) < 0)
819 if (arg_realize != REALIZE_RELOAD) {
820 HASHMAP_FOREACH(j, have_installed, i)
821 if ((q = install_info_run(bus, j)) < 0)
825 if (arg_action == ACTION_DISABLE)
826 if ((q = daemon_reload(bus)) < 0)
831 dbus_connection_close(bus);
832 dbus_connection_unref(bus);
835 dbus_error_free(&error);
841 int main(int argc, char *argv[]) {
842 int r, retval = 1, j;
845 char *config_path = NULL;
849 log_parse_environment();
851 if ((r = parse_argv(argc, argv)) < 0)
858 if ((r = lookup_paths_init(&paths, arg_where == WHERE_SYSTEM ? MANAGER_SYSTEM : MANAGER_SESSION)) < 0) {
859 log_error("Failed to determine lookup paths: %s", strerror(-r));
863 if (!(config_path = get_config_path())) {
864 log_error("Failed to determine config path");
868 will_install = hashmap_new(string_hash_func, string_compare_func);
869 have_installed = hashmap_new(string_hash_func, string_compare_func);
871 if (!will_install || !have_installed) {
872 log_error("Failed to allocate unit sets.");
876 for (j = optind; j < argc; j++)
877 if ((r = install_info_add(argv[j])) < 0)
880 while ((i = hashmap_first(will_install))) {
881 assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
883 if ((r = install_info_apply(&paths, i, config_path)) != 0) {
888 /* In test mode and found something */
894 if (do_realize() < 0)
897 retval = arg_action == ACTION_TEST ? 1 : 0;
900 install_info_hashmap_free(will_install);
901 install_info_hashmap_free(have_installed);
903 lookup_paths_free(&paths);