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