chiark / gitweb /
logind: be more verbose on errors
[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 %s",
484                                   scope, bus_error(&error, r), error.name);
485                         dbus_error_free(&error);
486
487                         free(scope);
488                         return r;
489                 } else {
490                         s->scope = scope;
491
492                         free(s->scope_job);
493                         s->scope_job = job;
494                 }
495         }
496
497         if (s->scope)
498                 hashmap_put(s->manager->session_units, s->scope, s);
499
500         return 0;
501 }
502
503 int session_start(Session *s) {
504         int r;
505
506         assert(s);
507
508         if (!s->user)
509                 return -ESTALE;
510
511         if (s->started)
512                 return 0;
513
514         r = user_start(s->user);
515         if (r < 0)
516                 return r;
517
518         /* Create cgroup */
519         r = session_start_scope(s);
520         if (r < 0)
521                 return r;
522
523         log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
524                    MESSAGE_ID(SD_MESSAGE_SESSION_START),
525                    "SESSION_ID=%s", s->id,
526                    "USER_ID=%s", s->user->name,
527                    "LEADER=%lu", (unsigned long) s->leader,
528                    "MESSAGE=New session %s of user %s.", s->id, s->user->name,
529                    NULL);
530
531         /* Create X11 symlink */
532         session_link_x11_socket(s);
533
534         if (!dual_timestamp_is_set(&s->timestamp))
535                 dual_timestamp_get(&s->timestamp);
536
537         if (s->seat)
538                 seat_read_active_vt(s->seat);
539
540         s->started = true;
541
542         /* Save session data */
543         session_save(s);
544         user_save(s->user);
545
546         session_send_signal(s, true);
547
548         if (s->seat) {
549                 seat_save(s->seat);
550
551                 if (s->seat->active == s)
552                         seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
553                 else
554                         seat_send_changed(s->seat, "Sessions\0");
555         }
556
557         user_send_changed(s->user, "Sessions\0");
558
559         return 0;
560 }
561
562 static int session_stop_scope(Session *s) {
563         DBusError error;
564         char *job;
565         int r;
566
567         assert(s);
568
569         dbus_error_init(&error);
570
571         if (!s->scope)
572                 return 0;
573
574         r = manager_stop_unit(s->manager, s->scope, &error, &job);
575         if (r < 0) {
576                 log_error("Failed to stop session scope: %s", bus_error(&error, r));
577                 dbus_error_free(&error);
578                 return r;
579         }
580
581         free(s->scope_job);
582         s->scope_job = job;
583
584         return 0;
585 }
586
587 static int session_unlink_x11_socket(Session *s) {
588         char *t;
589         int r;
590
591         assert(s);
592         assert(s->user);
593
594         if (s->user->display != s)
595                 return 0;
596
597         s->user->display = NULL;
598
599         t = strappend(s->user->runtime_path, "/X11-display");
600         if (!t)
601                 return log_oom();
602
603         r = unlink(t);
604         free(t);
605
606         return r < 0 ? -errno : 0;
607 }
608
609 int session_stop(Session *s) {
610         int r;
611
612         assert(s);
613
614         if (!s->user)
615                 return -ESTALE;
616
617         /* Kill cgroup */
618         r = session_stop_scope(s);
619
620         session_save(s);
621
622         return r;
623 }
624
625 int session_finalize(Session *s) {
626         int r = 0;
627
628         assert(s);
629
630         if (!s->user)
631                 return -ESTALE;
632
633         if (s->started)
634                 log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
635                            MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
636                            "SESSION_ID=%s", s->id,
637                            "USER_ID=%s", s->user->name,
638                            "LEADER=%lu", (unsigned long) s->leader,
639                            "MESSAGE=Removed session %s.", s->id,
640                            NULL);
641
642         /* Remove X11 symlink */
643         session_unlink_x11_socket(s);
644
645         unlink(s->state_file);
646         session_add_to_gc_queue(s);
647         user_add_to_gc_queue(s->user);
648
649         if (s->started) {
650                 session_send_signal(s, false);
651                 s->started = false;
652         }
653
654         if (s->seat) {
655                 if (s->seat->active == s)
656                         seat_set_active(s->seat, NULL);
657
658                 seat_send_changed(s->seat, "Sessions\0");
659                 seat_save(s->seat);
660         }
661
662         user_send_changed(s->user, "Sessions\0");
663         user_save(s->user);
664
665         return r;
666 }
667
668 bool session_is_active(Session *s) {
669         assert(s);
670
671         if (!s->seat)
672                 return true;
673
674         return s->seat->active == s;
675 }
676
677 static int get_tty_atime(const char *tty, usec_t *atime) {
678         _cleanup_free_ char *p = NULL;
679         struct stat st;
680
681         assert(tty);
682         assert(atime);
683
684         if (!path_is_absolute(tty)) {
685                 p = strappend("/dev/", tty);
686                 if (!p)
687                         return -ENOMEM;
688
689                 tty = p;
690         } else if (!path_startswith(tty, "/dev/"))
691                 return -ENOENT;
692
693         if (lstat(tty, &st) < 0)
694                 return -errno;
695
696         *atime = timespec_load(&st.st_atim);
697         return 0;
698 }
699
700 static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
701         _cleanup_free_ char *p = NULL;
702         int r;
703
704         assert(pid > 0);
705         assert(atime);
706
707         r = get_ctty(pid, NULL, &p);
708         if (r < 0)
709                 return r;
710
711         return get_tty_atime(p, atime);
712 }
713
714 int session_get_idle_hint(Session *s, dual_timestamp *t) {
715         usec_t atime = 0, n;
716         int r;
717
718         assert(s);
719
720         /* Explicit idle hint is set */
721         if (s->idle_hint) {
722                 if (t)
723                         *t = s->idle_hint_timestamp;
724
725                 return s->idle_hint;
726         }
727
728         /* Graphical sessions should really implement a real
729          * idle hint logic */
730         if (s->display)
731                 goto dont_know;
732
733         /* For sessions with an explicitly configured tty, let's check
734          * its atime */
735         if (s->tty) {
736                 r = get_tty_atime(s->tty, &atime);
737                 if (r >= 0)
738                         goto found_atime;
739         }
740
741         /* For sessions with a leader but no explicitly configured
742          * tty, let's check the controlling tty of the leader */
743         if (s->leader > 0) {
744                 r = get_process_ctty_atime(s->leader, &atime);
745                 if (r >= 0)
746                         goto found_atime;
747         }
748
749 dont_know:
750         if (t)
751                 *t = s->idle_hint_timestamp;
752
753         return 0;
754
755 found_atime:
756         if (t)
757                 dual_timestamp_from_realtime(t, atime);
758
759         n = now(CLOCK_REALTIME);
760
761         if (s->manager->idle_action_usec <= 0)
762                 return 0;
763
764         return atime + s->manager->idle_action_usec <= n;
765 }
766
767 void session_set_idle_hint(Session *s, bool b) {
768         assert(s);
769
770         if (s->idle_hint == b)
771                 return;
772
773         s->idle_hint = b;
774         dual_timestamp_get(&s->idle_hint_timestamp);
775
776         session_send_changed(s,
777                              "IdleHint\0"
778                              "IdleSinceHint\0"
779                              "IdleSinceHintMonotonic\0");
780
781         if (s->seat)
782                 seat_send_changed(s->seat,
783                                   "IdleHint\0"
784                                   "IdleSinceHint\0"
785                                   "IdleSinceHintMonotonic\0");
786
787         user_send_changed(s->user,
788                           "IdleHint\0"
789                           "IdleSinceHint\0"
790                           "IdleSinceHintMonotonic\0");
791
792         manager_send_changed(s->manager,
793                              "IdleHint\0"
794                              "IdleSinceHint\0"
795                              "IdleSinceHintMonotonic\0");
796 }
797
798 int session_create_fifo(Session *s) {
799         int r;
800
801         assert(s);
802
803         /* Create FIFO */
804         if (!s->fifo_path) {
805                 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
806                 if (r < 0)
807                         return r;
808
809                 if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
810                         return -ENOMEM;
811
812                 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
813                         return -errno;
814         }
815
816         /* Open reading side */
817         if (s->fifo_fd < 0) {
818                 struct epoll_event ev = {};
819
820                 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
821                 if (s->fifo_fd < 0)
822                         return -errno;
823
824                 r = hashmap_put(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1), s);
825                 if (r < 0)
826                         return r;
827
828                 ev.events = 0;
829                 ev.data.u32 = FD_OTHER_BASE + s->fifo_fd;
830
831                 if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, s->fifo_fd, &ev) < 0)
832                         return -errno;
833         }
834
835         /* Open writing side */
836         r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
837         if (r < 0)
838                 return -errno;
839
840         return r;
841 }
842
843 void session_remove_fifo(Session *s) {
844         assert(s);
845
846         if (s->fifo_fd >= 0) {
847                 assert_se(hashmap_remove(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1)) == s);
848                 assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->fifo_fd, NULL) == 0);
849                 close_nointr_nofail(s->fifo_fd);
850                 s->fifo_fd = -1;
851
852                 session_save(s);
853                 user_save(s->user);
854         }
855
856         if (s->fifo_path) {
857                 unlink(s->fifo_path);
858                 free(s->fifo_path);
859                 s->fifo_path = NULL;
860         }
861 }
862
863 int session_check_gc(Session *s, bool drop_not_started) {
864         int r;
865
866         assert(s);
867
868         if (drop_not_started && !s->started)
869                 return 0;
870
871         if (!s->user)
872                 return 0;
873
874         if (s->fifo_fd >= 0) {
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->closing)
906                 return SESSION_CLOSING;
907
908         if (s->scope_job)
909                 return SESSION_OPENING;
910
911         if (s->fifo_fd < 0)
912                 return SESSION_CLOSING;
913
914         if (session_is_active(s))
915                 return SESSION_ACTIVE;
916
917         return SESSION_ONLINE;
918 }
919
920 int session_kill(Session *s, KillWho who, int signo) {
921         assert(s);
922
923         if (!s->scope)
924                 return -ESRCH;
925
926         return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
927 }
928
929 static const char* const session_state_table[_SESSION_STATE_MAX] = {
930         [SESSION_OPENING] = "opening",
931         [SESSION_ONLINE] = "online",
932         [SESSION_ACTIVE] = "active",
933         [SESSION_CLOSING] = "closing"
934 };
935
936 DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState);
937
938 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
939         [SESSION_TTY] = "tty",
940         [SESSION_X11] = "x11",
941         [SESSION_UNSPECIFIED] = "unspecified"
942 };
943
944 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
945
946 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
947         [SESSION_USER] = "user",
948         [SESSION_GREETER] = "greeter",
949         [SESSION_LOCK_SCREEN] = "lock-screen",
950         [SESSION_BACKGROUND] = "background"
951 };
952
953 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
954
955 static const char* const kill_who_table[_KILL_WHO_MAX] = {
956         [KILL_LEADER] = "leader",
957         [KILL_ALL] = "all"
958 };
959
960 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);