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