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