]>
Commit | Line | Data |
---|---|---|
20263082 LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2011 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
5430f7f2 LP |
9 | under the terms of the GNU Lesser General Public License as published by |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
20263082 LP |
11 | (at your option) any later version. |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
5430f7f2 | 16 | Lesser General Public License for more details. |
20263082 | 17 | |
5430f7f2 | 18 | You should have received a copy of the GNU Lesser General Public License |
20263082 LP |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. |
20 | ***/ | |
21 | ||
22 | #include <errno.h> | |
23 | #include <string.h> | |
24 | #include <unistd.h> | |
31b79c2b | 25 | #include <sys/epoll.h> |
932e3ee7 | 26 | #include <fcntl.h> |
20263082 | 27 | |
fb6becb4 LP |
28 | #include <systemd/sd-id128.h> |
29 | #include <systemd/sd-messages.h> | |
30 | ||
20263082 LP |
31 | #include "strv.h" |
32 | #include "util.h" | |
49e942b2 | 33 | #include "mkdir.h" |
9eb977db | 34 | #include "path-util.h" |
a5c32cff | 35 | #include "fileio.h" |
fb6becb4 LP |
36 | #include "dbus-common.h" |
37 | #include "logind-session.h" | |
20263082 | 38 | |
9444b1f2 | 39 | Session* session_new(Manager *m, const char *id) { |
20263082 LP |
40 | Session *s; |
41 | ||
42 | assert(m); | |
43 | assert(id); | |
4b549144 | 44 | assert(session_id_valid(id)); |
20263082 | 45 | |
14c3baca | 46 | s = new0(Session, 1); |
20263082 LP |
47 | if (!s) |
48 | return NULL; | |
49 | ||
98a28fef | 50 | s->state_file = strappend("/run/systemd/sessions/", id); |
20263082 LP |
51 | if (!s->state_file) { |
52 | free(s); | |
53 | return NULL; | |
54 | } | |
55 | ||
118ecf32 DH |
56 | s->devices = hashmap_new(trivial_hash_func, trivial_compare_func); |
57 | if (!s->devices) { | |
58 | free(s->state_file); | |
59 | free(s); | |
60 | return NULL; | |
61 | } | |
62 | ||
9eb977db | 63 | s->id = path_get_file_name(s->state_file); |
20263082 LP |
64 | |
65 | if (hashmap_put(m->sessions, s->id, s) < 0) { | |
118ecf32 | 66 | hashmap_free(s->devices); |
f8e2fb7b | 67 | free(s->state_file); |
20263082 LP |
68 | free(s); |
69 | return NULL; | |
70 | } | |
71 | ||
72 | s->manager = m; | |
932e3ee7 | 73 | s->fifo_fd = -1; |
20263082 LP |
74 | |
75 | return s; | |
76 | } | |
77 | ||
78 | void session_free(Session *s) { | |
118ecf32 DH |
79 | SessionDevice *sd; |
80 | ||
20263082 LP |
81 | assert(s); |
82 | ||
14c3baca LP |
83 | if (s->in_gc_queue) |
84 | LIST_REMOVE(Session, gc_queue, s->manager->session_gc_queue, s); | |
85 | ||
ae5e06bd DH |
86 | session_drop_controller(s); |
87 | ||
118ecf32 DH |
88 | while ((sd = hashmap_first(s->devices))) |
89 | session_device_free(sd); | |
90 | ||
91 | hashmap_free(s->devices); | |
92 | ||
20263082 LP |
93 | if (s->user) { |
94 | LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s); | |
95 | ||
96 | if (s->user->display == s) | |
97 | s->user->display = NULL; | |
98 | } | |
99 | ||
9418f147 LP |
100 | if (s->seat) { |
101 | if (s->seat->active == s) | |
102 | s->seat->active = NULL; | |
103 | ||
20263082 | 104 | LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s); |
9418f147 | 105 | } |
20263082 | 106 | |
fb6becb4 LP |
107 | if (s->scope) { |
108 | hashmap_remove(s->manager->session_units, s->scope); | |
109 | free(s->scope); | |
110 | } | |
111 | ||
112 | free(s->scope_job); | |
1713813d | 113 | |
fb6becb4 LP |
114 | if (s->create_message) |
115 | dbus_message_unref(s->create_message); | |
20263082 LP |
116 | |
117 | free(s->tty); | |
118 | free(s->display); | |
119 | free(s->remote_host); | |
3f49d45a | 120 | free(s->remote_user); |
98a28fef | 121 | free(s->service); |
20263082 LP |
122 | |
123 | hashmap_remove(s->manager->sessions, s->id); | |
932e3ee7 | 124 | session_remove_fifo(s); |
98a28fef | 125 | |
d2f92cdf | 126 | free(s->state_file); |
20263082 LP |
127 | free(s); |
128 | } | |
129 | ||
9444b1f2 LP |
130 | void session_set_user(Session *s, User *u) { |
131 | assert(s); | |
132 | assert(!s->user); | |
133 | ||
134 | s->user = u; | |
135 | LIST_PREPEND(Session, sessions_by_user, u->sessions, s); | |
136 | } | |
137 | ||
20263082 | 138 | int session_save(Session *s) { |
507f22bd ZJS |
139 | _cleanup_fclose_ FILE *f = NULL; |
140 | _cleanup_free_ char *temp_path = NULL; | |
20263082 LP |
141 | int r = 0; |
142 | ||
143 | assert(s); | |
144 | ||
9444b1f2 LP |
145 | if (!s->user) |
146 | return -ESTALE; | |
147 | ||
accaeded LP |
148 | if (!s->started) |
149 | return 0; | |
150 | ||
d2e54fae | 151 | r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0); |
20263082 | 152 | if (r < 0) |
14c3baca | 153 | goto finish; |
20263082 | 154 | |
14c3baca LP |
155 | r = fopen_temporary(s->state_file, &f, &temp_path); |
156 | if (r < 0) | |
157 | goto finish; | |
20263082 LP |
158 | |
159 | assert(s->user); | |
160 | ||
14c3baca LP |
161 | fchmod(fileno(f), 0644); |
162 | ||
20263082 LP |
163 | fprintf(f, |
164 | "# This is private data. Do not parse.\n" | |
165 | "UID=%lu\n" | |
166 | "USER=%s\n" | |
167 | "ACTIVE=%i\n" | |
0604381b | 168 | "STATE=%s\n" |
fb6becb4 | 169 | "REMOTE=%i\n", |
20263082 LP |
170 | (unsigned long) s->user->uid, |
171 | s->user->name, | |
172 | session_is_active(s), | |
0604381b | 173 | session_state_to_string(session_get_state(s)), |
fb6becb4 | 174 | s->remote); |
20263082 | 175 | |
a91e4e53 | 176 | if (s->type >= 0) |
507f22bd | 177 | fprintf(f, "TYPE=%s\n", session_type_to_string(s->type)); |
a91e4e53 | 178 | |
55efac6c | 179 | if (s->class >= 0) |
507f22bd | 180 | fprintf(f, "CLASS=%s\n", session_class_to_string(s->class)); |
55efac6c | 181 | |
fb6becb4 LP |
182 | if (s->scope) |
183 | fprintf(f, "SCOPE=%s\n", s->scope); | |
184 | ||
185 | if (s->scope_job) | |
186 | fprintf(f, "SCOPE_JOB=%s\n", s->scope_job); | |
20263082 | 187 | |
932e3ee7 | 188 | if (s->fifo_path) |
507f22bd | 189 | fprintf(f, "FIFO=%s\n", s->fifo_path); |
932e3ee7 | 190 | |
20263082 | 191 | if (s->seat) |
507f22bd | 192 | fprintf(f, "SEAT=%s\n", s->seat->id); |
20263082 LP |
193 | |
194 | if (s->tty) | |
507f22bd | 195 | fprintf(f, "TTY=%s\n", s->tty); |
20263082 LP |
196 | |
197 | if (s->display) | |
507f22bd | 198 | fprintf(f, "DISPLAY=%s\n", s->display); |
20263082 LP |
199 | |
200 | if (s->remote_host) | |
507f22bd | 201 | fprintf(f, "REMOTE_HOST=%s\n", s->remote_host); |
20263082 | 202 | |
3f49d45a | 203 | if (s->remote_user) |
507f22bd | 204 | fprintf(f, "REMOTE_USER=%s\n", s->remote_user); |
3f49d45a | 205 | |
98a28fef | 206 | if (s->service) |
507f22bd | 207 | fprintf(f, "SERVICE=%s\n", s->service); |
98a28fef | 208 | |
bf7825ae | 209 | if (s->seat && seat_has_vts(s->seat)) |
507f22bd | 210 | fprintf(f, "VTNR=%i\n", s->vtnr); |
20263082 LP |
211 | |
212 | if (s->leader > 0) | |
507f22bd | 213 | fprintf(f, "LEADER=%lu\n", (unsigned long) s->leader); |
20263082 LP |
214 | |
215 | if (s->audit_id > 0) | |
507f22bd | 216 | fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id); |
20263082 | 217 | |
9444b1f2 LP |
218 | if (dual_timestamp_is_set(&s->timestamp)) |
219 | fprintf(f, | |
220 | "REALTIME=%llu\n" | |
221 | "MONOTONIC=%llu\n", | |
222 | (unsigned long long) s->timestamp.realtime, | |
223 | (unsigned long long) s->timestamp.monotonic); | |
224 | ||
20263082 | 225 | fflush(f); |
14c3baca LP |
226 | |
227 | if (ferror(f) || rename(temp_path, s->state_file) < 0) { | |
20263082 LP |
228 | r = -errno; |
229 | unlink(s->state_file); | |
14c3baca | 230 | unlink(temp_path); |
20263082 LP |
231 | } |
232 | ||
14c3baca LP |
233 | finish: |
234 | if (r < 0) | |
235 | log_error("Failed to save session data for %s: %s", s->id, strerror(-r)); | |
236 | ||
20263082 LP |
237 | return r; |
238 | } | |
239 | ||
240 | int session_load(Session *s) { | |
9444b1f2 | 241 | _cleanup_free_ char *remote = NULL, |
a185c5aa LP |
242 | *seat = NULL, |
243 | *vtnr = NULL, | |
244 | *leader = NULL, | |
a91e4e53 | 245 | *audit_id = NULL, |
55efac6c | 246 | *type = NULL, |
9444b1f2 LP |
247 | *class = NULL, |
248 | *uid = NULL, | |
249 | *realtime = NULL, | |
250 | *monotonic = NULL; | |
a185c5aa LP |
251 | |
252 | int k, r; | |
253 | ||
20263082 LP |
254 | assert(s); |
255 | ||
a185c5aa LP |
256 | r = parse_env_file(s->state_file, NEWLINE, |
257 | "REMOTE", &remote, | |
fb6becb4 LP |
258 | "SCOPE", &s->scope, |
259 | "SCOPE_JOB", &s->scope_job, | |
932e3ee7 | 260 | "FIFO", &s->fifo_path, |
a185c5aa LP |
261 | "SEAT", &seat, |
262 | "TTY", &s->tty, | |
263 | "DISPLAY", &s->display, | |
264 | "REMOTE_HOST", &s->remote_host, | |
265 | "REMOTE_USER", &s->remote_user, | |
98a28fef | 266 | "SERVICE", &s->service, |
a185c5aa LP |
267 | "VTNR", &vtnr, |
268 | "LEADER", &leader, | |
a91e4e53 | 269 | "TYPE", &type, |
55efac6c | 270 | "CLASS", &class, |
9444b1f2 LP |
271 | "UID", &uid, |
272 | "REALTIME", &realtime, | |
273 | "MONOTONIC", &monotonic, | |
a185c5aa LP |
274 | NULL); |
275 | ||
9444b1f2 LP |
276 | if (r < 0) { |
277 | log_error("Failed to read %s: %s", s->state_file, strerror(-r)); | |
278 | return r; | |
279 | } | |
280 | ||
281 | if (!s->user) { | |
282 | uid_t u; | |
283 | User *user; | |
284 | ||
285 | if (!uid) { | |
286 | log_error("UID not specified for session %s", s->id); | |
287 | return -ENOENT; | |
288 | } | |
289 | ||
290 | r = parse_uid(uid, &u); | |
291 | if (r < 0) { | |
292 | log_error("Failed to parse UID value %s for session %s.", uid, s->id); | |
293 | return r; | |
294 | } | |
295 | ||
296 | user = hashmap_get(s->manager->users, ULONG_TO_PTR((unsigned long) u)); | |
297 | if (!user) { | |
298 | log_error("User of session %s not known.", s->id); | |
299 | return -ENOENT; | |
300 | } | |
301 | ||
302 | session_set_user(s, user); | |
303 | } | |
a185c5aa LP |
304 | |
305 | if (remote) { | |
306 | k = parse_boolean(remote); | |
307 | if (k >= 0) | |
308 | s->remote = k; | |
309 | } | |
310 | ||
9418f147 | 311 | if (seat && !s->seat) { |
a185c5aa LP |
312 | Seat *o; |
313 | ||
314 | o = hashmap_get(s->manager->seats, seat); | |
315 | if (o) | |
316 | seat_attach_session(o, s); | |
317 | } | |
318 | ||
bf7825ae | 319 | if (vtnr && s->seat && seat_has_vts(s->seat)) { |
a185c5aa LP |
320 | int v; |
321 | ||
322 | k = safe_atoi(vtnr, &v); | |
323 | if (k >= 0 && v >= 1) | |
324 | s->vtnr = v; | |
325 | } | |
326 | ||
327 | if (leader) { | |
f8e2fb7b LP |
328 | k = parse_pid(leader, &s->leader); |
329 | if (k >= 0) | |
330 | audit_session_from_pid(s->leader, &s->audit_id); | |
a185c5aa LP |
331 | } |
332 | ||
a91e4e53 LP |
333 | if (type) { |
334 | SessionType t; | |
335 | ||
336 | t = session_type_from_string(type); | |
337 | if (t >= 0) | |
338 | s->type = t; | |
339 | } | |
340 | ||
55efac6c LP |
341 | if (class) { |
342 | SessionClass c; | |
343 | ||
344 | c = session_class_from_string(class); | |
345 | if (c >= 0) | |
346 | s->class = c; | |
347 | } | |
348 | ||
b4f78aea LP |
349 | if (s->fifo_path) { |
350 | int fd; | |
351 | ||
352 | /* If we open an unopened pipe for reading we will not | |
353 | get an EOF. to trigger an EOF we hence open it for | |
354 | reading, but close it right-away which then will | |
355 | trigger the EOF. */ | |
356 | ||
357 | fd = session_create_fifo(s); | |
358 | if (fd >= 0) | |
359 | close_nointr_nofail(fd); | |
360 | } | |
361 | ||
9444b1f2 LP |
362 | if (realtime) { |
363 | unsigned long long l; | |
364 | if (sscanf(realtime, "%llu", &l) > 0) | |
365 | s->timestamp.realtime = l; | |
366 | } | |
367 | ||
368 | if (monotonic) { | |
369 | unsigned long long l; | |
370 | if (sscanf(monotonic, "%llu", &l) > 0) | |
371 | s->timestamp.monotonic = l; | |
372 | } | |
a185c5aa LP |
373 | |
374 | return r; | |
20263082 LP |
375 | } |
376 | ||
377 | int session_activate(Session *s) { | |
20263082 | 378 | assert(s); |
9444b1f2 | 379 | assert(s->user); |
20263082 | 380 | |
9209d512 | 381 | if (s->vtnr <= 0) |
20263082 LP |
382 | return -ENOTSUP; |
383 | ||
384 | if (!s->seat) | |
385 | return -ENOTSUP; | |
386 | ||
387 | if (s->seat->active == s) | |
388 | return 0; | |
389 | ||
bf7825ae | 390 | assert(seat_has_vts(s->seat)); |
20263082 | 391 | |
ab60f2ff | 392 | return chvt(s->vtnr); |
20263082 LP |
393 | } |
394 | ||
20263082 | 395 | static int session_link_x11_socket(Session *s) { |
5af726cc ZJS |
396 | _cleanup_free_ char *t = NULL, *f = NULL; |
397 | char *c; | |
20263082 LP |
398 | size_t k; |
399 | ||
400 | assert(s); | |
401 | assert(s->user); | |
402 | assert(s->user->runtime_path); | |
403 | ||
404 | if (s->user->display) | |
405 | return 0; | |
406 | ||
4d6d6518 | 407 | if (!s->display || !display_is_local(s->display)) |
20263082 LP |
408 | return 0; |
409 | ||
410 | k = strspn(s->display+1, "0123456789"); | |
411 | f = new(char, sizeof("/tmp/.X11-unix/X") + k); | |
0d0f0c50 SL |
412 | if (!f) |
413 | return log_oom(); | |
20263082 LP |
414 | |
415 | c = stpcpy(f, "/tmp/.X11-unix/X"); | |
416 | memcpy(c, s->display+1, k); | |
417 | c[k] = 0; | |
418 | ||
419 | if (access(f, F_OK) < 0) { | |
6ccf7562 | 420 | log_warning("Session %s has display %s with non-existing socket %s.", s->id, s->display, f); |
20263082 LP |
421 | return -ENOENT; |
422 | } | |
423 | ||
c9d8629b LP |
424 | /* Note that this cannot be in a subdir to avoid |
425 | * vulnerabilities since we are privileged but the runtime | |
426 | * path is owned by the user */ | |
427 | ||
fc3c1c6e | 428 | t = strappend(s->user->runtime_path, "/X11-display"); |
5af726cc | 429 | if (!t) |
0d0f0c50 | 430 | return log_oom(); |
20263082 LP |
431 | |
432 | if (link(f, t) < 0) { | |
433 | if (errno == EEXIST) { | |
434 | unlink(t); | |
435 | ||
436 | if (link(f, t) >= 0) | |
437 | goto done; | |
438 | } | |
439 | ||
440 | if (symlink(f, t) < 0) { | |
441 | ||
442 | if (errno == EEXIST) { | |
443 | unlink(t); | |
444 | ||
445 | if (symlink(f, t) >= 0) | |
446 | goto done; | |
447 | } | |
448 | ||
449 | log_error("Failed to link %s to %s: %m", f, t); | |
20263082 LP |
450 | return -errno; |
451 | } | |
452 | } | |
453 | ||
454 | done: | |
455 | log_info("Linked %s to %s.", f, t); | |
20263082 LP |
456 | s->user->display = s; |
457 | ||
458 | return 0; | |
459 | } | |
460 | ||
fb6becb4 | 461 | static int session_start_scope(Session *s) { |
fb6becb4 | 462 | DBusError error; |
98a28fef LP |
463 | int r; |
464 | ||
465 | assert(s); | |
9444b1f2 | 466 | assert(s->user); |
fb6becb4 | 467 | assert(s->user->slice); |
98a28fef | 468 | |
fb6becb4 | 469 | dbus_error_init(&error); |
ae018d9b | 470 | |
fb6becb4 | 471 | if (!s->scope) { |
d0af76e6 | 472 | _cleanup_free_ char *description = NULL; |
405e0255 | 473 | const char *kill_mode; |
d0af76e6 LP |
474 | char *scope, *job; |
475 | ||
405e0255 LP |
476 | description = strjoin("Session ", s->id, " of user ", s->user->name, NULL); |
477 | if (!description) | |
478 | return log_oom(); | |
479 | ||
d0af76e6 LP |
480 | scope = strjoin("session-", s->id, ".scope", NULL); |
481 | if (!scope) | |
ae018d9b LP |
482 | return log_oom(); |
483 | ||
405e0255 | 484 | kill_mode = manager_shall_kill(s->manager, s->user->name) ? "control-group" : "none"; |
98a28fef | 485 | |
405e0255 | 486 | r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "systemd-user-sessions.service", kill_mode, &error, &job); |
d0af76e6 | 487 | if (r < 0) { |
042f5988 ZJS |
488 | log_error("Failed to start session scope %s: %s %s", |
489 | scope, bus_error(&error, r), error.name); | |
d0af76e6 | 490 | dbus_error_free(&error); |
193197e8 | 491 | |
d0af76e6 | 492 | free(scope); |
f2d4f98d | 493 | return r; |
d0af76e6 LP |
494 | } else { |
495 | s->scope = scope; | |
496 | ||
497 | free(s->scope_job); | |
498 | s->scope_job = job; | |
499 | } | |
20263082 LP |
500 | } |
501 | ||
d0af76e6 LP |
502 | if (s->scope) |
503 | hashmap_put(s->manager->session_units, s->scope, s); | |
504 | ||
20263082 LP |
505 | return 0; |
506 | } | |
507 | ||
508 | int session_start(Session *s) { | |
509 | int r; | |
510 | ||
511 | assert(s); | |
9444b1f2 LP |
512 | |
513 | if (!s->user) | |
514 | return -ESTALE; | |
20263082 | 515 | |
9418f147 LP |
516 | if (s->started) |
517 | return 0; | |
518 | ||
ed18b08b LP |
519 | r = user_start(s->user); |
520 | if (r < 0) | |
521 | return r; | |
522 | ||
fb6becb4 LP |
523 | /* Create cgroup */ |
524 | r = session_start_scope(s); | |
525 | if (r < 0) | |
526 | return r; | |
527 | ||
877d54e9 | 528 | log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG, |
1ca6783f | 529 | MESSAGE_ID(SD_MESSAGE_SESSION_START), |
877d54e9 LP |
530 | "SESSION_ID=%s", s->id, |
531 | "USER_ID=%s", s->user->name, | |
532 | "LEADER=%lu", (unsigned long) s->leader, | |
533 | "MESSAGE=New session %s of user %s.", s->id, s->user->name, | |
534 | NULL); | |
98a28fef | 535 | |
20263082 LP |
536 | /* Create X11 symlink */ |
537 | session_link_x11_socket(s); | |
14c3baca | 538 | |
9444b1f2 LP |
539 | if (!dual_timestamp_is_set(&s->timestamp)) |
540 | dual_timestamp_get(&s->timestamp); | |
14c3baca | 541 | |
e9816c48 LP |
542 | if (s->seat) |
543 | seat_read_active_vt(s->seat); | |
544 | ||
9418f147 LP |
545 | s->started = true; |
546 | ||
e9816c48 LP |
547 | /* Save session data */ |
548 | session_save(s); | |
7f7bb946 | 549 | user_save(s->user); |
e9816c48 | 550 | |
da119395 LP |
551 | session_send_signal(s, true); |
552 | ||
9418f147 | 553 | if (s->seat) { |
7f7bb946 LP |
554 | seat_save(s->seat); |
555 | ||
9418f147 LP |
556 | if (s->seat->active == s) |
557 | seat_send_changed(s->seat, "Sessions\0ActiveSession\0"); | |
558 | else | |
559 | seat_send_changed(s->seat, "Sessions\0"); | |
560 | } | |
561 | ||
562 | user_send_changed(s->user, "Sessions\0"); | |
563 | ||
20263082 LP |
564 | return 0; |
565 | } | |
566 | ||
fb6becb4 LP |
567 | static int session_stop_scope(Session *s) { |
568 | DBusError error; | |
569 | char *job; | |
20263082 | 570 | int r; |
20263082 LP |
571 | |
572 | assert(s); | |
573 | ||
fb6becb4 | 574 | dbus_error_init(&error); |
20263082 | 575 | |
fb6becb4 LP |
576 | if (!s->scope) |
577 | return 0; | |
9b221b63 | 578 | |
fb6becb4 LP |
579 | r = manager_stop_unit(s->manager, s->scope, &error, &job); |
580 | if (r < 0) { | |
581 | log_error("Failed to stop session scope: %s", bus_error(&error, r)); | |
582 | dbus_error_free(&error); | |
583 | return r; | |
20263082 LP |
584 | } |
585 | ||
fb6becb4 LP |
586 | free(s->scope_job); |
587 | s->scope_job = job; | |
20263082 | 588 | |
9b221b63 | 589 | return 0; |
20263082 LP |
590 | } |
591 | ||
592 | static int session_unlink_x11_socket(Session *s) { | |
5af726cc | 593 | _cleanup_free_ char *t = NULL; |
20263082 LP |
594 | int r; |
595 | ||
596 | assert(s); | |
597 | assert(s->user); | |
598 | ||
599 | if (s->user->display != s) | |
600 | return 0; | |
601 | ||
602 | s->user->display = NULL; | |
603 | ||
fc3c1c6e | 604 | t = strappend(s->user->runtime_path, "/X11-display"); |
0d0f0c50 SL |
605 | if (!t) |
606 | return log_oom(); | |
20263082 LP |
607 | |
608 | r = unlink(t); | |
20263082 LP |
609 | return r < 0 ? -errno : 0; |
610 | } | |
611 | ||
612 | int session_stop(Session *s) { | |
405e0255 LP |
613 | int r; |
614 | ||
615 | assert(s); | |
616 | ||
617 | if (!s->user) | |
618 | return -ESTALE; | |
619 | ||
620 | /* Kill cgroup */ | |
621 | r = session_stop_scope(s); | |
622 | ||
623 | session_save(s); | |
624 | ||
625 | return r; | |
626 | } | |
627 | ||
628 | int session_finalize(Session *s) { | |
629 | int r = 0; | |
118ecf32 | 630 | SessionDevice *sd; |
20263082 LP |
631 | |
632 | assert(s); | |
633 | ||
9444b1f2 LP |
634 | if (!s->user) |
635 | return -ESTALE; | |
636 | ||
ed18b08b | 637 | if (s->started) |
877d54e9 | 638 | log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG, |
1ca6783f | 639 | MESSAGE_ID(SD_MESSAGE_SESSION_STOP), |
877d54e9 LP |
640 | "SESSION_ID=%s", s->id, |
641 | "USER_ID=%s", s->user->name, | |
642 | "LEADER=%lu", (unsigned long) s->leader, | |
643 | "MESSAGE=Removed session %s.", s->id, | |
644 | NULL); | |
98a28fef | 645 | |
118ecf32 DH |
646 | /* Kill session devices */ |
647 | while ((sd = hashmap_first(s->devices))) | |
648 | session_device_free(sd); | |
649 | ||
20263082 LP |
650 | /* Remove X11 symlink */ |
651 | session_unlink_x11_socket(s); | |
652 | ||
d2f92cdf LP |
653 | unlink(s->state_file); |
654 | session_add_to_gc_queue(s); | |
ed18b08b | 655 | user_add_to_gc_queue(s->user); |
14c3baca | 656 | |
405e0255 | 657 | if (s->started) { |
ed18b08b | 658 | session_send_signal(s, false); |
405e0255 LP |
659 | s->started = false; |
660 | } | |
50fb9793 | 661 | |
9418f147 LP |
662 | if (s->seat) { |
663 | if (s->seat->active == s) | |
664 | seat_set_active(s->seat, NULL); | |
665 | ||
666 | seat_send_changed(s->seat, "Sessions\0"); | |
23bd3b62 | 667 | seat_save(s->seat); |
9418f147 LP |
668 | } |
669 | ||
670 | user_send_changed(s->user, "Sessions\0"); | |
23bd3b62 | 671 | user_save(s->user); |
9418f147 | 672 | |
20263082 LP |
673 | return r; |
674 | } | |
675 | ||
676 | bool session_is_active(Session *s) { | |
677 | assert(s); | |
678 | ||
679 | if (!s->seat) | |
680 | return true; | |
681 | ||
682 | return s->seat->active == s; | |
683 | } | |
684 | ||
23406ce5 LP |
685 | static int get_tty_atime(const char *tty, usec_t *atime) { |
686 | _cleanup_free_ char *p = NULL; | |
a185c5aa | 687 | struct stat st; |
23406ce5 LP |
688 | |
689 | assert(tty); | |
690 | assert(atime); | |
691 | ||
692 | if (!path_is_absolute(tty)) { | |
693 | p = strappend("/dev/", tty); | |
694 | if (!p) | |
695 | return -ENOMEM; | |
696 | ||
697 | tty = p; | |
698 | } else if (!path_startswith(tty, "/dev/")) | |
699 | return -ENOENT; | |
700 | ||
701 | if (lstat(tty, &st) < 0) | |
702 | return -errno; | |
703 | ||
704 | *atime = timespec_load(&st.st_atim); | |
705 | return 0; | |
706 | } | |
707 | ||
708 | static int get_process_ctty_atime(pid_t pid, usec_t *atime) { | |
709 | _cleanup_free_ char *p = NULL; | |
710 | int r; | |
711 | ||
712 | assert(pid > 0); | |
713 | assert(atime); | |
714 | ||
715 | r = get_ctty(pid, NULL, &p); | |
716 | if (r < 0) | |
717 | return r; | |
718 | ||
719 | return get_tty_atime(p, atime); | |
720 | } | |
721 | ||
722 | int session_get_idle_hint(Session *s, dual_timestamp *t) { | |
23406ce5 LP |
723 | usec_t atime = 0, n; |
724 | int r; | |
a185c5aa LP |
725 | |
726 | assert(s); | |
727 | ||
23406ce5 | 728 | /* Explicit idle hint is set */ |
a185c5aa LP |
729 | if (s->idle_hint) { |
730 | if (t) | |
731 | *t = s->idle_hint_timestamp; | |
732 | ||
733 | return s->idle_hint; | |
734 | } | |
735 | ||
0762eaa3 | 736 | /* Graphical sessions should really implement a real |
23406ce5 LP |
737 | * idle hint logic */ |
738 | if (s->display) | |
a185c5aa LP |
739 | goto dont_know; |
740 | ||
23406ce5 LP |
741 | /* For sessions with an explicitly configured tty, let's check |
742 | * its atime */ | |
743 | if (s->tty) { | |
744 | r = get_tty_atime(s->tty, &atime); | |
745 | if (r >= 0) | |
746 | goto found_atime; | |
747 | } | |
a185c5aa | 748 | |
23406ce5 LP |
749 | /* For sessions with a leader but no explicitly configured |
750 | * tty, let's check the controlling tty of the leader */ | |
751 | if (s->leader > 0) { | |
752 | r = get_process_ctty_atime(s->leader, &atime); | |
753 | if (r >= 0) | |
754 | goto found_atime; | |
a185c5aa LP |
755 | } |
756 | ||
a185c5aa LP |
757 | dont_know: |
758 | if (t) | |
759 | *t = s->idle_hint_timestamp; | |
760 | ||
761 | return 0; | |
23406ce5 LP |
762 | |
763 | found_atime: | |
764 | if (t) | |
765 | dual_timestamp_from_realtime(t, atime); | |
766 | ||
767 | n = now(CLOCK_REALTIME); | |
768 | ||
769 | if (s->manager->idle_action_usec <= 0) | |
770 | return 0; | |
771 | ||
772 | return atime + s->manager->idle_action_usec <= n; | |
a185c5aa LP |
773 | } |
774 | ||
bef422ae LP |
775 | void session_set_idle_hint(Session *s, bool b) { |
776 | assert(s); | |
777 | ||
778 | if (s->idle_hint == b) | |
779 | return; | |
780 | ||
781 | s->idle_hint = b; | |
782 | dual_timestamp_get(&s->idle_hint_timestamp); | |
9418f147 LP |
783 | |
784 | session_send_changed(s, | |
785 | "IdleHint\0" | |
786 | "IdleSinceHint\0" | |
787 | "IdleSinceHintMonotonic\0"); | |
788 | ||
789 | if (s->seat) | |
790 | seat_send_changed(s->seat, | |
791 | "IdleHint\0" | |
792 | "IdleSinceHint\0" | |
793 | "IdleSinceHintMonotonic\0"); | |
794 | ||
795 | user_send_changed(s->user, | |
796 | "IdleHint\0" | |
797 | "IdleSinceHint\0" | |
798 | "IdleSinceHintMonotonic\0"); | |
799 | ||
800 | manager_send_changed(s->manager, | |
801 | "IdleHint\0" | |
802 | "IdleSinceHint\0" | |
803 | "IdleSinceHintMonotonic\0"); | |
bef422ae LP |
804 | } |
805 | ||
932e3ee7 LP |
806 | int session_create_fifo(Session *s) { |
807 | int r; | |
808 | ||
31b79c2b LP |
809 | assert(s); |
810 | ||
b4f78aea | 811 | /* Create FIFO */ |
932e3ee7 | 812 | if (!s->fifo_path) { |
d2e54fae | 813 | r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0); |
e6061ab2 LP |
814 | if (r < 0) |
815 | return r; | |
816 | ||
932e3ee7 LP |
817 | if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0) |
818 | return -ENOMEM; | |
31b79c2b | 819 | |
932e3ee7 LP |
820 | if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST) |
821 | return -errno; | |
822 | } | |
31b79c2b | 823 | |
932e3ee7 | 824 | /* Open reading side */ |
b4f78aea | 825 | if (s->fifo_fd < 0) { |
b92bea5d | 826 | struct epoll_event ev = {}; |
b4f78aea LP |
827 | |
828 | s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); | |
829 | if (s->fifo_fd < 0) | |
830 | return -errno; | |
831 | ||
f8e2fb7b | 832 | r = hashmap_put(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1), s); |
b4f78aea LP |
833 | if (r < 0) |
834 | return r; | |
835 | ||
b4f78aea | 836 | ev.events = 0; |
069cfc85 | 837 | ev.data.u32 = FD_OTHER_BASE + s->fifo_fd; |
b4f78aea LP |
838 | |
839 | if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, s->fifo_fd, &ev) < 0) | |
840 | return -errno; | |
841 | } | |
932e3ee7 LP |
842 | |
843 | /* Open writing side */ | |
844 | r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); | |
845 | if (r < 0) | |
846 | return -errno; | |
31b79c2b | 847 | |
932e3ee7 LP |
848 | return r; |
849 | } | |
850 | ||
851 | void session_remove_fifo(Session *s) { | |
852 | assert(s); | |
853 | ||
854 | if (s->fifo_fd >= 0) { | |
f8e2fb7b | 855 | assert_se(hashmap_remove(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1)) == s); |
932e3ee7 LP |
856 | assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->fifo_fd, NULL) == 0); |
857 | close_nointr_nofail(s->fifo_fd); | |
858 | s->fifo_fd = -1; | |
23bd3b62 CG |
859 | |
860 | session_save(s); | |
861 | user_save(s->user); | |
932e3ee7 LP |
862 | } |
863 | ||
864 | if (s->fifo_path) { | |
865 | unlink(s->fifo_path); | |
866 | free(s->fifo_path); | |
867 | s->fifo_path = NULL; | |
868 | } | |
31b79c2b LP |
869 | } |
870 | ||
4a4b033f | 871 | int session_check_gc(Session *s, bool drop_not_started) { |
20263082 LP |
872 | int r; |
873 | ||
874 | assert(s); | |
875 | ||
4a4b033f | 876 | if (drop_not_started && !s->started) |
932e3ee7 LP |
877 | return 0; |
878 | ||
9444b1f2 LP |
879 | if (!s->user) |
880 | return 0; | |
881 | ||
932e3ee7 | 882 | if (s->fifo_fd >= 0) { |
932e3ee7 | 883 | r = pipe_eof(s->fifo_fd); |
20263082 LP |
884 | if (r < 0) |
885 | return r; | |
886 | ||
14c3baca | 887 | if (r == 0) |
20263082 LP |
888 | return 1; |
889 | } | |
890 | ||
fb6becb4 LP |
891 | if (s->scope_job) |
892 | return 1; | |
20263082 | 893 | |
fb6becb4 LP |
894 | if (s->scope) |
895 | return manager_unit_is_active(s->manager, s->scope) != 0; | |
20263082 LP |
896 | |
897 | return 0; | |
898 | } | |
899 | ||
14c3baca LP |
900 | void session_add_to_gc_queue(Session *s) { |
901 | assert(s); | |
902 | ||
903 | if (s->in_gc_queue) | |
904 | return; | |
905 | ||
906 | LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s); | |
907 | s->in_gc_queue = true; | |
908 | } | |
909 | ||
0604381b LP |
910 | SessionState session_get_state(Session *s) { |
911 | assert(s); | |
912 | ||
405e0255 LP |
913 | if (s->closing) |
914 | return SESSION_CLOSING; | |
915 | ||
fb6becb4 | 916 | if (s->scope_job) |
405e0255 | 917 | return SESSION_OPENING; |
fb6becb4 | 918 | |
0604381b LP |
919 | if (s->fifo_fd < 0) |
920 | return SESSION_CLOSING; | |
921 | ||
922 | if (session_is_active(s)) | |
923 | return SESSION_ACTIVE; | |
924 | ||
925 | return SESSION_ONLINE; | |
926 | } | |
927 | ||
de07ab16 | 928 | int session_kill(Session *s, KillWho who, int signo) { |
de07ab16 LP |
929 | assert(s); |
930 | ||
fb6becb4 | 931 | if (!s->scope) |
de07ab16 LP |
932 | return -ESRCH; |
933 | ||
fb6becb4 | 934 | return manager_kill_unit(s->manager, s->scope, who, signo, NULL); |
de07ab16 LP |
935 | } |
936 | ||
ae5e06bd DH |
937 | bool session_is_controller(Session *s, const char *sender) |
938 | { | |
939 | assert(s); | |
940 | ||
941 | return streq_ptr(s->controller, sender); | |
942 | } | |
943 | ||
944 | int session_set_controller(Session *s, const char *sender, bool force) { | |
945 | char *t; | |
946 | int r; | |
947 | ||
948 | assert(s); | |
949 | assert(sender); | |
950 | ||
951 | if (session_is_controller(s, sender)) | |
952 | return 0; | |
953 | if (s->controller && !force) | |
954 | return -EBUSY; | |
955 | ||
956 | t = strdup(sender); | |
957 | if (!t) | |
958 | return -ENOMEM; | |
959 | ||
960 | r = manager_watch_busname(s->manager, sender); | |
961 | if (r) { | |
962 | free(t); | |
963 | return r; | |
964 | } | |
965 | ||
966 | session_drop_controller(s); | |
967 | ||
968 | s->controller = t; | |
969 | return 0; | |
970 | } | |
971 | ||
972 | void session_drop_controller(Session *s) { | |
118ecf32 DH |
973 | SessionDevice *sd; |
974 | ||
ae5e06bd DH |
975 | assert(s); |
976 | ||
977 | if (!s->controller) | |
978 | return; | |
979 | ||
980 | manager_drop_busname(s->manager, s->controller); | |
981 | free(s->controller); | |
982 | s->controller = NULL; | |
118ecf32 DH |
983 | |
984 | /* Drop all devices as they're now unused. Do that after the controller | |
985 | * is released to avoid sending out useles dbus signals. */ | |
986 | while ((sd = hashmap_first(s->devices))) | |
987 | session_device_free(sd); | |
ae5e06bd DH |
988 | } |
989 | ||
fb6becb4 LP |
990 | static const char* const session_state_table[_SESSION_STATE_MAX] = { |
991 | [SESSION_OPENING] = "opening", | |
0604381b LP |
992 | [SESSION_ONLINE] = "online", |
993 | [SESSION_ACTIVE] = "active", | |
994 | [SESSION_CLOSING] = "closing" | |
995 | }; | |
996 | ||
997 | DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState); | |
998 | ||
20263082 | 999 | static const char* const session_type_table[_SESSION_TYPE_MAX] = { |
3f49d45a | 1000 | [SESSION_TTY] = "tty", |
98a28fef | 1001 | [SESSION_X11] = "x11", |
a91e4e53 | 1002 | [SESSION_UNSPECIFIED] = "unspecified" |
20263082 LP |
1003 | }; |
1004 | ||
1005 | DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType); | |
de07ab16 | 1006 | |
55efac6c LP |
1007 | static const char* const session_class_table[_SESSION_CLASS_MAX] = { |
1008 | [SESSION_USER] = "user", | |
1009 | [SESSION_GREETER] = "greeter", | |
e2acb67b LP |
1010 | [SESSION_LOCK_SCREEN] = "lock-screen", |
1011 | [SESSION_BACKGROUND] = "background" | |
55efac6c LP |
1012 | }; |
1013 | ||
1014 | DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass); | |
1015 | ||
de07ab16 LP |
1016 | static const char* const kill_who_table[_KILL_WHO_MAX] = { |
1017 | [KILL_LEADER] = "leader", | |
1018 | [KILL_ALL] = "all" | |
1019 | }; | |
1020 | ||
1021 | DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); |