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@.service.m4 \ units/serial-getty@.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@.service $(pkgsysconfdir)/system/getty.target.wants/getty@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 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel