]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-session.c
hashmap: introduce hash_ops to make struct Hashmap smaller
[thirdparty/systemd.git] / src / login / logind-session.c
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 Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <linux/vt.h>
25 #include <linux/kd.h>
26 #include <signal.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <unistd.h>
30
31 #include "sd-id128.h"
32 #include "sd-messages.h"
33 #include "strv.h"
34 #include "util.h"
35 #include "mkdir.h"
36 #include "path-util.h"
37 #include "fileio.h"
38 #include "audit.h"
39 #include "bus-util.h"
40 #include "bus-error.h"
41 #include "logind-session.h"
42
43 #define RELEASE_USEC (20*USEC_PER_SEC)
44
45 static void session_remove_fifo(Session *s);
46
47 Session* session_new(Manager *m, const char *id) {
48 Session *s;
49
50 assert(m);
51 assert(id);
52 assert(session_id_valid(id));
53
54 s = new0(Session, 1);
55 if (!s)
56 return NULL;
57
58 s->state_file = strappend("/run/systemd/sessions/", id);
59 if (!s->state_file) {
60 free(s);
61 return NULL;
62 }
63
64 s->devices = hashmap_new(&devt_hash_ops);
65 if (!s->devices) {
66 free(s->state_file);
67 free(s);
68 return NULL;
69 }
70
71 s->id = basename(s->state_file);
72
73 if (hashmap_put(m->sessions, s->id, s) < 0) {
74 hashmap_free(s->devices);
75 free(s->state_file);
76 free(s);
77 return NULL;
78 }
79
80 s->manager = m;
81 s->fifo_fd = -1;
82 s->vtfd = -1;
83
84 return s;
85 }
86
87 void session_free(Session *s) {
88 SessionDevice *sd;
89
90 assert(s);
91
92 if (s->in_gc_queue)
93 LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s);
94
95 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
96
97 session_remove_fifo(s);
98
99 session_drop_controller(s);
100
101 while ((sd = hashmap_first(s->devices)))
102 session_device_free(sd);
103
104 hashmap_free(s->devices);
105
106 if (s->user) {
107 LIST_REMOVE(sessions_by_user, s->user->sessions, s);
108
109 if (s->user->display == s)
110 s->user->display = NULL;
111 }
112
113 if (s->seat) {
114 if (s->seat->active == s)
115 s->seat->active = NULL;
116 if (s->seat->pending_switch == s)
117 s->seat->pending_switch = NULL;
118
119 seat_evict_position(s->seat, s);
120 LIST_REMOVE(sessions_by_seat, s->seat->sessions, s);
121 }
122
123 if (s->scope) {
124 hashmap_remove(s->manager->session_units, s->scope);
125 free(s->scope);
126 }
127
128 free(s->scope_job);
129
130 sd_bus_message_unref(s->create_message);
131
132 free(s->tty);
133 free(s->display);
134 free(s->remote_host);
135 free(s->remote_user);
136 free(s->service);
137 free(s->desktop);
138
139 hashmap_remove(s->manager->sessions, s->id);
140
141 free(s->state_file);
142 free(s);
143 }
144
145 void session_set_user(Session *s, User *u) {
146 assert(s);
147 assert(!s->user);
148
149 s->user = u;
150 LIST_PREPEND(sessions_by_user, u->sessions, s);
151 }
152
153 int session_save(Session *s) {
154 _cleanup_free_ char *temp_path = NULL;
155 _cleanup_fclose_ FILE *f = NULL;
156 int r = 0;
157
158 assert(s);
159
160 if (!s->user)
161 return -ESTALE;
162
163 if (!s->started)
164 return 0;
165
166 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
167 if (r < 0)
168 goto finish;
169
170 r = fopen_temporary(s->state_file, &f, &temp_path);
171 if (r < 0)
172 goto finish;
173
174 assert(s->user);
175
176 fchmod(fileno(f), 0644);
177
178 fprintf(f,
179 "# This is private data. Do not parse.\n"
180 "UID="UID_FMT"\n"
181 "USER=%s\n"
182 "ACTIVE=%i\n"
183 "STATE=%s\n"
184 "REMOTE=%i\n",
185 s->user->uid,
186 s->user->name,
187 session_is_active(s),
188 session_state_to_string(session_get_state(s)),
189 s->remote);
190
191 if (s->type >= 0)
192 fprintf(f, "TYPE=%s\n", session_type_to_string(s->type));
193
194 if (s->class >= 0)
195 fprintf(f, "CLASS=%s\n", session_class_to_string(s->class));
196
197 if (s->scope)
198 fprintf(f, "SCOPE=%s\n", s->scope);
199 if (s->scope_job)
200 fprintf(f, "SCOPE_JOB=%s\n", s->scope_job);
201
202 if (s->fifo_path)
203 fprintf(f, "FIFO=%s\n", s->fifo_path);
204
205 if (s->seat)
206 fprintf(f, "SEAT=%s\n", s->seat->id);
207
208 if (s->tty)
209 fprintf(f, "TTY=%s\n", s->tty);
210
211 if (s->display)
212 fprintf(f, "DISPLAY=%s\n", s->display);
213
214 if (s->remote_host) {
215 _cleanup_free_ char *escaped;
216
217 escaped = cescape(s->remote_host);
218 if (!escaped) {
219 r = -ENOMEM;
220 goto finish;
221 }
222
223 fprintf(f, "REMOTE_HOST=%s\n", escaped);
224 }
225
226 if (s->remote_user) {
227 _cleanup_free_ char *escaped;
228
229 escaped = cescape(s->remote_user);
230 if (!escaped) {
231 r = -ENOMEM;
232 goto finish;
233 }
234
235 fprintf(f, "REMOTE_USER=%s\n", escaped);
236 }
237
238 if (s->service) {
239 _cleanup_free_ char *escaped;
240
241 escaped = cescape(s->service);
242 if (!escaped) {
243 r = -ENOMEM;
244 goto finish;
245 }
246
247 fprintf(f, "SERVICE=%s\n", escaped);
248 }
249
250 if (s->desktop) {
251 _cleanup_free_ char *escaped;
252
253
254 escaped = cescape(s->desktop);
255 if (!escaped) {
256 r = -ENOMEM;
257 goto finish;
258 }
259
260 fprintf(f, "DESKTOP=%s\n", escaped);
261 }
262
263 if (s->seat && seat_has_vts(s->seat))
264 fprintf(f, "VTNR=%u\n", s->vtnr);
265
266 if (!s->vtnr)
267 fprintf(f, "POS=%u\n", s->pos);
268
269 if (s->leader > 0)
270 fprintf(f, "LEADER="PID_FMT"\n", s->leader);
271
272 if (s->audit_id > 0)
273 fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id);
274
275 if (dual_timestamp_is_set(&s->timestamp))
276 fprintf(f,
277 "REALTIME="USEC_FMT"\n"
278 "MONOTONIC="USEC_FMT"\n",
279 s->timestamp.realtime,
280 s->timestamp.monotonic);
281
282 if (s->controller)
283 fprintf(f, "CONTROLLER=%s\n", s->controller);
284
285 fflush(f);
286
287 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
288 r = -errno;
289 unlink(s->state_file);
290 unlink(temp_path);
291 }
292
293 finish:
294 if (r < 0)
295 log_error("Failed to save session data %s: %s", s->state_file, strerror(-r));
296
297 return r;
298 }
299
300 int session_load(Session *s) {
301 _cleanup_free_ char *remote = NULL,
302 *seat = NULL,
303 *vtnr = NULL,
304 *pos = NULL,
305 *leader = NULL,
306 *type = NULL,
307 *class = NULL,
308 *uid = NULL,
309 *realtime = NULL,
310 *monotonic = NULL,
311 *controller = NULL;
312
313 int k, r;
314
315 assert(s);
316
317 r = parse_env_file(s->state_file, NEWLINE,
318 "REMOTE", &remote,
319 "SCOPE", &s->scope,
320 "SCOPE_JOB", &s->scope_job,
321 "FIFO", &s->fifo_path,
322 "SEAT", &seat,
323 "TTY", &s->tty,
324 "DISPLAY", &s->display,
325 "REMOTE_HOST", &s->remote_host,
326 "REMOTE_USER", &s->remote_user,
327 "SERVICE", &s->service,
328 "DESKTOP", &s->desktop,
329 "VTNR", &vtnr,
330 "POS", &pos,
331 "LEADER", &leader,
332 "TYPE", &type,
333 "CLASS", &class,
334 "UID", &uid,
335 "REALTIME", &realtime,
336 "MONOTONIC", &monotonic,
337 "CONTROLLER", &controller,
338 NULL);
339
340 if (r < 0) {
341 log_error("Failed to read %s: %s", s->state_file, strerror(-r));
342 return r;
343 }
344
345 if (!s->user) {
346 uid_t u;
347 User *user;
348
349 if (!uid) {
350 log_error("UID not specified for session %s", s->id);
351 return -ENOENT;
352 }
353
354 r = parse_uid(uid, &u);
355 if (r < 0) {
356 log_error("Failed to parse UID value %s for session %s.", uid, s->id);
357 return r;
358 }
359
360 user = hashmap_get(s->manager->users, ULONG_TO_PTR((unsigned long) u));
361 if (!user) {
362 log_error("User of session %s not known.", s->id);
363 return -ENOENT;
364 }
365
366 session_set_user(s, user);
367 }
368
369 if (remote) {
370 k = parse_boolean(remote);
371 if (k >= 0)
372 s->remote = k;
373 }
374
375 if (vtnr)
376 safe_atou(vtnr, &s->vtnr);
377
378 if (seat && !s->seat) {
379 Seat *o;
380
381 o = hashmap_get(s->manager->seats, seat);
382 if (o)
383 r = seat_attach_session(o, s);
384 if (!o || r < 0)
385 log_error("Cannot attach session %s to seat %s", s->id, seat);
386 }
387
388 if (!s->seat || !seat_has_vts(s->seat))
389 s->vtnr = 0;
390
391 if (pos && s->seat) {
392 unsigned int npos;
393
394 safe_atou(pos, &npos);
395 seat_claim_position(s->seat, s, npos);
396 }
397
398 if (leader) {
399 k = parse_pid(leader, &s->leader);
400 if (k >= 0)
401 audit_session_from_pid(s->leader, &s->audit_id);
402 }
403
404 if (type) {
405 SessionType t;
406
407 t = session_type_from_string(type);
408 if (t >= 0)
409 s->type = t;
410 }
411
412 if (class) {
413 SessionClass c;
414
415 c = session_class_from_string(class);
416 if (c >= 0)
417 s->class = c;
418 }
419
420 if (s->fifo_path) {
421 int fd;
422
423 /* If we open an unopened pipe for reading we will not
424 get an EOF. to trigger an EOF we hence open it for
425 reading, but close it right-away which then will
426 trigger the EOF. */
427
428 fd = session_create_fifo(s);
429 safe_close(fd);
430 }
431
432 if (realtime) {
433 unsigned long long l;
434 if (sscanf(realtime, "%llu", &l) > 0)
435 s->timestamp.realtime = l;
436 }
437
438 if (monotonic) {
439 unsigned long long l;
440 if (sscanf(monotonic, "%llu", &l) > 0)
441 s->timestamp.monotonic = l;
442 }
443
444 if (controller) {
445 if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0)
446 session_set_controller(s, controller, false);
447 else
448 session_restore_vt(s);
449 }
450
451 return r;
452 }
453
454 int session_activate(Session *s) {
455 unsigned int num_pending;
456
457 assert(s);
458 assert(s->user);
459
460 if (!s->seat)
461 return -ENOTSUP;
462
463 if (s->seat->active == s)
464 return 0;
465
466 /* on seats with VTs, we let VTs manage session-switching */
467 if (seat_has_vts(s->seat)) {
468 if (!s->vtnr)
469 return -ENOTSUP;
470
471 return chvt(s->vtnr);
472 }
473
474 /* On seats without VTs, we implement session-switching in logind. We
475 * try to pause all session-devices and wait until the session
476 * controller acknowledged them. Once all devices are asleep, we simply
477 * switch the active session and be done.
478 * We save the session we want to switch to in seat->pending_switch and
479 * seat_complete_switch() will perform the final switch. */
480
481 s->seat->pending_switch = s;
482
483 /* if no devices are running, immediately perform the session switch */
484 num_pending = session_device_try_pause_all(s);
485 if (!num_pending)
486 seat_complete_switch(s->seat);
487
488 return 0;
489 }
490
491 static int session_start_scope(Session *s) {
492 int r;
493
494 assert(s);
495 assert(s->user);
496 assert(s->user->slice);
497
498 if (!s->scope) {
499 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
500 _cleanup_free_ char *description = NULL;
501 char *scope, *job = NULL;
502
503 description = strjoin("Session ", s->id, " of user ", s->user->name, NULL);
504 if (!description)
505 return log_oom();
506
507 scope = strjoin("session-", s->id, ".scope", NULL);
508 if (!scope)
509 return log_oom();
510
511 r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "systemd-logind.service", "systemd-user-sessions.service", &error, &job);
512 if (r < 0) {
513 log_error("Failed to start session scope %s: %s %s",
514 scope, bus_error_message(&error, r), error.name);
515 free(scope);
516 return r;
517 } else {
518 s->scope = scope;
519
520 free(s->scope_job);
521 s->scope_job = job;
522 }
523 }
524
525 if (s->scope)
526 hashmap_put(s->manager->session_units, s->scope, s);
527
528 return 0;
529 }
530
531 int session_start(Session *s) {
532 int r;
533
534 assert(s);
535
536 if (!s->user)
537 return -ESTALE;
538
539 if (s->started)
540 return 0;
541
542 r = user_start(s->user);
543 if (r < 0)
544 return r;
545
546 /* Create cgroup */
547 r = session_start_scope(s);
548 if (r < 0)
549 return r;
550
551 log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
552 MESSAGE_ID(SD_MESSAGE_SESSION_START),
553 "SESSION_ID=%s", s->id,
554 "USER_ID=%s", s->user->name,
555 "LEADER="PID_FMT, s->leader,
556 "MESSAGE=New session %s of user %s.", s->id, s->user->name,
557 NULL);
558
559 if (!dual_timestamp_is_set(&s->timestamp))
560 dual_timestamp_get(&s->timestamp);
561
562 if (s->seat)
563 seat_read_active_vt(s->seat);
564
565 s->started = true;
566
567 user_elect_display(s->user);
568
569 /* Save data */
570 session_save(s);
571 user_save(s->user);
572 if (s->seat)
573 seat_save(s->seat);
574
575 /* Send signals */
576 session_send_signal(s, true);
577 user_send_changed(s->user, "Sessions", "Display", NULL);
578 if (s->seat) {
579 if (s->seat->active == s)
580 seat_send_changed(s->seat, "Sessions", "ActiveSession", NULL);
581 else
582 seat_send_changed(s->seat, "Sessions", NULL);
583 }
584
585 return 0;
586 }
587
588 static int session_stop_scope(Session *s, bool force) {
589 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
590 char *job = NULL;
591 int r;
592
593 assert(s);
594
595 if (!s->scope)
596 return 0;
597
598 if (force || manager_shall_kill(s->manager, s->user->name)) {
599 r = manager_stop_unit(s->manager, s->scope, &error, &job);
600 if (r < 0) {
601 log_error("Failed to stop session scope: %s", bus_error_message(&error, r));
602 return r;
603 }
604
605 free(s->scope_job);
606 s->scope_job = job;
607 } else {
608 r = manager_abandon_scope(s->manager, s->scope, &error);
609 if (r < 0) {
610 log_error("Failed to abandon session scope: %s", bus_error_message(&error, r));
611 return r;
612 }
613 }
614
615 return 0;
616 }
617
618 int session_stop(Session *s, bool force) {
619 int r;
620
621 assert(s);
622
623 if (!s->user)
624 return -ESTALE;
625
626 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
627
628 /* We are going down, don't care about FIFOs anymore */
629 session_remove_fifo(s);
630
631 /* Kill cgroup */
632 r = session_stop_scope(s, force);
633
634 s->stopping = true;
635
636 user_elect_display(s->user);
637
638 session_save(s);
639 user_save(s->user);
640
641 return r;
642 }
643
644 int session_finalize(Session *s) {
645 int r = 0;
646 SessionDevice *sd;
647
648 assert(s);
649
650 if (!s->user)
651 return -ESTALE;
652
653 if (s->started)
654 log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
655 MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
656 "SESSION_ID=%s", s->id,
657 "USER_ID=%s", s->user->name,
658 "LEADER="PID_FMT, s->leader,
659 "MESSAGE=Removed session %s.", s->id,
660 NULL);
661
662 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
663
664 /* Kill session devices */
665 while ((sd = hashmap_first(s->devices)))
666 session_device_free(sd);
667
668 unlink(s->state_file);
669 session_add_to_gc_queue(s);
670 user_add_to_gc_queue(s->user);
671
672 if (s->started) {
673 session_send_signal(s, false);
674 s->started = false;
675 }
676
677 if (s->seat) {
678 if (s->seat->active == s)
679 seat_set_active(s->seat, NULL);
680
681 seat_save(s->seat);
682 seat_send_changed(s->seat, "Sessions", NULL);
683 }
684
685 user_save(s->user);
686 user_send_changed(s->user, "Sessions", "Display", NULL);
687
688 return r;
689 }
690
691 static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) {
692 Session *s = userdata;
693
694 assert(es);
695 assert(s);
696
697 session_stop(s, false);
698 return 0;
699 }
700
701 void session_release(Session *s) {
702 assert(s);
703
704 if (!s->started || s->stopping)
705 return;
706
707 if (!s->timer_event_source)
708 sd_event_add_time(s->manager->event,
709 &s->timer_event_source,
710 CLOCK_MONOTONIC,
711 now(CLOCK_MONOTONIC) + RELEASE_USEC, 0,
712 release_timeout_callback, s);
713 }
714
715 bool session_is_active(Session *s) {
716 assert(s);
717
718 if (!s->seat)
719 return true;
720
721 return s->seat->active == s;
722 }
723
724 static int get_tty_atime(const char *tty, usec_t *atime) {
725 _cleanup_free_ char *p = NULL;
726 struct stat st;
727
728 assert(tty);
729 assert(atime);
730
731 if (!path_is_absolute(tty)) {
732 p = strappend("/dev/", tty);
733 if (!p)
734 return -ENOMEM;
735
736 tty = p;
737 } else if (!path_startswith(tty, "/dev/"))
738 return -ENOENT;
739
740 if (lstat(tty, &st) < 0)
741 return -errno;
742
743 *atime = timespec_load(&st.st_atim);
744 return 0;
745 }
746
747 static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
748 _cleanup_free_ char *p = NULL;
749 int r;
750
751 assert(pid > 0);
752 assert(atime);
753
754 r = get_ctty(pid, NULL, &p);
755 if (r < 0)
756 return r;
757
758 return get_tty_atime(p, atime);
759 }
760
761 int session_get_idle_hint(Session *s, dual_timestamp *t) {
762 usec_t atime = 0, n;
763 int r;
764
765 assert(s);
766
767 /* Explicit idle hint is set */
768 if (s->idle_hint) {
769 if (t)
770 *t = s->idle_hint_timestamp;
771
772 return s->idle_hint;
773 }
774
775 /* Graphical sessions should really implement a real
776 * idle hint logic */
777 if (s->display)
778 goto dont_know;
779
780 /* For sessions with an explicitly configured tty, let's check
781 * its atime */
782 if (s->tty) {
783 r = get_tty_atime(s->tty, &atime);
784 if (r >= 0)
785 goto found_atime;
786 }
787
788 /* For sessions with a leader but no explicitly configured
789 * tty, let's check the controlling tty of the leader */
790 if (s->leader > 0) {
791 r = get_process_ctty_atime(s->leader, &atime);
792 if (r >= 0)
793 goto found_atime;
794 }
795
796 dont_know:
797 if (t)
798 *t = s->idle_hint_timestamp;
799
800 return 0;
801
802 found_atime:
803 if (t)
804 dual_timestamp_from_realtime(t, atime);
805
806 n = now(CLOCK_REALTIME);
807
808 if (s->manager->idle_action_usec <= 0)
809 return 0;
810
811 return atime + s->manager->idle_action_usec <= n;
812 }
813
814 void session_set_idle_hint(Session *s, bool b) {
815 assert(s);
816
817 if (s->idle_hint == b)
818 return;
819
820 s->idle_hint = b;
821 dual_timestamp_get(&s->idle_hint_timestamp);
822
823 session_send_changed(s, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
824
825 if (s->seat)
826 seat_send_changed(s->seat, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
827
828 user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
829 manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
830 }
831
832 static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
833 Session *s = userdata;
834
835 assert(s);
836 assert(s->fifo_fd == fd);
837
838 /* EOF on the FIFO means the session died abnormally. */
839
840 session_remove_fifo(s);
841 session_stop(s, false);
842
843 return 1;
844 }
845
846 int session_create_fifo(Session *s) {
847 int r;
848
849 assert(s);
850
851 /* Create FIFO */
852 if (!s->fifo_path) {
853 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
854 if (r < 0)
855 return r;
856
857 if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
858 return -ENOMEM;
859
860 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
861 return -errno;
862 }
863
864 /* Open reading side */
865 if (s->fifo_fd < 0) {
866 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
867 if (s->fifo_fd < 0)
868 return -errno;
869
870 }
871
872 if (!s->fifo_event_source) {
873 r = sd_event_add_io(s->manager->event, &s->fifo_event_source, s->fifo_fd, 0, session_dispatch_fifo, s);
874 if (r < 0)
875 return r;
876
877 r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_IDLE);
878 if (r < 0)
879 return r;
880 }
881
882 /* Open writing side */
883 r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
884 if (r < 0)
885 return -errno;
886
887 return r;
888 }
889
890 static void session_remove_fifo(Session *s) {
891 assert(s);
892
893 s->fifo_event_source = sd_event_source_unref(s->fifo_event_source);
894 s->fifo_fd = safe_close(s->fifo_fd);
895
896 if (s->fifo_path) {
897 unlink(s->fifo_path);
898 free(s->fifo_path);
899 s->fifo_path = NULL;
900 }
901 }
902
903 bool session_check_gc(Session *s, bool drop_not_started) {
904 assert(s);
905
906 if (drop_not_started && !s->started)
907 return false;
908
909 if (!s->user)
910 return false;
911
912 if (s->fifo_fd >= 0) {
913 if (pipe_eof(s->fifo_fd) <= 0)
914 return true;
915 }
916
917 if (s->scope_job && manager_job_is_active(s->manager, s->scope_job))
918 return true;
919
920 if (s->scope && manager_unit_is_active(s->manager, s->scope))
921 return true;
922
923 return false;
924 }
925
926 void session_add_to_gc_queue(Session *s) {
927 assert(s);
928
929 if (s->in_gc_queue)
930 return;
931
932 LIST_PREPEND(gc_queue, s->manager->session_gc_queue, s);
933 s->in_gc_queue = true;
934 }
935
936 SessionState session_get_state(Session *s) {
937 assert(s);
938
939 /* always check closing first */
940 if (s->stopping || s->timer_event_source)
941 return SESSION_CLOSING;
942
943 if (s->scope_job || s->fifo_fd < 0)
944 return SESSION_OPENING;
945
946 if (session_is_active(s))
947 return SESSION_ACTIVE;
948
949 return SESSION_ONLINE;
950 }
951
952 int session_kill(Session *s, KillWho who, int signo) {
953 assert(s);
954
955 if (!s->scope)
956 return -ESRCH;
957
958 return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
959 }
960
961 static int session_open_vt(Session *s) {
962 char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)];
963
964 if (s->vtnr < 1)
965 return -ENODEV;
966
967 if (s->vtfd >= 0)
968 return s->vtfd;
969
970 sprintf(path, "/dev/tty%u", s->vtnr);
971 s->vtfd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
972 if (s->vtfd < 0) {
973 log_error("cannot open VT %s of session %s: %m", path, s->id);
974 return -errno;
975 }
976
977 return s->vtfd;
978 }
979
980 int session_prepare_vt(Session *s) {
981 int vt, r;
982 struct vt_mode mode = { 0 };
983
984 if (s->vtnr < 1)
985 return 0;
986
987 vt = session_open_vt(s);
988 if (vt < 0)
989 return vt;
990
991 r = fchown(vt, s->user->uid, -1);
992 if (r < 0) {
993 r = -errno;
994 log_error("Cannot change owner of /dev/tty%u: %m", s->vtnr);
995 goto error;
996 }
997
998 r = ioctl(vt, KDSKBMODE, K_OFF);
999 if (r < 0) {
1000 r = -errno;
1001 log_error("Cannot set K_OFF on /dev/tty%u: %m", s->vtnr);
1002 goto error;
1003 }
1004
1005 r = ioctl(vt, KDSETMODE, KD_GRAPHICS);
1006 if (r < 0) {
1007 r = -errno;
1008 log_error("Cannot set KD_GRAPHICS on /dev/tty%u: %m", s->vtnr);
1009 goto error;
1010 }
1011
1012 /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS.
1013 * So we need a dummy handler here which just acknowledges *all* VT
1014 * switch requests. */
1015 mode.mode = VT_PROCESS;
1016 mode.relsig = SIGRTMIN;
1017 mode.acqsig = SIGRTMIN + 1;
1018 r = ioctl(vt, VT_SETMODE, &mode);
1019 if (r < 0) {
1020 r = -errno;
1021 log_error("Cannot set VT_PROCESS on /dev/tty%u: %m", s->vtnr);
1022 goto error;
1023 }
1024
1025 return 0;
1026
1027 error:
1028 session_restore_vt(s);
1029 return r;
1030 }
1031
1032 void session_restore_vt(Session *s) {
1033 _cleanup_free_ char *utf8 = NULL;
1034 int vt, kb = K_XLATE;
1035 struct vt_mode mode = { 0 };
1036
1037 vt = session_open_vt(s);
1038 if (vt < 0)
1039 return;
1040
1041 ioctl(vt, KDSETMODE, KD_TEXT);
1042
1043 if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1')
1044 kb = K_UNICODE;
1045
1046 ioctl(vt, KDSKBMODE, kb);
1047
1048 mode.mode = VT_AUTO;
1049 ioctl(vt, VT_SETMODE, &mode);
1050
1051 fchown(vt, 0, -1);
1052
1053 s->vtfd = safe_close(s->vtfd);
1054 }
1055
1056 bool session_is_controller(Session *s, const char *sender) {
1057 assert(s);
1058
1059 return streq_ptr(s->controller, sender);
1060 }
1061
1062 static void session_release_controller(Session *s, bool notify) {
1063 _cleanup_free_ char *name = NULL;
1064 SessionDevice *sd;
1065
1066 if (!s->controller)
1067 return;
1068
1069 name = s->controller;
1070
1071 /* By resetting the controller before releasing the devices, we won't
1072 * send notification signals. This avoids sending useless notifications
1073 * if the controller is released on disconnects. */
1074 if (!notify)
1075 s->controller = NULL;
1076
1077 while ((sd = hashmap_first(s->devices)))
1078 session_device_free(sd);
1079
1080 s->controller = NULL;
1081 manager_drop_busname(s->manager, name);
1082 }
1083
1084 int session_set_controller(Session *s, const char *sender, bool force) {
1085 _cleanup_free_ char *name = NULL;
1086 int r;
1087
1088 assert(s);
1089 assert(sender);
1090
1091 if (session_is_controller(s, sender))
1092 return 0;
1093 if (s->controller && !force)
1094 return -EBUSY;
1095
1096 name = strdup(sender);
1097 if (!name)
1098 return -ENOMEM;
1099
1100 r = manager_watch_busname(s->manager, name);
1101 if (r)
1102 return r;
1103
1104 /* When setting a session controller, we forcibly mute the VT and set
1105 * it into graphics-mode. Applications can override that by changing
1106 * VT state after calling TakeControl(). However, this serves as a good
1107 * default and well-behaving controllers can now ignore VTs entirely.
1108 * Note that we reset the VT on ReleaseControl() and if the controller
1109 * exits.
1110 * If logind crashes/restarts, we restore the controller during restart
1111 * or reset the VT in case it crashed/exited, too. */
1112 r = session_prepare_vt(s);
1113 if (r < 0) {
1114 manager_drop_busname(s->manager, name);
1115 return r;
1116 }
1117
1118 session_release_controller(s, true);
1119 s->controller = name;
1120 name = NULL;
1121 session_save(s);
1122
1123 return 0;
1124 }
1125
1126 void session_drop_controller(Session *s) {
1127 assert(s);
1128
1129 if (!s->controller)
1130 return;
1131
1132 session_release_controller(s, false);
1133 session_save(s);
1134 session_restore_vt(s);
1135 }
1136
1137 static const char* const session_state_table[_SESSION_STATE_MAX] = {
1138 [SESSION_OPENING] = "opening",
1139 [SESSION_ONLINE] = "online",
1140 [SESSION_ACTIVE] = "active",
1141 [SESSION_CLOSING] = "closing"
1142 };
1143
1144 DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState);
1145
1146 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
1147 [SESSION_UNSPECIFIED] = "unspecified",
1148 [SESSION_TTY] = "tty",
1149 [SESSION_X11] = "x11",
1150 [SESSION_WAYLAND] = "wayland",
1151 [SESSION_MIR] = "mir",
1152 [SESSION_WEB] = "web",
1153 };
1154
1155 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
1156
1157 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
1158 [SESSION_USER] = "user",
1159 [SESSION_GREETER] = "greeter",
1160 [SESSION_LOCK_SCREEN] = "lock-screen",
1161 [SESSION_BACKGROUND] = "background"
1162 };
1163
1164 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
1165
1166 static const char* const kill_who_table[_KILL_WHO_MAX] = {
1167 [KILL_LEADER] = "leader",
1168 [KILL_ALL] = "all"
1169 };
1170
1171 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);