]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
20263082 | 2 | |
90a18413 | 3 | #include <fcntl.h> |
90a18413 | 4 | #include <linux/kd.h> |
4f5dd394 | 5 | #include <linux/vt.h> |
90a18413 | 6 | #include <signal.h> |
90a18413 | 7 | #include <sys/ioctl.h> |
ca78ad1d | 8 | #include <sys/stat.h> |
20263082 LP |
9 | #include <unistd.h> |
10 | ||
6ad1d1ed DDM |
11 | #include "sd-bus.h" |
12 | #include "sd-event.h" | |
cc377381 | 13 | #include "sd-messages.h" |
6ad1d1ed | 14 | #include "sd-varlink.h" |
4f5dd394 | 15 | |
b5efdb8a | 16 | #include "alloc-util.h" |
430f0182 | 17 | #include "audit-util.h" |
cc377381 | 18 | #include "bus-error.h" |
4f5dd394 | 19 | #include "bus-util.h" |
9d5b6901 | 20 | #include "daemon-util.h" |
7176f06c | 21 | #include "devnum-util.h" |
686d13b9 | 22 | #include "env-file.h" |
6ad1d1ed | 23 | #include "errno-util.h" |
6ad1d1ed | 24 | #include "extract-word.h" |
3ffd4af2 | 25 | #include "fd-util.h" |
f97b34a6 | 26 | #include "format-util.h" |
70f1280c | 27 | #include "fs-util.h" |
6ad1d1ed | 28 | #include "hashmap.h" |
214c93c8 DDM |
29 | #include "login-util.h" |
30 | #include "logind.h" | |
6ecda0fb | 31 | #include "logind-dbus.h" |
214c93c8 | 32 | #include "logind-seat.h" |
6ecda0fb | 33 | #include "logind-seat-dbus.h" |
1cf40697 | 34 | #include "logind-session.h" |
6ecda0fb | 35 | #include "logind-session-dbus.h" |
214c93c8 | 36 | #include "logind-session-device.h" |
214c93c8 | 37 | #include "logind-user.h" |
6ecda0fb | 38 | #include "logind-user-dbus.h" |
2baca6c2 | 39 | #include "logind-varlink.h" |
35cd0ba5 | 40 | #include "mkdir-label.h" |
6bedfcbb | 41 | #include "parse-util.h" |
4f5dd394 | 42 | #include "path-util.h" |
8c29a457 | 43 | #include "process-util.h" |
d68c645b | 44 | #include "serialize.h" |
8b43440b | 45 | #include "string-table.h" |
288a74cc | 46 | #include "terminal-util.h" |
e4de7287 | 47 | #include "tmpfile-util.h" |
6ad1d1ed | 48 | #include "user-record.h" |
b1d4f8e1 | 49 | #include "user-util.h" |
20263082 | 50 | |
5f41d1f1 LP |
51 | #define RELEASE_USEC (20*USEC_PER_SEC) |
52 | ||
0212126c | 53 | static void session_restore_vt(Session *s); |
5f41d1f1 | 54 | |
2454cee3 | 55 | int session_new(Manager *m, const char *id, Session **ret) { |
8c29a457 LP |
56 | _cleanup_(session_freep) Session *s = NULL; |
57 | int r; | |
20263082 LP |
58 | |
59 | assert(m); | |
60 | assert(id); | |
2454cee3 | 61 | assert(ret); |
20263082 | 62 | |
8c29a457 LP |
63 | if (!session_id_valid(id)) |
64 | return -EINVAL; | |
65 | ||
66 | s = new(Session, 1); | |
20263082 | 67 | if (!s) |
8c29a457 LP |
68 | return -ENOMEM; |
69 | ||
70 | *s = (Session) { | |
71 | .manager = m, | |
c1f04b83 MY |
72 | .id = strdup(id), |
73 | .state_file = path_join("/run/systemd/sessions/", id), | |
254d1313 | 74 | .vtfd = -EBADF, |
8c29a457 | 75 | .audit_id = AUDIT_SESSION_INVALID, |
3d0ef5c7 | 76 | .tty_validity = _TTY_VALIDITY_INVALID, |
89bad70f | 77 | .leader = PIDREF_NULL, |
8c29a457 | 78 | }; |
c1f04b83 | 79 | if (!s->id || !s->state_file) |
8c29a457 | 80 | return -ENOMEM; |
118ecf32 | 81 | |
8c29a457 LP |
82 | s->devices = hashmap_new(&devt_hash_ops); |
83 | if (!s->devices) | |
84 | return -ENOMEM; | |
20263082 | 85 | |
8c29a457 LP |
86 | r = hashmap_put(m->sessions, s->id, s); |
87 | if (r < 0) | |
88 | return r; | |
20263082 | 89 | |
8c29a457 LP |
90 | *ret = TAKE_PTR(s); |
91 | return 0; | |
20263082 LP |
92 | } |
93 | ||
0ae9073f MY |
94 | static int session_dispatch_leader_pidfd(sd_event_source *es, int fd, uint32_t revents, void *userdata) { |
95 | Session *s = ASSERT_PTR(userdata); | |
96 | ||
97 | assert(s->leader.fd == fd); | |
b2a41090 | 98 | |
99 | s->leader_pidfd_event_source = sd_event_source_unref(s->leader_pidfd_event_source); | |
100 | ||
0ae9073f MY |
101 | session_stop(s, /* force= */ false); |
102 | ||
a6bccda2 | 103 | session_add_to_gc_queue(s); |
104 | ||
0ae9073f MY |
105 | return 1; |
106 | } | |
107 | ||
108 | static int session_watch_pidfd(Session *s) { | |
109 | int r; | |
110 | ||
111 | assert(s); | |
112 | assert(s->manager); | |
113 | assert(pidref_is_set(&s->leader)); | |
3180c4d4 | 114 | assert(s->leader.fd >= 0); |
1b3449d8 | 115 | assert(!s->leader_pidfd_event_source); |
0ae9073f | 116 | |
0ae9073f MY |
117 | r = sd_event_add_io(s->manager->event, &s->leader_pidfd_event_source, s->leader.fd, EPOLLIN, session_dispatch_leader_pidfd, s); |
118 | if (r < 0) | |
119 | return r; | |
120 | ||
121 | r = sd_event_source_set_priority(s->leader_pidfd_event_source, SD_EVENT_PRIORITY_IMPORTANT); | |
122 | if (r < 0) | |
123 | return r; | |
124 | ||
125 | (void) sd_event_source_set_description(s->leader_pidfd_event_source, "session-pidfd"); | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
9d5b6901 | 130 | static void session_reset_leader(Session *s, bool keep_fdstore) { |
8494f562 MY |
131 | assert(s); |
132 | ||
9d5b6901 MY |
133 | if (!keep_fdstore) { |
134 | /* Clear fdstore if we're asked to, no matter if s->leader is set or not, so that when | |
135 | * initially deserializing leader fd we clear the old fd too. */ | |
136 | (void) notify_remove_fd_warnf("session-%s-leader-fd", s->id); | |
137 | s->leader_fd_saved = false; | |
138 | } | |
139 | ||
8494f562 MY |
140 | if (!pidref_is_set(&s->leader)) |
141 | return; | |
142 | ||
0ae9073f MY |
143 | s->leader_pidfd_event_source = sd_event_source_disable_unref(s->leader_pidfd_event_source); |
144 | ||
889975bb | 145 | (void) hashmap_remove_value(s->manager->sessions_by_leader, &s->leader, s); |
8494f562 MY |
146 | |
147 | return pidref_done(&s->leader); | |
148 | } | |
149 | ||
8c29a457 | 150 | Session* session_free(Session *s) { |
118ecf32 DH |
151 | SessionDevice *sd; |
152 | ||
8c29a457 LP |
153 | if (!s) |
154 | return NULL; | |
20263082 | 155 | |
c1f04b83 MY |
156 | sd_event_source_unref(s->stop_on_idle_event_source); |
157 | ||
1b3449d8 LP |
158 | if (s->in_gc_queue) { |
159 | assert(s->manager); | |
71fda00f | 160 | LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s); |
1b3449d8 | 161 | } |
14c3baca | 162 | |
c1f04b83 | 163 | sd_event_source_unref(s->timer_event_source); |
5f41d1f1 | 164 | |
ae5e06bd DH |
165 | session_drop_controller(s); |
166 | ||
118ecf32 DH |
167 | while ((sd = hashmap_first(s->devices))) |
168 | session_device_free(sd); | |
169 | ||
170 | hashmap_free(s->devices); | |
171 | ||
20263082 | 172 | if (s->user) { |
71fda00f | 173 | LIST_REMOVE(sessions_by_user, s->user->sessions, s); |
20263082 LP |
174 | |
175 | if (s->user->display == s) | |
176 | s->user->display = NULL; | |
9afe9efb LP |
177 | |
178 | user_update_last_session_timer(s->user); | |
20263082 LP |
179 | } |
180 | ||
9418f147 LP |
181 | if (s->seat) { |
182 | if (s->seat->active == s) | |
183 | s->seat->active = NULL; | |
d7bd01b5 DH |
184 | if (s->seat->pending_switch == s) |
185 | s->seat->pending_switch = NULL; | |
9418f147 | 186 | |
49e6fdbf | 187 | seat_evict_position(s->seat, s); |
71fda00f | 188 | LIST_REMOVE(sessions_by_seat, s->seat->sessions, s); |
9418f147 | 189 | } |
20263082 | 190 | |
fb6becb4 LP |
191 | if (s->scope) { |
192 | hashmap_remove(s->manager->session_units, s->scope); | |
193 | free(s->scope); | |
194 | } | |
195 | ||
196 | free(s->scope_job); | |
1713813d | 197 | |
9d5b6901 | 198 | session_reset_leader(s, /* keep_fdstore = */ true); |
8494f562 | 199 | |
cc377381 | 200 | sd_bus_message_unref(s->create_message); |
6e9bf0ad | 201 | sd_bus_message_unref(s->upgrade_message); |
20263082 | 202 | |
2baca6c2 LP |
203 | sd_varlink_unref(s->create_link); |
204 | ||
20263082 LP |
205 | free(s->tty); |
206 | free(s->display); | |
207 | free(s->remote_host); | |
3f49d45a | 208 | free(s->remote_user); |
98a28fef | 209 | free(s->service); |
a4cd87e9 | 210 | free(s->desktop); |
20263082 LP |
211 | |
212 | hashmap_remove(s->manager->sessions, s->id); | |
98a28fef | 213 | |
3180c4d4 | 214 | /* Note that we don't remove the state file here, since it's supposed to survive daemon restarts */ |
c1f04b83 MY |
215 | free(s->state_file); |
216 | free(s->id); | |
82325af3 | 217 | |
8c29a457 | 218 | return mfree(s); |
20263082 LP |
219 | } |
220 | ||
9444b1f2 LP |
221 | void session_set_user(Session *s, User *u) { |
222 | assert(s); | |
223 | assert(!s->user); | |
224 | ||
225 | s->user = u; | |
71fda00f | 226 | LIST_PREPEND(sessions_by_user, u->sessions, s); |
9afe9efb LP |
227 | |
228 | user_update_last_session_timer(u); | |
9444b1f2 LP |
229 | } |
230 | ||
76f2191d MS |
231 | int session_set_leader_consume(Session *s, PidRef _leader) { |
232 | _cleanup_(pidref_done) PidRef pidref = _leader; | |
238794b1 LP |
233 | int r; |
234 | ||
235 | assert(s); | |
76f2191d | 236 | assert(pidref_is_set(&pidref)); |
3180c4d4 | 237 | assert(pidref.fd >= 0); |
238794b1 | 238 | |
76f2191d | 239 | if (pidref_equal(&s->leader, &pidref)) |
238794b1 LP |
240 | return 0; |
241 | ||
9d5b6901 | 242 | session_reset_leader(s, /* keep_fdstore = */ false); |
8494f562 MY |
243 | |
244 | s->leader = TAKE_PIDREF(pidref); | |
245 | ||
0ae9073f MY |
246 | r = session_watch_pidfd(s); |
247 | if (r < 0) | |
248 | return log_error_errno(r, "Failed to watch leader pidfd for session '%s': %m", s->id); | |
249 | ||
8494f562 | 250 | r = hashmap_ensure_put(&s->manager->sessions_by_leader, &pidref_hash_ops, &s->leader, s); |
89bad70f LP |
251 | if (r < 0) |
252 | return r; | |
8494f562 | 253 | assert(r > 0); |
89bad70f | 254 | |
9d5b6901 MY |
255 | if (s->leader.fd >= 0) { |
256 | r = notify_push_fdf(s->leader.fd, "session-%s-leader-fd", s->id); | |
257 | if (r < 0) | |
258 | log_warning_errno(r, "Failed to push leader pidfd for session '%s', ignoring: %m", s->id); | |
259 | else | |
260 | s->leader_fd_saved = true; | |
261 | } | |
262 | ||
7e02ee98 | 263 | (void) audit_session_from_pid(&s->leader, &s->audit_id); |
238794b1 LP |
264 | |
265 | return 1; | |
266 | } | |
267 | ||
aed24c4c FB |
268 | static void session_save_devices(Session *s, FILE *f) { |
269 | SessionDevice *sd; | |
aed24c4c | 270 | |
c3c79aeb LB |
271 | if (!hashmap_isempty(s->devices)) { |
272 | fprintf(f, "DEVICES="); | |
273 | HASHMAP_FOREACH(sd, s->devices) | |
274 | fprintf(f, DEVNUM_FORMAT_STR " ", DEVNUM_FORMAT_VAL(sd->dev)); | |
275 | fprintf(f, "\n"); | |
aed24c4c FB |
276 | } |
277 | } | |
278 | ||
20263082 | 279 | int session_save(Session *s) { |
6d95e7d9 | 280 | int r; |
20263082 LP |
281 | |
282 | assert(s); | |
283 | ||
9444b1f2 LP |
284 | if (!s->user) |
285 | return -ESTALE; | |
286 | ||
accaeded LP |
287 | if (!s->started) |
288 | return 0; | |
289 | ||
37c1d5e9 | 290 | r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0, MKDIR_WARN_MODE); |
20263082 | 291 | if (r < 0) |
019c8ea2 | 292 | return log_error_errno(r, "Failed to create /run/systemd/sessions/: %m"); |
20263082 | 293 | |
019c8ea2 LP |
294 | _cleanup_(unlink_and_freep) char *temp_path = NULL; |
295 | _cleanup_fclose_ FILE *f = NULL; | |
296 | r = fopen_tmpfile_linkable(s->state_file, O_WRONLY|O_CLOEXEC, &temp_path, &f); | |
14c3baca | 297 | if (r < 0) |
019c8ea2 | 298 | return log_error_errno(r, "Failed to create state file '%s': %m", s->state_file); |
20263082 | 299 | |
019c8ea2 LP |
300 | if (fchmod(fileno(f), 0644) < 0) |
301 | return log_error_errno(errno, "Failed to set access mode for state file '%s' to 0644: %m", s->state_file); | |
14c3baca | 302 | |
20263082 LP |
303 | fprintf(f, |
304 | "# This is private data. Do not parse.\n" | |
90b2de37 | 305 | "UID="UID_FMT"\n" |
fdbb56dc MY |
306 | "ACTIVE=%s\n" |
307 | "IS_DISPLAY=%s\n" | |
0604381b | 308 | "STATE=%s\n" |
9d5b6901 MY |
309 | "REMOTE=%s\n" |
310 | "LEADER_FD_SAVED=%s\n", | |
22c902fa | 311 | s->user->user_record->uid, |
fdbb56dc MY |
312 | one_zero(session_is_active(s)), |
313 | one_zero(s->user->display == s), | |
0604381b | 314 | session_state_to_string(session_get_state(s)), |
9d5b6901 MY |
315 | one_zero(s->remote), |
316 | one_zero(s->leader_fd_saved)); | |
20263082 | 317 | |
232f4e12 LP |
318 | env_file_fputs_assignment(f, "USER=", s->user->user_record->user_name); |
319 | ||
a91e4e53 | 320 | if (s->type >= 0) |
507f22bd | 321 | fprintf(f, "TYPE=%s\n", session_type_to_string(s->type)); |
a91e4e53 | 322 | |
db72aea4 CH |
323 | if (s->original_type >= 0) |
324 | fprintf(f, "ORIGINAL_TYPE=%s\n", session_type_to_string(s->original_type)); | |
325 | ||
55efac6c | 326 | if (s->class >= 0) |
507f22bd | 327 | fprintf(f, "CLASS=%s\n", session_class_to_string(s->class)); |
55efac6c | 328 | |
232f4e12 LP |
329 | env_file_fputs_assignment(f, "SCOPE=", s->scope); |
330 | env_file_fputs_assignment(f, "SCOPE_JOB=", s->scope_job); | |
20263082 | 331 | if (s->seat) |
232f4e12 LP |
332 | env_file_fputs_assignment(f, "SEAT=", s->seat->id); |
333 | env_file_fputs_assignment(f, "TTY=", s->tty); | |
20263082 | 334 | |
3d0ef5c7 LP |
335 | if (s->tty_validity >= 0) |
336 | fprintf(f, "TTY_VALIDITY=%s\n", tty_validity_to_string(s->tty_validity)); | |
337 | ||
232f4e12 LP |
338 | env_file_fputs_assignment(f, "DISPLAY=", s->display); |
339 | env_file_fputs_assignment(f, "REMOTE_HOST=", s->remote_host); | |
340 | env_file_fputs_assignment(f, "REMOTE_USER=", s->remote_user); | |
341 | env_file_fputs_assignment(f, "SERVICE=", s->service); | |
342 | env_file_fputs_assignment(f, "DESKTOP=", s->desktop); | |
a4cd87e9 | 343 | |
736b6277 LP |
344 | if (s->seat) { |
345 | if (!seat_has_vts(s->seat)) | |
346 | fprintf(f, "POSITION=%u\n", s->position); | |
347 | else if (s->vtnr > 0) | |
348 | fprintf(f, "VTNR=%u\n", s->vtnr); | |
349 | } | |
49e6fdbf | 350 | |
f01d8658 | 351 | if (pidref_is_set(&s->leader)) { |
89bad70f | 352 | fprintf(f, "LEADER="PID_FMT"\n", s->leader.pid); |
f01d8658 LP |
353 | (void) pidref_acquire_pidfd_id(&s->leader); |
354 | if (s->leader.fd_id != 0) | |
355 | fprintf(f, "LEADER_PIDFDID=%" PRIu64 "\n", s->leader.fd_id); | |
356 | } | |
20263082 | 357 | |
3a87a86e | 358 | if (audit_session_is_valid(s->audit_id)) |
507f22bd | 359 | fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id); |
20263082 | 360 | |
9444b1f2 LP |
361 | if (dual_timestamp_is_set(&s->timestamp)) |
362 | fprintf(f, | |
90b2de37 ZJS |
363 | "REALTIME="USEC_FMT"\n" |
364 | "MONOTONIC="USEC_FMT"\n", | |
365 | s->timestamp.realtime, | |
366 | s->timestamp.monotonic); | |
9444b1f2 | 367 | |
aed24c4c | 368 | if (s->controller) { |
232f4e12 | 369 | env_file_fputs_assignment(f, "CONTROLLER=", s->controller); |
aed24c4c FB |
370 | session_save_devices(s, f); |
371 | } | |
6d33772f | 372 | |
019c8ea2 | 373 | r = flink_tmpfile(f, temp_path, s->state_file, LINK_TMPFILE_REPLACE); |
dacd6cee | 374 | if (r < 0) |
019c8ea2 | 375 | return log_error_errno(r, "Failed to move '%s' into place: %m", s->state_file); |
14c3baca | 376 | |
019c8ea2 | 377 | temp_path = mfree(temp_path); /* disarm auto-destroy: temporary file does not exist anymore */ |
dacd6cee | 378 | return 0; |
20263082 LP |
379 | } |
380 | ||
aed24c4c | 381 | static int session_load_devices(Session *s, const char *devices) { |
aed24c4c FB |
382 | int r = 0; |
383 | ||
384 | assert(s); | |
385 | ||
e5c09aad | 386 | for (const char *p = devices;;) { |
aed24c4c | 387 | _cleanup_free_ char *word = NULL; |
aed24c4c FB |
388 | dev_t dev; |
389 | int k; | |
390 | ||
391 | k = extract_first_word(&p, &word, NULL, 0); | |
41935c0c MY |
392 | if (k <= 0) { |
393 | RET_GATHER(r, k); | |
aed24c4c FB |
394 | break; |
395 | } | |
396 | ||
7176f06c | 397 | k = parse_devnum(word, &dev); |
aed24c4c | 398 | if (k < 0) { |
41935c0c | 399 | RET_GATHER(r, k); |
aed24c4c FB |
400 | continue; |
401 | } | |
402 | ||
403 | /* The file descriptors for loaded devices will be reattached later. */ | |
41935c0c | 404 | RET_GATHER(r, session_device_new(s, dev, /* open_device = */ false, /* ret = */ NULL)); |
aed24c4c FB |
405 | } |
406 | ||
407 | if (r < 0) | |
41935c0c | 408 | log_error_errno(r, "Failed to load some session devices for session '%s': %m", s->id); |
aed24c4c FB |
409 | return r; |
410 | } | |
dacd6cee | 411 | |
20263082 | 412 | int session_load(Session *s) { |
9444b1f2 | 413 | _cleanup_free_ char *remote = NULL, |
a185c5aa | 414 | *seat = NULL, |
3d0ef5c7 | 415 | *tty_validity = NULL, |
a185c5aa | 416 | *vtnr = NULL, |
be94d954 | 417 | *state = NULL, |
e6494a07 | 418 | *position = NULL, |
9d5b6901 MY |
419 | *leader_pid = NULL, |
420 | *leader_fd_saved = NULL, | |
55efac6c | 421 | *type = NULL, |
db72aea4 | 422 | *original_type = NULL, |
9444b1f2 LP |
423 | *class = NULL, |
424 | *uid = NULL, | |
425 | *realtime = NULL, | |
6d33772f | 426 | *monotonic = NULL, |
aed24c4c FB |
427 | *controller = NULL, |
428 | *active = NULL, | |
1c8280fd | 429 | *devices = NULL, |
3180c4d4 MY |
430 | *is_display = NULL, |
431 | *fifo_path = NULL; /* compat only, not used */ | |
a185c5aa LP |
432 | |
433 | int k, r; | |
434 | ||
20263082 LP |
435 | assert(s); |
436 | ||
aa8fbc74 | 437 | r = parse_env_file(NULL, s->state_file, |
9d5b6901 MY |
438 | "REMOTE", &remote, |
439 | "SCOPE", &s->scope, | |
440 | "SCOPE_JOB", &s->scope_job, | |
3180c4d4 | 441 | "FIFO", &fifo_path, |
9d5b6901 MY |
442 | "SEAT", &seat, |
443 | "TTY", &s->tty, | |
444 | "TTY_VALIDITY", &tty_validity, | |
445 | "DISPLAY", &s->display, | |
446 | "REMOTE_HOST", &s->remote_host, | |
447 | "REMOTE_USER", &s->remote_user, | |
448 | "SERVICE", &s->service, | |
449 | "DESKTOP", &s->desktop, | |
450 | "VTNR", &vtnr, | |
451 | "STATE", &state, | |
452 | "POSITION", &position, | |
453 | "LEADER", &leader_pid, | |
454 | "LEADER_FD_SAVED", &leader_fd_saved, | |
455 | "TYPE", &type, | |
456 | "ORIGINAL_TYPE", &original_type, | |
457 | "CLASS", &class, | |
458 | "UID", &uid, | |
459 | "REALTIME", &realtime, | |
460 | "MONOTONIC", &monotonic, | |
461 | "CONTROLLER", &controller, | |
462 | "ACTIVE", &active, | |
463 | "DEVICES", &devices, | |
464 | "IS_DISPLAY", &is_display); | |
f647962d MS |
465 | if (r < 0) |
466 | return log_error_errno(r, "Failed to read %s: %m", s->state_file); | |
9444b1f2 LP |
467 | |
468 | if (!s->user) { | |
469 | uid_t u; | |
470 | User *user; | |
471 | ||
baaa35ad ZJS |
472 | if (!uid) |
473 | return log_error_errno(SYNTHETIC_ERRNO(ENOENT), | |
474 | "UID not specified for session %s", | |
475 | s->id); | |
9444b1f2 LP |
476 | |
477 | r = parse_uid(uid, &u); | |
478 | if (r < 0) { | |
479 | log_error("Failed to parse UID value %s for session %s.", uid, s->id); | |
480 | return r; | |
481 | } | |
482 | ||
8cb4ab00 | 483 | user = hashmap_get(s->manager->users, UID_TO_PTR(u)); |
baaa35ad ZJS |
484 | if (!user) |
485 | return log_error_errno(SYNTHETIC_ERRNO(ENOENT), | |
486 | "User of session %s not known.", | |
487 | s->id); | |
9444b1f2 LP |
488 | |
489 | session_set_user(s, user); | |
490 | } | |
a185c5aa LP |
491 | |
492 | if (remote) { | |
493 | k = parse_boolean(remote); | |
494 | if (k >= 0) | |
495 | s->remote = k; | |
496 | } | |
497 | ||
c506027a | 498 | if (vtnr) |
736b6277 | 499 | (void) safe_atou(vtnr, &s->vtnr); |
c506027a | 500 | |
9418f147 | 501 | if (seat && !s->seat) { |
a185c5aa LP |
502 | Seat *o; |
503 | ||
504 | o = hashmap_get(s->manager->seats, seat); | |
505 | if (o) | |
c506027a DH |
506 | r = seat_attach_session(o, s); |
507 | if (!o || r < 0) | |
508 | log_error("Cannot attach session %s to seat %s", s->id, seat); | |
a185c5aa LP |
509 | } |
510 | ||
c506027a DH |
511 | if (!s->seat || !seat_has_vts(s->seat)) |
512 | s->vtnr = 0; | |
a185c5aa | 513 | |
e6494a07 | 514 | if (position && s->seat) { |
14cb109d | 515 | unsigned npos; |
49e6fdbf | 516 | |
736b6277 | 517 | (void) safe_atou(position, &npos); |
49e6fdbf DH |
518 | seat_claim_position(s->seat, s, npos); |
519 | } | |
520 | ||
3d0ef5c7 LP |
521 | if (tty_validity) { |
522 | TTYValidity v; | |
523 | ||
524 | v = tty_validity_from_string(tty_validity); | |
525 | if (v < 0) | |
526 | log_debug("Failed to parse TTY validity: %s", tty_validity); | |
527 | else | |
528 | s->tty_validity = v; | |
529 | } | |
530 | ||
a91e4e53 LP |
531 | if (type) { |
532 | SessionType t; | |
533 | ||
534 | t = session_type_from_string(type); | |
535 | if (t >= 0) | |
536 | s->type = t; | |
537 | } | |
538 | ||
db72aea4 CH |
539 | if (original_type) { |
540 | SessionType ot; | |
541 | ||
542 | ot = session_type_from_string(original_type); | |
543 | if (ot >= 0) | |
544 | s->original_type = ot; | |
545 | } else | |
546 | /* Pre-v246 compat: initialize original_type if not set in the state file */ | |
547 | s->original_type = s->type; | |
548 | ||
55efac6c LP |
549 | if (class) { |
550 | SessionClass c; | |
551 | ||
552 | c = session_class_from_string(class); | |
553 | if (c >= 0) | |
554 | s->class = c; | |
555 | } | |
556 | ||
e5c09aad | 557 | if (streq_ptr(state, "closing")) |
be94d954 MP |
558 | s->stopping = true; |
559 | ||
3180c4d4 MY |
560 | /* logind before v258 used a fifo for session close notification. Since v258 we fully employ |
561 | * pidfd for the job, hence just unlink the legacy fifo. */ | |
562 | if (fifo_path) | |
563 | (void) unlink(fifo_path); | |
b4f78aea | 564 | |
b895a735 | 565 | if (realtime) |
d68c645b | 566 | (void) deserialize_usec(realtime, &s->timestamp.realtime); |
b895a735 | 567 | if (monotonic) |
d68c645b | 568 | (void) deserialize_usec(monotonic, &s->timestamp.monotonic); |
a185c5aa | 569 | |
aed24c4c FB |
570 | if (active) { |
571 | k = parse_boolean(active); | |
572 | if (k >= 0) | |
573 | s->was_active = k; | |
574 | } | |
575 | ||
1c8280fd LP |
576 | if (is_display) { |
577 | /* Note that when enumerating users are loaded before sessions, hence the display session to use is | |
578 | * something we have to store along with the session and not the user, as in that case we couldn't | |
579 | * apply it at the time we load the user. */ | |
580 | ||
581 | k = parse_boolean(is_display); | |
582 | if (k < 0) | |
583 | log_warning_errno(k, "Failed to parse IS_DISPLAY session property: %m"); | |
584 | else if (k > 0) | |
585 | s->user->display = s; | |
586 | } | |
587 | ||
6d33772f | 588 | if (controller) { |
aed24c4c | 589 | if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) { |
dc6284e9 | 590 | session_set_controller(s, controller, false, false); |
aed24c4c FB |
591 | session_load_devices(s, devices); |
592 | } else | |
90a18413 | 593 | session_restore_vt(s); |
6d33772f DH |
594 | } |
595 | ||
9d5b6901 MY |
596 | if (leader_pid) { |
597 | assert(!pidref_is_set(&s->leader)); | |
598 | ||
599 | r = parse_pid(leader_pid, &s->deserialized_pid); | |
600 | if (r < 0) | |
601 | return log_error_errno(r, "Failed to parse LEADER=%s: %m", leader_pid); | |
602 | ||
603 | if (leader_fd_saved) { | |
604 | r = parse_boolean(leader_fd_saved); | |
605 | if (r < 0) | |
606 | return log_error_errno(r, "Failed to parse LEADER_FD_SAVED=%s: %m", leader_fd_saved); | |
607 | s->leader_fd_saved = r > 0; | |
608 | ||
609 | if (s->leader_fd_saved) | |
610 | /* The leader fd will be acquired from fdstore later */ | |
611 | return 0; | |
612 | } | |
613 | ||
614 | _cleanup_(pidref_done) PidRef p = PIDREF_NULL; | |
615 | ||
616 | r = pidref_set_pid(&p, s->deserialized_pid); | |
9d5b6901 | 617 | if (r < 0) |
3180c4d4 MY |
618 | return log_error_errno(r, "Failed to deserialize leader PID for session '%s': %m", s->id); |
619 | if (p.fd < 0) | |
620 | return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), | |
621 | "Failed to acquire pidfd for session leader '" PID_FMT "', refusing.", | |
622 | p.pid); | |
623 | ||
624 | r = session_set_leader_consume(s, TAKE_PIDREF(p)); | |
625 | if (r < 0) | |
626 | return log_error_errno(r, "Failed to set leader PID for session '%s': %m", s->id); | |
9d5b6901 MY |
627 | } |
628 | ||
3180c4d4 | 629 | return 0; |
20263082 LP |
630 | } |
631 | ||
632 | int session_activate(Session *s) { | |
14cb109d | 633 | unsigned num_pending; |
d7bd01b5 | 634 | |
20263082 | 635 | assert(s); |
9444b1f2 | 636 | assert(s->user); |
20263082 | 637 | |
20263082 | 638 | if (!s->seat) |
15411c0c | 639 | return -EOPNOTSUPP; |
20263082 LP |
640 | |
641 | if (s->seat->active == s) | |
642 | return 0; | |
643 | ||
d7bd01b5 DH |
644 | /* on seats with VTs, we let VTs manage session-switching */ |
645 | if (seat_has_vts(s->seat)) { | |
709d0587 | 646 | if (s->vtnr == 0) |
15411c0c | 647 | return -EOPNOTSUPP; |
d7bd01b5 DH |
648 | |
649 | return chvt(s->vtnr); | |
650 | } | |
651 | ||
652 | /* On seats without VTs, we implement session-switching in logind. We | |
653 | * try to pause all session-devices and wait until the session | |
654 | * controller acknowledged them. Once all devices are asleep, we simply | |
655 | * switch the active session and be done. | |
656 | * We save the session we want to switch to in seat->pending_switch and | |
657 | * seat_complete_switch() will perform the final switch. */ | |
658 | ||
659 | s->seat->pending_switch = s; | |
660 | ||
661 | /* if no devices are running, immediately perform the session switch */ | |
662 | num_pending = session_device_try_pause_all(s); | |
663 | if (!num_pending) | |
664 | seat_complete_switch(s->seat); | |
20263082 | 665 | |
d7bd01b5 | 666 | return 0; |
20263082 LP |
667 | } |
668 | ||
25a1ab4e | 669 | static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_error *error) { |
52bcc872 MY |
670 | _cleanup_free_ char *scope = NULL; |
671 | const char *description; | |
98a28fef LP |
672 | int r; |
673 | ||
674 | assert(s); | |
9444b1f2 | 675 | assert(s->user); |
98a28fef | 676 | |
5099a50d LP |
677 | if (!SESSION_CLASS_WANTS_SCOPE(s->class)) |
678 | return 0; | |
679 | ||
52bcc872 MY |
680 | if (s->scope) |
681 | goto finish; | |
682 | ||
683 | s->scope_job = mfree(s->scope_job); | |
684 | ||
685 | scope = strjoin("session-", s->id, ".scope"); | |
686 | if (!scope) | |
687 | return log_oom(); | |
688 | ||
689 | description = strjoina("Session ", s->id, " of User ", s->user->user_record->user_name); | |
690 | ||
691 | r = manager_start_scope( | |
692 | s->manager, | |
693 | scope, | |
694 | &s->leader, | |
8ba3efed | 695 | /* allow_pidfd = */ true, |
52bcc872 MY |
696 | s->user->slice, |
697 | description, | |
698 | /* These should have been pulled in explicitly in user_start(). Just to be sure. */ | |
26f78eff MY |
699 | /* requires = */ STRV_MAKE_CONST(s->user->runtime_dir_unit), |
700 | /* wants = */ STRV_MAKE_CONST(SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) ? s->user->service_manager_unit : NULL), | |
52bcc872 MY |
701 | /* We usually want to order session scopes after systemd-user-sessions.service |
702 | * since the unit is used as login session barrier for unprivileged users. However | |
703 | * the barrier doesn't apply for root as sysadmin should always be able to log in | |
704 | * (and without waiting for any timeout to expire) in case something goes wrong | |
705 | * during the boot process. */ | |
26f78eff MY |
706 | /* extra_after = */ STRV_MAKE_CONST("systemd-logind.service", |
707 | SESSION_CLASS_IS_EARLY(s->class) ? NULL : "systemd-user-sessions.service"), | |
52bcc872 MY |
708 | user_record_home_directory(s->user->user_record), |
709 | properties, | |
710 | error, | |
711 | &s->scope_job); | |
712 | if (r < 0) | |
713 | return log_error_errno(r, "Failed to start session scope %s: %s", | |
714 | scope, bus_error_message(error, r)); | |
fb2367ed | 715 | |
52bcc872 | 716 | s->scope = TAKE_PTR(scope); |
20263082 | 717 | |
52bcc872 | 718 | finish: |
124d7cb2 | 719 | (void) hashmap_put(s->manager->session_units, s->scope, s); |
20263082 LP |
720 | return 0; |
721 | } | |
722 | ||
82325af3 MS |
723 | static int session_dispatch_stop_on_idle(sd_event_source *source, uint64_t t, void *userdata) { |
724 | Session *s = userdata; | |
725 | dual_timestamp ts; | |
726 | int r, idle; | |
727 | ||
728 | assert(s); | |
729 | ||
730 | if (s->stopping) | |
731 | return 0; | |
732 | ||
733 | idle = session_get_idle_hint(s, &ts); | |
734 | if (idle) { | |
6269ffe7 | 735 | log_info("Session \"%s\" of user \"%s\" is idle, stopping.", s->id, s->user->user_record->user_name); |
82325af3 MS |
736 | |
737 | return session_stop(s, /* force */ true); | |
738 | } | |
739 | ||
ab7ae746 MS |
740 | r = sd_event_source_set_time( |
741 | source, | |
742 | usec_add(dual_timestamp_is_set(&ts) ? ts.monotonic : now(CLOCK_MONOTONIC), | |
743 | s->manager->stop_idle_session_usec)); | |
82325af3 MS |
744 | if (r < 0) |
745 | return log_error_errno(r, "Failed to configure stop on idle session event source: %m"); | |
746 | ||
747 | r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); | |
748 | if (r < 0) | |
749 | return log_error_errno(r, "Failed to enable stop on idle session event source: %m"); | |
750 | ||
751 | return 1; | |
752 | } | |
753 | ||
754 | static int session_setup_stop_on_idle_timer(Session *s) { | |
755 | int r; | |
756 | ||
757 | assert(s); | |
758 | ||
ad23439e | 759 | if (s->manager->stop_idle_session_usec == USEC_INFINITY || !SESSION_CLASS_CAN_STOP_ON_IDLE(s->class)) |
82325af3 MS |
760 | return 0; |
761 | ||
762 | r = sd_event_add_time_relative( | |
763 | s->manager->event, | |
764 | &s->stop_on_idle_event_source, | |
765 | CLOCK_MONOTONIC, | |
766 | s->manager->stop_idle_session_usec, | |
767 | 0, | |
768 | session_dispatch_stop_on_idle, s); | |
769 | if (r < 0) | |
770 | return log_error_errno(r, "Failed to add stop on idle session event source: %m"); | |
771 | ||
772 | return 0; | |
773 | } | |
774 | ||
25a1ab4e | 775 | int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) { |
20263082 LP |
776 | int r; |
777 | ||
778 | assert(s); | |
9444b1f2 LP |
779 | |
780 | if (!s->user) | |
781 | return -ESTALE; | |
20263082 | 782 | |
25a1ab4e LP |
783 | if (s->stopping) |
784 | return -EINVAL; | |
785 | ||
9418f147 LP |
786 | if (s->started) |
787 | return 0; | |
788 | ||
513cf7da | 789 | r = user_start(s->user); |
ed18b08b LP |
790 | if (r < 0) |
791 | return r; | |
792 | ||
25a1ab4e | 793 | r = session_start_scope(s, properties, error); |
fb6becb4 LP |
794 | if (r < 0) |
795 | return r; | |
796 | ||
82325af3 MS |
797 | r = session_setup_stop_on_idle_timer(s); |
798 | if (r < 0) | |
799 | return r; | |
800 | ||
d9eb81f9 | 801 | log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, |
3cf6a3a3 YW |
802 | LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START_STR), |
803 | LOG_ITEM("SESSION_ID=%s", s->id), | |
804 | LOG_ITEM("USER_ID=%s", s->user->user_record->user_name), | |
805 | LOG_ITEM("LEADER="PID_FMT, s->leader.pid), | |
806 | LOG_ITEM("CLASS=%s", session_class_to_string(s->class)), | |
807 | LOG_ITEM("TYPE=%s", session_type_to_string(s->type)), | |
07b35565 LP |
808 | LOG_MESSAGE("New session '%s' of user '%s' with class '%s' and type '%s'.", |
809 | s->id, | |
810 | s->user->user_record->user_name, | |
811 | session_class_to_string(s->class), | |
812 | session_type_to_string(s->type))); | |
98a28fef | 813 | |
9444b1f2 | 814 | if (!dual_timestamp_is_set(&s->timestamp)) |
fa5a0251 | 815 | dual_timestamp_now(&s->timestamp); |
14c3baca | 816 | |
e9816c48 LP |
817 | if (s->seat) |
818 | seat_read_active_vt(s->seat); | |
819 | ||
9418f147 LP |
820 | s->started = true; |
821 | ||
952d3260 LP |
822 | user_elect_display(s->user); |
823 | ||
5f41d1f1 | 824 | /* Save data */ |
5157b0d8 LP |
825 | (void) session_save(s); |
826 | (void) user_save(s->user); | |
5f41d1f1 | 827 | if (s->seat) |
5157b0d8 | 828 | (void) seat_save(s->seat); |
e9816c48 | 829 | |
5f41d1f1 | 830 | /* Send signals */ |
da119395 | 831 | session_send_signal(s, true); |
38c9ca53 | 832 | user_send_changed(s->user, "Display"); |
40771cf5 LP |
833 | |
834 | if (s->seat && s->seat->active == s) | |
38c9ca53 | 835 | (void) seat_send_changed(s->seat, "ActiveSession"); |
9418f147 | 836 | |
20263082 LP |
837 | return 0; |
838 | } | |
839 | ||
9bb69af4 | 840 | static int session_stop_scope(Session *s, bool force) { |
4afd3348 | 841 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; |
20263082 | 842 | int r; |
20263082 LP |
843 | |
844 | assert(s); | |
845 | ||
fb6becb4 LP |
846 | if (!s->scope) |
847 | return 0; | |
9b221b63 | 848 | |
756ed0e2 | 849 | /* Let's always abandon the scope first. This tells systemd that we are not interested anymore, and everything |
629ff674 | 850 | * that is left in the scope is "left-over". Informing systemd about this has the benefit that it will log |
756ed0e2 LP |
851 | * when killing any processes left after this point. */ |
852 | r = manager_abandon_scope(s->manager, s->scope, &error); | |
25a1ab4e | 853 | if (r < 0) { |
756ed0e2 | 854 | log_warning_errno(r, "Failed to abandon session scope, ignoring: %s", bus_error_message(&error, r)); |
25a1ab4e LP |
855 | sd_bus_error_free(&error); |
856 | } | |
857 | ||
858 | s->scope_job = mfree(s->scope_job); | |
756ed0e2 LP |
859 | |
860 | /* Optionally, let's kill everything that's left now. */ | |
156a3637 LP |
861 | if (force || |
862 | (s->user->user_record->kill_processes != 0 && | |
863 | (s->user->user_record->kill_processes > 0 || | |
864 | manager_shall_kill(s->manager, s->user->user_record->user_name)))) { | |
801a884d | 865 | |
1a42ce09 | 866 | r = manager_stop_unit(s->manager, s->scope, force ? "replace" : "fail", &error, &s->scope_job); |
25a1ab4e LP |
867 | if (r < 0) { |
868 | if (force) | |
869 | return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r)); | |
20263082 | 870 | |
25a1ab4e LP |
871 | log_warning_errno(r, "Failed to stop session scope, ignoring: %s", bus_error_message(&error, r)); |
872 | } | |
8150acb1 | 873 | } else { |
20263082 | 874 | |
8150acb1 AJ |
875 | /* With no killing, this session is allowed to persist in "closing" state indefinitely. |
876 | * Therefore session stop and session removal may be two distinct events. | |
877 | * Session stop is quite significant on its own, let's log it. */ | |
878 | log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, | |
3cf6a3a3 YW |
879 | LOG_ITEM("SESSION_ID=%s", s->id), |
880 | LOG_ITEM("USER_ID=%s", s->user->user_record->user_name), | |
881 | LOG_ITEM("LEADER="PID_FMT, s->leader.pid), | |
f4cf1d66 | 882 | LOG_MESSAGE("Session %s logged out. Waiting for processes to exit.", s->id)); |
8150acb1 AJ |
883 | } |
884 | ||
9b221b63 | 885 | return 0; |
20263082 LP |
886 | } |
887 | ||
9bb69af4 | 888 | int session_stop(Session *s, bool force) { |
405e0255 LP |
889 | int r; |
890 | ||
891 | assert(s); | |
892 | ||
25a1ab4e LP |
893 | /* This is called whenever we begin with tearing down a session record. It's called in four cases: explicit API |
894 | * request via the bus (either directly for the session object or for the seat or user object this session | |
895 | * belongs to; 'force' is true), or due to automatic GC (i.e. scope vanished; 'force' is false), or because the | |
896 | * session FIFO saw an EOF ('force' is false), or because the release timer hit ('force' is false). */ | |
897 | ||
405e0255 LP |
898 | if (!s->user) |
899 | return -ESTALE; | |
25a1ab4e LP |
900 | if (!s->started) |
901 | return 0; | |
902 | if (s->stopping) | |
903 | return 0; | |
405e0255 | 904 | |
5f41d1f1 LP |
905 | s->timer_event_source = sd_event_source_unref(s->timer_event_source); |
906 | ||
10189fd6 DH |
907 | if (s->seat) |
908 | seat_evict_position(s->seat, s); | |
909 | ||
405e0255 | 910 | /* Kill cgroup */ |
9bb69af4 | 911 | r = session_stop_scope(s, force); |
405e0255 | 912 | |
5f41d1f1 LP |
913 | s->stopping = true; |
914 | ||
952d3260 LP |
915 | user_elect_display(s->user); |
916 | ||
5157b0d8 LP |
917 | (void) session_save(s); |
918 | (void) user_save(s->user); | |
405e0255 LP |
919 | |
920 | return r; | |
921 | } | |
922 | ||
923 | int session_finalize(Session *s) { | |
118ecf32 | 924 | SessionDevice *sd; |
20263082 LP |
925 | |
926 | assert(s); | |
927 | ||
9444b1f2 LP |
928 | if (!s->user) |
929 | return -ESTALE; | |
930 | ||
ed18b08b | 931 | if (s->started) |
d9eb81f9 | 932 | log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, |
3cf6a3a3 YW |
933 | LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP_STR), |
934 | LOG_ITEM("SESSION_ID=%s", s->id), | |
935 | LOG_ITEM("USER_ID=%s", s->user->user_record->user_name), | |
936 | LOG_ITEM("LEADER="PID_FMT, s->leader.pid), | |
a1230ff9 | 937 | LOG_MESSAGE("Removed session %s.", s->id)); |
98a28fef | 938 | |
5f41d1f1 LP |
939 | s->timer_event_source = sd_event_source_unref(s->timer_event_source); |
940 | ||
10189fd6 DH |
941 | if (s->seat) |
942 | seat_evict_position(s->seat, s); | |
943 | ||
118ecf32 DH |
944 | /* Kill session devices */ |
945 | while ((sd = hashmap_first(s->devices))) | |
946 | session_device_free(sd); | |
947 | ||
491ac9f2 | 948 | (void) unlink(s->state_file); |
d2f92cdf | 949 | session_add_to_gc_queue(s); |
ed18b08b | 950 | user_add_to_gc_queue(s->user); |
14c3baca | 951 | |
405e0255 | 952 | if (s->started) { |
ed18b08b | 953 | session_send_signal(s, false); |
405e0255 LP |
954 | s->started = false; |
955 | } | |
50fb9793 | 956 | |
9418f147 LP |
957 | if (s->seat) { |
958 | if (s->seat->active == s) | |
959 | seat_set_active(s->seat, NULL); | |
960 | ||
23bd3b62 | 961 | seat_save(s->seat); |
9418f147 LP |
962 | } |
963 | ||
9d5b6901 | 964 | session_reset_leader(s, /* keep_fdstore = */ false); |
76f2191d | 965 | |
5157b0d8 | 966 | (void) user_save(s->user); |
38c9ca53 | 967 | (void) user_send_changed(s->user, "Display"); |
9418f147 | 968 | |
491ac9f2 | 969 | return 0; |
20263082 LP |
970 | } |
971 | ||
5f41d1f1 | 972 | static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) { |
99534007 | 973 | Session *s = ASSERT_PTR(userdata); |
5f41d1f1 LP |
974 | |
975 | assert(es); | |
5f41d1f1 | 976 | |
bda62573 | 977 | session_stop(s, /* force = */ false); |
5f41d1f1 LP |
978 | return 0; |
979 | } | |
980 | ||
ad8780c9 | 981 | int session_release(Session *s) { |
5f41d1f1 LP |
982 | assert(s); |
983 | ||
984 | if (!s->started || s->stopping) | |
ad8780c9 ZJS |
985 | return 0; |
986 | ||
987 | if (s->timer_event_source) | |
988 | return 0; | |
989 | ||
39cf0351 LP |
990 | return sd_event_add_time_relative( |
991 | s->manager->event, | |
992 | &s->timer_event_source, | |
993 | CLOCK_MONOTONIC, | |
994 | RELEASE_USEC, 0, | |
995 | release_timeout_callback, s); | |
5f41d1f1 LP |
996 | } |
997 | ||
20263082 LP |
998 | bool session_is_active(Session *s) { |
999 | assert(s); | |
1000 | ||
1001 | if (!s->seat) | |
1002 | return true; | |
1003 | ||
1004 | return s->seat->active == s; | |
1005 | } | |
1006 | ||
23406ce5 LP |
1007 | static int get_tty_atime(const char *tty, usec_t *atime) { |
1008 | _cleanup_free_ char *p = NULL; | |
a185c5aa | 1009 | struct stat st; |
23406ce5 LP |
1010 | |
1011 | assert(tty); | |
1012 | assert(atime); | |
1013 | ||
1014 | if (!path_is_absolute(tty)) { | |
b910cc72 | 1015 | p = path_join("/dev", tty); |
23406ce5 LP |
1016 | if (!p) |
1017 | return -ENOMEM; | |
1018 | ||
1019 | tty = p; | |
1020 | } else if (!path_startswith(tty, "/dev/")) | |
1021 | return -ENOENT; | |
1022 | ||
1023 | if (lstat(tty, &st) < 0) | |
1024 | return -errno; | |
1025 | ||
1026 | *atime = timespec_load(&st.st_atim); | |
1027 | return 0; | |
1028 | } | |
1029 | ||
1030 | static int get_process_ctty_atime(pid_t pid, usec_t *atime) { | |
1031 | _cleanup_free_ char *p = NULL; | |
1032 | int r; | |
1033 | ||
1034 | assert(pid > 0); | |
1035 | assert(atime); | |
1036 | ||
1037 | r = get_ctty(pid, NULL, &p); | |
1038 | if (r < 0) | |
1039 | return r; | |
1040 | ||
1041 | return get_tty_atime(p, atime); | |
1042 | } | |
1043 | ||
1044 | int session_get_idle_hint(Session *s, dual_timestamp *t) { | |
82325af3 | 1045 | usec_t atime = 0, dtime = 0; |
23406ce5 | 1046 | int r; |
a185c5aa LP |
1047 | |
1048 | assert(s); | |
1049 | ||
1bf9e308 LP |
1050 | if (!SESSION_CLASS_CAN_IDLE(s->class)) |
1051 | return false; | |
1052 | ||
be2bb14f LP |
1053 | /* Graphical sessions have an explicit idle hint */ |
1054 | if (SESSION_TYPE_IS_GRAPHICAL(s->type)) { | |
a185c5aa LP |
1055 | if (t) |
1056 | *t = s->idle_hint_timestamp; | |
1057 | ||
1058 | return s->idle_hint; | |
1059 | } | |
1060 | ||
20604ff2 LP |
1061 | if (s->type == SESSION_TTY) { |
1062 | /* For sessions with an explicitly configured tty, let's check its atime */ | |
1063 | if (s->tty) { | |
1064 | r = get_tty_atime(s->tty, &atime); | |
1065 | if (r >= 0) | |
1066 | goto found_atime; | |
1067 | } | |
a185c5aa | 1068 | |
20604ff2 LP |
1069 | /* For sessions with a leader but no explicitly configured tty, let's check the controlling tty of |
1070 | * the leader */ | |
1071 | if (pidref_is_set(&s->leader)) { | |
1072 | r = get_process_ctty_atime(s->leader.pid, &atime); | |
1073 | if (r >= 0) | |
1074 | goto found_atime; | |
1075 | } | |
a185c5aa LP |
1076 | } |
1077 | ||
a185c5aa | 1078 | if (t) |
be2bb14f | 1079 | *t = DUAL_TIMESTAMP_NULL; |
a185c5aa | 1080 | |
be2bb14f | 1081 | return false; |
23406ce5 LP |
1082 | |
1083 | found_atime: | |
1084 | if (t) | |
1085 | dual_timestamp_from_realtime(t, atime); | |
1086 | ||
82325af3 MS |
1087 | if (s->manager->idle_action_usec > 0 && s->manager->stop_idle_session_usec != USEC_INFINITY) |
1088 | dtime = MIN(s->manager->idle_action_usec, s->manager->stop_idle_session_usec); | |
1089 | else if (s->manager->idle_action_usec > 0) | |
1090 | dtime = s->manager->idle_action_usec; | |
1091 | else if (s->manager->stop_idle_session_usec != USEC_INFINITY) | |
1092 | dtime = s->manager->stop_idle_session_usec; | |
1093 | else | |
be2bb14f | 1094 | return false; |
23406ce5 | 1095 | |
82325af3 | 1096 | return usec_add(atime, dtime) <= now(CLOCK_REALTIME); |
a185c5aa LP |
1097 | } |
1098 | ||
be2bb14f | 1099 | int session_set_idle_hint(Session *s, bool b) { |
bef422ae LP |
1100 | assert(s); |
1101 | ||
b4f01bc1 LP |
1102 | if (!SESSION_CLASS_CAN_IDLE(s->class)) /* Only some session classes know the idle concept at all */ |
1103 | return -ENOTTY; | |
1104 | if (!SESSION_TYPE_IS_GRAPHICAL(s->type)) /* And only graphical session types can set the field explicitly */ | |
be2bb14f LP |
1105 | return -ENOTTY; |
1106 | ||
bef422ae | 1107 | if (s->idle_hint == b) |
be2bb14f | 1108 | return 0; |
bef422ae LP |
1109 | |
1110 | s->idle_hint = b; | |
fa5a0251 | 1111 | dual_timestamp_now(&s->idle_hint_timestamp); |
9418f147 | 1112 | |
38c9ca53 | 1113 | session_send_changed(s, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic"); |
9418f147 LP |
1114 | |
1115 | if (s->seat) | |
38c9ca53 | 1116 | seat_send_changed(s->seat, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic"); |
cc377381 | 1117 | |
38c9ca53 DDM |
1118 | user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic"); |
1119 | manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic"); | |
be2bb14f LP |
1120 | |
1121 | return 1; | |
cc377381 LP |
1122 | } |
1123 | ||
42d35e13 VT |
1124 | int session_get_locked_hint(Session *s) { |
1125 | assert(s); | |
1126 | ||
1127 | return s->locked_hint; | |
1128 | } | |
1129 | ||
b4f01bc1 | 1130 | int session_set_locked_hint(Session *s, bool b) { |
42d35e13 VT |
1131 | assert(s); |
1132 | ||
b4f01bc1 LP |
1133 | if (!SESSION_CLASS_CAN_LOCK(s->class)) |
1134 | return -ENOTTY; | |
1135 | ||
42d35e13 | 1136 | if (s->locked_hint == b) |
b4f01bc1 | 1137 | return 0; |
42d35e13 VT |
1138 | |
1139 | s->locked_hint = b; | |
b4f01bc1 | 1140 | (void) session_save(s); |
38c9ca53 | 1141 | (void) session_send_changed(s, "LockedHint"); |
42d35e13 | 1142 | |
b4f01bc1 | 1143 | return 1; |
42d35e13 VT |
1144 | } |
1145 | ||
db72aea4 CH |
1146 | void session_set_type(Session *s, SessionType t) { |
1147 | assert(s); | |
1148 | ||
1149 | if (s->type == t) | |
1150 | return; | |
1151 | ||
1152 | s->type = t; | |
5157b0d8 | 1153 | (void) session_save(s); |
38c9ca53 | 1154 | (void) session_send_changed(s, "Type"); |
db72aea4 CH |
1155 | } |
1156 | ||
6e9bf0ad LP |
1157 | void session_set_class(Session *s, SessionClass c) { |
1158 | assert(s); | |
1159 | ||
1160 | if (s->class == c) | |
1161 | return; | |
1162 | ||
1163 | s->class = c; | |
1164 | (void) session_save(s); | |
38c9ca53 | 1165 | (void) session_send_changed(s, "Class"); |
6e9bf0ad | 1166 | |
e2a42c0c MY |
1167 | /* This class change might mean we need the per-user session manager now. Try to start it. */ |
1168 | (void) user_start_service_manager(s->user); | |
6e9bf0ad LP |
1169 | } |
1170 | ||
4885d749 DT |
1171 | int session_set_display(Session *s, const char *display) { |
1172 | int r; | |
1173 | ||
1174 | assert(s); | |
1175 | assert(display); | |
1176 | ||
4885d749 | 1177 | r = free_and_strdup(&s->display, display); |
e5c09aad | 1178 | if (r <= 0) /* 0 means the strings were equal */ |
4885d749 DT |
1179 | return r; |
1180 | ||
5157b0d8 | 1181 | (void) session_save(s); |
38c9ca53 | 1182 | (void) session_send_changed(s, "Display"); |
4885d749 DT |
1183 | |
1184 | return 1; | |
1185 | } | |
1186 | ||
092e6cd1 TK |
1187 | int session_set_tty(Session *s, const char *tty) { |
1188 | int r; | |
1189 | ||
1190 | assert(s); | |
1191 | assert(tty); | |
1192 | ||
1193 | r = free_and_strdup(&s->tty, tty); | |
1194 | if (r <= 0) /* 0 means the strings were equal */ | |
1195 | return r; | |
1196 | ||
5157b0d8 | 1197 | (void) session_save(s); |
38c9ca53 | 1198 | (void) session_send_changed(s, "TTY"); |
092e6cd1 TK |
1199 | |
1200 | return 1; | |
1201 | } | |
1202 | ||
5c093a23 | 1203 | bool session_may_gc(Session *s, bool drop_not_started) { |
bd26aee1 LP |
1204 | int r; |
1205 | ||
20263082 LP |
1206 | assert(s); |
1207 | ||
4a4b033f | 1208 | if (drop_not_started && !s->started) |
5c093a23 | 1209 | return true; |
932e3ee7 | 1210 | |
9444b1f2 | 1211 | if (!s->user) |
5c093a23 | 1212 | return true; |
9444b1f2 | 1213 | |
76f2191d | 1214 | r = pidref_is_alive(&s->leader); |
9d5b6901 MY |
1215 | if (r == -ESRCH) |
1216 | /* Session has no leader. This is probably because the leader vanished before deserializing | |
1217 | * pidfd from FD store. */ | |
1218 | return true; | |
76f2191d | 1219 | if (r < 0) |
9d5b6901 | 1220 | log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not: %m", s->leader.pid); |
76f2191d MS |
1221 | if (r > 0) |
1222 | return false; | |
1223 | ||
bd26aee1 LP |
1224 | if (s->scope_job) { |
1225 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
1226 | ||
1227 | r = manager_job_is_active(s->manager, s->scope_job, &error); | |
1228 | if (r < 0) | |
1229 | log_debug_errno(r, "Failed to determine whether job '%s' is pending, ignoring: %s", s->scope_job, bus_error_message(&error, r)); | |
1230 | if (r != 0) | |
1231 | return false; | |
1232 | } | |
20263082 | 1233 | |
bd26aee1 LP |
1234 | if (s->scope) { |
1235 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
1236 | ||
1237 | r = manager_unit_is_active(s->manager, s->scope, &error); | |
1238 | if (r < 0) | |
1239 | log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", s->scope, bus_error_message(&error, r)); | |
1240 | if (r != 0) | |
1241 | return false; | |
1242 | } | |
20263082 | 1243 | |
5c093a23 | 1244 | return true; |
20263082 LP |
1245 | } |
1246 | ||
14c3baca LP |
1247 | void session_add_to_gc_queue(Session *s) { |
1248 | assert(s); | |
1249 | ||
1250 | if (s->in_gc_queue) | |
1251 | return; | |
1252 | ||
71fda00f | 1253 | LIST_PREPEND(gc_queue, s->manager->session_gc_queue, s); |
14c3baca LP |
1254 | s->in_gc_queue = true; |
1255 | } | |
1256 | ||
0604381b LP |
1257 | SessionState session_get_state(Session *s) { |
1258 | assert(s); | |
1259 | ||
8fe63cd4 | 1260 | /* always check closing first */ |
5f41d1f1 LP |
1261 | if (s->stopping || s->timer_event_source) |
1262 | return SESSION_CLOSING; | |
1263 | ||
3180c4d4 | 1264 | if (s->scope_job || !pidref_is_set(&s->leader)) |
405e0255 | 1265 | return SESSION_OPENING; |
fb6becb4 | 1266 | |
0604381b LP |
1267 | if (session_is_active(s)) |
1268 | return SESSION_ACTIVE; | |
1269 | ||
1270 | return SESSION_ONLINE; | |
1271 | } | |
1272 | ||
9c6dc69f | 1273 | int session_kill(Session *s, KillWhom whom, int signo, sd_bus_error *error) { |
de07ab16 LP |
1274 | assert(s); |
1275 | ||
82547550 | 1276 | switch (whom) { |
1277 | ||
1278 | case KILL_ALL: | |
9c6dc69f MY |
1279 | if (!SESSION_CLASS_WANTS_SCOPE(s->class)) |
1280 | return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, | |
1281 | "Session '%s' has no associated scope", s->id); | |
1282 | ||
82547550 | 1283 | if (!s->scope) |
9c6dc69f | 1284 | return sd_bus_error_set_errnof(error, ESRCH, "Scope for session '%s' not active", s->id); |
82547550 | 1285 | |
9c6dc69f | 1286 | return manager_kill_unit(s->manager, s->scope, KILL_ALL, signo, error); |
de07ab16 | 1287 | |
82547550 | 1288 | case KILL_LEADER: |
1289 | return pidref_kill(&s->leader, signo); | |
1290 | ||
1291 | default: | |
1292 | assert_not_reached(); | |
1293 | } | |
de07ab16 LP |
1294 | } |
1295 | ||
93041c60 MY |
1296 | static int session_open_vt(Session *s, bool reopen) { |
1297 | _cleanup_close_ int fd = -EBADF; | |
5f41d1f1 | 1298 | char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)]; |
90a18413 | 1299 | |
a8d5378a MY |
1300 | assert(s); |
1301 | ||
baccf3e4 OB |
1302 | if (s->vtnr < 1) |
1303 | return -ENODEV; | |
90a18413 | 1304 | |
93041c60 | 1305 | if (!reopen && s->vtfd >= 0) |
90a18413 DH |
1306 | return s->vtfd; |
1307 | ||
92bd5ff3 | 1308 | sprintf(path, "/dev/tty%u", s->vtnr); |
90a18413 | 1309 | |
93041c60 MY |
1310 | fd = open_terminal(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY); |
1311 | if (fd < 0) | |
1312 | return log_error_errno(fd, "Cannot open VT %s of session %s: %m", path, s->id); | |
1313 | ||
1314 | close_and_replace(s->vtfd, fd); | |
90a18413 DH |
1315 | return s->vtfd; |
1316 | } | |
1317 | ||
a03cdb17 | 1318 | static int session_prepare_vt(Session *s) { |
90a18413 | 1319 | int vt, r; |
9fb2c8b8 | 1320 | struct vt_mode mode = {}; |
90a18413 | 1321 | |
a8d5378a MY |
1322 | assert(s); |
1323 | ||
baccf3e4 OB |
1324 | if (s->vtnr < 1) |
1325 | return 0; | |
1326 | ||
93041c60 | 1327 | vt = session_open_vt(s, /* reopen = */ false); |
90a18413 | 1328 | if (vt < 0) |
baccf3e4 | 1329 | return vt; |
90a18413 | 1330 | |
22c902fa | 1331 | r = fchown(vt, s->user->user_record->uid, -1); |
baccf3e4 | 1332 | if (r < 0) { |
94c156cd LP |
1333 | r = log_error_errno(errno, |
1334 | "Cannot change owner of /dev/tty%u: %m", | |
1335 | s->vtnr); | |
d6176c6c | 1336 | goto error; |
baccf3e4 | 1337 | } |
d6176c6c | 1338 | |
90a18413 | 1339 | r = ioctl(vt, KDSKBMODE, K_OFF); |
baccf3e4 | 1340 | if (r < 0) { |
94c156cd LP |
1341 | r = log_error_errno(errno, |
1342 | "Cannot set K_OFF on /dev/tty%u: %m", | |
1343 | s->vtnr); | |
90a18413 | 1344 | goto error; |
baccf3e4 | 1345 | } |
90a18413 DH |
1346 | |
1347 | r = ioctl(vt, KDSETMODE, KD_GRAPHICS); | |
baccf3e4 | 1348 | if (r < 0) { |
94c156cd LP |
1349 | r = log_error_errno(errno, |
1350 | "Cannot set KD_GRAPHICS on /dev/tty%u: %m", | |
1351 | s->vtnr); | |
90a18413 | 1352 | goto error; |
baccf3e4 | 1353 | } |
90a18413 | 1354 | |
90a18413 DH |
1355 | /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS. |
1356 | * So we need a dummy handler here which just acknowledges *all* VT | |
1357 | * switch requests. */ | |
1358 | mode.mode = VT_PROCESS; | |
92683ad2 DH |
1359 | mode.relsig = SIGRTMIN; |
1360 | mode.acqsig = SIGRTMIN + 1; | |
90a18413 | 1361 | r = ioctl(vt, VT_SETMODE, &mode); |
baccf3e4 | 1362 | if (r < 0) { |
94c156cd LP |
1363 | r = log_error_errno(errno, |
1364 | "Cannot set VT_PROCESS on /dev/tty%u: %m", | |
1365 | s->vtnr); | |
90a18413 | 1366 | goto error; |
baccf3e4 | 1367 | } |
90a18413 | 1368 | |
baccf3e4 | 1369 | return 0; |
90a18413 DH |
1370 | |
1371 | error: | |
90a18413 | 1372 | session_restore_vt(s); |
baccf3e4 | 1373 | return r; |
90a18413 DH |
1374 | } |
1375 | ||
0212126c | 1376 | static void session_restore_vt(Session *s) { |
8246905a | 1377 | int r; |
128df4cf | 1378 | |
a8d5378a MY |
1379 | assert(s); |
1380 | ||
092e6cd1 TK |
1381 | if (s->vtfd < 0) |
1382 | return; | |
1383 | ||
8246905a FB |
1384 | r = vt_restore(s->vtfd); |
1385 | if (r == -EIO) { | |
8246905a FB |
1386 | /* It might happen if the controlling process exited before or while we were |
1387 | * restoring the VT as it would leave the old file-descriptor in a hung-up | |
1388 | * state. In this case let's retry with a fresh handle to the virtual terminal. */ | |
ad96887a | 1389 | |
8246905a FB |
1390 | /* We do a little dance to avoid having the terminal be available |
1391 | * for reuse before we've cleaned it up. */ | |
8246905a | 1392 | |
93041c60 MY |
1393 | int fd = session_open_vt(s, /* reopen = */ true); |
1394 | if (fd >= 0) | |
1395 | r = vt_restore(fd); | |
8246905a | 1396 | } |
c0f34168 FB |
1397 | if (r < 0) |
1398 | log_warning_errno(r, "Failed to restore VT, ignoring: %m"); | |
d6176c6c | 1399 | |
03e334a1 | 1400 | s->vtfd = safe_close(s->vtfd); |
90a18413 DH |
1401 | } |
1402 | ||
2ec3ff66 | 1403 | void session_leave_vt(Session *s) { |
ce540a24 DH |
1404 | int r; |
1405 | ||
2ec3ff66 DH |
1406 | assert(s); |
1407 | ||
1408 | /* This is called whenever we get a VT-switch signal from the kernel. | |
1409 | * We acknowledge all of them unconditionally. Note that session are | |
1410 | * free to overwrite those handlers and we only register them for | |
1411 | * sessions with controllers. Legacy sessions are not affected. | |
1412 | * However, if we switch from a non-legacy to a legacy session, we must | |
1413 | * make sure to pause all device before acknowledging the switch. We | |
1414 | * process the real switch only after we are notified via sysfs, so the | |
1415 | * legacy session might have already started using the devices. If we | |
1416 | * don't pause the devices before the switch, we might confuse the | |
1417 | * session we switch to. */ | |
1418 | ||
1419 | if (s->vtfd < 0) | |
1420 | return; | |
1421 | ||
1422 | session_device_pause_all(s); | |
93041c60 | 1423 | r = vt_release(s->vtfd, /* restore = */ false); |
99d4ad71 | 1424 | if (r == -EIO) { |
93041c60 | 1425 | /* Handle the same VT hung-up case as in session_restore_vt */ |
99d4ad71 | 1426 | |
93041c60 MY |
1427 | int fd = session_open_vt(s, /* reopen = */ true); |
1428 | if (fd >= 0) | |
1429 | r = vt_release(fd, /* restore = */ false); | |
99d4ad71 | 1430 | } |
ce540a24 | 1431 | if (r < 0) |
27dafac9 | 1432 | log_debug_errno(r, "Cannot release VT of session %s: %m", s->id); |
2ec3ff66 DH |
1433 | } |
1434 | ||
cc377381 | 1435 | bool session_is_controller(Session *s, const char *sender) { |
e5c09aad | 1436 | return streq_ptr(ASSERT_PTR(s)->controller, sender); |
ae5e06bd DH |
1437 | } |
1438 | ||
b12e5615 | 1439 | static void session_release_controller(Session *s, bool notify) { |
d7ac0952 | 1440 | _unused_ _cleanup_free_ char *name = NULL; |
6d33772f DH |
1441 | SessionDevice *sd; |
1442 | ||
a8d5378a MY |
1443 | assert(s); |
1444 | ||
b12e5615 DH |
1445 | if (!s->controller) |
1446 | return; | |
6d33772f | 1447 | |
b12e5615 | 1448 | name = s->controller; |
90a18413 | 1449 | |
6c75e317 ZJS |
1450 | /* By resetting the controller before releasing the devices, we won't send notification signals. |
1451 | * This avoids sending useless notifications if the controller is released on disconnects. */ | |
b12e5615 DH |
1452 | if (!notify) |
1453 | s->controller = NULL; | |
6d33772f | 1454 | |
b12e5615 DH |
1455 | while ((sd = hashmap_first(s->devices))) |
1456 | session_device_free(sd); | |
1457 | ||
1458 | s->controller = NULL; | |
3cde9e8f DM |
1459 | s->track = sd_bus_track_unref(s->track); |
1460 | } | |
1461 | ||
1462 | static int on_bus_track(sd_bus_track *track, void *userdata) { | |
99534007 | 1463 | Session *s = ASSERT_PTR(userdata); |
3cde9e8f DM |
1464 | |
1465 | assert(track); | |
3cde9e8f DM |
1466 | |
1467 | session_drop_controller(s); | |
1468 | ||
1469 | return 0; | |
6d33772f DH |
1470 | } |
1471 | ||
dc6284e9 | 1472 | int session_set_controller(Session *s, const char *sender, bool force, bool prepare) { |
b12e5615 | 1473 | _cleanup_free_ char *name = NULL; |
ae5e06bd DH |
1474 | int r; |
1475 | ||
1476 | assert(s); | |
1477 | assert(sender); | |
1478 | ||
1479 | if (session_is_controller(s, sender)) | |
1480 | return 0; | |
1481 | if (s->controller && !force) | |
1482 | return -EBUSY; | |
1483 | ||
b12e5615 DH |
1484 | name = strdup(sender); |
1485 | if (!name) | |
ae5e06bd DH |
1486 | return -ENOMEM; |
1487 | ||
3cde9e8f DM |
1488 | s->track = sd_bus_track_unref(s->track); |
1489 | r = sd_bus_track_new(s->manager->bus, &s->track, on_bus_track, s); | |
1490 | if (r < 0) | |
1491 | return r; | |
1492 | ||
1493 | r = sd_bus_track_add_name(s->track, name); | |
1494 | if (r < 0) | |
ae5e06bd | 1495 | return r; |
ae5e06bd | 1496 | |
90a18413 DH |
1497 | /* When setting a session controller, we forcibly mute the VT and set |
1498 | * it into graphics-mode. Applications can override that by changing | |
1499 | * VT state after calling TakeControl(). However, this serves as a good | |
1500 | * default and well-behaving controllers can now ignore VTs entirely. | |
1501 | * Note that we reset the VT on ReleaseControl() and if the controller | |
1502 | * exits. | |
1503 | * If logind crashes/restarts, we restore the controller during restart | |
dc6284e9 FB |
1504 | * (without preparing the VT since the controller has probably overridden |
1505 | * VT state by now) or reset the VT in case it crashed/exited, too. */ | |
1506 | if (prepare) { | |
1507 | r = session_prepare_vt(s); | |
1508 | if (r < 0) { | |
1509 | s->track = sd_bus_track_unref(s->track); | |
1510 | return r; | |
1511 | } | |
13f493dc | 1512 | } |
baccf3e4 | 1513 | |
b12e5615 | 1514 | session_release_controller(s, true); |
ae2a15bc | 1515 | s->controller = TAKE_PTR(name); |
5157b0d8 | 1516 | (void) session_save(s); |
90a18413 | 1517 | |
ae5e06bd DH |
1518 | return 0; |
1519 | } | |
1520 | ||
1521 | void session_drop_controller(Session *s) { | |
1522 | assert(s); | |
1523 | ||
1524 | if (!s->controller) | |
1525 | return; | |
1526 | ||
3cde9e8f | 1527 | s->track = sd_bus_track_unref(s->track); |
db72aea4 | 1528 | session_set_type(s, s->original_type); |
b12e5615 | 1529 | session_release_controller(s, false); |
5157b0d8 | 1530 | (void) session_save(s); |
b12e5615 | 1531 | session_restore_vt(s); |
ae5e06bd DH |
1532 | } |
1533 | ||
a551f584 LP |
1534 | bool session_job_pending(Session *s) { |
1535 | assert(s); | |
1536 | assert(s->user); | |
1537 | ||
1538 | /* Check if we have some jobs enqueued and not finished yet. Each time we get JobRemoved signal about | |
1539 | * relevant units, session_send_create_reply and hence us is called (see match_job_removed). | |
1540 | * Note that we don't care about job result here. */ | |
1541 | ||
1542 | return s->scope_job || | |
1543 | s->user->runtime_dir_job || | |
1544 | (SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) && s->user->service_manager_job); | |
1545 | } | |
1546 | ||
1547 | int session_send_create_reply(Session *s, const sd_bus_error *error) { | |
2baca6c2 LP |
1548 | int r; |
1549 | ||
a551f584 LP |
1550 | assert(s); |
1551 | ||
1552 | /* If error occurred, return it immediately. Otherwise let's wait for all jobs to finish before | |
1553 | * continuing. */ | |
1554 | if (!sd_bus_error_is_set(error) && session_job_pending(s)) | |
1555 | return 0; | |
1556 | ||
2baca6c2 LP |
1557 | r = 0; |
1558 | RET_GATHER(r, session_send_create_reply_bus(s, error)); | |
1559 | RET_GATHER(r, session_send_create_reply_varlink(s, error)); | |
1560 | return r; | |
a551f584 LP |
1561 | } |
1562 | ||
6ad1d1ed DDM |
1563 | bool session_is_self(const char *name) { |
1564 | return isempty(name) || streq(name, "self"); | |
1565 | } | |
1566 | ||
1567 | bool session_is_auto(const char *name) { | |
1568 | return streq_ptr(name, "auto"); | |
1569 | } | |
1570 | ||
fb6becb4 LP |
1571 | static const char* const session_state_table[_SESSION_STATE_MAX] = { |
1572 | [SESSION_OPENING] = "opening", | |
6c75e317 ZJS |
1573 | [SESSION_ONLINE] = "online", |
1574 | [SESSION_ACTIVE] = "active", | |
1575 | [SESSION_CLOSING] = "closing", | |
0604381b LP |
1576 | }; |
1577 | ||
1578 | DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState); | |
1579 | ||
20263082 | 1580 | static const char* const session_type_table[_SESSION_TYPE_MAX] = { |
2c5859af | 1581 | [SESSION_UNSPECIFIED] = "unspecified", |
6c75e317 ZJS |
1582 | [SESSION_TTY] = "tty", |
1583 | [SESSION_X11] = "x11", | |
1584 | [SESSION_WAYLAND] = "wayland", | |
1585 | [SESSION_MIR] = "mir", | |
1586 | [SESSION_WEB] = "web", | |
20263082 LP |
1587 | }; |
1588 | ||
1589 | DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType); | |
de07ab16 | 1590 | |
55efac6c | 1591 | static const char* const session_class_table[_SESSION_CLASS_MAX] = { |
b5100c73 LP |
1592 | [SESSION_USER] = "user", |
1593 | [SESSION_USER_EARLY] = "user-early", | |
53ebde6d | 1594 | [SESSION_USER_INCOMPLETE] = "user-incomplete", |
cf8f6cd0 LP |
1595 | [SESSION_USER_LIGHT] = "user-light", |
1596 | [SESSION_USER_EARLY_LIGHT] = "user-early-light", | |
b5100c73 LP |
1597 | [SESSION_GREETER] = "greeter", |
1598 | [SESSION_LOCK_SCREEN] = "lock-screen", | |
1599 | [SESSION_BACKGROUND] = "background", | |
1600 | [SESSION_BACKGROUND_LIGHT] = "background-light", | |
1601 | [SESSION_MANAGER] = "manager", | |
1602 | [SESSION_MANAGER_EARLY] = "manager-early", | |
90ee2c59 | 1603 | [SESSION_NONE] = "none", |
55efac6c LP |
1604 | }; |
1605 | ||
1606 | DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass); | |
1607 | ||
cd2fb049 | 1608 | static const char* const kill_whom_table[_KILL_WHOM_MAX] = { |
de07ab16 | 1609 | [KILL_LEADER] = "leader", |
6c75e317 | 1610 | [KILL_ALL] = "all", |
de07ab16 LP |
1611 | }; |
1612 | ||
cd2fb049 | 1613 | DEFINE_STRING_TABLE_LOOKUP(kill_whom, KillWhom); |
3d0ef5c7 LP |
1614 | |
1615 | static const char* const tty_validity_table[_TTY_VALIDITY_MAX] = { | |
6c75e317 ZJS |
1616 | [TTY_FROM_PAM] = "from-pam", |
1617 | [TTY_FROM_UTMP] = "from-utmp", | |
3d0ef5c7 LP |
1618 | [TTY_UTMP_INCONSISTENT] = "utmp-inconsistent", |
1619 | }; | |
1620 | ||
1621 | DEFINE_STRING_TABLE_LOOKUP(tty_validity, TTYValidity); |