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"
36 static bool arg_force
= false;
42 } arg_where
= WHERE_SYSTEM
;
49 } arg_action
= ACTION_INVALID
;
52 START_NO
, /* Don't start/stop or anything */
53 START_MINIMAL
, /* Only shutdown/restart if running. */
54 START_MAYBE
, /* Start if WantedBy= suggests */
55 START_YES
/* Start unconditionally */
56 } arg_start
= START_NO
;
66 Hashmap
*will_install
= NULL
, *have_installed
= NULL
;
68 static int help(void) {
70 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
71 "Install init system units.\n\n"
72 " -h --help Show this help\n"
73 " --force Override existing links\n"
74 " --system Install into system\n"
75 " --session Install into session\n"
76 " --global Install into all sessions\n"
77 " --start[=MODE] Start/stop/restart unit after installation\n"
78 " Takes 'no', 'minimal', 'maybe' or 'yes'\n\n"
80 " enable [NAME...] Enable one or more units\n"
81 " disable [NAME...] Disable one or more units\n"
82 " test [NAME...] Test whether any of the specified units are enabled\n",
83 program_invocation_short_name
);
88 static int parse_argv(int argc
, char *argv
[]) {
98 static const struct option options
[] = {
99 { "help", no_argument
, NULL
, 'h' },
100 { "session", no_argument
, NULL
, ARG_SESSION
},
101 { "system", no_argument
, NULL
, ARG_SYSTEM
},
102 { "global", no_argument
, NULL
, ARG_GLOBAL
},
103 { "force", no_argument
, NULL
, ARG_FORCE
},
104 { "start", optional_argument
, NULL
, ARG_START
},
113 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0) {
122 arg_where
= WHERE_SESSION
;
126 arg_where
= WHERE_SYSTEM
;
130 arg_where
= WHERE_GLOBAL
;
140 arg_start
= START_MAYBE
;
141 else if (streq(optarg
, "no"))
142 arg_start
= START_NO
;
143 else if (streq(optarg
, "minimal"))
144 arg_start
= START_MINIMAL
;
145 else if (streq(optarg
, "maybe"))
146 arg_start
= START_MAYBE
;
147 else if (streq(optarg
, "yes"))
148 arg_start
= START_YES
;
150 log_error("Invalid --start argument %s", optarg
);
160 log_error("Unknown option code %c", c
);
165 if (optind
>= argc
) {
170 if (streq(argv
[optind
], "enable"))
171 arg_action
= ACTION_ENABLE
;
172 else if (streq(argv
[optind
], "disable"))
173 arg_action
= ACTION_DISABLE
;
174 else if (streq(argv
[optind
], "test"))
175 arg_action
= ACTION_TEST
;
177 log_error("Unknown verb %s.", argv
[optind
]);
183 if (optind
>= argc
) {
184 log_error("Missing unit name.");
192 static void install_info_free(InstallInfo
*i
) {
197 strv_free(i
->aliases
);
198 strv_free(i
->wanted_by
);
202 static void install_info_hashmap_free(Hashmap
*m
) {
205 while ((i
= hashmap_steal_first(m
)))
206 install_info_free(i
);
211 static bool unit_name_valid(const char *name
) {
213 /* This is a minimal version of unit_name_valid() from
219 if (ignore_file(name
))
225 static int install_info_add(const char *name
) {
229 if (!unit_name_valid(name
))
232 if (hashmap_get(have_installed
, name
) ||
233 hashmap_get(will_install
, name
))
236 if (!(i
= new0(InstallInfo
, 1))) {
241 if (!(i
->name
= strdup(name
))) {
246 if ((r
= hashmap_put(will_install
, i
->name
, i
)) < 0)
253 install_info_free(i
);
258 static int daemon_reload(DBusConnection
*bus
) {
259 DBusMessage
*m
= NULL
, *reply
= NULL
;
265 dbus_error_init(&error
);
267 if (!(m
= dbus_message_new_method_call(
268 "org.freedesktop.systemd1",
269 "/org/freedesktop/systemd1",
270 "org.freedesktop.systemd1.Manager",
272 log_error("Could not allocate message.");
276 if (!(reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
))) {
277 log_error("Failed to reload configuration: %s", error
.message
);
286 dbus_message_unref(m
);
289 dbus_message_unref(reply
);
291 dbus_error_free(&error
);
296 static int install_info_run(DBusConnection
*bus
, InstallInfo
*i
) {
297 DBusMessage
*m
= NULL
, *reply
= NULL
;
300 const char *mode
= "replace";
305 dbus_error_init(&error
);
307 if (arg_action
== ACTION_ENABLE
) {
309 if (arg_start
== START_MAYBE
) {
311 bool yes_please
= false;
313 STRV_FOREACH(k
, i
->wanted_by
) {
314 DBusMessageIter sub
, iter
;
316 const char *path
, *state
;
317 const char *interface
= "org.freedesktop.systemd1.Unit";
318 const char *property
= "ActiveState";
320 if (!(m
= dbus_message_new_method_call(
321 "org.freedesktop.systemd1",
322 "/org/freedesktop/systemd1",
323 "org.freedesktop.systemd1.Manager",
325 log_error("Could not allocate message.");
330 if (!dbus_message_append_args(m
,
332 DBUS_TYPE_INVALID
)) {
333 log_error("Could not append arguments to message.");
338 if (!(reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
))) {
339 /* Hmm, this unit doesn't exist, let's try the next one */
340 dbus_message_unref(m
);
345 if (!dbus_message_get_args(reply
, &error
,
346 DBUS_TYPE_OBJECT_PATH
, &path
,
347 DBUS_TYPE_INVALID
)) {
348 log_error("Failed to parse reply: %s", error
.message
);
353 dbus_message_unref(m
);
354 if (!(m
= dbus_message_new_method_call(
355 "org.freedesktop.systemd1",
357 "org.freedesktop.DBus.Properties",
359 log_error("Could not allocate message.");
364 if (!dbus_message_append_args(m
,
365 DBUS_TYPE_STRING
, &interface
,
366 DBUS_TYPE_STRING
, &property
,
367 DBUS_TYPE_INVALID
)) {
368 log_error("Could not append arguments to message.");
373 dbus_message_unref(reply
);
374 if (!(reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
))) {
375 log_error("Failed to issue method call: %s", error
.message
);
380 if (!dbus_message_iter_init(reply
, &iter
) ||
381 dbus_message_iter_get_arg_type(&iter
) != DBUS_TYPE_VARIANT
) {
382 log_error("Failed to parse reply.");
387 dbus_message_iter_recurse(&iter
, &sub
);
389 if (dbus_message_iter_get_arg_type(&sub
) != DBUS_TYPE_STRING
) {
390 log_error("Failed to parse reply.");
395 dbus_message_iter_get_basic(&sub
, &state
);
397 dbus_message_unref(m
);
398 dbus_message_unref(reply
);
401 if (streq(state
, "active") ||
402 startswith(state
, "reloading") ||
403 startswith(state
, "activating")) {
415 if (!(m
= dbus_message_new_method_call(
416 "org.freedesktop.systemd1",
417 "/org/freedesktop/systemd1",
418 "org.freedesktop.systemd1.Manager",
419 arg_start
== START_MINIMAL
? "TryRestartUnit" : "RestartUnit"))) {
420 log_error("Could not allocate message.");
425 if (!dbus_message_append_args(m
,
426 DBUS_TYPE_STRING
, &i
->name
,
427 DBUS_TYPE_STRING
, &mode
,
428 DBUS_TYPE_INVALID
)) {
429 log_error("Could not append arguments to message.");
435 } else if (arg_action
== ACTION_DISABLE
) {
437 if (!(m
= dbus_message_new_method_call(
438 "org.freedesktop.systemd1",
439 "/org/freedesktop/systemd1",
440 "org.freedesktop.systemd1.Manager",
442 log_error("Could not allocate message.");
447 if (!dbus_message_append_args(m
,
448 DBUS_TYPE_STRING
, &i
->name
,
449 DBUS_TYPE_STRING
, &mode
,
450 DBUS_TYPE_INVALID
)) {
451 log_error("Could not append arguments to message.");
457 if (!(reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
))) {
458 log_error("Failed to reload configuration: %s", error
.message
);
467 dbus_message_unref(m
);
470 dbus_message_unref(reply
);
472 dbus_error_free(&error
);
477 static int config_parse_also(
478 const char *filename
,
494 FOREACH_WORD_QUOTED(w
, l
, rvalue
, state
) {
498 if (!(n
= strndup(w
, l
)))
501 r
= install_info_add(n
);
511 static int create_symlink(const char *old_path
, const char *new_path
) {
517 if (arg_action
== ACTION_ENABLE
) {
520 mkdir_parents(new_path
, 0755);
522 if (symlink(old_path
, new_path
) >= 0)
525 if (errno
!= EEXIST
) {
526 log_error("Cannot link %s to %s: %m", old_path
, new_path
);
530 if ((r
= readlink_and_make_absolute(new_path
, &dest
)) < 0) {
532 if (errno
== EINVAL
) {
533 log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path
, new_path
);
537 log_error("readlink() failed: %s", strerror(-r
));
541 if (streq(dest
, old_path
)) {
547 log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path
, new_path
, dest
);
555 if (symlink(old_path
, new_path
) >= 0)
558 log_error("Cannot link %s to %s: %m", old_path
, new_path
);
561 } else if (arg_action
== ACTION_DISABLE
) {
564 if ((r
= readlink_and_make_absolute(new_path
, &dest
)) < 0) {
568 if (errno
== EINVAL
) {
569 log_warning("File %s not a symlink, ignoring.", old_path
);
573 log_error("readlink() failed: %s", strerror(-r
));
577 if (!streq(dest
, old_path
)) {
578 log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path
, old_path
, dest
);
584 if (unlink(new_path
) >= 0)
587 log_error("Cannot unlink %s: %m", new_path
);
590 } else if (arg_action
== ACTION_TEST
) {
593 if ((r
= readlink_and_make_absolute(new_path
, &dest
)) < 0) {
595 if (errno
== ENOENT
|| errno
== EINVAL
)
598 log_error("readlink() failed: %s", strerror(-r
));
602 if (streq(dest
, old_path
)) {
610 assert_not_reached("Unknown action.");
613 static int install_info_symlink_alias(InstallInfo
*i
, const char *config_path
) {
615 char *alias_path
= NULL
;
620 STRV_FOREACH(s
, i
->aliases
) {
622 if (!unit_name_valid(*s
)) {
623 log_error("Invalid name %s.", *s
);
629 if (!(alias_path
= path_make_absolute(*s
, config_path
))) {
630 log_error("Out of memory");
635 if ((r
= create_symlink(i
->path
, alias_path
)) != 0)
638 if (arg_action
== ACTION_DISABLE
)
639 rmdir_parents(alias_path
, config_path
);
650 static int install_info_symlink_wants(InstallInfo
*i
, const char *config_path
) {
652 char *alias_path
= NULL
;
657 STRV_FOREACH(s
, i
->wanted_by
) {
658 if (!unit_name_valid(*s
)) {
659 log_error("Invalid name %s.", *s
);
667 if (asprintf(&alias_path
, "%s/%s.wants/%s", config_path
, *s
, i
->name
) < 0) {
668 log_error("Out of memory");
673 if ((r
= create_symlink(i
->path
, alias_path
)) != 0)
676 if (arg_action
== ACTION_DISABLE
)
677 rmdir_parents(alias_path
, config_path
);
688 static int install_info_apply(LookupPaths
*paths
, InstallInfo
*i
, const char *config_path
) {
690 const ConfigItem items
[] = {
691 { "Alias", config_parse_strv
, &i
->aliases
, "Install" },
692 { "WantedBy", config_parse_strv
, &i
->wanted_by
, "Install" },
693 { "Also", config_parse_also
, NULL
, "Install" },
695 { NULL
, NULL
, NULL
, NULL
}
699 char *filename
= NULL
;
706 STRV_FOREACH(p
, paths
->unit_path
) {
708 if (!(filename
= path_make_absolute(i
->name
, *p
))) {
709 log_error("Out of memory");
713 if ((f
= fopen(filename
, "re")))
719 if (errno
!= ENOENT
) {
720 log_error("Failed to open %s: %m", filename
);
726 log_error("Couldn't find %s.", i
->name
);
732 if ((r
= config_parse(filename
, f
, NULL
, items
, true, i
)) < 0) {
739 if ((r
= install_info_symlink_alias(i
, config_path
)) != 0)
742 if ((r
= install_info_symlink_wants(i
, config_path
)) != 0)
748 static char *get_config_path(void) {
753 return strdup(SYSTEM_CONFIG_UNIT_PATH
);
756 return strdup(SESSION_CONFIG_UNIT_PATH
);
758 case WHERE_SESSION
: {
761 if (session_config_home(&p
) < 0)
768 assert_not_reached("Unknown config path.");
772 static int do_run(void) {
773 DBusConnection
*bus
= NULL
;
779 dbus_error_init(&error
);
781 if (arg_start
== START_NO
)
784 if (arg_where
== WHERE_GLOBAL
) {
785 log_warning("Warning: --start has no effect with --global.");
789 if (arg_action
!= ACTION_ENABLE
&& arg_action
!= ACTION_DISABLE
) {
790 log_warning("Warning: --start has no effect with test.");
794 if ((r
= bus_connect(arg_where
== WHERE_SESSION
? DBUS_BUS_SESSION
: DBUS_BUS_SYSTEM
, &bus
, &error
)) < 0) {
795 log_error("Failed to get D-Bus connection: %s", error
.message
);
801 if (arg_action
== ACTION_ENABLE
)
802 if ((r
= daemon_reload(bus
)) < 0)
805 HASHMAP_FOREACH(j
, have_installed
, i
)
806 if ((q
= install_info_run(bus
, j
)) < 0)
809 if (arg_action
== ACTION_DISABLE
)
810 if ((q
= daemon_reload(bus
)) < 0)
815 dbus_connection_unref(bus
);
817 dbus_error_free(&error
);
823 int main(int argc
, char *argv
[]) {
824 int r
, retval
= 1, j
;
827 char *config_path
= NULL
;
831 log_parse_environment();
833 if ((r
= parse_argv(argc
, argv
)) < 0)
840 if ((r
= lookup_paths_init(&paths
, arg_where
== WHERE_SYSTEM
? MANAGER_SYSTEM
: MANAGER_SESSION
)) < 0) {
841 log_error("Failed to determine lookup paths: %s", strerror(-r
));
845 if (!(config_path
= get_config_path())) {
846 log_error("Failed to determine config path");
850 will_install
= hashmap_new(string_hash_func
, string_compare_func
);
851 have_installed
= hashmap_new(string_hash_func
, string_compare_func
);
853 if (!will_install
|| !have_installed
) {
854 log_error("Failed to allocate unit sets.");
858 for (j
= optind
; j
< argc
; j
++)
859 if ((r
= install_info_add(argv
[j
])) < 0)
862 while ((i
= hashmap_first(will_install
))) {
863 assert_se(hashmap_move_one(have_installed
, will_install
, i
->name
) == 0);
865 if ((r
= install_info_apply(&paths
, i
, config_path
)) != 0) {
870 /* In test mode and found something */
879 retval
= arg_action
== ACTION_TEST
? 1 : 0;
882 install_info_hashmap_free(will_install
);
883 install_info_hashmap_free(have_installed
);
885 lookup_paths_free(&paths
);