diff options
author | Lennart Poettering <lennart@poettering.net> | 2015-09-23 19:46:23 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2015-09-29 21:55:51 +0200 |
commit | 5f5d8eab1f2f5f5e088bc301533b3e4636de96c7 (patch) | |
tree | d5341f3e35dc5cef2105368633c145ee75553545 | |
parent | 0521e286fc34d1ab58edc1a3fab7b8285c4b36e5 (diff) |
core: allow setting WorkingDirectory= to the special value ~
If set to ~ the working directory is set to the home directory of the
user configured in User=.
This change also exposes the existing switch for the working directory
that allowed making missing working directories non-fatal.
This also changes "machinectl shell" to make use of this to ensure that
the invoked shell is by default in the user's home directory.
Fixes #1268.
-rw-r--r-- | man/systemd.exec.xml | 23 | ||||
-rw-r--r-- | src/core/dbus-execute.c | 87 | ||||
-rw-r--r-- | src/core/execute.c | 21 | ||||
-rw-r--r-- | src/core/execute.h | 1 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 2 | ||||
-rw-r--r-- | src/core/load-fragment.c | 129 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 | ||||
-rw-r--r-- | src/machine/machine-dbus.c | 5 |
8 files changed, 197 insertions, 72 deletions
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 7633948645..d3f56fee40 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -84,22 +84,27 @@ <varlistentry> <term><varname>WorkingDirectory=</varname></term> - <listitem><para>Takes an absolute directory path. Sets the - working directory for executed processes. If not set, defaults - to the root directory when systemd is running as a system - instance and the respective user's home directory if run as - user.</para></listitem> + <listitem><para>Takes an absolute directory path, or the + special value <literal>~</literal>. Sets the working directory + for executed processes. If set to <literal>~</literal> the + home directory of the user specified in + <varname>User=</varname> is used. If not set, defaults to the + root directory when systemd is running as a system instance + and the respective user's home directory if run as user. If + the setting is prefixed with the <literal>-</literal> + character, a missing working directory is not considered + fatal.</para></listitem> </varlistentry> <varlistentry> <term><varname>RootDirectory=</varname></term> <listitem><para>Takes an absolute directory path. Sets the - root directory for executed processes, with the - <citerefentry project='man-pages'><refentrytitle>chroot</refentrytitle><manvolnum>2</manvolnum></citerefentry> + root directory for executed processes, with the <citerefentry + project='man-pages'><refentrytitle>chroot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call. If this is used, it must be ensured that the - process and all its auxiliary files are available in the - <function>chroot()</function> jail.</para></listitem> + process binary and all its auxiliary files are available in + the <function>chroot()</function> jail.</para></listitem> </varlistentry> <varlistentry> diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 868c8cc05a..adf613d328 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -595,6 +595,33 @@ static int property_get_address_families( return sd_bus_message_close_container(reply); } +static int property_get_working_directory( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = userdata; + const char *wd; + + assert(bus); + assert(reply); + assert(c); + + if (c->working_directory_home) + wd = "~"; + else + wd = c->working_directory; + + if (c->working_directory_missing_ok) + wd = strjoina("!", wd); + + return sd_bus_message_append(reply, "s", wd); +} + const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST), @@ -616,7 +643,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("LimitNICE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LimitRTPRIO", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LimitRTTIME", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("WorkingDirectory", "s", NULL, offsetof(ExecContext, working_directory), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -847,8 +874,7 @@ int bus_exec_context_set_transient_property( return 1; - } else if (STR_IN_SET(name, - "TTYPath", "WorkingDirectory", "RootDirectory")) { + } else if (STR_IN_SET(name, "TTYPath", "RootDirectory")) { const char *s; r = sd_bus_message_read(message, "s", &s); @@ -859,24 +885,51 @@ int bus_exec_context_set_transient_property( return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s takes an absolute path", name); if (mode != UNIT_CHECK) { - char *t; + if (streq(name, "TTYPath")) + r = free_and_strdup(&c->tty_path, s); + else { + assert(streq(name, "RootDirectory")); + r = free_and_strdup(&c->root_directory, s); + } + if (r < 0) + return r; - t = strdup(s); - if (!t) - return -ENOMEM; + unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, s); + } - if (streq(name, "TTYPath")) { - free(c->tty_path); - c->tty_path = t; - } else if (streq(name, "WorkingDirectory")) { - free(c->working_directory); - c->working_directory = t; - } else if (streq(name, "RootDirectory")) { - free(c->root_directory); - c->root_directory = t; + return 1; + + } else if (streq(name, "WorkingDirectory")) { + const char *s; + bool missing_ok; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + if (s[0] == '-') { + missing_ok = true; + s++; + } else + missing_ok = false; + + if (!streq(s, "~") && !path_is_absolute(s)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "WorkingDirectory= expects an absolute path or '~'"); + + if (mode != UNIT_CHECK) { + if (streq(s, "~")) { + c->working_directory = mfree(c->working_directory); + c->working_directory_home = true; + } else { + r = free_and_strdup(&c->working_directory, s); + if (r < 0) + return r; + + c->working_directory_home = false; } - unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, s); + c->working_directory_missing_ok = missing_ok; + unit_write_drop_in_private_format(u, mode, name, "WorkingDirectory=%s%s", missing_ok ? "-" : "", s); } return 1; diff --git a/src/core/execute.c b/src/core/execute.c index 7796c07fcf..137a176c18 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1325,7 +1325,7 @@ static int exec_child( _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; _cleanup_free_ char *mac_selinux_context_net = NULL; - const char *username = NULL, *home = NULL, *shell = NULL; + const char *username = NULL, *home = NULL, *shell = NULL, *wd; unsigned n_dont_close = 0; int dont_close[n_fds + 4]; uid_t uid = UID_INVALID; @@ -1698,6 +1698,13 @@ static int exec_child( } } + if (context->working_directory_home) + wd = home; + else if (context->working_directory) + wd = context->working_directory; + else + wd = "/"; + if (params->apply_chroot) { if (!needs_mount_namespace && context->root_directory) if (chroot(context->root_directory) < 0) { @@ -1705,21 +1712,15 @@ static int exec_child( return -errno; } - if (chdir(context->working_directory ?: "/") < 0 && + if (chdir(wd) < 0 && !context->working_directory_missing_ok) { *exit_status = EXIT_CHDIR; return -errno; } } else { - _cleanup_free_ char *d = NULL; - - if (asprintf(&d, "%s/%s", - context->root_directory ?: "", - context->working_directory ?: "") < 0) { - *exit_status = EXIT_MEMORY; - return -ENOMEM; - } + const char *d; + d = strjoina(strempty(context->root_directory), "/", strempty(wd)); if (chdir(d) < 0 && !context->working_directory_missing_ok) { *exit_status = EXIT_CHDIR; diff --git a/src/core/execute.h b/src/core/execute.h index a750246a89..2c93044748 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -103,6 +103,7 @@ struct ExecContext { struct rlimit *rlimit[_RLIMIT_MAX]; char *working_directory, *root_directory; bool working_directory_missing_ok; + bool working_directory_home; mode_t umask; int oom_score_adjust; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index fd293d8287..27159aa4c7 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -17,7 +17,7 @@ struct ConfigPerfItem; %% m4_dnl Define the context options only once m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', -`$1.WorkingDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.working_directory) +`$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context) $1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory) $1.User, config_parse_unit_string_printf, 0, offsetof($1, exec_context.user) $1.Group, config_parse_unit_string_printf, 0, offsetof($1, exec_context.group) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index a13f42b5e0..7045c31f2e 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -20,44 +20,42 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <linux/oom.h> #include <errno.h> -#include <string.h> #include <fcntl.h> -#include <sched.h> #include <linux/fs.h> -#include <sys/stat.h> +#include <linux/oom.h> +#include <sched.h> +#include <string.h> #include <sys/resource.h> - +#include <sys/stat.h> #ifdef HAVE_SECCOMP #include <seccomp.h> #endif -#include "unit.h" -#include "strv.h" +#include "af-list.h" +#include "bus-error.h" +#include "bus-internal.h" +#include "bus-util.h" +#include "cap-list.h" +#include "cgroup.h" #include "conf-parser.h" -#include "load-fragment.h" -#include "log.h" +#include "env-util.h" +#include "errno-list.h" #include "ioprio.h" -#include "securebits.h" +#include "log.h" #include "missing.h" -#include "unit-name.h" -#include "unit-printf.h" -#include "utf8.h" #include "path-util.h" -#include "env-util.h" -#include "cgroup.h" -#include "bus-util.h" -#include "bus-error.h" -#include "errno-list.h" -#include "af-list.h" -#include "cap-list.h" -#include "signal-util.h" -#include "bus-internal.h" - #ifdef HAVE_SECCOMP #include "seccomp-util.h" #endif +#include "securebits.h" +#include "signal-util.h" +#include "strv.h" +#include "unit-name.h" +#include "unit-printf.h" +#include "unit.h" +#include "utf8.h" +#include "load-fragment.h" int config_parse_warn_compat( const char *unit, @@ -195,16 +193,17 @@ int config_parse_unit_strv_printf(const char *unit, k ? k : rvalue, data, userdata); } -int config_parse_unit_path_printf(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_unit_path_printf( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { _cleanup_free_ char *k = NULL; Unit *u = userdata; @@ -1846,6 +1845,70 @@ int config_parse_bus_endpoint_policy( return bus_endpoint_add_policy(c->bus_endpoint, name, access); } +int config_parse_working_directory( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + ExecContext *c = data; + Unit *u = userdata; + bool missing_ok; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(c); + assert(u); + + if (rvalue[0] == '-') { + missing_ok = true; + rvalue++; + } else + missing_ok = false; + + if (streq(rvalue, "~")) { + c->working_directory_home = true; + c->working_directory = mfree(c->working_directory); + } else { + _cleanup_free_ char *k = NULL; + + r = unit_full_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue); + return 0; + } + + path_kill_slashes(k); + + if (!utf8_is_valid(k)) { + log_invalid_utf8(unit, LOG_ERR, filename, line, 0, rvalue); + return 0; + } + + if (!path_is_absolute(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue); + return 0; + } + + free(c->working_directory); + c->working_directory = k; + k = NULL; + + c->working_directory_home = false; + } + + c->working_directory_missing_ok = missing_ok; + return 0; +} + int config_parse_unit_env_file(const char *unit, const char *filename, unsigned line, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 5d0a09249f..6ee7c71bc4 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -106,6 +106,7 @@ int config_parse_protect_home(const char* unit, const char *filename, unsigned l int config_parse_protect_system(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bus_name(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_working_directory(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index b010c90989..21026829a9 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -735,7 +735,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu description = strjoina("Shell for User ", isempty(user) ? "root" : user); r = sd_bus_message_append(tm, - "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)", + "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)", "Description", "s", description, "StandardInput", "s", "tty", "StandardOutput", "s", "tty", @@ -748,7 +748,8 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu "TTYReset", "b", true, "UtmpIdentifier", "s", utmp_id, "UtmpMode", "s", "user", - "PAMName", "s", "login"); + "PAMName", "s", "login", + "WorkingDirectory", "s", "-~"); if (r < 0) return r; |