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