chiark / gitweb /
switch from udev keymaps to hwdb
[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, "systemd-user-sessions.service", &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                         return r;
484                 } else {
485                         s->scope = scope;
486
487                         free(s->scope_job);
488                         s->scope_job = job;
489                 }
490         }
491
492         if (s->scope)
493                 hashmap_put(s->manager->session_units, s->scope, s);
494
495         return 0;
496 }
497
498 int session_start(Session *s) {
499         int r;
500
501         assert(s);
502
503         if (!s->user)
504                 return -ESTALE;
505
506         if (s->started)
507                 return 0;
508
509         r = user_start(s->user);
510         if (r < 0)
511                 return r;
512
513         /* Create cgroup */
514         r = session_start_scope(s);
515         if (r < 0)
516                 return r;
517
518         log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
519                    MESSAGE_ID(SD_MESSAGE_SESSION_START),
520                    "SESSION_ID=%s", s->id,
521                    "USER_ID=%s", s->user->name,
522                    "LEADER=%lu", (unsigned long) s->leader,
523                    "MESSAGE=New session %s of user %s.", s->id, s->user->name,
524                    NULL);
525
526         /* Create X11 symlink */
527         session_link_x11_socket(s);
528
529         if (!dual_timestamp_is_set(&s->timestamp))
530                 dual_timestamp_get(&s->timestamp);
531
532         if (s->seat)
533                 seat_read_active_vt(s->seat);
534
535         s->started = true;
536
537         /* Save session data */
538         session_save(s);
539         user_save(s->user);
540
541         session_send_signal(s, true);
542
543         if (s->seat) {
544                 seat_save(s->seat);
545
546                 if (s->seat->active == s)
547                         seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
548                 else
549                         seat_send_changed(s->seat, "Sessions\0");
550         }
551
552         user_send_changed(s->user, "Sessions\0");
553
554         return 0;
555 }
556
557 /* static bool session_shall_kill(Session *s) { */
558 /*         assert(s); */
559
560 /*         if (!s->kill_processes) */
561 /*                 return false; */
562
563 /*         if (strv_contains(s->manager->kill_exclude_users, s->user->name)) */
564 /*                 return false; */
565
566 /*         if (strv_isempty(s->manager->kill_only_users)) */
567 /*                 return true; */
568
569 /*         return strv_contains(s->manager->kill_only_users, s->user->name); */
570 /* } */
571
572 static int session_stop_scope(Session *s) {
573         DBusError error;
574         char *job;
575         int r;
576
577         assert(s);
578
579         dbus_error_init(&error);
580
581         if (!s->scope)
582                 return 0;
583
584         r = manager_stop_unit(s->manager, s->scope, &error, &job);
585         if (r < 0) {
586                 log_error("Failed to stop session scope: %s", bus_error(&error, r));
587                 dbus_error_free(&error);
588                 return r;
589         }
590
591         free(s->scope_job);
592         s->scope_job = job;
593
594         return 0;
595 }
596
597 static int session_unlink_x11_socket(Session *s) {
598         char *t;
599         int r;
600
601         assert(s);
602         assert(s->user);
603
604         if (s->user->display != s)
605                 return 0;
606
607         s->user->display = NULL;
608
609         t = strappend(s->user->runtime_path, "/X11-display");
610         if (!t)
611                 return log_oom();
612
613         r = unlink(t);
614         free(t);
615
616         return r < 0 ? -errno : 0;
617 }
618
619 int session_stop(Session *s) {
620         int r = 0, k;
621
622         assert(s);
623
624         if (!s->user)
625                 return -ESTALE;
626
627         if (s->started)
628                 log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
629                            MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
630                            "SESSION_ID=%s", s->id,
631                            "USER_ID=%s", s->user->name,
632                            "LEADER=%lu", (unsigned long) s->leader,
633                            "MESSAGE=Removed session %s.", s->id,
634                            NULL);
635
636         /* Kill cgroup */
637         k = session_stop_scope(s);
638         if (k < 0)
639                 r = k;
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
651         s->started = false;
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
875                 r = pipe_eof(s->fifo_fd);
876                 if (r < 0)
877                         return r;
878
879                 if (r == 0)
880                         return 1;
881         }
882
883         if (s->scope_job)
884                 return 1;
885
886         if (s->scope)
887                 return manager_unit_is_active(s->manager, s->scope) != 0;
888
889         return 0;
890 }
891
892 void session_add_to_gc_queue(Session *s) {
893         assert(s);
894
895         if (s->in_gc_queue)
896                 return;
897
898         LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s);
899         s->in_gc_queue = true;
900 }
901
902 SessionState session_get_state(Session *s) {
903         assert(s);
904
905         if (s->scope_job)
906                 return s->started ? SESSION_OPENING : SESSION_CLOSING;
907
908         if (s->fifo_fd < 0)
909                 return SESSION_CLOSING;
910
911         if (session_is_active(s))
912                 return SESSION_ACTIVE;
913
914         return SESSION_ONLINE;
915 }
916
917 int session_kill(Session *s, KillWho who, int signo) {
918         assert(s);
919
920         if (!s->scope)
921                 return -ESRCH;
922
923         return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
924 }
925
926 static const char* const session_state_table[_SESSION_STATE_MAX] = {
927         [SESSION_OPENING] = "opening",
928         [SESSION_ONLINE] = "online",
929         [SESSION_ACTIVE] = "active",
930         [SESSION_CLOSING] = "closing"
931 };
932
933 DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState);
934
935 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
936         [SESSION_TTY] = "tty",
937         [SESSION_X11] = "x11",
938         [SESSION_UNSPECIFIED] = "unspecified"
939 };
940
941 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
942
943 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
944         [SESSION_USER] = "user",
945         [SESSION_GREETER] = "greeter",
946         [SESSION_LOCK_SCREEN] = "lock-screen",
947         [SESSION_BACKGROUND] = "background"
948 };
949
950 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
951
952 static const char* const kill_who_table[_KILL_WHO_MAX] = {
953         [KILL_LEADER] = "leader",
954         [KILL_ALL] = "all"
955 };
956
957 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);