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/>.
30 #include "path-lookup.h"
34 #include "conf-parser.h"
35 #include "dbus-common.h"
36 #include "sd-daemon.h"
38 static bool arg_force = false;
44 } arg_where = WHERE_SYSTEM;
52 } arg_action = ACTION_INVALID;
55 REALIZE_NO, /* Don't reload/start/stop or anything */
56 REALIZE_RELOAD, /* Only reload daemon config, don't stop/start */
57 REALIZE_MINIMAL, /* Only shutdown/restart if running. */
58 REALIZE_MAYBE, /* Start if WantedBy= suggests */
59 REALIZE_YES /* Start unconditionally */
60 } arg_realize = REALIZE_NO;
70 Hashmap *will_install = NULL, *have_installed = NULL;
72 static int help(void) {
74 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
75 "Install init system units.\n\n"
76 " -h --help Show this help\n"
77 " --force Override existing links\n"
78 " --system Install into system\n"
79 " --session Install into session\n"
80 " --global Install into all sessions\n"
81 " --realize[=MODE] Start/stop/restart unit after installation\n"
82 " Takes 'no', 'minimal', 'maybe' or 'yes'\n\n"
84 " enable [NAME...] Enable one or more units\n"
85 " disable [NAME...] Disable one or more units\n"
86 " realize [NAME...] Test whether any of the specified units are enabled\n"
87 " and the start/stop/restart units accordingly\n"
88 " test [NAME...] Test whether any of the specified units are enabled\n",
89 program_invocation_short_name);
94 static int parse_argv(int argc, char *argv[]) {
104 static const struct option options[] = {
105 { "help", no_argument, NULL, 'h' },
106 { "session", no_argument, NULL, ARG_SESSION },
107 { "system", no_argument, NULL, ARG_SYSTEM },
108 { "global", no_argument, NULL, ARG_GLOBAL },
109 { "force", no_argument, NULL, ARG_FORCE },
110 { "realize", optional_argument, NULL, ARG_REALIZE },
115 bool realize_switch = false;
120 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
129 arg_where = WHERE_SESSION;
133 arg_where = WHERE_SYSTEM;
137 arg_where = WHERE_GLOBAL;
146 realize_switch = true;
149 arg_realize = REALIZE_MAYBE;
150 else if (streq(optarg, "no"))
151 arg_realize = REALIZE_NO;
152 else if (streq(optarg, "minimal"))
153 arg_realize = REALIZE_MINIMAL;
154 else if (streq(optarg, "maybe"))
155 arg_realize = REALIZE_MAYBE;
156 else if (streq(optarg, "yes"))
157 arg_realize = REALIZE_YES;
158 else if (streq(optarg, "reload"))
159 arg_realize = REALIZE_RELOAD;
161 log_error("Invalid --realize argument %s", optarg);
171 log_error("Unknown option code %c", c);
176 if (optind >= argc) {
181 if (streq(argv[optind], "enable"))
182 arg_action = ACTION_ENABLE;
183 else if (streq(argv[optind], "disable"))
184 arg_action = ACTION_DISABLE;
185 else if (streq(argv[optind], "test"))
186 arg_action = ACTION_TEST;
187 else if (streq(argv[optind], "realize")) {
188 arg_action = ACTION_REALIZE;
191 arg_realize = REALIZE_MAYBE;
193 log_error("Unknown verb %s.", argv[optind]);
199 if (optind >= argc) {
200 log_error("Missing unit name.");
208 static void install_info_free(InstallInfo *i) {
213 strv_free(i->aliases);
214 strv_free(i->wanted_by);
218 static void install_info_hashmap_free(Hashmap *m) {
221 while ((i = hashmap_steal_first(m)))
222 install_info_free(i);
227 static bool unit_name_valid(const char *name) {
229 /* This is a minimal version of unit_name_valid() from
235 if (ignore_file(name))
241 static int install_info_add(const char *name) {
245 if (!unit_name_valid(name))
248 if (hashmap_get(have_installed, name) ||
249 hashmap_get(will_install, name))
252 if (!(i = new0(InstallInfo, 1))) {
257 if (!(i->name = strdup(name))) {
262 if ((r = hashmap_put(will_install, i->name, i)) < 0)
269 install_info_free(i);
274 static int daemon_reload(DBusConnection *bus) {
275 DBusMessage *m = NULL, *reply = NULL;
281 dbus_error_init(&error);
283 if (!(m = dbus_message_new_method_call(
284 "org.freedesktop.systemd1",
285 "/org/freedesktop/systemd1",
286 "org.freedesktop.systemd1.Manager",
288 log_error("Could not allocate message.");
292 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
293 log_error("Failed to reload configuration: %s", error.message);
302 dbus_message_unref(m);
305 dbus_message_unref(reply);
307 dbus_error_free(&error);
312 static int install_info_run(DBusConnection *bus, InstallInfo *i, bool enabled) {
313 DBusMessage *m = NULL, *reply = NULL;
316 const char *mode = "replace";
321 dbus_error_init(&error);
323 if (arg_action == ACTION_ENABLE ||
324 (arg_action == ACTION_REALIZE && enabled)) {
326 if (arg_realize == REALIZE_MAYBE) {
328 bool yes_please = false;
330 STRV_FOREACH(k, i->wanted_by) {
331 DBusMessageIter sub, iter;
333 const char *path, *state;
334 const char *interface = "org.freedesktop.systemd1.Unit";
335 const char *property = "ActiveState";
337 if (!(m = dbus_message_new_method_call(
338 "org.freedesktop.systemd1",
339 "/org/freedesktop/systemd1",
340 "org.freedesktop.systemd1.Manager",
342 log_error("Could not allocate message.");
347 if (!dbus_message_append_args(m,
349 DBUS_TYPE_INVALID)) {
350 log_error("Could not append arguments to message.");
355 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
356 /* Hmm, this unit doesn't exist, let's try the next one */
357 dbus_message_unref(m);
362 if (!dbus_message_get_args(reply, &error,
363 DBUS_TYPE_OBJECT_PATH, &path,
364 DBUS_TYPE_INVALID)) {
365 log_error("Failed to parse reply: %s", error.message);
370 dbus_message_unref(m);
371 if (!(m = dbus_message_new_method_call(
372 "org.freedesktop.systemd1",
374 "org.freedesktop.DBus.Properties",
376 log_error("Could not allocate message.");
381 if (!dbus_message_append_args(m,
382 DBUS_TYPE_STRING, &interface,
383 DBUS_TYPE_STRING, &property,
384 DBUS_TYPE_INVALID)) {
385 log_error("Could not append arguments to message.");
390 dbus_message_unref(reply);
391 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
392 log_error("Failed to issue method call: %s", error.message);
397 if (!dbus_message_iter_init(reply, &iter) ||
398 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
399 log_error("Failed to parse reply.");
404 dbus_message_iter_recurse(&iter, &sub);
406 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
407 log_error("Failed to parse reply.");
412 dbus_message_iter_get_basic(&sub, &state);
414 dbus_message_unref(m);
415 dbus_message_unref(reply);
418 if (streq(state, "active") ||
419 startswith(state, "reloading") ||
420 startswith(state, "activating")) {
432 if (!(m = dbus_message_new_method_call(
433 "org.freedesktop.systemd1",
434 "/org/freedesktop/systemd1",
435 "org.freedesktop.systemd1.Manager",
436 arg_realize == REALIZE_MINIMAL ? "TryRestartUnit" : "RestartUnit"))) {
437 log_error("Could not allocate message.");
442 if (!dbus_message_append_args(m,
443 DBUS_TYPE_STRING, &i->name,
444 DBUS_TYPE_STRING, &mode,
445 DBUS_TYPE_INVALID)) {
446 log_error("Could not append arguments to message.");
452 } else if (arg_action == ACTION_DISABLE ||
453 (arg_action == ACTION_REALIZE && !enabled)) {
455 if (!(m = dbus_message_new_method_call(
456 "org.freedesktop.systemd1",
457 "/org/freedesktop/systemd1",
458 "org.freedesktop.systemd1.Manager",
460 log_error("Could not allocate message.");
465 if (!dbus_message_append_args(m,
466 DBUS_TYPE_STRING, &i->name,
467 DBUS_TYPE_STRING, &mode,
468 DBUS_TYPE_INVALID)) {
469 log_error("Could not append arguments to message.");
474 assert_not_reached("install_info_run() called but nothing to do?");
476 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
477 log_error("Failed to realize unit: %s", error.message);
486 dbus_message_unref(m);
489 dbus_message_unref(reply);
491 dbus_error_free(&error);
496 static int config_parse_also(
497 const char *filename,
513 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
517 if (!(n = strndup(w, l)))
520 r = install_info_add(n);
530 static int create_symlink(const char *old_path, const char *new_path) {
536 if (arg_action == ACTION_ENABLE) {
539 mkdir_parents(new_path, 0755);
541 if (symlink(old_path, new_path) >= 0)
544 if (errno != EEXIST) {
545 log_error("Cannot link %s to %s: %m", old_path, new_path);
549 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
551 if (errno == EINVAL) {
552 log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
556 log_error("readlink() failed: %s", strerror(-r));
560 if (streq(dest, old_path)) {
566 log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
574 if (symlink(old_path, new_path) >= 0)
577 log_error("Cannot link %s to %s: %m", old_path, new_path);
580 } else if (arg_action == ACTION_DISABLE) {
583 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
587 if (errno == EINVAL) {
588 log_warning("File %s not a symlink, ignoring.", old_path);
592 log_error("readlink() failed: %s", strerror(-r));
596 if (!streq(dest, old_path)) {
597 log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest);
603 if (unlink(new_path) >= 0)
606 log_error("Cannot unlink %s: %m", new_path);
609 } else if (arg_action == ACTION_TEST || arg_action == ACTION_REALIZE) {
612 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
614 if (errno == ENOENT || errno == EINVAL)
617 log_error("readlink() failed: %s", strerror(-r));
621 if (streq(dest, old_path)) {
629 assert_not_reached("Unknown action.");
632 static int install_info_symlink_alias(InstallInfo *i, const char *config_path) {
634 char *alias_path = NULL;
639 STRV_FOREACH(s, i->aliases) {
641 if (!unit_name_valid(*s)) {
642 log_error("Invalid name %s.", *s);
648 if (!(alias_path = path_make_absolute(*s, config_path))) {
649 log_error("Out of memory");
654 if ((r = create_symlink(i->path, alias_path)) != 0)
657 if (arg_action == ACTION_DISABLE)
658 rmdir_parents(alias_path, config_path);
669 static int install_info_symlink_wants(InstallInfo *i, const char *config_path) {
671 char *alias_path = NULL;
676 STRV_FOREACH(s, i->wanted_by) {
677 if (!unit_name_valid(*s)) {
678 log_error("Invalid name %s.", *s);
686 if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
687 log_error("Out of memory");
692 if ((r = create_symlink(i->path, alias_path)) != 0)
695 if (arg_action == ACTION_DISABLE)
696 rmdir_parents(alias_path, config_path);
707 static int install_info_apply(LookupPaths *paths, InstallInfo *i, const char *config_path) {
709 const ConfigItem items[] = {
710 { "Alias", config_parse_strv, &i->aliases, "Install" },
711 { "WantedBy", config_parse_strv, &i->wanted_by, "Install" },
712 { "Also", config_parse_also, NULL, "Install" },
714 { NULL, NULL, NULL, NULL }
718 char *filename = NULL;
725 STRV_FOREACH(p, paths->unit_path) {
728 if (!(filename = path_make_absolute(i->name, *p))) {
729 log_error("Out of memory");
733 /* Ensure that we don't follow symlinks */
734 if ((fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOCTTY)) >= 0)
735 if ((f = fdopen(fd, "re")))
738 if (errno == ELOOP) {
739 log_error("Refusing to operate on symlinks, please pass unit names or absolute paths to unit files.");
744 if (errno != ENOENT) {
745 log_error("Failed to open %s: %m", filename);
755 log_error("Couldn't find %s.", i->name);
761 if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
768 if ((r = install_info_symlink_alias(i, config_path)) != 0)
771 if ((r = install_info_symlink_wants(i, config_path)) != 0)
777 static char *get_config_path(void) {
782 return strdup(SYSTEM_CONFIG_UNIT_PATH);
785 return strdup(SESSION_CONFIG_UNIT_PATH);
787 case WHERE_SESSION: {
790 if (session_config_home(&p) < 0)
797 assert_not_reached("Unknown config path.");
801 static int do_realize(bool enabled) {
802 DBusConnection *bus = NULL;
808 dbus_error_init(&error);
810 if (arg_realize == REALIZE_NO)
813 if (arg_where == WHERE_GLOBAL) {
814 log_warning("Warning: --realize has no effect with --global.");
818 if (arg_action == ACTION_TEST) {
819 log_warning("Warning: --realize has no effect with test.");
823 if (arg_where == WHERE_SYSTEM && sd_booted() <= 0) {
824 log_info("systemd is not running, --realize has no effect.");
828 if (arg_where == WHERE_SYSTEM && running_in_chroot() > 0) {
829 log_info("Running in a chroot() environment, --realize has no effect.");
833 if ((r = bus_connect(arg_where == WHERE_SESSION ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, NULL, &error)) < 0) {
834 log_error("Failed to get D-Bus connection: %s", error.message);
840 if (arg_action == ACTION_ENABLE || arg_action == ACTION_REALIZE)
841 if ((r = daemon_reload(bus)) < 0)
844 if (arg_realize != REALIZE_RELOAD) {
845 HASHMAP_FOREACH(j, have_installed, i)
846 if ((q = install_info_run(bus, j, enabled)) < 0)
850 if (arg_action == ACTION_DISABLE)
851 if ((q = daemon_reload(bus)) < 0)
856 dbus_connection_close(bus);
857 dbus_connection_unref(bus);
860 dbus_error_free(&error);
866 int main(int argc, char *argv[]) {
867 int r, retval = 1, j;
870 char *config_path = NULL;
874 log_parse_environment();
876 if ((r = parse_argv(argc, argv)) < 0)
883 if ((r = lookup_paths_init(&paths, arg_where == WHERE_SYSTEM ? MANAGER_SYSTEM : MANAGER_SESSION)) < 0) {
884 log_error("Failed to determine lookup paths: %s", strerror(-r));
888 if (!(config_path = get_config_path())) {
889 log_error("Failed to determine config path");
893 will_install = hashmap_new(string_hash_func, string_compare_func);
894 have_installed = hashmap_new(string_hash_func, string_compare_func);
896 if (!will_install || !have_installed) {
897 log_error("Failed to allocate unit sets.");
901 for (j = optind; j < argc; j++)
902 if ((r = install_info_add(argv[j])) < 0)
905 while ((i = hashmap_first(will_install))) {
906 assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
908 if ((r = install_info_apply(&paths, i, config_path)) != 0) {
913 /* In test mode and found something */
919 if (do_realize(!retval) < 0)
923 install_info_hashmap_free(will_install);
924 install_info_hashmap_free(have_installed);
926 lookup_paths_free(&paths);