chiark / gitweb /
5490366cc145e79bf75a9c648e8e073e18b8fffc
[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
331         return r;
332 }
333
334 int session_activate(Session *s) {
335         int r;
336
337         assert(s);
338
339         if (s->vtnr < 0)
340                 return -ENOTSUP;
341
342         if (!s->seat)
343                 return -ENOTSUP;
344
345         if (s->seat->active == s)
346                 return 0;
347
348         assert(seat_is_vtconsole(s->seat));
349
350         r = chvt(s->vtnr);
351         if (r < 0)
352                 return r;
353
354         return seat_set_active(s->seat, s);
355 }
356
357 static int session_link_x11_socket(Session *s) {
358         char *t, *f, *c;
359         size_t k;
360
361         assert(s);
362         assert(s->user);
363         assert(s->user->runtime_path);
364
365         if (s->user->display)
366                 return 0;
367
368         if (!s->display || !display_is_local(s->display))
369                 return 0;
370
371         k = strspn(s->display+1, "0123456789");
372         f = new(char, sizeof("/tmp/.X11-unix/X") + k);
373         if (!f) {
374                 log_error("Out of memory");
375                 return -ENOMEM;
376         }
377
378         c = stpcpy(f, "/tmp/.X11-unix/X");
379         memcpy(c, s->display+1, k);
380         c[k] = 0;
381
382         if (access(f, F_OK) < 0) {
383                 log_warning("Session %s has display %s with nonexisting socket %s.", s->id, s->display, f);
384                 free(f);
385                 return -ENOENT;
386         }
387
388         /* Note that this cannot be in a subdir to avoid
389          * vulnerabilities since we are privileged but the runtime
390          * path is owned by the user */
391
392         t = strappend(s->user->runtime_path, "/X11-display");
393         if (!t) {
394                 log_error("Out of memory");
395                 free(f);
396                 return -ENOMEM;
397         }
398
399         if (link(f, t) < 0) {
400                 if (errno == EEXIST) {
401                         unlink(t);
402
403                         if (link(f, t) >= 0)
404                                 goto done;
405                 }
406
407                 if (symlink(f, t) < 0) {
408
409                         if (errno == EEXIST) {
410                                 unlink(t);
411
412                                 if (symlink(f, t) >= 0)
413                                         goto done;
414                         }
415
416                         log_error("Failed to link %s to %s: %m", f, t);
417                         free(f);
418                         free(t);
419                         return -errno;
420                 }
421         }
422
423 done:
424         log_info("Linked %s to %s.", f, t);
425         free(f);
426         free(t);
427
428         s->user->display = s;
429
430         return 0;
431 }
432
433 static int session_create_one_group(Session *s, const char *controller, const char *path) {
434         int r;
435
436         assert(s);
437         assert(controller);
438         assert(path);
439
440         if (s->leader > 0) {
441                 r = cg_create_and_attach(controller, path, s->leader);
442                 if (r < 0)
443                         r = cg_create(controller, path);
444         } else
445                 r = cg_create(controller, path);
446
447         if (r < 0)
448                 return r;
449
450         r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid, -1);
451         if (r >= 0)
452                 r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
453
454         return r;
455 }
456
457 static int session_create_cgroup(Session *s) {
458         char **k;
459         char *p;
460         int r;
461
462         assert(s);
463         assert(s->user);
464         assert(s->user->cgroup_path);
465
466         if (!s->cgroup_path) {
467                 if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0) {
468                         log_error("Out of memory");
469                         return -ENOMEM;
470                 }
471         } else
472                 p = s->cgroup_path;
473
474         r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, p);
475         if (r < 0) {
476                 log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
477                 free(p);
478                 s->cgroup_path = NULL;
479                 return r;
480         }
481
482         s->cgroup_path = p;
483
484         STRV_FOREACH(k, s->controllers) {
485
486                 if (strv_contains(s->reset_controllers, *k))
487                         continue;
488
489                 r = session_create_one_group(s, *k, p);
490                 if (r < 0)
491                         log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
492         }
493
494         STRV_FOREACH(k, s->manager->controllers) {
495
496                 if (strv_contains(s->reset_controllers, *k) ||
497                     strv_contains(s->manager->reset_controllers, *k) ||
498                     strv_contains(s->controllers, *k))
499                         continue;
500
501                 r = session_create_one_group(s, *k, p);
502                 if (r < 0)
503                         log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
504         }
505
506         if (s->leader > 0) {
507
508                 STRV_FOREACH(k, s->reset_controllers) {
509                         r = cg_attach(*k, "/", s->leader);
510                         if (r < 0)
511                                 log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
512
513                 }
514
515                 STRV_FOREACH(k, s->manager->reset_controllers) {
516
517                         if (strv_contains(s->reset_controllers, *k) ||
518                             strv_contains(s->controllers, *k))
519                                 continue;
520
521                         r = cg_attach(*k, "/", s->leader);
522                         if (r < 0)
523                                 log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
524
525                 }
526         }
527
528         hashmap_put(s->manager->cgroups, s->cgroup_path, s);
529
530         return 0;
531 }
532
533 int session_start(Session *s) {
534         int r;
535
536         assert(s);
537         assert(s->user);
538
539         if (s->started)
540                 return 0;
541
542         r = user_start(s->user);
543         if (r < 0)
544                 return r;
545
546         log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
547                  "New session %s of user %s.", s->id, s->user->name);
548
549         /* Create cgroup */
550         r = session_create_cgroup(s);
551         if (r < 0)
552                 return r;
553
554         /* Create X11 symlink */
555         session_link_x11_socket(s);
556
557         dual_timestamp_get(&s->timestamp);
558
559         if (s->seat)
560                 seat_read_active_vt(s->seat);
561
562         s->started = true;
563
564         /* Save session data */
565         session_save(s);
566         user_save(s->user);
567
568         session_send_signal(s, true);
569
570         if (s->seat) {
571                 seat_save(s->seat);
572
573                 if (s->seat->active == s)
574                         seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
575                 else
576                         seat_send_changed(s->seat, "Sessions\0");
577         }
578
579         user_send_changed(s->user, "Sessions\0");
580
581         return 0;
582 }
583
584 static bool session_shall_kill(Session *s) {
585         assert(s);
586
587         if (!s->kill_processes)
588                 return false;
589
590         if (strv_contains(s->manager->kill_exclude_users, s->user->name))
591                 return false;
592
593         if (strv_isempty(s->manager->kill_only_users))
594                 return true;
595
596         return strv_contains(s->manager->kill_only_users, s->user->name);
597 }
598
599 static int session_terminate_cgroup(Session *s) {
600         int r;
601         char **k;
602
603         assert(s);
604
605         if (!s->cgroup_path)
606                 return 0;
607
608         cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
609
610         if (session_shall_kill(s)) {
611
612                 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
613                 if (r < 0)
614                         log_error("Failed to kill session cgroup: %s", strerror(-r));
615
616         } else {
617                 if (s->leader > 0) {
618                         Session *t;
619
620                         /* We still send a HUP to the leader process,
621                          * even if we are not supposed to kill the
622                          * whole cgroup. But let's first check the
623                          * leader still exists and belongs to our
624                          * session... */
625
626                         r = manager_get_session_by_pid(s->manager, s->leader, &t);
627                         if (r > 0 && t == s) {
628                                 kill(s->leader, SIGTERM); /* for normal processes */
629                                 kill(s->leader, SIGHUP);  /* for shells */
630                                 kill(s->leader, SIGCONT); /* in case they are stopped */
631                         }
632                 }
633
634                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
635                 if (r < 0)
636                         log_error("Failed to check session cgroup: %s", strerror(-r));
637                 else if (r > 0) {
638                         r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
639                         if (r < 0)
640                                 log_error("Failed to delete session cgroup: %s", strerror(-r));
641                 }
642         }
643
644         STRV_FOREACH(k, s->user->manager->controllers)
645                 cg_trim(*k, s->cgroup_path, true);
646
647         hashmap_remove(s->manager->cgroups, s->cgroup_path);
648
649         free(s->cgroup_path);
650         s->cgroup_path = NULL;
651
652         return 0;
653 }
654
655 static int session_unlink_x11_socket(Session *s) {
656         char *t;
657         int r;
658
659         assert(s);
660         assert(s->user);
661
662         if (s->user->display != s)
663                 return 0;
664
665         s->user->display = NULL;
666
667         t = strappend(s->user->runtime_path, "/X11-display");
668         if (!t) {
669                 log_error("Out of memory");
670                 return -ENOMEM;
671         }
672
673         r = unlink(t);
674         free(t);
675
676         return r < 0 ? -errno : 0;
677 }
678
679 int session_stop(Session *s) {
680         int r = 0, k;
681
682         assert(s);
683
684         if (s->started)
685                 log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
686                          "Removed session %s.", s->id);
687
688         /* Kill cgroup */
689         k = session_terminate_cgroup(s);
690         if (k < 0)
691                 r = k;
692
693         /* Remove X11 symlink */
694         session_unlink_x11_socket(s);
695
696         unlink(s->state_file);
697         session_add_to_gc_queue(s);
698         user_add_to_gc_queue(s->user);
699
700         if (s->started)
701                 session_send_signal(s, false);
702
703         if (s->seat) {
704                 if (s->seat->active == s)
705                         seat_set_active(s->seat, NULL);
706
707                 seat_send_changed(s->seat, "Sessions\0");
708         }
709
710         user_send_changed(s->user, "Sessions\0");
711
712         s->started = false;
713
714         return r;
715 }
716
717 bool session_is_active(Session *s) {
718         assert(s);
719
720         if (!s->seat)
721                 return true;
722
723         return s->seat->active == s;
724 }
725
726 int session_get_idle_hint(Session *s, dual_timestamp *t) {
727         char *p;
728         struct stat st;
729         usec_t u, n;
730         bool b;
731         int k;
732
733         assert(s);
734
735         if (s->idle_hint) {
736                 if (t)
737                         *t = s->idle_hint_timestamp;
738
739                 return s->idle_hint;
740         }
741
742         if (isempty(s->tty))
743                 goto dont_know;
744
745         if (s->tty[0] != '/') {
746                 p = strappend("/dev/", s->tty);
747                 if (!p)
748                         return -ENOMEM;
749         } else
750                 p = NULL;
751
752         if (!startswith(p ? p : s->tty, "/dev/")) {
753                 free(p);
754                 goto dont_know;
755         }
756
757         k = lstat(p ? p : s->tty, &st);
758         free(p);
759
760         if (k < 0)
761                 goto dont_know;
762
763         u = timespec_load(&st.st_atim);
764         n = now(CLOCK_REALTIME);
765         b = u + IDLE_THRESHOLD_USEC < n;
766
767         if (t)
768                 dual_timestamp_from_realtime(t, u + b ? IDLE_THRESHOLD_USEC : 0);
769
770         return b;
771
772 dont_know:
773         if (t)
774                 *t = s->idle_hint_timestamp;
775
776         return 0;
777 }
778
779 void session_set_idle_hint(Session *s, bool b) {
780         assert(s);
781
782         if (s->idle_hint == b)
783                 return;
784
785         s->idle_hint = b;
786         dual_timestamp_get(&s->idle_hint_timestamp);
787
788         session_send_changed(s,
789                              "IdleHint\0"
790                              "IdleSinceHint\0"
791                              "IdleSinceHintMonotonic\0");
792
793         if (s->seat)
794                 seat_send_changed(s->seat,
795                                   "IdleHint\0"
796                                   "IdleSinceHint\0"
797                                   "IdleSinceHintMonotonic\0");
798
799         user_send_changed(s->user,
800                           "IdleHint\0"
801                           "IdleSinceHint\0"
802                           "IdleSinceHintMonotonic\0");
803
804         manager_send_changed(s->manager,
805                              "IdleHint\0"
806                              "IdleSinceHint\0"
807                              "IdleSinceHintMonotonic\0");
808 }
809
810 int session_create_fifo(Session *s) {
811         int r;
812
813         assert(s);
814
815         /* Create FIFO */
816         if (!s->fifo_path) {
817                 r = safe_mkdir("/run/systemd/sessions", 0755, 0, 0);
818                 if (r < 0)
819                         return r;
820
821                 if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
822                         return -ENOMEM;
823
824                 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
825                         return -errno;
826         }
827
828         /* Open reading side */
829         if (s->fifo_fd < 0) {
830                 struct epoll_event ev;
831
832                 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
833                 if (s->fifo_fd < 0)
834                         return -errno;
835
836                 r = hashmap_put(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1), s);
837                 if (r < 0)
838                         return r;
839
840                 zero(ev);
841                 ev.events = 0;
842                 ev.data.u32 = FD_FIFO_BASE + s->fifo_fd;
843
844                 if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, s->fifo_fd, &ev) < 0)
845                         return -errno;
846         }
847
848         /* Open writing side */
849         r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
850         if (r < 0)
851                 return -errno;
852
853         return r;
854 }
855
856 void session_remove_fifo(Session *s) {
857         assert(s);
858
859         if (s->fifo_fd >= 0) {
860                 assert_se(hashmap_remove(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1)) == s);
861                 assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->fifo_fd, NULL) == 0);
862                 close_nointr_nofail(s->fifo_fd);
863                 s->fifo_fd = -1;
864         }
865
866         if (s->fifo_path) {
867                 unlink(s->fifo_path);
868                 free(s->fifo_path);
869                 s->fifo_path = NULL;
870         }
871 }
872
873 int session_check_gc(Session *s, bool drop_not_started) {
874         int r;
875
876         assert(s);
877
878         if (drop_not_started && !s->started)
879                 return 0;
880
881         if (s->fifo_fd >= 0) {
882
883                 r = pipe_eof(s->fifo_fd);
884                 if (r < 0)
885                         return r;
886
887                 if (r == 0)
888                         return 1;
889         }
890
891         if (s->cgroup_path) {
892
893                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
894                 if (r < 0)
895                         return r;
896
897                 if (r <= 0)
898                         return 1;
899         }
900
901         return 0;
902 }
903
904 void session_add_to_gc_queue(Session *s) {
905         assert(s);
906
907         if (s->in_gc_queue)
908                 return;
909
910         LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s);
911         s->in_gc_queue = true;
912 }
913
914 int session_kill(Session *s, KillWho who, int signo) {
915         int r = 0;
916         Set *pid_set = NULL;
917
918         assert(s);
919
920         if (!s->cgroup_path)
921                 return -ESRCH;
922
923         if (s->leader <= 0 && who == KILL_LEADER)
924                 return -ESRCH;
925
926         if (s->leader > 0)
927                 if (kill(s->leader, signo) < 0)
928                         r = -errno;
929
930         if (who == KILL_ALL) {
931                 int q;
932
933                 pid_set = set_new(trivial_hash_func, trivial_compare_func);
934                 if (!pid_set)
935                         return -ENOMEM;
936
937                 if (s->leader > 0) {
938                         q = set_put(pid_set, LONG_TO_PTR(s->leader));
939                         if (q < 0)
940                                 r = q;
941                 }
942
943                 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, signo, false, true, false, pid_set);
944                 if (q < 0)
945                         if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
946                                 r = q;
947         }
948
949         if (pid_set)
950                 set_free(pid_set);
951
952         return r;
953 }
954
955 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
956         [SESSION_TTY] = "tty",
957         [SESSION_X11] = "x11",
958         [SESSION_UNSPECIFIED] = "unspecified"
959 };
960
961 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
962
963 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
964         [SESSION_USER] = "user",
965         [SESSION_GREETER] = "greeter",
966         [SESSION_LOCK_SCREEN] = "lock-screen"
967 };
968
969 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
970
971 static const char* const kill_who_table[_KILL_WHO_MAX] = {
972         [KILL_LEADER] = "leader",
973         [KILL_ALL] = "all"
974 };
975
976 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);