]>
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 | |
9 | under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
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 | |
16 | General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
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> |
20263082 | 26 | |
90821c93 | 27 | #include "logind-session.h" |
20263082 LP |
28 | #include "strv.h" |
29 | #include "util.h" | |
30 | #include "cgroup-util.h" | |
31 | ||
a185c5aa LP |
32 | #define IDLE_THRESHOLD_USEC (5*USEC_PER_MINUTE) |
33 | ||
20263082 LP |
34 | Session* session_new(Manager *m, User *u, const char *id) { |
35 | Session *s; | |
36 | ||
37 | assert(m); | |
38 | assert(id); | |
39 | ||
14c3baca | 40 | s = new0(Session, 1); |
20263082 LP |
41 | if (!s) |
42 | return NULL; | |
43 | ||
98a28fef | 44 | s->state_file = strappend("/run/systemd/sessions/", id); |
20263082 LP |
45 | if (!s->state_file) { |
46 | free(s); | |
47 | return NULL; | |
48 | } | |
49 | ||
50 | s->id = file_name_from_path(s->state_file); | |
51 | ||
52 | if (hashmap_put(m->sessions, s->id, s) < 0) { | |
53 | free(s->id); | |
54 | free(s); | |
55 | return NULL; | |
56 | } | |
57 | ||
58 | s->manager = m; | |
59 | s->pipe_fd = -1; | |
60 | s->user = u; | |
61 | ||
14c3baca | 62 | LIST_PREPEND(Session, sessions_by_user, u->sessions, s); |
20263082 LP |
63 | |
64 | return s; | |
65 | } | |
66 | ||
67 | void session_free(Session *s) { | |
68 | assert(s); | |
69 | ||
14c3baca LP |
70 | if (s->in_gc_queue) |
71 | LIST_REMOVE(Session, gc_queue, s->manager->session_gc_queue, s); | |
72 | ||
20263082 LP |
73 | if (s->user) { |
74 | LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s); | |
75 | ||
76 | if (s->user->display == s) | |
77 | s->user->display = NULL; | |
78 | } | |
79 | ||
9418f147 LP |
80 | if (s->seat) { |
81 | if (s->seat->active == s) | |
82 | s->seat->active = NULL; | |
83 | ||
20263082 | 84 | LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s); |
9418f147 | 85 | } |
20263082 | 86 | |
1713813d LP |
87 | if (s->cgroup_path) |
88 | hashmap_remove(s->manager->cgroups, s->cgroup_path); | |
89 | ||
20263082 LP |
90 | free(s->cgroup_path); |
91 | strv_free(s->controllers); | |
92 | ||
93 | free(s->tty); | |
94 | free(s->display); | |
95 | free(s->remote_host); | |
3f49d45a | 96 | free(s->remote_user); |
98a28fef | 97 | free(s->service); |
20263082 LP |
98 | |
99 | hashmap_remove(s->manager->sessions, s->id); | |
100 | ||
31b79c2b | 101 | session_unset_pipe_fd(s); |
98a28fef | 102 | |
d2f92cdf | 103 | free(s->state_file); |
20263082 LP |
104 | free(s); |
105 | } | |
106 | ||
107 | int session_save(Session *s) { | |
108 | FILE *f; | |
109 | int r = 0; | |
14c3baca | 110 | char *temp_path; |
20263082 LP |
111 | |
112 | assert(s); | |
113 | ||
accaeded LP |
114 | if (!s->started) |
115 | return 0; | |
116 | ||
98a28fef | 117 | r = safe_mkdir("/run/systemd/sessions", 0755, 0, 0); |
20263082 | 118 | if (r < 0) |
14c3baca | 119 | goto finish; |
20263082 | 120 | |
14c3baca LP |
121 | r = fopen_temporary(s->state_file, &f, &temp_path); |
122 | if (r < 0) | |
123 | goto finish; | |
20263082 LP |
124 | |
125 | assert(s->user); | |
126 | ||
14c3baca LP |
127 | fchmod(fileno(f), 0644); |
128 | ||
20263082 LP |
129 | fprintf(f, |
130 | "# This is private data. Do not parse.\n" | |
131 | "UID=%lu\n" | |
132 | "USER=%s\n" | |
133 | "ACTIVE=%i\n" | |
134 | "REMOTE=%i\n" | |
135 | "KILL_PROCESSES=%i\n", | |
136 | (unsigned long) s->user->uid, | |
137 | s->user->name, | |
138 | session_is_active(s), | |
139 | s->remote, | |
140 | s->kill_processes); | |
141 | ||
a91e4e53 LP |
142 | if (s->type >= 0) |
143 | fprintf(f, | |
144 | "TYPE=%s\n", | |
145 | session_type_to_string(s->type)); | |
146 | ||
20263082 LP |
147 | if (s->cgroup_path) |
148 | fprintf(f, | |
149 | "CGROUP=%s\n", | |
150 | s->cgroup_path); | |
151 | ||
152 | if (s->seat) | |
153 | fprintf(f, | |
154 | "SEAT=%s\n", | |
155 | s->seat->id); | |
156 | ||
157 | if (s->tty) | |
158 | fprintf(f, | |
159 | "TTY=%s\n", | |
160 | s->tty); | |
161 | ||
162 | if (s->display) | |
163 | fprintf(f, | |
164 | "DISPLAY=%s\n", | |
165 | s->display); | |
166 | ||
167 | if (s->remote_host) | |
168 | fprintf(f, | |
169 | "REMOTE_HOST=%s\n", | |
170 | s->remote_host); | |
171 | ||
3f49d45a LP |
172 | if (s->remote_user) |
173 | fprintf(f, | |
174 | "REMOTE_USER=%s\n", | |
175 | s->remote_user); | |
176 | ||
98a28fef LP |
177 | if (s->service) |
178 | fprintf(f, | |
179 | "SERVICE=%s\n", | |
180 | s->service); | |
181 | ||
a185c5aa | 182 | if (s->seat && seat_is_vtconsole(s->seat)) |
20263082 LP |
183 | fprintf(f, |
184 | "VTNR=%i\n", | |
185 | s->vtnr); | |
186 | ||
187 | if (s->leader > 0) | |
188 | fprintf(f, | |
189 | "LEADER=%lu\n", | |
190 | (unsigned long) s->leader); | |
191 | ||
192 | if (s->audit_id > 0) | |
193 | fprintf(f, | |
194 | "AUDIT=%llu\n", | |
195 | (unsigned long long) s->audit_id); | |
196 | ||
197 | fflush(f); | |
14c3baca LP |
198 | |
199 | if (ferror(f) || rename(temp_path, s->state_file) < 0) { | |
20263082 LP |
200 | r = -errno; |
201 | unlink(s->state_file); | |
14c3baca | 202 | unlink(temp_path); |
20263082 LP |
203 | } |
204 | ||
205 | fclose(f); | |
14c3baca LP |
206 | free(temp_path); |
207 | ||
208 | finish: | |
209 | if (r < 0) | |
210 | log_error("Failed to save session data for %s: %s", s->id, strerror(-r)); | |
211 | ||
20263082 LP |
212 | return r; |
213 | } | |
214 | ||
215 | int session_load(Session *s) { | |
a185c5aa LP |
216 | char *remote = NULL, |
217 | *kill_processes = NULL, | |
218 | *seat = NULL, | |
219 | *vtnr = NULL, | |
220 | *leader = NULL, | |
a91e4e53 LP |
221 | *audit_id = NULL, |
222 | *type = NULL; | |
a185c5aa LP |
223 | |
224 | int k, r; | |
225 | ||
20263082 LP |
226 | assert(s); |
227 | ||
a185c5aa LP |
228 | r = parse_env_file(s->state_file, NEWLINE, |
229 | "REMOTE", &remote, | |
230 | "KILL_PROCESSES", &kill_processes, | |
231 | "CGROUP", &s->cgroup_path, | |
232 | "SEAT", &seat, | |
233 | "TTY", &s->tty, | |
234 | "DISPLAY", &s->display, | |
235 | "REMOTE_HOST", &s->remote_host, | |
236 | "REMOTE_USER", &s->remote_user, | |
98a28fef | 237 | "SERVICE", &s->service, |
a185c5aa LP |
238 | "VTNR", &vtnr, |
239 | "LEADER", &leader, | |
a91e4e53 | 240 | "TYPE", &type, |
a185c5aa LP |
241 | NULL); |
242 | ||
243 | if (r < 0) | |
244 | goto finish; | |
245 | ||
246 | if (remote) { | |
247 | k = parse_boolean(remote); | |
248 | if (k >= 0) | |
249 | s->remote = k; | |
250 | } | |
251 | ||
252 | if (kill_processes) { | |
253 | k = parse_boolean(kill_processes); | |
254 | if (k >= 0) | |
255 | s->kill_processes = k; | |
256 | } | |
257 | ||
9418f147 | 258 | if (seat && !s->seat) { |
a185c5aa LP |
259 | Seat *o; |
260 | ||
261 | o = hashmap_get(s->manager->seats, seat); | |
262 | if (o) | |
263 | seat_attach_session(o, s); | |
264 | } | |
265 | ||
266 | if (vtnr && s->seat && seat_is_vtconsole(s->seat)) { | |
267 | int v; | |
268 | ||
269 | k = safe_atoi(vtnr, &v); | |
270 | if (k >= 0 && v >= 1) | |
271 | s->vtnr = v; | |
272 | } | |
273 | ||
274 | if (leader) { | |
275 | pid_t pid; | |
276 | ||
277 | k = parse_pid(leader, &pid); | |
98a28fef | 278 | if (k >= 0 && pid >= 1) { |
a185c5aa | 279 | s->leader = pid; |
a185c5aa | 280 | |
98a28fef LP |
281 | audit_session_from_pid(pid, &s->audit_id); |
282 | } | |
a185c5aa LP |
283 | } |
284 | ||
a91e4e53 LP |
285 | if (type) { |
286 | SessionType t; | |
287 | ||
288 | t = session_type_from_string(type); | |
289 | if (t >= 0) | |
290 | s->type = t; | |
291 | } | |
292 | ||
a185c5aa LP |
293 | finish: |
294 | free(remote); | |
295 | free(kill_processes); | |
296 | free(seat); | |
297 | free(vtnr); | |
298 | free(leader); | |
299 | free(audit_id); | |
300 | ||
301 | return r; | |
20263082 LP |
302 | } |
303 | ||
304 | int session_activate(Session *s) { | |
305 | int r; | |
5eda94dd | 306 | Session *old_active; |
20263082 LP |
307 | |
308 | assert(s); | |
309 | ||
310 | if (s->vtnr < 0) | |
311 | return -ENOTSUP; | |
312 | ||
313 | if (!s->seat) | |
314 | return -ENOTSUP; | |
315 | ||
316 | if (s->seat->active == s) | |
317 | return 0; | |
318 | ||
a185c5aa | 319 | assert(seat_is_vtconsole(s->seat)); |
20263082 LP |
320 | |
321 | r = chvt(s->vtnr); | |
322 | if (r < 0) | |
323 | return r; | |
324 | ||
5eda94dd | 325 | old_active = s->seat->active; |
20263082 LP |
326 | s->seat->active = s; |
327 | ||
14c3baca | 328 | return seat_apply_acls(s->seat, old_active); |
20263082 LP |
329 | } |
330 | ||
20263082 LP |
331 | |
332 | static int session_link_x11_socket(Session *s) { | |
333 | char *t, *f, *c; | |
334 | size_t k; | |
335 | ||
336 | assert(s); | |
337 | assert(s->user); | |
338 | assert(s->user->runtime_path); | |
339 | ||
340 | if (s->user->display) | |
341 | return 0; | |
342 | ||
4d6d6518 | 343 | if (!s->display || !display_is_local(s->display)) |
20263082 LP |
344 | return 0; |
345 | ||
346 | k = strspn(s->display+1, "0123456789"); | |
347 | f = new(char, sizeof("/tmp/.X11-unix/X") + k); | |
348 | if (!f) { | |
349 | log_error("Out of memory"); | |
350 | return -ENOMEM; | |
351 | } | |
352 | ||
353 | c = stpcpy(f, "/tmp/.X11-unix/X"); | |
354 | memcpy(c, s->display+1, k); | |
355 | c[k] = 0; | |
356 | ||
357 | if (access(f, F_OK) < 0) { | |
358 | log_warning("Session %s has display %s with nonexisting socket %s.", s->id, s->display, f); | |
359 | free(f); | |
360 | return -ENOENT; | |
361 | } | |
362 | ||
363 | t = strappend(s->user->runtime_path, "/display"); | |
364 | if (!t) { | |
365 | log_error("Out of memory"); | |
366 | free(f); | |
367 | return -ENOMEM; | |
368 | } | |
369 | ||
370 | if (link(f, t) < 0) { | |
371 | if (errno == EEXIST) { | |
372 | unlink(t); | |
373 | ||
374 | if (link(f, t) >= 0) | |
375 | goto done; | |
376 | } | |
377 | ||
378 | if (symlink(f, t) < 0) { | |
379 | ||
380 | if (errno == EEXIST) { | |
381 | unlink(t); | |
382 | ||
383 | if (symlink(f, t) >= 0) | |
384 | goto done; | |
385 | } | |
386 | ||
387 | log_error("Failed to link %s to %s: %m", f, t); | |
388 | free(f); | |
389 | free(t); | |
390 | return -errno; | |
391 | } | |
392 | } | |
393 | ||
394 | done: | |
395 | log_info("Linked %s to %s.", f, t); | |
396 | free(f); | |
397 | free(t); | |
398 | ||
399 | s->user->display = s; | |
400 | ||
401 | return 0; | |
402 | } | |
403 | ||
98a28fef LP |
404 | static int session_create_one_group(Session *s, const char *controller, const char *path) { |
405 | int r; | |
406 | ||
407 | assert(s); | |
408 | assert(controller); | |
409 | assert(path); | |
410 | ||
b6f68af1 | 411 | if (s->leader > 0) { |
98a28fef | 412 | r = cg_create_and_attach(controller, path, s->leader); |
b6f68af1 LP |
413 | if (r < 0) |
414 | r = cg_create(controller, path); | |
415 | } else | |
98a28fef LP |
416 | r = cg_create(controller, path); |
417 | ||
418 | if (r < 0) | |
419 | return r; | |
420 | ||
421 | r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid); | |
422 | if (r >= 0) | |
423 | r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid); | |
424 | ||
425 | return r; | |
426 | } | |
427 | ||
20263082 LP |
428 | static int session_create_cgroup(Session *s) { |
429 | char **k; | |
430 | char *p; | |
431 | int r; | |
432 | ||
433 | assert(s); | |
434 | assert(s->user); | |
435 | assert(s->user->cgroup_path); | |
436 | ||
437 | if (!s->cgroup_path) { | |
438 | if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0) { | |
439 | log_error("Out of memory"); | |
440 | return -ENOMEM; | |
441 | } | |
442 | } else | |
443 | p = s->cgroup_path; | |
444 | ||
98a28fef | 445 | r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, p); |
20263082 | 446 | if (r < 0) { |
4d6d6518 | 447 | log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r)); |
20263082 LP |
448 | free(p); |
449 | s->cgroup_path = NULL; | |
20263082 LP |
450 | return r; |
451 | } | |
452 | ||
453 | s->cgroup_path = p; | |
454 | ||
98a28fef LP |
455 | STRV_FOREACH(k, s->controllers) { |
456 | ||
457 | if (strv_contains(s->reset_controllers, *k)) | |
458 | continue; | |
459 | ||
460 | r = session_create_one_group(s, *k, p); | |
461 | if (r < 0) | |
462 | log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r)); | |
463 | } | |
464 | ||
20263082 | 465 | STRV_FOREACH(k, s->manager->controllers) { |
20263082 | 466 | |
98a28fef LP |
467 | if (strv_contains(s->reset_controllers, *k) || |
468 | strv_contains(s->controllers, *k)) | |
469 | continue; | |
470 | ||
471 | r = session_create_one_group(s, *k, p); | |
20263082 | 472 | if (r < 0) |
98a28fef LP |
473 | log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r)); |
474 | } | |
475 | ||
476 | if (s->leader > 0) { | |
477 | ||
478 | STRV_FOREACH(k, s->reset_controllers) { | |
479 | r = cg_attach(*k, "/", s->leader); | |
480 | if (r < 0) | |
481 | log_warning("Failed to reset controller %s: %s", *k, strerror(-r)); | |
482 | ||
483 | } | |
20263082 LP |
484 | } |
485 | ||
1713813d LP |
486 | hashmap_put(s->manager->cgroups, s->cgroup_path, s); |
487 | ||
20263082 LP |
488 | return 0; |
489 | } | |
490 | ||
491 | int session_start(Session *s) { | |
492 | int r; | |
493 | ||
494 | assert(s); | |
495 | assert(s->user); | |
496 | ||
9418f147 LP |
497 | if (s->started) |
498 | return 0; | |
499 | ||
ed18b08b LP |
500 | r = user_start(s->user); |
501 | if (r < 0) | |
502 | return r; | |
503 | ||
98a28fef LP |
504 | log_info("New session %s of user %s.", s->id, s->user->name); |
505 | ||
20263082 LP |
506 | /* Create cgroup */ |
507 | r = session_create_cgroup(s); | |
508 | if (r < 0) | |
509 | return r; | |
510 | ||
511 | /* Create X11 symlink */ | |
512 | session_link_x11_socket(s); | |
14c3baca | 513 | |
14c3baca LP |
514 | dual_timestamp_get(&s->timestamp); |
515 | ||
e9816c48 LP |
516 | if (s->seat) |
517 | seat_read_active_vt(s->seat); | |
518 | ||
9418f147 LP |
519 | s->started = true; |
520 | ||
e9816c48 LP |
521 | /* Save session data */ |
522 | session_save(s); | |
523 | ||
da119395 LP |
524 | session_send_signal(s, true); |
525 | ||
9418f147 LP |
526 | if (s->seat) { |
527 | if (s->seat->active == s) | |
528 | seat_send_changed(s->seat, "Sessions\0ActiveSession\0"); | |
529 | else | |
530 | seat_send_changed(s->seat, "Sessions\0"); | |
531 | } | |
532 | ||
533 | user_send_changed(s->user, "Sessions\0"); | |
534 | ||
20263082 LP |
535 | return 0; |
536 | } | |
537 | ||
538 | static bool session_shall_kill(Session *s) { | |
539 | assert(s); | |
540 | ||
ed18b08b LP |
541 | if (!s->kill_processes) |
542 | return false; | |
543 | ||
544 | if (strv_contains(s->manager->kill_exclude_users, s->user->name)) | |
545 | return false; | |
546 | ||
547 | if (strv_isempty(s->manager->kill_only_users)) | |
548 | return true; | |
549 | ||
550 | return strv_contains(s->manager->kill_only_users, s->user->name); | |
20263082 LP |
551 | } |
552 | ||
553 | static int session_kill_cgroup(Session *s) { | |
554 | int r; | |
555 | char **k; | |
556 | ||
557 | assert(s); | |
558 | ||
559 | if (!s->cgroup_path) | |
560 | return 0; | |
561 | ||
562 | cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false); | |
563 | ||
564 | if (session_shall_kill(s)) { | |
565 | ||
566 | r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true); | |
567 | if (r < 0) | |
568 | log_error("Failed to kill session cgroup: %s", strerror(-r)); | |
569 | ||
570 | } else { | |
571 | r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true); | |
572 | if (r < 0) | |
573 | log_error("Failed to check session cgroup: %s", strerror(-r)); | |
574 | else if (r > 0) { | |
575 | r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path); | |
576 | if (r < 0) | |
577 | log_error("Failed to delete session cgroup: %s", strerror(-r)); | |
578 | } else | |
579 | r = -EBUSY; | |
580 | } | |
581 | ||
582 | STRV_FOREACH(k, s->user->manager->controllers) | |
583 | cg_trim(*k, s->cgroup_path, true); | |
584 | ||
1713813d LP |
585 | hashmap_remove(s->manager->cgroups, s->cgroup_path); |
586 | ||
20263082 LP |
587 | free(s->cgroup_path); |
588 | s->cgroup_path = NULL; | |
589 | ||
590 | return r; | |
591 | } | |
592 | ||
593 | static int session_unlink_x11_socket(Session *s) { | |
594 | char *t; | |
595 | int r; | |
596 | ||
597 | assert(s); | |
598 | assert(s->user); | |
599 | ||
600 | if (s->user->display != s) | |
601 | return 0; | |
602 | ||
603 | s->user->display = NULL; | |
604 | ||
605 | t = strappend(s->user->runtime_path, "/display"); | |
606 | if (!t) { | |
607 | log_error("Out of memory"); | |
608 | return -ENOMEM; | |
609 | } | |
610 | ||
611 | r = unlink(t); | |
612 | free(t); | |
613 | ||
614 | return r < 0 ? -errno : 0; | |
615 | } | |
616 | ||
617 | int session_stop(Session *s) { | |
618 | int r = 0, k; | |
619 | ||
620 | assert(s); | |
621 | ||
ed18b08b LP |
622 | if (s->started) |
623 | log_info("Removed session %s.", s->id); | |
98a28fef | 624 | |
20263082 LP |
625 | /* Kill cgroup */ |
626 | k = session_kill_cgroup(s); | |
627 | if (k < 0) | |
628 | r = k; | |
629 | ||
630 | /* Remove X11 symlink */ | |
631 | session_unlink_x11_socket(s); | |
632 | ||
d2f92cdf LP |
633 | unlink(s->state_file); |
634 | session_add_to_gc_queue(s); | |
ed18b08b | 635 | user_add_to_gc_queue(s->user); |
14c3baca | 636 | |
ed18b08b LP |
637 | if (s->started) |
638 | session_send_signal(s, false); | |
9418f147 LP |
639 | |
640 | if (s->seat) { | |
641 | if (s->seat->active == s) | |
642 | seat_set_active(s->seat, NULL); | |
643 | ||
644 | seat_send_changed(s->seat, "Sessions\0"); | |
645 | } | |
646 | ||
647 | user_send_changed(s->user, "Sessions\0"); | |
648 | ||
649 | s->started = false; | |
650 | ||
20263082 LP |
651 | return r; |
652 | } | |
653 | ||
654 | bool session_is_active(Session *s) { | |
655 | assert(s); | |
656 | ||
657 | if (!s->seat) | |
658 | return true; | |
659 | ||
660 | return s->seat->active == s; | |
661 | } | |
662 | ||
a185c5aa LP |
663 | int session_get_idle_hint(Session *s, dual_timestamp *t) { |
664 | char *p; | |
665 | struct stat st; | |
666 | usec_t u, n; | |
667 | bool b; | |
668 | int k; | |
669 | ||
670 | assert(s); | |
671 | ||
672 | if (s->idle_hint) { | |
673 | if (t) | |
674 | *t = s->idle_hint_timestamp; | |
675 | ||
676 | return s->idle_hint; | |
677 | } | |
678 | ||
679 | if (isempty(s->tty)) | |
680 | goto dont_know; | |
681 | ||
682 | if (s->tty[0] != '/') { | |
683 | p = strappend("/dev/", s->tty); | |
684 | if (!p) | |
685 | return -ENOMEM; | |
686 | } else | |
687 | p = NULL; | |
688 | ||
689 | if (!startswith(p ? p : s->tty, "/dev/")) { | |
690 | free(p); | |
691 | goto dont_know; | |
692 | } | |
693 | ||
694 | k = lstat(p ? p : s->tty, &st); | |
695 | free(p); | |
696 | ||
697 | if (k < 0) | |
698 | goto dont_know; | |
699 | ||
700 | u = timespec_load(&st.st_atim); | |
701 | n = now(CLOCK_REALTIME); | |
702 | b = u + IDLE_THRESHOLD_USEC < n; | |
703 | ||
704 | if (t) | |
705 | dual_timestamp_from_realtime(t, u + b ? IDLE_THRESHOLD_USEC : 0); | |
706 | ||
707 | return b; | |
708 | ||
709 | dont_know: | |
710 | if (t) | |
711 | *t = s->idle_hint_timestamp; | |
712 | ||
713 | return 0; | |
714 | } | |
715 | ||
bef422ae LP |
716 | void session_set_idle_hint(Session *s, bool b) { |
717 | assert(s); | |
718 | ||
719 | if (s->idle_hint == b) | |
720 | return; | |
721 | ||
722 | s->idle_hint = b; | |
723 | dual_timestamp_get(&s->idle_hint_timestamp); | |
9418f147 LP |
724 | |
725 | session_send_changed(s, | |
726 | "IdleHint\0" | |
727 | "IdleSinceHint\0" | |
728 | "IdleSinceHintMonotonic\0"); | |
729 | ||
730 | if (s->seat) | |
731 | seat_send_changed(s->seat, | |
732 | "IdleHint\0" | |
733 | "IdleSinceHint\0" | |
734 | "IdleSinceHintMonotonic\0"); | |
735 | ||
736 | user_send_changed(s->user, | |
737 | "IdleHint\0" | |
738 | "IdleSinceHint\0" | |
739 | "IdleSinceHintMonotonic\0"); | |
740 | ||
741 | manager_send_changed(s->manager, | |
742 | "IdleHint\0" | |
743 | "IdleSinceHint\0" | |
744 | "IdleSinceHintMonotonic\0"); | |
bef422ae LP |
745 | } |
746 | ||
31b79c2b LP |
747 | int session_set_pipe_fd(Session *s, int fd) { |
748 | struct epoll_event ev; | |
749 | int r; | |
750 | ||
751 | assert(s); | |
752 | assert(fd >= 0); | |
753 | assert(s->pipe_fd < 0); | |
754 | ||
755 | r = hashmap_put(s->manager->pipe_fds, INT_TO_PTR(fd + 1), s); | |
756 | if (r < 0) | |
757 | return r; | |
758 | ||
759 | zero(ev); | |
760 | ev.events = 0; | |
761 | ev.data.u32 = FD_PIPE_BASE + fd; | |
762 | ||
763 | if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { | |
764 | assert_se(hashmap_remove(s->manager->pipe_fds, INT_TO_PTR(fd + 1)) == s); | |
765 | return -errno; | |
766 | } | |
767 | ||
768 | s->pipe_fd = fd; | |
769 | return 0; | |
770 | } | |
771 | ||
772 | void session_unset_pipe_fd(Session *s) { | |
773 | assert(s); | |
774 | ||
775 | if (s->pipe_fd < 0) | |
776 | return; | |
777 | ||
778 | assert_se(hashmap_remove(s->manager->pipe_fds, INT_TO_PTR(s->pipe_fd + 1)) == s); | |
779 | ||
780 | assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->pipe_fd, NULL) == 0); | |
781 | ||
782 | close_nointr_nofail(s->pipe_fd); | |
783 | s->pipe_fd = -1; | |
784 | } | |
785 | ||
20263082 LP |
786 | int session_check_gc(Session *s) { |
787 | int r; | |
788 | ||
789 | assert(s); | |
790 | ||
791 | if (s->pipe_fd >= 0) { | |
792 | ||
793 | r = pipe_eof(s->pipe_fd); | |
794 | if (r < 0) | |
795 | return r; | |
796 | ||
14c3baca | 797 | if (r == 0) |
20263082 LP |
798 | return 1; |
799 | } | |
800 | ||
801 | if (s->cgroup_path) { | |
802 | ||
803 | r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false); | |
804 | if (r < 0) | |
805 | return r; | |
806 | ||
807 | if (r <= 0) | |
808 | return 1; | |
809 | } | |
810 | ||
811 | return 0; | |
812 | } | |
813 | ||
14c3baca LP |
814 | void session_add_to_gc_queue(Session *s) { |
815 | assert(s); | |
816 | ||
817 | if (s->in_gc_queue) | |
818 | return; | |
819 | ||
820 | LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s); | |
821 | s->in_gc_queue = true; | |
822 | } | |
823 | ||
20263082 | 824 | static const char* const session_type_table[_SESSION_TYPE_MAX] = { |
3f49d45a | 825 | [SESSION_TTY] = "tty", |
98a28fef | 826 | [SESSION_X11] = "x11", |
a91e4e53 | 827 | [SESSION_UNSPECIFIED] = "unspecified" |
20263082 LP |
828 | }; |
829 | ||
830 | DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType); |