mirror of
https://github.com/systemd/systemd.git
synced 2024-11-24 18:53:33 +08:00
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.
This commit is contained in:
parent
0521e286fc
commit
5f5d8eab1f
@ -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>
|
||||
|
@ -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,28 +885,55 @@ 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;
|
||||
|
||||
t = strdup(s);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, s);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
c->working_directory_missing_ok = missing_ok;
|
||||
unit_write_drop_in_private_format(u, mode, name, "WorkingDirectory=%s%s", missing_ok ? "-" : "", s);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "StandardInput")) {
|
||||
const char *s;
|
||||
ExecInput p;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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 "conf-parser.h"
|
||||
#include "load-fragment.h"
|
||||
#include "log.h"
|
||||
#include "ioprio.h"
|
||||
#include "securebits.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-error.h"
|
||||
#include "bus-internal.h"
|
||||
|
||||
#include "bus-util.h"
|
||||
#include "cap-list.h"
|
||||
#include "cgroup.h"
|
||||
#include "conf-parser.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-list.h"
|
||||
#include "ioprio.h"
|
||||
#include "log.h"
|
||||
#include "missing.h"
|
||||
#include "path-util.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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user