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