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