core: allow setting WorkingDirectory= to the special value ~
authorLennart Poettering <lennart@poettering.net>
Wed, 23 Sep 2015 17:46:23 +0000 (19:46 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 29 Sep 2015 19:55:51 +0000 (21:55 +0200)
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.

man/systemd.exec.xml
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/machine/machine-dbus.c

index 7633948..d3f56fe 100644 (file)
       <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>
index 868c8cc..adf613d 100644 (file)
@@ -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;
index 7796c07..137a176 100644 (file)
@@ -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;
index a750246..2c93044 100644 (file)
@@ -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;
index fd293d8..27159aa 100644 (file)
@@ -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)
index a13f42b..7045c31 100644 (file)
   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,
index 5d0a092..6ee7c71 100644 (file)
@@ -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);
index b010c90..2102682 100644 (file)
@@ -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;