chiark / gitweb /
350a597d10351caf95e46dc8a0701e8e6d43eaed
[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 "cgroup-util.h"
40 #include "def.h"
41 #include "logind-session.h"
42 #include "formats-util.h"
43 #include "terminal-util.h"
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 static int session_start_cgroup(Session *s) {
483         int r;
484
485         assert(s);
486         assert(s->user);
487         assert(s->leader > 0);
488
489         /* First, create our own group */
490         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, s->id);
491         if (r < 0)
492                 return log_error_errno(r, "Failed to create cgroup %s: %m", s->id);
493
494         r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, s->id, s->leader);
495         if (r < 0)
496                 log_warning_errno(r, "Failed to attach PID %d to cgroup %s: %m", s->leader, s->id);
497
498         return 0;
499 }
500
501 int session_start(Session *s) {
502         int r;
503
504         assert(s);
505
506         if (!s->user)
507                 return -ESTALE;
508
509         if (s->started)
510                 return 0;
511
512         r = user_start(s->user);
513         if (r < 0)
514                 return r;
515
516         r = session_start_cgroup(s);
517         if (r < 0)
518                 return r;
519
520         log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
521                    LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START),
522                    "SESSION_ID=%s", s->id,
523                    "USER_ID=%s", s->user->name,
524                    "LEADER="PID_FMT, s->leader,
525                    LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name),
526                    NULL);
527
528         if (!dual_timestamp_is_set(&s->timestamp))
529                 dual_timestamp_get(&s->timestamp);
530
531         if (s->seat)
532                 seat_read_active_vt(s->seat);
533
534         s->started = true;
535
536         user_elect_display(s->user);
537
538         /* Save data */
539         session_save(s);
540         user_save(s->user);
541         if (s->seat)
542                 seat_save(s->seat);
543
544         /* Send signals */
545         session_send_signal(s, true);
546         user_send_changed(s->user, "Sessions", "Display", NULL);
547         if (s->seat) {
548                 if (s->seat->active == s)
549                         seat_send_changed(s->seat, "Sessions", "ActiveSession", NULL);
550                 else
551                         seat_send_changed(s->seat, "Sessions", NULL);
552         }
553
554         return 0;
555 }
556
557 static int session_stop_cgroup(Session *s, bool force) {
558         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
559         int r;
560
561         assert(s);
562
563         if (force || manager_shall_kill(s->manager, s->user->name)) {
564                 r = session_kill(s, KILL_ALL, SIGTERM);
565                 if (r < 0)
566                         return r;
567         }
568
569         return 0;
570 }
571
572 int session_stop(Session *s, bool force) {
573         int r;
574
575         assert(s);
576
577         if (!s->user)
578                 return -ESTALE;
579
580         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
581
582         if (s->seat)
583                 seat_evict_position(s->seat, s);
584
585         /* We are going down, don't care about FIFOs anymore */
586         session_remove_fifo(s);
587
588         /* Kill cgroup */
589         r = session_stop_cgroup(s, force);
590
591         s->stopping = true;
592
593         user_elect_display(s->user);
594
595         session_save(s);
596         user_save(s->user);
597
598         return r;
599 }
600
601 int session_finalize(Session *s) {
602         int r = 0;
603         SessionDevice *sd;
604
605         assert(s);
606
607         if (!s->user)
608                 return -ESTALE;
609
610         if (s->started)
611                 log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
612                            LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
613                            "SESSION_ID=%s", s->id,
614                            "USER_ID=%s", s->user->name,
615                            "LEADER="PID_FMT, s->leader,
616                            LOG_MESSAGE("Removed session %s.", s->id),
617                            NULL);
618
619         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
620
621         if (s->seat)
622                 seat_evict_position(s->seat, s);
623
624         /* Kill session devices */
625         while ((sd = hashmap_first(s->devices)))
626                 session_device_free(sd);
627
628         unlink(s->state_file);
629         session_add_to_gc_queue(s);
630         user_add_to_gc_queue(s->user);
631
632         if (s->started) {
633                 session_send_signal(s, false);
634                 s->started = false;
635         }
636
637         if (s->seat) {
638                 if (s->seat->active == s)
639                         seat_set_active(s->seat, NULL);
640
641                 seat_save(s->seat);
642                 seat_send_changed(s->seat, "Sessions", NULL);
643         }
644
645         user_save(s->user);
646         user_send_changed(s->user, "Sessions", "Display", NULL);
647
648         return r;
649 }
650
651 int session_release(Session *s) {
652         assert(s);
653
654         if (!s->started || s->stopping)
655                 return 0;
656
657         if (s->timer_event_source)
658                 return 0;
659
660         /* In systemd, session release is triggered by user jobs
661            dying.  In elogind we don't have that so go ahead and stop
662            now.  */
663         return session_stop(s, false);
664 }
665
666 bool session_is_active(Session *s) {
667         assert(s);
668
669         if (!s->seat)
670                 return true;
671
672         return s->seat->active == s;
673 }
674
675 static int get_tty_atime(const char *tty, usec_t *atime) {
676         _cleanup_free_ char *p = NULL;
677         struct stat st;
678
679         assert(tty);
680         assert(atime);
681
682         if (!path_is_absolute(tty)) {
683                 p = strappend("/dev/", tty);
684                 if (!p)
685                         return -ENOMEM;
686
687                 tty = p;
688         } else if (!path_startswith(tty, "/dev/"))
689                 return -ENOENT;
690
691         if (lstat(tty, &st) < 0)
692                 return -errno;
693
694         *atime = timespec_load(&st.st_atim);
695         return 0;
696 }
697
698 static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
699         _cleanup_free_ char *p = NULL;
700         int r;
701
702         assert(pid > 0);
703         assert(atime);
704
705         r = get_ctty(pid, NULL, &p);
706         if (r < 0)
707                 return r;
708
709         return get_tty_atime(p, atime);
710 }
711
712 int session_get_idle_hint(Session *s, dual_timestamp *t) {
713         usec_t atime = 0, n;
714         int r;
715
716         assert(s);
717
718         /* Explicit idle hint is set */
719         if (s->idle_hint) {
720                 if (t)
721                         *t = s->idle_hint_timestamp;
722
723                 return s->idle_hint;
724         }
725
726         /* Graphical sessions should really implement a real
727          * idle hint logic */
728         if (s->display)
729                 goto dont_know;
730
731         /* For sessions with an explicitly configured tty, let's check
732          * its atime */
733         if (s->tty) {
734                 r = get_tty_atime(s->tty, &atime);
735                 if (r >= 0)
736                         goto found_atime;
737         }
738
739         /* For sessions with a leader but no explicitly configured
740          * tty, let's check the controlling tty of the leader */
741         if (s->leader > 0) {
742                 r = get_process_ctty_atime(s->leader, &atime);
743                 if (r >= 0)
744                         goto found_atime;
745         }
746
747 dont_know:
748         if (t)
749                 *t = s->idle_hint_timestamp;
750
751         return 0;
752
753 found_atime:
754         if (t)
755                 dual_timestamp_from_realtime(t, atime);
756
757         n = now(CLOCK_REALTIME);
758
759         if (s->manager->idle_action_usec <= 0)
760                 return 0;
761
762         return atime + s->manager->idle_action_usec <= n;
763 }
764
765 void session_set_idle_hint(Session *s, bool b) {
766         assert(s);
767
768         if (s->idle_hint == b)
769                 return;
770
771         s->idle_hint = b;
772         dual_timestamp_get(&s->idle_hint_timestamp);
773
774         session_send_changed(s, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
775
776         if (s->seat)
777                 seat_send_changed(s->seat, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
778
779         user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
780         manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
781 }
782
783 static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
784         Session *s = userdata;
785
786         assert(s);
787         assert(s->fifo_fd == fd);
788
789         /* EOF on the FIFO means the session died abnormally. */
790
791         session_remove_fifo(s);
792         session_stop(s, false);
793
794         return 1;
795 }
796
797 int session_create_fifo(Session *s) {
798         int r;
799
800         assert(s);
801
802         /* Create FIFO */
803         if (!s->fifo_path) {
804                 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
805                 if (r < 0)
806                         return r;
807
808                 if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
809                         return -ENOMEM;
810
811                 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
812                         return -errno;
813         }
814
815         /* Open reading side */
816         if (s->fifo_fd < 0) {
817                 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
818                 if (s->fifo_fd < 0)
819                         return -errno;
820
821         }
822
823         if (!s->fifo_event_source) {
824                 r = sd_event_add_io(s->manager->event, &s->fifo_event_source, s->fifo_fd, 0, session_dispatch_fifo, s);
825                 if (r < 0)
826                         return r;
827
828                 r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_IDLE);
829                 if (r < 0)
830                         return r;
831         }
832
833         /* Open writing side */
834         r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
835         if (r < 0)
836                 return -errno;
837
838         return r;
839 }
840
841 static void session_remove_fifo(Session *s) {
842         assert(s);
843
844         s->fifo_event_source = sd_event_source_unref(s->fifo_event_source);
845         s->fifo_fd = safe_close(s->fifo_fd);
846
847         if (s->fifo_path) {
848                 unlink(s->fifo_path);
849                 free(s->fifo_path);
850                 s->fifo_path = NULL;
851         }
852 }
853
854 bool session_check_gc(Session *s, bool drop_not_started) {
855         assert(s);
856
857         if (drop_not_started && !s->started)
858                 return false;
859
860         if (!s->user)
861                 return false;
862
863         if (s->fifo_fd >= 0) {
864                 if (pipe_eof(s->fifo_fd) <= 0)
865                         return true;
866         }
867
868         if (cg_is_empty_recursive (SYSTEMD_CGROUP_CONTROLLER, s->id, false) > 0)
869                 return true;
870
871         return false;
872 }
873
874 void session_add_to_gc_queue(Session *s) {
875         assert(s);
876
877         if (s->in_gc_queue)
878                 return;
879
880         LIST_PREPEND(gc_queue, s->manager->session_gc_queue, s);
881         s->in_gc_queue = true;
882 }
883
884 SessionState session_get_state(Session *s) {
885         assert(s);
886
887         /* always check closing first */
888         if (s->stopping || s->timer_event_source)
889                 return SESSION_CLOSING;
890
891         if (s->fifo_fd < 0)
892                 return SESSION_OPENING;
893
894         if (session_is_active(s))
895                 return SESSION_ACTIVE;
896
897         return SESSION_ONLINE;
898 }
899
900 int session_kill(Session *s, KillWho who, int signo) {
901         assert(s);
902
903         if (who == KILL_LEADER) {
904                 if (s->leader <= 0)
905                         return -ESRCH;
906
907                 /* FIXME: verify that leader is in cgroup?  */
908
909                 if (kill(s->leader, signo) < 0) {
910                         return log_error_errno(errno, "Failed to kill process leader %d for session %s: %m", s->leader, s->id);
911                 }
912                 return 0;
913         } else {
914                 bool sigcont = false;
915                 bool ignore_self = true;
916                 bool rem = true;
917                 return cg_kill_recursive (SYSTEMD_CGROUP_CONTROLLER, s->id, signo,
918                                           sigcont, ignore_self, rem, NULL);
919         }
920 }
921
922 static int session_open_vt(Session *s) {
923         char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)];
924
925         if (s->vtnr < 1)
926                 return -ENODEV;
927
928         if (s->vtfd >= 0)
929                 return s->vtfd;
930
931         sprintf(path, "/dev/tty%u", s->vtnr);
932         s->vtfd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
933         if (s->vtfd < 0)
934                 return log_error_errno(errno, "cannot open VT %s of session %s: %m", path, s->id);
935
936         return s->vtfd;
937 }
938
939 int session_prepare_vt(Session *s) {
940         int vt, r;
941         struct vt_mode mode = { 0 };
942
943         if (s->vtnr < 1)
944                 return 0;
945
946         vt = session_open_vt(s);
947         if (vt < 0)
948                 return vt;
949
950         r = fchown(vt, s->user->uid, -1);
951         if (r < 0) {
952                 r = -errno;
953                 log_error_errno(errno, "Cannot change owner of /dev/tty%u: %m", s->vtnr);
954                 goto error;
955         }
956
957         r = ioctl(vt, KDSKBMODE, K_OFF);
958         if (r < 0) {
959                 r = -errno;
960                 log_error_errno(errno, "Cannot set K_OFF on /dev/tty%u: %m", s->vtnr);
961                 goto error;
962         }
963
964         r = ioctl(vt, KDSETMODE, KD_GRAPHICS);
965         if (r < 0) {
966                 r = -errno;
967                 log_error_errno(errno, "Cannot set KD_GRAPHICS on /dev/tty%u: %m", s->vtnr);
968                 goto error;
969         }
970
971         /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS.
972          * So we need a dummy handler here which just acknowledges *all* VT
973          * switch requests. */
974         mode.mode = VT_PROCESS;
975         mode.relsig = SIGRTMIN;
976         mode.acqsig = SIGRTMIN + 1;
977         r = ioctl(vt, VT_SETMODE, &mode);
978         if (r < 0) {
979                 r = -errno;
980                 log_error_errno(errno, "Cannot set VT_PROCESS on /dev/tty%u: %m", s->vtnr);
981                 goto error;
982         }
983
984         return 0;
985
986 error:
987         session_restore_vt(s);
988         return r;
989 }
990
991 void session_restore_vt(Session *s) {
992         _cleanup_free_ char *utf8 = NULL;
993         int vt, kb = K_XLATE;
994         struct vt_mode mode = { 0 };
995
996         vt = session_open_vt(s);
997         if (vt < 0)
998                 return;
999
1000         (void) ioctl(vt, KDSETMODE, KD_TEXT);
1001
1002         if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1')
1003                 kb = K_UNICODE;
1004
1005         (void) ioctl(vt, KDSKBMODE, kb);
1006
1007         mode.mode = VT_AUTO;
1008         (void) ioctl(vt, VT_SETMODE, &mode);
1009
1010         fchown(vt, 0, -1);
1011
1012         s->vtfd = safe_close(s->vtfd);
1013 }
1014
1015 void session_leave_vt(Session *s) {
1016         int r;
1017
1018         assert(s);
1019
1020         /* This is called whenever we get a VT-switch signal from the kernel.
1021          * We acknowledge all of them unconditionally. Note that session are
1022          * free to overwrite those handlers and we only register them for
1023          * sessions with controllers. Legacy sessions are not affected.
1024          * However, if we switch from a non-legacy to a legacy session, we must
1025          * make sure to pause all device before acknowledging the switch. We
1026          * process the real switch only after we are notified via sysfs, so the
1027          * legacy session might have already started using the devices. If we
1028          * don't pause the devices before the switch, we might confuse the
1029          * session we switch to. */
1030
1031         if (s->vtfd < 0)
1032                 return;
1033
1034         session_device_pause_all(s);
1035         r = ioctl(s->vtfd, VT_RELDISP, 1);
1036         if (r < 0)
1037                 log_debug_errno(errno, "Cannot release VT of session %s: %m", s->id);
1038 }
1039
1040 bool session_is_controller(Session *s, const char *sender) {
1041         assert(s);
1042
1043         return streq_ptr(s->controller, sender);
1044 }
1045
1046 static void session_release_controller(Session *s, bool notify) {
1047         _cleanup_free_ char *name = NULL;
1048         SessionDevice *sd;
1049
1050         if (!s->controller)
1051                 return;
1052
1053         name = s->controller;
1054
1055         /* By resetting the controller before releasing the devices, we won't
1056          * send notification signals. This avoids sending useless notifications
1057          * if the controller is released on disconnects. */
1058         if (!notify)
1059                 s->controller = NULL;
1060
1061         while ((sd = hashmap_first(s->devices)))
1062                 session_device_free(sd);
1063
1064         s->controller = NULL;
1065         manager_drop_busname(s->manager, name);
1066 }
1067
1068 int session_set_controller(Session *s, const char *sender, bool force) {
1069         _cleanup_free_ char *name = NULL;
1070         int r;
1071
1072         assert(s);
1073         assert(sender);
1074
1075         if (session_is_controller(s, sender))
1076                 return 0;
1077         if (s->controller && !force)
1078                 return -EBUSY;
1079
1080         name = strdup(sender);
1081         if (!name)
1082                 return -ENOMEM;
1083
1084         r = manager_watch_busname(s->manager, name);
1085         if (r)
1086                 return r;
1087
1088         /* When setting a session controller, we forcibly mute the VT and set
1089          * it into graphics-mode. Applications can override that by changing
1090          * VT state after calling TakeControl(). However, this serves as a good
1091          * default and well-behaving controllers can now ignore VTs entirely.
1092          * Note that we reset the VT on ReleaseControl() and if the controller
1093          * exits.
1094          * If logind crashes/restarts, we restore the controller during restart
1095          * or reset the VT in case it crashed/exited, too. */
1096         r = session_prepare_vt(s);
1097         if (r < 0) {
1098                 manager_drop_busname(s->manager, name);
1099                 return r;
1100         }
1101
1102         session_release_controller(s, true);
1103         s->controller = name;
1104         name = NULL;
1105         session_save(s);
1106
1107         return 0;
1108 }
1109
1110 void session_drop_controller(Session *s) {
1111         assert(s);
1112
1113         if (!s->controller)
1114                 return;
1115
1116         session_release_controller(s, false);
1117         session_save(s);
1118         session_restore_vt(s);
1119 }
1120
1121 static const char* const session_state_table[_SESSION_STATE_MAX] = {
1122         [SESSION_OPENING] = "opening",
1123         [SESSION_ONLINE] = "online",
1124         [SESSION_ACTIVE] = "active",
1125         [SESSION_CLOSING] = "closing"
1126 };
1127
1128 DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState);
1129
1130 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
1131         [SESSION_UNSPECIFIED] = "unspecified",
1132         [SESSION_TTY] = "tty",
1133         [SESSION_X11] = "x11",
1134         [SESSION_WAYLAND] = "wayland",
1135         [SESSION_MIR] = "mir",
1136         [SESSION_WEB] = "web",
1137 };
1138
1139 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
1140
1141 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
1142         [SESSION_USER] = "user",
1143         [SESSION_GREETER] = "greeter",
1144         [SESSION_LOCK_SCREEN] = "lock-screen",
1145         [SESSION_BACKGROUND] = "background"
1146 };
1147
1148 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
1149
1150 static const char* const kill_who_table[_KILL_WHO_MAX] = {
1151         [KILL_LEADER] = "leader",
1152         [KILL_ALL] = "all"
1153 };
1154
1155 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);