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