chiark / gitweb /
2892c38417f690fae404c5844076677ac8ce0fc2
[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         DBusError error;
459         int r;
460
461         assert(s);
462         assert(s->user);
463         assert(s->user->slice);
464
465         dbus_error_init(&error);
466
467         if (!s->scope) {
468                 _cleanup_free_ char *description = NULL;
469                 char *scope, *job;
470
471                 scope = strjoin("session-", s->id, ".scope", NULL);
472                 if (!scope)
473                         return log_oom();
474
475                 description = strjoin("Session ", s->id, " of user ", s->user->name, NULL);
476
477                 r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, &error, &job);
478                 if (r < 0) {
479                         log_error("Failed to start session scope: %s %s", bus_error(&error, r), error.name);
480                         dbus_error_free(&error);
481
482                         free(scope);
483                 } else {
484                         s->scope = scope;
485
486                         free(s->scope_job);
487                         s->scope_job = job;
488                 }
489         }
490
491         if (s->scope)
492                 hashmap_put(s->manager->session_units, s->scope, s);
493
494         return 0;
495 }
496
497 int session_start(Session *s) {
498         int r;
499
500         assert(s);
501
502         if (!s->user)
503                 return -ESTALE;
504
505         if (s->started)
506                 return 0;
507
508         r = user_start(s->user);
509         if (r < 0)
510                 return r;
511
512         /* Create cgroup */
513         r = session_start_scope(s);
514         if (r < 0)
515                 return r;
516
517         log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
518                    MESSAGE_ID(SD_MESSAGE_SESSION_START),
519                    "SESSION_ID=%s", s->id,
520                    "USER_ID=%s", s->user->name,
521                    "LEADER=%lu", (unsigned long) s->leader,
522                    "MESSAGE=New session %s of user %s.", s->id, s->user->name,
523                    NULL);
524
525         /* Create X11 symlink */
526         session_link_x11_socket(s);
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         /* Save session data */
537         session_save(s);
538         user_save(s->user);
539
540         session_send_signal(s, true);
541
542         if (s->seat) {
543                 seat_save(s->seat);
544
545                 if (s->seat->active == s)
546                         seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
547                 else
548                         seat_send_changed(s->seat, "Sessions\0");
549         }
550
551         user_send_changed(s->user, "Sessions\0");
552
553         return 0;
554 }
555
556 /* static bool session_shall_kill(Session *s) { */
557 /*         assert(s); */
558
559 /*         if (!s->kill_processes) */
560 /*                 return false; */
561
562 /*         if (strv_contains(s->manager->kill_exclude_users, s->user->name)) */
563 /*                 return false; */
564
565 /*         if (strv_isempty(s->manager->kill_only_users)) */
566 /*                 return true; */
567
568 /*         return strv_contains(s->manager->kill_only_users, s->user->name); */
569 /* } */
570
571 static int session_stop_scope(Session *s) {
572         DBusError error;
573         char *job;
574         int r;
575
576         assert(s);
577
578         dbus_error_init(&error);
579
580         if (!s->scope)
581                 return 0;
582
583         r = manager_stop_unit(s->manager, s->scope, &error, &job);
584         if (r < 0) {
585                 log_error("Failed to stop session scope: %s", bus_error(&error, r));
586                 dbus_error_free(&error);
587                 return r;
588         }
589
590         free(s->scope_job);
591         s->scope_job = job;
592
593         return 0;
594 }
595
596 static int session_unlink_x11_socket(Session *s) {
597         char *t;
598         int r;
599
600         assert(s);
601         assert(s->user);
602
603         if (s->user->display != s)
604                 return 0;
605
606         s->user->display = NULL;
607
608         t = strappend(s->user->runtime_path, "/X11-display");
609         if (!t)
610                 return log_oom();
611
612         r = unlink(t);
613         free(t);
614
615         return r < 0 ? -errno : 0;
616 }
617
618 int session_stop(Session *s) {
619         int r = 0, k;
620
621         assert(s);
622
623         if (!s->user)
624                 return -ESTALE;
625
626         if (s->started)
627                 log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
628                            MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
629                            "SESSION_ID=%s", s->id,
630                            "USER_ID=%s", s->user->name,
631                            "LEADER=%lu", (unsigned long) s->leader,
632                            "MESSAGE=Removed session %s.", s->id,
633                            NULL);
634
635         /* Kill cgroup */
636         k = session_stop_scope(s);
637         if (k < 0)
638                 r = k;
639
640         /* Remove X11 symlink */
641         session_unlink_x11_socket(s);
642
643         unlink(s->state_file);
644         session_add_to_gc_queue(s);
645         user_add_to_gc_queue(s->user);
646
647         if (s->started)
648                 session_send_signal(s, false);
649
650         s->started = false;
651
652         if (s->seat) {
653                 if (s->seat->active == s)
654                         seat_set_active(s->seat, NULL);
655
656                 seat_send_changed(s->seat, "Sessions\0");
657                 seat_save(s->seat);
658         }
659
660         user_send_changed(s->user, "Sessions\0");
661         user_save(s->user);
662
663         return r;
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,
775                              "IdleHint\0"
776                              "IdleSinceHint\0"
777                              "IdleSinceHintMonotonic\0");
778
779         if (s->seat)
780                 seat_send_changed(s->seat,
781                                   "IdleHint\0"
782                                   "IdleSinceHint\0"
783                                   "IdleSinceHintMonotonic\0");
784
785         user_send_changed(s->user,
786                           "IdleHint\0"
787                           "IdleSinceHint\0"
788                           "IdleSinceHintMonotonic\0");
789
790         manager_send_changed(s->manager,
791                              "IdleHint\0"
792                              "IdleSinceHint\0"
793                              "IdleSinceHintMonotonic\0");
794 }
795
796 int session_create_fifo(Session *s) {
797         int r;
798
799         assert(s);
800
801         /* Create FIFO */
802         if (!s->fifo_path) {
803                 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
804                 if (r < 0)
805                         return r;
806
807                 if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
808                         return -ENOMEM;
809
810                 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
811                         return -errno;
812         }
813
814         /* Open reading side */
815         if (s->fifo_fd < 0) {
816                 struct epoll_event ev = {};
817
818                 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
819                 if (s->fifo_fd < 0)
820                         return -errno;
821
822                 r = hashmap_put(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1), s);
823                 if (r < 0)
824                         return r;
825
826                 ev.events = 0;
827                 ev.data.u32 = FD_OTHER_BASE + s->fifo_fd;
828
829                 if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, s->fifo_fd, &ev) < 0)
830                         return -errno;
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 void session_remove_fifo(Session *s) {
842         assert(s);
843
844         if (s->fifo_fd >= 0) {
845                 assert_se(hashmap_remove(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1)) == s);
846                 assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->fifo_fd, NULL) == 0);
847                 close_nointr_nofail(s->fifo_fd);
848                 s->fifo_fd = -1;
849
850                 session_save(s);
851                 user_save(s->user);
852         }
853
854         if (s->fifo_path) {
855                 unlink(s->fifo_path);
856                 free(s->fifo_path);
857                 s->fifo_path = NULL;
858         }
859 }
860
861 int session_check_gc(Session *s, bool drop_not_started) {
862         int r;
863
864         assert(s);
865
866         if (drop_not_started && !s->started)
867                 return 0;
868
869         if (!s->user)
870                 return 0;
871
872         if (s->fifo_fd >= 0) {
873
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->scope_job)
905                 return s->started ? SESSION_OPENING : SESSION_CLOSING;
906
907         if (s->fifo_fd < 0)
908                 return SESSION_CLOSING;
909
910         if (session_is_active(s))
911                 return SESSION_ACTIVE;
912
913         return SESSION_ONLINE;
914 }
915
916 int session_kill(Session *s, KillWho who, int signo) {
917         assert(s);
918
919         if (!s->scope)
920                 return -ESRCH;
921
922         return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
923 }
924
925 static const char* const session_state_table[_SESSION_STATE_MAX] = {
926         [SESSION_OPENING] = "opening",
927         [SESSION_ONLINE] = "online",
928         [SESSION_ACTIVE] = "active",
929         [SESSION_CLOSING] = "closing"
930 };
931
932 DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState);
933
934 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
935         [SESSION_TTY] = "tty",
936         [SESSION_X11] = "x11",
937         [SESSION_UNSPECIFIED] = "unspecified"
938 };
939
940 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
941
942 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
943         [SESSION_USER] = "user",
944         [SESSION_GREETER] = "greeter",
945         [SESSION_LOCK_SCREEN] = "lock-screen",
946         [SESSION_BACKGROUND] = "background"
947 };
948
949 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
950
951 static const char* const kill_who_table[_KILL_WHO_MAX] = {
952         [KILL_LEADER] = "leader",
953         [KILL_ALL] = "all"
954 };
955
956 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);