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