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