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