logind-session.c revision b12e56156e5f363ebb8dc4ea5c10f5fd0665dc9d
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering This file is part of systemd.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering Copyright 2011 Lennart Poettering
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering systemd is free software; you can redistribute it and/or modify it
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering under the terms of the GNU Lesser General Public License as published by
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering (at your option) any later version.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering systemd is distributed in the hope that it will be useful, but
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering Lesser General Public License for more details.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering You should have received a copy of the GNU Lesser General Public License
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringstatic void session_remove_fifo(Session *s);
7085053a437456ab87d726f3697002dd811fdf7aDaniel WallaceSession* session_new(Manager *m, const char *id) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering s->state_file = strappend("/run/systemd/sessions/", id);
46e65dcc3a522b5e992e165b5e61d14254026859Lennart Poettering s->devices = hashmap_new(devt_hash_func, devt_compare_func);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (hashmap_put(m->sessions, s->id, s) < 0) {
a34286684ebb78dd3db0d7f34feb2c121c9d00ccMichal Sekletar LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s);
a34286684ebb78dd3db0d7f34feb2c121c9d00ccMichal Sekletar s->timer_event_source = sd_event_source_unref(s->timer_event_source);
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt LIST_REMOVE(sessions_by_user, s->user->sessions, s);
a34286684ebb78dd3db0d7f34feb2c121c9d00ccMichal Sekletar LIST_REMOVE(sessions_by_seat, s->seat->sessions, s);
a34286684ebb78dd3db0d7f34feb2c121c9d00ccMichal Sekletar hashmap_remove(s->manager->session_units, s->scope);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering hashmap_remove(s->manager->sessions, s->id);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringvoid session_set_user(Session *s, User *u) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering LIST_PREPEND(sessions_by_user, u->sessions, s);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers r = fopen_temporary(s->state_file, &f, &temp_path);
ff9b60f38bf68eba4a47cabff14547d92e083214Torstein Husebø "# This is private data. Do not parse.\n"
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers "ACTIVE=%i\n"
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers "REMOTE=%i\n",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering fprintf(f, "TYPE=%s\n", session_type_to_string(s->type));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering fprintf(f, "CLASS=%s\n", session_class_to_string(s->class));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering fprintf(f, "SCOPE_JOB=%s\n", s->scope_job);
7c2d80944afb4196f2eff614e8da1450dffcbeaaThomas Hindoe Paaboel Andersen fprintf(f, "REMOTE_USER=%s\n", escaped);
e1636421f46db6d06fbd028ef20a3113fa3e11f8Lennart Poettering fprintf(f, "LEADER="PID_FMT"\n", s->leader);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (dual_timestamp_is_set(&s->timestamp))
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering fprintf(f, "CONTROLLER=%s\n", s->controller);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (ferror(f) || rename(temp_path, s->state_file) < 0) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_error("Failed to save session data %s: %s", s->state_file, strerror(-r));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = parse_env_file(s->state_file, NEWLINE,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_error("Failed to read %s: %s", s->state_file, strerror(-r));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_error("UID not specified for session %s", s->id);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_error("Failed to parse UID value %s for session %s.", uid, s->id);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering user = hashmap_get(s->manager->users, ULONG_TO_PTR((unsigned long) u));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_error("User of session %s not known.", s->id);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering if (!o || r < 0)
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering log_error("Cannot attach session %s to seat %s", s->id, seat);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering audit_session_from_pid(s->leader, &s->audit_id);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering /* If we open an unopened pipe for reading we will not
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering get an EOF. to trigger an EOF we hence open it for
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering reading, but close it right-away which then will
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering trigger the EOF. */
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering unsigned long long l;
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering unsigned long long l;
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0)
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering session_set_controller(s, controller, false);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering /* on seats with VTs, we let VTs manage session-switching */
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering /* On seats without VTs, we implement session-switching in logind. We
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering * try to pause all session-devices and wait until the session
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering * controller acknowledged them. Once all devices are asleep, we simply
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering * switch the active session and be done.
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering * We save the session we want to switch to in seat->pending_switch and
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering * seat_complete_switch() will perform the final switch. */
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering /* if no devices are running, immediately perform the session switch */
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering num_pending = session_device_try_pause_all(s);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poetteringstatic int session_start_scope(Session *s) {
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
601185b43da638b1c74153deae01dbd518680889Zbigniew Jędrzejewski-Szmek description = strjoin("Session ", s->id, " of user ", s->user->name, NULL);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering scope = strjoin("session-", s->id, ".scope", NULL);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "systemd-logind.service", "systemd-user-sessions.service", &error, &job);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_error("Failed to start session scope %s: %s %s",
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering scope, bus_error_message(&error, r), error.name);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering hashmap_put(s->manager->session_units, s->scope, s);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Create cgroup */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers "MESSAGE=New session %s of user %s.", s->id, s->user->name,
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Save data */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Send signals */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering user_send_changed(s->user, "Sessions", "Display", NULL);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering seat_send_changed(s->seat, "Sessions", "ActiveSession", NULL);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering seat_send_changed(s->seat, "Sessions", NULL);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poetteringstatic int session_stop_scope(Session *s, bool force) {
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (force || manager_shall_kill(s->manager, s->user->name)) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = manager_stop_unit(s->manager, s->scope, &error, &job);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_error("Failed to stop session scope: %s", bus_error_message(&error, r));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = manager_abandon_scope(s->manager, s->scope, &error);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_error("Failed to abandon session scope: %s", bus_error_message(&error, r));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringint session_stop(Session *s, bool force) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering s->timer_event_source = sd_event_source_unref(s->timer_event_source);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* We are going down, don't care about FIFOs anymore */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Kill cgroup */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
84f6181c2ac99a0514ca5e0c8fc8c8e284caf789Lennart Poettering s->timer_event_source = sd_event_source_unref(s->timer_event_source);
a9cdc94f7ff40f22a3cf9472f612a80730a1b010Dave Reisner /* Kill session devices */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering seat_send_changed(s->seat, "Sessions", NULL);
assert(s);
session_stop(s, false);
assert(s);
if (!s->timer_event_source)
&s->timer_event_source,
assert(s);
if (!s->seat)
return -ENOMEM;
tty = p;
return -ENOENT;
return -errno;
assert(s);
if (s->idle_hint) {
*t = s->idle_hint_timestamp;
return s->idle_hint;
if (s->display)
goto dont_know;
if (s->tty) {
goto found_atime;
if (s->leader > 0) {
goto found_atime;
*t = s->idle_hint_timestamp;
assert(s);
if (s->idle_hint == b)
s->idle_hint = b;
if (s->seat)
assert(s);
session_stop(s, false);
assert(s);
if (!s->fifo_path) {
return -ENOMEM;
return -errno;
if (s->fifo_fd < 0) {
if (s->fifo_fd < 0)
return -errno;
if (!s->fifo_event_source) {
r = sd_event_add_io(s->manager->event, &s->fifo_event_source, s->fifo_fd, 0, session_dispatch_fifo, s);
return -errno;
assert(s);
if (s->fifo_path) {
assert(s);
if (!s->user)
if (s->fifo_fd >= 0) {
assert(s);
if (s->in_gc_queue)
s->in_gc_queue = true;
assert(s);
return SESSION_CLOSING;
return SESSION_OPENING;
if (session_is_active(s))
return SESSION_ACTIVE;
return SESSION_ONLINE;
assert(s);
if (!s->scope)
return -ESRCH;
return -ENODEV;
if (s->vtfd >= 0)
return s->vtfd;
if (s->vtfd < 0) {
return -errno;
return s->vtfd;
int vt, r;
if (vt < 0)
return vt;
r = -errno;
goto error;
r = -errno;
goto error;
r = -errno;
goto error;
r = -errno;
goto error;
if (vt < 0)
assert(s);
if (!s->controller)
if (!notify)
assert(s);
return -EBUSY;
if (!name)
return -ENOMEM;
r = session_prepare_vt(s);
session_release_controller(s, true);
session_save(s);
assert(s);
if (!s->controller)
session_release_controller(s, false);
session_save(s);