diff options
author | Václav Pavlín <vpavlin@redhat.com> | 2013-05-28 11:05:48 +0200 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2013-05-30 20:44:41 -0400 |
commit | 99504dd4c13af7516a976fffc0f68e6f26d3faac (patch) | |
tree | b0650f8436617bb48701353f28310ac7f9a62de9 | |
parent | 9749cd77bc6121a304a7f1eb0f03f26e620dc9da (diff) |
systemctl: add commands set-default and get-default
systemctl set-default NAME links the default.target to the given unit,
get-default prints out the path to the currently set default target.
-rw-r--r-- | man/systemctl.xml | 18 | ||||
-rw-r--r-- | shell-completion/bash/systemctl | 6 | ||||
-rw-r--r-- | src/core/dbus-manager.c | 28 | ||||
-rw-r--r-- | src/core/org.freedesktop.systemd1.conf | 4 | ||||
-rw-r--r-- | src/shared/install.c | 86 | ||||
-rw-r--r-- | src/shared/install.h | 2 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 65 |
7 files changed, 206 insertions, 3 deletions
diff --git a/man/systemctl.xml b/man/systemctl.xml index 0afb0cc626..430e16c327 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -998,6 +998,24 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service </varlistentry> <varlistentry> + <term><command>get-default</command></term> + + <listitem> + <para>Get the default target specified + via <filename>default.target</filename> link.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><command>set-default <replaceable>NAME</replaceable></command></term> + + <listitem> + <para>Set the default target to boot into. Command links + <filename>default.target</filename> to the given unit.</para> + </listitem> + </varlistentry> + + <varlistentry> <term><command>load <replaceable>NAME</replaceable>...</command></term> <listitem> diff --git a/shell-completion/bash/systemctl b/shell-completion/bash/systemctl index 191b8d13ec..a05b756980 100644 --- a/shell-completion/bash/systemctl +++ b/shell-completion/bash/systemctl @@ -134,9 +134,10 @@ _systemctl () { [STANDALONE]='daemon-reexec daemon-reload default dump emergency exit halt hibernate hybrid-sleep kexec list-jobs list-units list-unit-files poweroff reboot rescue - show-environment suspend' + show-environment suspend get-default' [NAME]='snapshot load' [FILE]='link' + [TARGETS]='set-default' ) for ((i=0; $i <= $COMP_CWORD; i++)); do @@ -210,6 +211,9 @@ _systemctl () { elif __contains_word "$verb" ${VERBS[FILE]}; then comps=$( compgen -A file -- "$cur" ) compopt -o filenames + elif __contains_word "$verb" ${VERBS[TARGETS]}; then + comps=$( __systemctl $mode list-unit-files --type target --full --all \ + | { while read -r a b; do echo " $a"; done; } ) fi COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 56b02a1cf5..f3ddfc9761 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -227,6 +227,13 @@ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ + " </method>\n" \ + " <method name=\"SetDefaultTarget\">\n" \ + " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \ + " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \ + " </method>\n" \ + " <method name=\"GetDefaultTarget\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"out\"/>\n" \ " </method>\n" #define BUS_MANAGER_INTERFACE_SIGNALS \ @@ -1728,7 +1735,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReenableUnitFiles") || dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LinkUnitFiles") || dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PresetUnitFiles") || - dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles")) { + dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles") || + dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetDefaultTarget")) { char **l = NULL; DBusMessageIter iter; @@ -1771,6 +1779,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, carries_install_info = r; } else if (streq(member, "MaskUnitFiles")) r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes); + else if (streq(member, "SetDefaultTarget")) + r = unit_file_set_default(scope, NULL, l[0], &changes, &n_changes); else assert_not_reached("Uh? Wrong method"); @@ -1838,6 +1848,22 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, if (!reply) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetDefaultTarget")) { + UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; + _cleanup_free_ char *default_target = NULL; + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + r = unit_file_get_default(scope, NULL, &default_target); + + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &default_target, DBUS_TYPE_INVALID)) { + goto oom; + } } else { const BusBoundProperties bps[] = { { "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string }, diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index a07a8e1ce3..a375dce0b0 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -86,6 +86,10 @@ send_interface="org.freedesktop.systemd1.Manager" send_member="Dump"/> + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" + send_member="GetDefaultTarget"/> + <allow receive_sender="org.freedesktop.systemd1"/> </policy> diff --git a/src/shared/install.c b/src/shared/install.c index 8f27c6d479..954dcb1e71 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -1570,6 +1570,92 @@ int unit_file_reenable( return r; } +int unit_file_set_default( + UnitFileScope scope, + const char *root_dir, + char *file, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_install_context_done_ InstallContext c = {}; + _cleanup_free_ char *config_path = NULL; + char *path; + int r; + InstallInfo *i = NULL; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + if (unit_name_to_type(file) != UNIT_TARGET) + return -EINVAL; + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + r = get_config_path(scope, false, root_dir, &config_path); + if (r < 0) + return r; + + r = install_info_add_auto(&c, file); + if (r < 0) + return r; + + i = (InstallInfo*)hashmap_first(c.will_install); + + r = unit_file_search(&c, i, &paths, root_dir, false); + if (r < 0) + return r; + + path = strappenda(config_path, "/default.target"); + r = create_symlink(i->path, path, true, changes, n_changes); + if (r < 0) + return r; + + return 0; +} + +int unit_file_get_default( + UnitFileScope scope, + const char *root_dir, + char **name) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + char **p; + int r; + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + STRV_FOREACH(p, paths.unit_path) { + _cleanup_free_ char *path = NULL, *tmp = NULL; + + if (isempty(root_dir)) + path = strappend(*p, "/default.target"); + else + path = strjoin(root_dir, "/", *p, "/default.target", NULL); + + if (!path) + return -ENOMEM; + + r = readlink_malloc(path, &tmp); + if (r == -ENOENT) + continue; + else if (r < 0) + return r; + + *name = strdup(path_get_file_name(tmp)); + if (!*name) + return -ENOMEM; + + return 0; + } + + return -ENOENT; +} + UnitFileState unit_file_get_state( UnitFileScope scope, const char *root_dir, diff --git a/src/shared/install.h b/src/shared/install.h index 94516c9d05..5609d1e8df 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -80,6 +80,8 @@ int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes); +int unit_file_set_default(UnitFileScope scope, const char *root_dir, char *file, UnitFileChange **changes, unsigned *n_changes); +int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name); UnitFileState unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index f8573d315c..56021a6889 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1177,6 +1177,59 @@ static int list_dependencies(DBusConnection *bus, char **args) { return list_dependencies_one(bus, u, 0, &units, 0); } +static int get_default(DBusConnection *bus, char **args) { + char *path = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + int r; + _cleanup_dbus_error_free_ DBusError error; + + dbus_error_init(&error); + + if (!bus || avoid_bus()) { + r = unit_file_get_default(arg_scope, arg_root, &path); + + if (r < 0) { + log_error("Operation failed: %s", strerror(-r)); + goto finish; + } + + r = 0; + } else { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetDefaultTarget", + &reply, + NULL, + DBUS_TYPE_INVALID); + + if (r < 0) { + log_error("Operation failed: %s", strerror(-r)); + goto finish; + } + + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse reply: %s", bus_error_message(&error)); + dbus_error_free(&error); + return -EIO; + } + } + + if (path) + printf("%s\n", path); + +finish: + if ((!bus || avoid_bus()) && path) + free(path); + + return r; + +} + struct job_info { uint32_t id; char *name, *type, *state; @@ -4243,6 +4296,8 @@ static int enable_unit(DBusConnection *bus, char **args) { r = unit_file_mask(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); else if (streq(verb, "unmask")) r = unit_file_unmask(arg_scope, arg_runtime, arg_root, mangled_names, &changes, &n_changes); + else if (streq(verb, "set-default")) + r = unit_file_set_default(arg_scope, arg_root, args[1], &changes, &n_changes); else assert_not_reached("Unknown verb"); @@ -4286,6 +4341,8 @@ static int enable_unit(DBusConnection *bus, char **args) { else if (streq(verb, "unmask")) { method = "UnmaskUnitFiles"; send_force = false; + } else if (streq(verb, "set-default")) { + method = "SetDefaultTarget"; } else assert_not_reached("Unknown verb"); @@ -4585,6 +4642,8 @@ static int systemctl_help(void) { " unmask [NAME...] Unmask one or more units\n" " link [PATH...] Link one or more units files into\n" " the search path\n" + " get-default Get the name of the default target\n" + " set-default NAME Set the default target\n" " is-enabled [NAME...] Check whether unit files are enabled\n\n" "Job Commands:\n" " list-jobs List jobs\n" @@ -5646,6 +5705,8 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "link", MORE, 2, enable_unit }, { "switch-root", MORE, 2, switch_root }, { "list-dependencies", LESS, 2, list_dependencies }, + { "set-default", EQUAL, 2, enable_unit }, + { "get-default", LESS, 1, get_default }, }; int left; @@ -5717,7 +5778,9 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError !streq(verbs[i].verb, "preset") && !streq(verbs[i].verb, "mask") && !streq(verbs[i].verb, "unmask") && - !streq(verbs[i].verb, "link")) { + !streq(verbs[i].verb, "link") && + !streq(verbs[i].verb, "set-default") && + !streq(verbs[i].verb, "get-default")) { if (running_in_chroot() > 0) { log_info("Running in chroot, ignoring request."); |