[systemd-devel] [PATCH 4/7] Session mode
Hristo Venev
hristo at venev.name
Sun Dec 1 11:25:55 PST 2013
systemctl --session restart gnome-settings-daemon
Add a new environment variable:
XDG_SESSION_DIR=/run/session/$XDG_SESSION_ID
The session instance runs in session-*.scope and is started as a normal
process inside a session. The socket is stored in
$XDG_SESSION_DIR/systemd/private
It would be a good idea to implement DBus activation for most present
user services like the 47 I have in /usr/share/dbus-1/services. My
xsession also puts the DBus socket in $XDG_SESSION_DIR/dbus/session_bus_socket.
Good enough to become default? That's another patch series.
---
Makefile.am | 33 +++++
src/core/dbus.c | 17 +++
src/core/main.c | 18 ++-
src/core/service.c | 1 +
src/core/unit-printf.c | 56 +++++++++
src/core/unit.c | 24 ++++
src/libsystemd-bus/bus-util.c | 46 +++++++
src/libsystemd-bus/bus-util.h | 1 +
src/libsystemd-bus/sd-bus.c | 55 +++++++++
src/login/logind-dbus.c | 5 +-
src/login/logind-session-dbus.c | 6 +-
src/login/logind-session.c | 36 +++++-
src/login/logind-session.h | 1 +
src/login/pam-module.c | 11 +-
src/run/run.c | 7 ++
src/shared/install.c | 32 ++++-
src/shared/install.h | 2 +
src/shared/path-lookup.c | 225 +++++++++++++++++++++++++++++++++-
src/shared/path-lookup.h | 2 +
src/systemctl/systemctl.c | 14 +++
src/systemd/sd-bus.h | 2 +
units/session/.gitignore | 1 +
units/session/Makefile | 1 +
units/session/default.target | 11 ++
units/session/exit.target | 17 +++
units/session/systemd-exit.service.in | 17 +++
26 files changed, 629 insertions(+), 12 deletions(-)
create mode 100644 units/session/.gitignore
create mode 120000 units/session/Makefile
create mode 100644 units/session/default.target
create mode 100644 units/session/exit.target
create mode 100644 units/session/systemd-exit.service.in
diff --git a/Makefile.am b/Makefile.am
index 7a45029..985a7f7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -84,6 +84,7 @@ catalogstatedir=$(systemdstatedir)/catalog
# Our own, non-special dirs
pkgsysconfdir=$(sysconfdir)/systemd
userunitdir=$(prefix)/lib/systemd/user
+sessionunitdir=$(prefix)/lib/systemd/session
userpresetdir=$(prefix)/lib/systemd/user-preset
tmpfilesdir=$(prefix)/lib/tmpfiles.d
sysctldir=$(prefix)/lib/sysctl.d
@@ -91,6 +92,7 @@ networkdir=$(prefix)/lib/systemd/network
pkgincludedir=$(includedir)/systemd
systemgeneratordir=$(rootlibexecdir)/system-generators
usergeneratordir=$(prefix)/lib/systemd/user-generators
+sessiongeneratordir=$(prefix)/lib/systemd/session-generators
systemshutdowndir=$(rootlibexecdir)/system-shutdown
systemsleepdir=$(rootlibexecdir)/system-sleep
systemunitdir=$(rootprefix)/lib/systemd/system
@@ -154,6 +156,8 @@ AM_CPPFLAGS = \
-DSYSTEM_SYSVRCND_PATH=\"$(SYSTEM_SYSVRCND_PATH)\" \
-DUSER_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/user\" \
-DUSER_DATA_UNIT_PATH=\"$(userunitdir)\" \
+ -DSESSION_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/session\" \
+ -DSESSION_DATA_UNIT_PATH=\"$(sessionunitdir)\" \
-DCATALOG_DATABASE=\"$(catalogstatedir)/database\" \
-DSYSTEMD_CGROUP_AGENT_PATH=\"$(rootlibexecdir)/systemd-cgroups-agent\" \
-DSYSTEMD_BINARY_PATH=\"$(rootlibexecdir)/systemd\" \
@@ -168,6 +172,7 @@ AM_CPPFLAGS = \
-DSYSTEMD_CRYPTSETUP_PATH=\"$(rootlibexecdir)/systemd-cryptsetup\" \
-DSYSTEM_GENERATOR_PATH=\"$(systemgeneratordir)\" \
-DUSER_GENERATOR_PATH=\"$(usergeneratordir)\" \
+ -DSESSION_GENERATOR_PATH=\"$(sessiongeneratordir)\" \
-DSYSTEM_SHUTDOWN_PATH=\"$(systemshutdowndir)\" \
-DSYSTEM_SLEEP_PATH=\"$(systemsleepdir)\" \
-DSYSTEMD_KBD_MODEL_MAP=\"$(pkgdatadir)/kbd-model-map\" \
@@ -223,6 +228,7 @@ TIMERS_TARGET_WANTS =
SYSTEM_UNIT_ALIASES =
USER_UNIT_ALIASES =
+SESSION_UNIT_ALIASES =
GENERAL_ALIASES =
@@ -257,6 +263,8 @@ install-aliases-hook:
dir=$(systemunitdir) && $(install-aliases)
set -- $(USER_UNIT_ALIASES) && \
dir=$(userunitdir) && $(install-aliases)
+ set -- $(SESSION_UNIT_ALIASES) && \
+ dir=$(sessionunitdir) && $(install-aliases)
set -- $(GENERAL_ALIASES) && \
dir= && $(install-aliases)
@@ -460,9 +468,16 @@ dist_userunit_DATA = \
units/user/default.target \
units/user/exit.target
+dist_sessionunit_DATA = \
+ units/session/default.target \
+ units/session/exit.target
+
nodist_userunit_DATA = \
units/user/systemd-exit.service
+nodist_sessionunit_DATA = \
+ units/session/systemd-exit.service
+
EXTRA_DIST += \
units/getty at .service.m4 \
units/serial-getty at .service.m4 \
@@ -4354,9 +4369,11 @@ substitutions = \
'|pkgsysconfdir=$(pkgsysconfdir)|' \
'|SYSTEM_CONFIG_UNIT_PATH=$(pkgsysconfdir)/system|' \
'|USER_CONFIG_UNIT_PATH=$(pkgsysconfdir)/user|' \
+ '|SESSION_CONFIG_UNIT_PATH=$(pkgsysconfdir)/session|' \
'|pkgdatadir=$(pkgdatadir)|' \
'|systemunitdir=$(systemunitdir)|' \
'|userunitdir=$(userunitdir)|' \
+ '|sessionunitdir=$(sessionunitdir)|' \
'|systempresetdir=$(systempresetdir)|' \
'|userpresetdir=$(userpresetdir)|' \
'|udevhwdbdir=$(udevhwdbdir)|' \
@@ -4366,6 +4383,7 @@ substitutions = \
'|sysctldir=$(sysctldir)|' \
'|systemgeneratordir=$(systemgeneratordir)|' \
'|usergeneratordir=$(usergeneratordir)|' \
+ '|sessiongeneratordir=$(sessiongeneratordir)|' \
'|PACKAGE_VERSION=$(PACKAGE_VERSION)|' \
'|PACKAGE_NAME=$(PACKAGE_NAME)|' \
'|PACKAGE_URL=$(PACKAGE_URL)|' \
@@ -4451,6 +4469,7 @@ EXTRA_DIST += \
CLEANFILES += \
$(nodist_systemunit_DATA) \
$(nodist_userunit_DATA) \
+ $(nodist_sessionunit_DATA) \
$(pkgconfigdata_DATA) \
$(pkgconfiglib_DATA) \
$(nodist_polkitpolicy_DATA)
@@ -4591,10 +4610,21 @@ USER_UNIT_ALIASES += \
$(systemunitdir)/sound.target sound.target \
$(systemunitdir)/smartcard.target smartcard.target
+SESSION_UNIT_ALIASES += \
+ $(systemunitdir)/shutdown.target shutdown.target \
+ $(systemunitdir)/sockets.target sockets.target \
+ $(systemunitdir)/timers.target timers.target \
+ $(systemunitdir)/paths.target paths.target \
+ $(systemunitdir)/bluetooth.target bluetooth.target \
+ $(systemunitdir)/printer.target printer.target \
+ $(systemunitdir)/sound.target sound.target \
+ $(systemunitdir)/smartcard.target smartcard.target
+
GENERAL_ALIASES += \
$(systemunitdir)/remote-fs.target $(pkgsysconfdir)/system/multi-user.target.wants/remote-fs.target \
$(systemunitdir)/getty at .service $(pkgsysconfdir)/system/getty.target.wants/getty at tty1.service \
$(pkgsysconfdir)/user $(sysconfdir)/xdg/systemd/user \
+ $(pkgsysconfdir)/session $(sysconfdir)/xdg/systemd/session \
../system-services/org.freedesktop.systemd1.service $(dbussessionservicedir)/org.freedesktop.systemd1.service
if HAVE_SYSV_COMPAT
@@ -4619,12 +4649,15 @@ INSTALL_DIRS += \
$(systemsleepdir) \
$(systemgeneratordir) \
$(usergeneratordir) \
+ $(sessiongeneratordir) \
\
$(userunitdir) \
+ $(sessionunitdir) \
$(pkgsysconfdir)/system \
$(pkgsysconfdir)/system/multi-user.target.wants \
$(pkgsysconfdir)/system/getty.target.wants \
$(pkgsysconfdir)/user \
+ $(pkgsysconfdir)/session \
$(dbussessionservicedir) \
$(sysconfdir)/xdg/systemd
diff --git a/src/core/dbus.c b/src/core/dbus.c
index ef9a64b..024f5d3 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -986,6 +986,23 @@ static int bus_init_private(Manager *m) {
salen = sizeof(sa.un) - left;
mkdir_parents_label(sa.un.sun_path, 0755);
+ } else {
+ size_t left = sizeof(sa.un.sun_path);
+ char *p = sa.un.sun_path;
+ const char *e;
+
+ e = secure_getenv("XDG_SESSION_DIR");
+ if (!e) {
+ log_error("Failed to determine XDG_SESSION_DIR");
+ return -EHOSTDOWN;
+ }
+
+ left = strpcpy(&p, left, e);
+ left = strpcpy(&p, left, "/systemd/private");
+
+ salen = sizeof(sa.un) - left;
+
+ mkdir_parents_label(sa.un.sun_path, 0755);
}
unlink(sa.un.sun_path);
diff --git a/src/core/main.c b/src/core/main.c
index ce5b64c..3c6a8aa 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -739,6 +739,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_UNIT,
ARG_SYSTEM,
ARG_USER,
+ ARG_SESSION,
ARG_TEST,
ARG_VERSION,
ARG_DUMP_CONFIGURATION_ITEMS,
@@ -760,6 +761,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "unit", required_argument, NULL, ARG_UNIT },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
+ { "session", no_argument, NULL, ARG_SESSION },
{ "test", no_argument, NULL, ARG_TEST },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
@@ -863,6 +865,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_running_as = SYSTEMD_USER;
break;
+ case ARG_SESSION:
+ arg_running_as = SYSTEMD_SESSION;
+ break;
+
case ARG_TEST:
arg_action = ACTION_TEST;
break;
@@ -1008,6 +1014,7 @@ static int help(void) {
" --unit=UNIT Set default unit\n"
" --system Run a system instance, even if PID != 1\n"
" --user Run a user instance\n"
+ " --session Run a session instance\n"
" --dump-core[=0|1] Dump core on crash\n"
" --crash-shell[=0|1] Run shell on crash\n"
" --confirm-spawn[=0|1] Ask for confirmation when spawning processes\n"
@@ -1395,7 +1402,7 @@ int main(int argc, char *argv[]) {
if (arg_running_as != SYSTEMD_SYSTEM &&
arg_action == ACTION_RUN &&
sd_booted() <= 0) {
- log_error("Trying to run as user instance, but the system has not been booted with systemd.");
+ log_error("Trying to run as user or session instance, but the system has not been booted with systemd.");
goto finish;
}
@@ -1427,6 +1434,13 @@ int main(int argc, char *argv[]) {
goto finish;
}
+
+ if (arg_running_as == SYSTEMD_SESSION &&
+ !getenv("XDG_SESSION_DIR")) {
+ log_error("Trying to run as session instance, but $XDG_SESSION_DIR is not set.");
+ goto finish;
+ }
+
assert_se(arg_action == ACTION_RUN || arg_action == ACTION_TEST);
/* Close logging fds, in order not to confuse fdset below */
@@ -1774,7 +1788,7 @@ finish:
args[i++] = SYSTEMD_BINARY_PATH;
if (switch_root_dir)
args[i++] = "--switched-root";
- args[i++] = arg_running_as == SYSTEMD_SYSTEM ? "--system" : "--user";
+ args[i++] = arg_running_as == SYSTEMD_SYSTEM ? "--system" : arg_running_as == SYSTEMD_USER ? "--user" : "--session";
args[i++] = "--deserialize";
args[i++] = sfd;
args[i++] = NULL;
diff --git a/src/core/service.c b/src/core/service.c
index 76de567..676ec86 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1160,6 +1160,7 @@ static int service_add_default_dependencies(Service *s) {
SPECIAL_PATHS_TARGET, NULL, true);
if (r < 0)
return r;
+
}
/* Second, activate normal shutdown */
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c
index 1a29a98..910a908 100644
--- a/src/core/unit-printf.c
+++ b/src/core/unit-printf.c
@@ -154,6 +154,60 @@ static int specifier_cgroup_root(char specifier, void *data, void *userdata, cha
return 0;
}
+static int specifier_session_id(char specifier, void *data, void *userdata, char **ret) {
+ Unit *u = userdata;
+ char *n = NULL;
+
+ assert(u);
+
+ if (u->manager->running_as == SYSTEMD_SESSION) {
+ const char *e;
+
+ e = getenv("XDG_SESSION_ID");
+ if (e) {
+ n = strdup(e);
+ if (!n)
+ return -ENOMEM;
+ }
+ }
+
+ if (!n) {
+ n = strdup("");
+ if (!n)
+ return -ENOMEM;
+ }
+
+ *ret = n;
+ return 0;
+}
+
+static int specifier_session_dir(char specifier, void *data, void *userdata, char **ret) {
+ Unit *u = userdata;
+ char *n = NULL;
+
+ assert(u);
+
+ if (u->manager->running_as == SYSTEMD_SESSION) {
+ const char *e;
+
+ e = getenv("XDG_SESSION_DIR");
+ if (e) {
+ n = strdup(e);
+ if (!n)
+ return -ENOMEM;
+ }
+ }
+
+ if (!n) {
+ n = strdup("");
+ if (!n)
+ return -ENOMEM;
+ }
+
+ *ret = n;
+ return 0;
+}
+
static int specifier_runtime(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
char *n = NULL;
@@ -351,6 +405,8 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
{ 'u', specifier_user_name, NULL },
{ 'h', specifier_user_home, NULL },
{ 's', specifier_user_shell, NULL },
+ { 'E', specifier_session_id, NULL },
+ { 'e', specifier_session_dir, NULL },
{ 'm', specifier_machine_id, NULL },
{ 'H', specifier_host_name, NULL },
diff --git a/src/core/unit.c b/src/core/unit.c
index 69e701c..c963870 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -2734,6 +2734,16 @@ static int drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, c
return -ENOENT;
p = strjoin(c, "/", u->id, ".d", NULL);
+ } else if (u->manager->running_as == SYSTEMD_SESSION) {
+ _cleanup_free_ char *c = NULL;
+
+ r = session_config_home(&c);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ p = strjoin(c, "/", u->id, ".d", NULL);
} else if (mode & UNIT_PERSISTENT)
p = strjoin("/etc/systemd/system/", u->id, ".d", NULL);
else
@@ -2883,6 +2893,20 @@ int unit_make_transient(Unit *u) {
return -ENOMEM;
mkdir_p(c, 0755);
+ } else if (u->manager->running_as == SYSTEMD_SESSION) {
+ _cleanup_free_ char *c = NULL;
+
+ r = session_config_home(&c);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ u->fragment_path = strjoin(c, "/", u->id, NULL);
+ if (!u->fragment_path)
+ return -ENOMEM;
+
+ mkdir_p(c, 0755);
} else {
u->fragment_path = strappend("/run/systemd/system/", u->id);
if (!u->fragment_path)
diff --git a/src/libsystemd-bus/bus-util.c b/src/libsystemd-bus/bus-util.c
index 9459e6f..0c51eae 100644
--- a/src/libsystemd-bus/bus-util.c
+++ b/src/libsystemd-bus/bus-util.c
@@ -504,6 +504,46 @@ int bus_open_user_systemd(sd_bus **_bus) {
return 0;
}
+int bus_open_session_systemd(sd_bus **_bus) {
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_free_ char *p = NULL;
+ const char *e;
+ int r;
+
+ /* If we are supposed to talk to the instance, try via
+ * XDG_SESSION_DIR first, then fallback to normal bus
+ * access */
+
+ assert(_bus);
+
+ e = secure_getenv("XDG_SESSION_DIR");
+ if (e) {
+ if (asprintf(&p, "unix:path=%s/systemd/private", e) < 0)
+ return -ENOMEM;
+ }
+
+ r = sd_bus_new(&bus);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_set_address(bus, p);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_start(bus);
+ if (r < 0)
+ return r;
+
+ r = bus_check_peercred(bus);
+ if (r < 0)
+ return r;
+
+ *_bus = bus;
+ bus = NULL;
+
+ return 0;
+}
+
int bus_print_property(const char *name, sd_bus_message *property, bool all) {
char type;
const char *contents;
@@ -973,6 +1013,9 @@ int bus_open_transport(BusTransport transport, const char *host, SystemdRunningA
case SYSTEMD_USER:
r = sd_bus_default_user(bus);
break;
+ case SYSTEMD_SESSION:
+ r = sd_bus_default_session(bus);
+ break;
default:
assert_not_reached("Unknown running_as.");
}
@@ -1012,6 +1055,9 @@ int bus_open_transport_systemd(BusTransport transport, const char *host, Systemd
case SYSTEMD_USER:
r = bus_open_user_systemd(bus);
break;
+ case SYSTEMD_SESSION:
+ r = bus_open_session_systemd(bus);
+ break;
default:
assert_not_reached("Unknown running_as.");
}
diff --git a/src/libsystemd-bus/bus-util.h b/src/libsystemd-bus/bus-util.h
index 32ad8f9..a23ff73 100644
--- a/src/libsystemd-bus/bus-util.h
+++ b/src/libsystemd-bus/bus-util.h
@@ -68,6 +68,7 @@ void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry);
int bus_open_system_systemd(sd_bus **_bus);
int bus_open_user_systemd(sd_bus **_bus);
+int bus_open_session_systemd(sd_bus **_bus);
int bus_open_transport(BusTransport transport, const char *host, SystemdRunningAs running_as, sd_bus **bus);
int bus_open_transport_systemd(BusTransport transport, const char *host, SystemdRunningAs running_as, sd_bus **bus);
diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c
index 1ed08c0..9140a3f 100644
--- a/src/libsystemd-bus/sd-bus.c
+++ b/src/libsystemd-bus/sd-bus.c
@@ -1076,6 +1076,55 @@ fail:
return r;
}
+_public_ int sd_bus_open_session(sd_bus **ret) {
+ const char *e;
+ sd_bus *b;
+ size_t l;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = sd_bus_new(&b);
+ if (r < 0)
+ return r;
+
+ e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
+ if (e) {
+ r = sd_bus_set_address(b, e);
+ if (r < 0)
+ goto fail;
+ } else {
+ e = secure_getenv("XDG_SESSION_DIR");
+ if (!e) {
+ r = -ENOENT;
+ goto fail;
+ }
+
+ l = strlen(e);
+ if (l + 4 > sizeof(b->sockaddr.un.sun_path)) {
+ r = -E2BIG;
+ goto fail;
+ }
+
+ b->sockaddr.un.sun_family = AF_UNIX;
+ memcpy(mempcpy(b->sockaddr.un.sun_path, e, l), "/bus", 4);
+ b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l + 4;
+ }
+
+ b->bus_client = true;
+
+ r = sd_bus_start(b);
+ if (r < 0)
+ goto fail;
+
+ *ret = b;
+ return 0;
+
+fail:
+ bus_free(b);
+ return r;
+}
+
_public_ int sd_bus_open_system_remote(const char *host, sd_bus **ret) {
_cleanup_free_ char *e = NULL;
char *p = NULL;
@@ -2703,6 +2752,12 @@ _public_ int sd_bus_default_user(sd_bus **ret) {
return bus_default(sd_bus_open_user, &default_user_bus, ret);
}
+_public_ int sd_bus_default_session(sd_bus **ret) {
+ static __thread sd_bus *default_session_bus = NULL;
+
+ return bus_default(sd_bus_open_session, &default_session_bus, ret);
+}
+
_public_ int sd_bus_get_tid(sd_bus *b, pid_t *tid) {
assert_return(b, -EINVAL);
assert_return(tid, -EINVAL);
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index c3518f6..4708820 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -586,10 +586,11 @@ static int method_create_session(sd_bus *bus, sd_bus_message *message, void *use
return -ENOMEM;
return sd_bus_reply_method_return(
- message, "soshusub",
+ message, "sosshusub",
session->id,
path,
session->user->runtime_path,
+ session->session_path,
fifo_fd,
(uint32_t) session->user->uid,
session->seat ? session->seat->id : "",
@@ -1888,7 +1889,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("ListUsers", NULL, "a(uso)", method_list_users, 0),
SD_BUS_METHOD("ListSeats", NULL, "a(so)", method_list_seats, 0),
SD_BUS_METHOD("ListInhibitors", NULL, "a(ssssuu)", method_list_inhibitors, 0),
- SD_BUS_METHOD("CreateSession", "uussssussbssa(sv)", "soshusub", method_create_session, 0),
+ SD_BUS_METHOD("CreateSession", "uussssussbssa(sv)", "sosshusub", method_create_session, 0),
SD_BUS_METHOD("ReleaseSession", "s", NULL, method_release_session, 0),
SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, 0),
SD_BUS_METHOD("ActivateSessionOnSeat", "ss", NULL, method_activate_session_on_seat, 0),
diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
index 4bbe75e..bdb0639 100644
--- a/src/login/logind-session-dbus.c
+++ b/src/login/logind-session-dbus.c
@@ -673,19 +673,21 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
return -ENOMEM;
log_debug("Sending reply about created session: "
- "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u",
+ "id=%s object_path=%s runtime_path=%s session_path=%s session_fd=%d seat=%s vtnr=%u",
s->id,
p,
s->user->runtime_path,
+ s->session_path,
fifo_fd,
s->seat ? s->seat->id : "",
(uint32_t) s->vtnr);
return sd_bus_reply_method_return(
- c, "soshusub",
+ c, "sosshusub",
s->id,
p,
s->user->runtime_path,
+ s->session_path,
fifo_fd,
(uint32_t) s->user->uid,
s->seat ? s->seat->id : "",
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 66292ef..c5a3878 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -88,6 +88,8 @@ Session* session_new(Manager *m, const char *id) {
return NULL;
}
+ s->session_path = strappend("/run/session/", id);
+
s->manager = m;
s->fifo_fd = -1;
s->vtfd = -1;
@@ -107,6 +109,8 @@ void session_free(Session *s) {
session_drop_controller(s);
+ free(s->session_path);
+
while ((sd = hashmap_first(s->devices)))
session_device_free(sd);
@@ -193,7 +197,8 @@ int session_save(Session *s) {
s->user->name,
session_is_active(s),
session_state_to_string(session_get_state(s)),
- s->remote);
+ s->remote,
+ s->session_path);
if (s->type >= 0)
fprintf(f, "TYPE=%s\n", session_type_to_string(s->type));
@@ -568,6 +573,14 @@ int session_start(Session *s) {
if (r < 0)
return r;
+ r = mkdir_safe_label("/run/session", 0755, 0, 0);
+ if (r < 0)
+ return r;
+
+ r = mkdir_safe_label(s->session_path, 0700, s->user->uid, s->user->gid);
+ if (r < 0)
+ return r;
+
/* Create cgroup */
r = session_start_scope(s);
if (r < 0)
@@ -671,6 +684,24 @@ int session_stop(Session *s) {
return r;
}
+static int session_remove_session_path(Session *s) {
+ int r;
+
+ assert(s);
+
+ if (!s->session_path)
+ return 0;
+
+ r = rm_rf(s->session_path, false, true, false);
+ if (r < 0)
+ log_error("Failed to remove session directory %s: %s", s->session_path, strerror(-r));
+
+ free(s->session_path);
+ s->session_path = NULL;
+
+ return r;
+}
+
int session_finalize(Session *s) {
int r = 0;
SessionDevice *sd;
@@ -696,6 +727,9 @@ int session_finalize(Session *s) {
/* Remove X11 symlink */
session_unlink_x11_socket(s);
+ /* Kill XDG_SESSION_DIR */
+ session_remove_session_path(s);
+
unlink(s->state_file);
session_add_to_gc_queue(s);
user_add_to_gc_queue(s->user);
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index ee93101..fd74816 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -69,6 +69,7 @@ struct Session {
Manager *manager;
char *id;
+ char *session_path;
SessionType type;
SessionClass class;
diff --git a/src/login/pam-module.c b/src/login/pam-module.c
index 45428a0..c9f9f48 100644
--- a/src/login/pam-module.c
+++ b/src/login/pam-module.c
@@ -174,7 +174,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
const char
- *username, *id, *object_path, *runtime_path,
+ *username, *id, *object_path, *runtime_path, *session_path,
*service = NULL,
*tty = NULL, *display = NULL,
*remote_user = NULL, *remote_host = NULL,
@@ -351,10 +351,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
}
r = sd_bus_message_read(reply,
- "soshusub",
+ "sosshusub",
&id,
&object_path,
&runtime_path,
+ &session_path,
&session_fd,
&original_uid,
&seat,
@@ -383,6 +384,12 @@ _public_ PAM_EXTERN int pam_sm_open_session(
* in privileged apps clobbering the runtime directory
* unnecessarily. */
+ r = pam_misc_setenv(handle, "XDG_SESSION_DIR", session_path, 0);
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to set session dir.");
+ return r;
+ }
+
r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
if (r != PAM_SUCCESS) {
pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
diff --git a/src/run/run.c b/src/run/run.c
index ad23e83..537b725 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -47,6 +47,7 @@ static int help(void) {
" -h --help Show this help\n"
" --version Show package version\n"
" --user Run as user unit\n"
+ " --session Run as session unit\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
" --scope Run this as scope rather than service\n"
@@ -65,6 +66,7 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_USER,
+ ARG_SESSION,
ARG_SYSTEM,
ARG_SCOPE,
ARG_UNIT,
@@ -77,6 +79,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "user", no_argument, NULL, ARG_USER },
+ { "session", no_argument, NULL, ARG_SESSION },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "scope", no_argument, NULL, ARG_SCOPE },
{ "unit", required_argument, NULL, ARG_UNIT },
@@ -106,6 +109,10 @@ static int parse_argv(int argc, char *argv[]) {
puts(SYSTEMD_FEATURES);
return 0;
+ case ARG_SESSION:
+ arg_as = SYSTEMD_SESSION;
+ break;
+
case ARG_USER:
arg_as = SYSTEMD_USER;
break;
diff --git a/src/shared/install.c b/src/shared/install.c
index 100ed69..8199bdb 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -55,7 +55,7 @@ static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope)
zero(*paths);
return lookup_paths_init(paths,
- scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
+ scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : scope == UNIT_FILE_USER || scope == UNIT_FILE_GLOBAL ? SYSTEMD_USER : SYSTEMD_SESSION,
scope == UNIT_FILE_USER,
NULL, NULL, NULL);
}
@@ -105,6 +105,28 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d
break;
+ case UNIT_FILE_GLOBAL_SESSION:
+
+ if (root_dir)
+ return -EINVAL;
+
+ if (runtime)
+ p = strdup("/run/systemd/session");
+ else
+ p = strdup(SESSION_CONFIG_UNIT_PATH);
+ break;
+
+ case UNIT_FILE_SESSION:
+
+ if (root_dir || runtime)
+ return -EINVAL;
+
+ r = session_config_home(&p);
+ if (r <= 0)
+ return r < 0 ? r : -ENOENT;
+
+ break;
+
default:
assert_not_reached("Bad scope");
}
@@ -511,7 +533,7 @@ static int find_symlinks_in_scope(
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
- if (scope == UNIT_FILE_SYSTEM || scope == UNIT_FILE_GLOBAL) {
+ if (scope == UNIT_FILE_SYSTEM || scope == UNIT_FILE_GLOBAL || scope == UNIT_FILE_GLOBAL_SESSION) {
_cleanup_free_ char *path = NULL;
/* First look in runtime config path */
@@ -1765,6 +1787,12 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
"/usr/local/lib/systemd/user-preset",
"/usr/lib/systemd/user-preset",
NULL);
+ else if (scope == UNIT_FILE_GLOBAL_SESSION)
+ r = conf_files_list(&files, ".preset", NULL,
+ "/etc/systemd/session-preset",
+ "/usr/local/lib/systemd/session-preset",
+ "/usr/lib/systemd/session-preset",
+ NULL);
else
return 1;
diff --git a/src/shared/install.h b/src/shared/install.h
index e87c57e..38905c2 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -27,6 +27,8 @@ typedef enum UnitFileScope {
UNIT_FILE_SYSTEM,
UNIT_FILE_GLOBAL,
UNIT_FILE_USER,
+ UNIT_FILE_SESSION,
+ UNIT_FILE_GLOBAL_SESSION,
_UNIT_FILE_SCOPE_MAX,
_UNIT_FILE_SCOPE_INVALID = -1
} UnitFileScope;
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index be605ca..58b1315 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -34,7 +34,8 @@
static const char* const systemd_running_as_table[_SYSTEMD_RUNNING_AS_MAX] = {
[SYSTEMD_SYSTEM] = "system",
- [SYSTEMD_USER] = "user"
+ [SYSTEMD_USER] = "user",
+ [SYSTEMD_SESSION] = "session",
};
DEFINE_STRING_TABLE_LOOKUP(systemd_running_as, SystemdRunningAs);
@@ -68,6 +69,35 @@ int user_config_home(char **config_home) {
return 0;
}
+int session_config_home(char **config_home) {
+ const char *e;
+ char *r;
+
+ e = getenv("XDG_CONFIG_HOME");
+ if (e) {
+ r = strappend(e, "/systemd/session");
+ if (!r)
+ return -ENOMEM;
+
+ *config_home = r;
+ return 1;
+ } else {
+ const char *home;
+
+ home = getenv("HOME");
+ if (home) {
+ r = strappend(home, "/.config/systemd/session");
+ if (!r)
+ return -ENOMEM;
+
+ *config_home = r;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
static char** user_dirs(
const char *generator,
const char *generator_early,
@@ -235,6 +265,173 @@ fail:
goto finish;
}
+static char** session_dirs(
+ const char *generator,
+ const char *generator_early,
+ const char *generator_late) {
+
+ const char * const config_unit_paths[] = {
+ SESSION_CONFIG_UNIT_PATH,
+ "/etc/systemd/session",
+ "/run/systemd/session",
+ NULL
+ };
+
+ const char * const data_unit_paths[] = {
+ "/usr/local/lib/systemd/session",
+ "/usr/local/share/systemd/session",
+ SESSION_DATA_UNIT_PATH,
+ "/usr/lib/systemd/session",
+ "/usr/share/systemd/session",
+ NULL
+ };
+
+ const char *home, *e;
+ char *config_home = NULL, *data_home = NULL;
+ char **config_dirs = NULL, **data_dirs = NULL;
+ char **r = NULL, **t;
+
+ /* Implement the mechanisms defined in
+ *
+ * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
+ *
+ * We look in both the config and the data dirs because we
+ * want to encourage that distributors ship their unit files
+ * as data, and allow overriding as configuration.
+ */
+
+ if (session_config_home(&config_home) < 0)
+ goto fail;
+
+ home = getenv("HOME");
+
+ e = getenv("XDG_CONFIG_DIRS");
+ if (e) {
+ config_dirs = strv_split(e, ":");
+ if (!config_dirs)
+ goto fail;
+ }
+
+ /* We don't treat /etc/xdg/systemd here as the spec
+ * suggests because we assume that that is a link to
+ * /etc/systemd/ anyway. */
+
+ e = getenv("XDG_DATA_HOME");
+ if (e) {
+ if (asprintf(&data_home, "%s/systemd/session", e) < 0)
+ goto fail;
+
+ } else if (home) {
+ if (asprintf(&data_home, "%s/.local/share/systemd/session", home) < 0)
+ goto fail;
+
+ /* There is really no need for two unit dirs in $HOME,
+ * except to be fully compliant with the XDG spec. We
+ * now try to link the two dirs, so that we can
+ * minimize disk seeks a little. Further down we'll
+ * then filter out this link, if it is actually is
+ * one. */
+
+ mkdir_parents_label(data_home, 0777);
+ (void) symlink("../../../.config/systemd/session", data_home);
+ }
+
+ e = getenv("XDG_DATA_DIRS");
+ if (e)
+ data_dirs = strv_split(e, ":");
+ else
+ data_dirs = strv_new("/usr/local/share",
+ "/usr/share",
+ NULL);
+ if (!data_dirs)
+ goto fail;
+
+ /* Now merge everything we found. */
+ if (generator_early) {
+ t = strv_append(r, generator_early);
+ if (!t)
+ goto fail;
+ strv_free(r);
+ r = t;
+ }
+
+ if (config_home) {
+ t = strv_append(r, config_home);
+ if (!t)
+ goto fail;
+ strv_free(r);
+ r = t;
+ }
+
+ if (!strv_isempty(config_dirs)) {
+ t = strv_merge_concat(r, config_dirs, "/systemd/session");
+ if (!t)
+ goto finish;
+ strv_free(r);
+ r = t;
+ }
+
+ t = strv_merge(r, (char**) config_unit_paths);
+ if (!t)
+ goto fail;
+ strv_free(r);
+ r = t;
+
+ if (generator) {
+ t = strv_append(r, generator);
+ if (!t)
+ goto fail;
+ strv_free(r);
+ r = t;
+ }
+
+ if (data_home) {
+ t = strv_append(r, data_home);
+ if (!t)
+ goto fail;
+ strv_free(r);
+ r = t;
+ }
+
+ if (!strv_isempty(data_dirs)) {
+ t = strv_merge_concat(r, data_dirs, "/systemd/session");
+ if (!t)
+ goto fail;
+ strv_free(r);
+ r = t;
+ }
+
+ t = strv_merge(r, (char**) data_unit_paths);
+ if (!t)
+ goto fail;
+ strv_free(r);
+ r = t;
+
+ if (generator_late) {
+ t = strv_append(r, generator_late);
+ if (!t)
+ goto fail;
+ strv_free(r);
+ r = t;
+ }
+
+ if (!path_strv_make_absolute_cwd(r))
+ goto fail;
+
+finish:
+ free(config_home);
+ strv_free(config_dirs);
+ free(data_home);
+ strv_free(data_dirs);
+
+ return r;
+
+fail:
+ strv_free(r);
+ r = NULL;
+ goto finish;
+}
+
int lookup_paths_init(
LookupPaths *p,
SystemdRunningAs running_as,
@@ -313,7 +510,33 @@ int lookup_paths_init(
if (!p->unit_path)
return -ENOMEM;
+
+ } else if (running_as == SYSTEMD_SESSION) {
+ if (personal)
+ p->unit_path = session_dirs(generator, generator_early, generator_late);
+ else
+ p->unit_path = strv_new(
+ /* If you modify this you also want to modify
+ * systemduserunitpath= in systemd.pc.in, and
+ * the arrays in user_dirs() above! */
+ STRV_IFNOTNULL(generator_early),
+ SESSION_CONFIG_UNIT_PATH,
+ "/etc/systemd/session",
+ "/run/systemd/session",
+ STRV_IFNOTNULL(generator),
+ "/usr/local/lib/systemd/session",
+ "/usr/local/share/systemd/session",
+ SESSION_DATA_UNIT_PATH,
+ "/usr/lib/systemd/session",
+ "/usr/share/systemd/session",
+ STRV_IFNOTNULL(generator_late),
+ NULL);
+
+ if (!p->unit_path)
+ return -ENOMEM;
+
}
+
}
if (!path_strv_canonicalize(p->unit_path))
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
index a3ef824..b1341aa 100644
--- a/src/shared/path-lookup.h
+++ b/src/shared/path-lookup.h
@@ -32,6 +32,7 @@ typedef struct LookupPaths {
typedef enum SystemdRunningAs {
SYSTEMD_SYSTEM,
SYSTEMD_USER,
+ SYSTEMD_SESSION,
_SYSTEMD_RUNNING_AS_MAX,
_SYSTEMD_RUNNING_AS_INVALID = -1
} SystemdRunningAs;
@@ -42,6 +43,7 @@ const char* systemd_running_as_to_string(SystemdRunningAs i) _const_;
SystemdRunningAs systemd_running_as_from_string(const char *s) _pure_;
int user_config_home(char **config_home);
+int session_config_home(char **config_home);
int lookup_paths_init(LookupPaths *p, SystemdRunningAs running_as, bool personal, const char *generator, const char *generator_early, const char *generator_late);
void lookup_paths_free(LookupPaths *p);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index edc3cb6..3436784 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -4938,8 +4938,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_IGNORE_DEPENDENCIES,
ARG_VERSION,
ARG_USER,
+ ARG_SESSION,
ARG_SYSTEM,
ARG_GLOBAL,
+ ARG_GLOBAL_SESSION,
ARG_NO_BLOCK,
ARG_NO_LEGEND,
ARG_NO_PAGER,
@@ -4974,8 +4976,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, /* compatibility only */
{ "ignore-inhibitors", no_argument, NULL, 'i' },
{ "user", no_argument, NULL, ARG_USER },
+ { "session", no_argument, NULL, ARG_SESSION },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "global", no_argument, NULL, ARG_GLOBAL },
+ { "global-session", no_argument, NULL, ARG_GLOBAL_SESSION },
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
@@ -5130,6 +5134,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_as = SYSTEMD_USER;
break;
+ case ARG_SESSION:
+ arg_scope = UNIT_FILE_SESSION;
+ arg_as = SYSTEMD_SESSION;
+ break;
+
case ARG_SYSTEM:
arg_scope = UNIT_FILE_SYSTEM;
arg_as = SYSTEMD_SYSTEM;
@@ -5140,6 +5149,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_as = SYSTEMD_SYSTEM;
break;
+ case ARG_GLOBAL_SESSION:
+ arg_scope = UNIT_FILE_GLOBAL_SESSION;
+ arg_as = SYSTEMD_SYSTEM;
+ break;
+
case ARG_NO_BLOCK:
arg_no_block = true;
break;
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index 1cce9c5..16465ba 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -89,9 +89,11 @@ typedef int (*sd_bus_node_enumerator_t) (sd_bus *bus, const char *path, void *us
/* Connections */
int sd_bus_default_user(sd_bus **ret);
+int sd_bus_default_session(sd_bus **ret);
int sd_bus_default_system(sd_bus **ret);
int sd_bus_open_user(sd_bus **ret);
+int sd_bus_open_session(sd_bus **ret);
int sd_bus_open_system(sd_bus **ret);
int sd_bus_open_system_remote(const char *host, sd_bus **ret);
int sd_bus_open_system_container(const char *machine, sd_bus **ret);
diff --git a/units/session/.gitignore b/units/session/.gitignore
new file mode 100644
index 0000000..41a74f5
--- /dev/null
+++ b/units/session/.gitignore
@@ -0,0 +1 @@
+/systemd-exit.service
diff --git a/units/session/Makefile b/units/session/Makefile
new file mode 120000
index 0000000..50be211
--- /dev/null
+++ b/units/session/Makefile
@@ -0,0 +1 @@
+../../src/Makefile
\ No newline at end of file
diff --git a/units/session/default.target b/units/session/default.target
new file mode 100644
index 0000000..71eed51
--- /dev/null
+++ b/units/session/default.target
@@ -0,0 +1,11 @@
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Default
+Documentation=man:systemd.special(7)
+AllowIsolate=yes
diff --git a/units/session/exit.target b/units/session/exit.target
new file mode 100644
index 0000000..b0ad24c
--- /dev/null
+++ b/units/session/exit.target
@@ -0,0 +1,17 @@
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Exit the Session
+Documentation=man:systemd.special(7)
+DefaultDependencies=no
+Requires=systemd-exit.service
+After=systemd-exit.service
+AllowIsolate=yes
+
+[Install]
+Alias=ctrl-alt-del.target
diff --git a/units/session/systemd-exit.service.in b/units/session/systemd-exit.service.in
new file mode 100644
index 0000000..987fab8
--- /dev/null
+++ b/units/session/systemd-exit.service.in
@@ -0,0 +1,17 @@
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Exit the Session
+Documentation=man:systemd.special(7)
+DefaultDependencies=no
+Requires=shutdown.target
+After=shutdown.target
+
+[Service]
+Type=oneshot
+ExecStart=@KILL@ -s 58 $MANAGERPID
--
1.8.4.4
More information about the systemd-devel
mailing list