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