chiark / gitweb /
b5e4bee9ee947c77dff27b93a97605805d410da1
[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 <fcntl.h>
24 #include <linux/vt.h>
25 #include <linux/kd.h>
26 #include <signal.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <unistd.h>
30
31 #include "sd-messages.h"
32 #include "util.h"
33 #include "mkdir.h"
34 #include "path-util.h"
35 #include "fileio.h"
36 #include "audit.h"
37 #include "bus-util.h"
38 #include "bus-error.h"
39 #include "logind-session.h"
40 #include "formats-util.h"
41 #include "terminal-util.h"
42
43 // #define RELEASE_USEC (20*USEC_PER_SEC)
44
45 static void session_remove_fifo(Session *s);
46
47 Session* session_new(Manager *m, const char *id) {
48         Session *s;
49
50         assert(m);
51         assert(id);
52         assert(session_id_valid(id));
53
54         s = new0(Session, 1);
55         if (!s)
56                 return NULL;
57
58         s->state_file = strappend("/run/systemd/sessions/", id);
59         if (!s->state_file) {
60                 free(s);
61                 return NULL;
62         }
63
64         s->devices = hashmap_new(&devt_hash_ops);
65         if (!s->devices) {
66                 free(s->state_file);
67                 free(s);
68                 return NULL;
69         }
70
71         s->id = basename(s->state_file);
72
73         if (hashmap_put(m->sessions, s->id, s) < 0) {
74                 hashmap_free(s->devices);
75                 free(s->state_file);
76                 free(s);
77                 return NULL;
78         }
79
80         s->manager = m;
81         s->fifo_fd = -1;
82         s->vtfd = -1;
83
84         return s;
85 }
86
87 void session_free(Session *s) {
88         SessionDevice *sd;
89
90         assert(s);
91
92         if (s->in_gc_queue)
93                 LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s);
94
95         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
96
97         session_remove_fifo(s);
98
99         session_drop_controller(s);
100
101         while ((sd = hashmap_first(s->devices)))
102                 session_device_free(sd);
103
104         hashmap_free(s->devices);
105
106         if (s->user) {
107                 LIST_REMOVE(sessions_by_user, s->user->sessions, s);
108
109                 if (s->user->display == s)
110                         s->user->display = NULL;
111         }
112
113         if (s->seat) {
114                 if (s->seat->active == s)
115                         s->seat->active = NULL;
116                 if (s->seat->pending_switch == s)
117                         s->seat->pending_switch = NULL;
118
119                 seat_evict_position(s->seat, s);
120                 LIST_REMOVE(sessions_by_seat, s->seat->sessions, s);
121         }
122
123         if (s->scope) {
124                 hashmap_remove(s->manager->session_units, s->scope);
125                 free(s->scope);
126         }
127
128 /// elogind does not support systemd scope_jobs
129 #if 0
130         free(s->scope_job);
131 #endif // 0
132
133         sd_bus_message_unref(s->create_message);
134
135         free(s->tty);
136         free(s->display);
137         free(s->remote_host);
138         free(s->remote_user);
139         free(s->service);
140         free(s->desktop);
141
142         hashmap_remove(s->manager->sessions, s->id);
143
144         free(s->state_file);
145         free(s);
146 }
147
148 void session_set_user(Session *s, User *u) {
149         assert(s);
150         assert(!s->user);
151
152         s->user = u;
153         LIST_PREPEND(sessions_by_user, u->sessions, s);
154 }
155
156 int session_save(Session *s) {
157         _cleanup_free_ char *temp_path = NULL;
158         _cleanup_fclose_ FILE *f = NULL;
159         int r = 0;
160
161         assert(s);
162
163         if (!s->user)
164                 return -ESTALE;
165
166         if (!s->started)
167                 return 0;
168
169         r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
170         if (r < 0)
171                 goto fail;
172
173         r = fopen_temporary(s->state_file, &f, &temp_path);
174         if (r < 0)
175                 goto fail;
176
177         assert(s->user);
178
179         fchmod(fileno(f), 0644);
180
181         fprintf(f,
182                 "# This is private data. Do not parse.\n"
183                 "UID="UID_FMT"\n"
184                 "USER=%s\n"
185                 "ACTIVE=%i\n"
186                 "STATE=%s\n"
187                 "REMOTE=%i\n",
188                 s->user->uid,
189                 s->user->name,
190                 session_is_active(s),
191                 session_state_to_string(session_get_state(s)),
192                 s->remote);
193
194         if (s->type >= 0)
195                 fprintf(f, "TYPE=%s\n", session_type_to_string(s->type));
196
197         if (s->class >= 0)
198                 fprintf(f, "CLASS=%s\n", session_class_to_string(s->class));
199
200         if (s->scope)
201                 fprintf(f, "SCOPE=%s\n", s->scope);
202 /// elogind does not support systemd scope_jobs
203 #if 0
204         if (s->scope_job)
205                 fprintf(f, "SCOPE_JOB=%s\n", s->scope_job);
206 #endif // 0
207
208         if (s->fifo_path)
209                 fprintf(f, "FIFO=%s\n", s->fifo_path);
210
211         if (s->seat)
212                 fprintf(f, "SEAT=%s\n", s->seat->id);
213
214         if (s->tty)
215                 fprintf(f, "TTY=%s\n", s->tty);
216
217         if (s->display)
218                 fprintf(f, "DISPLAY=%s\n", s->display);
219
220         if (s->remote_host) {
221                 _cleanup_free_ char *escaped;
222
223                 escaped = cescape(s->remote_host);
224                 if (!escaped) {
225                         r = -ENOMEM;
226                         goto fail;
227                 }
228
229                 fprintf(f, "REMOTE_HOST=%s\n", escaped);
230         }
231
232         if (s->remote_user) {
233                 _cleanup_free_ char *escaped;
234
235                 escaped = cescape(s->remote_user);
236                 if (!escaped) {
237                         r = -ENOMEM;
238                         goto fail;
239                 }
240
241                 fprintf(f, "REMOTE_USER=%s\n", escaped);
242         }
243
244         if (s->service) {
245                 _cleanup_free_ char *escaped;
246
247                 escaped = cescape(s->service);
248                 if (!escaped) {
249                         r = -ENOMEM;
250                         goto fail;
251                 }
252
253                 fprintf(f, "SERVICE=%s\n", escaped);
254         }
255
256         if (s->desktop) {
257                 _cleanup_free_ char *escaped;
258
259
260                 escaped = cescape(s->desktop);
261                 if (!escaped) {
262                         r = -ENOMEM;
263                         goto fail;
264                 }
265
266                 fprintf(f, "DESKTOP=%s\n", escaped);
267         }
268
269         if (s->seat && seat_has_vts(s->seat))
270                 fprintf(f, "VTNR=%u\n", s->vtnr);
271
272         if (!s->vtnr)
273                 fprintf(f, "POSITION=%u\n", s->position);
274
275         if (s->leader > 0)
276                 fprintf(f, "LEADER="PID_FMT"\n", s->leader);
277
278         if (s->audit_id > 0)
279                 fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id);
280
281         if (dual_timestamp_is_set(&s->timestamp))
282                 fprintf(f,
283                         "REALTIME="USEC_FMT"\n"
284                         "MONOTONIC="USEC_FMT"\n",
285                         s->timestamp.realtime,
286                         s->timestamp.monotonic);
287
288         if (s->controller)
289                 fprintf(f, "CONTROLLER=%s\n", s->controller);
290
291         r = fflush_and_check(f);
292         if (r < 0)
293                 goto fail;
294
295         if (rename(temp_path, s->state_file) < 0) {
296                 r = -errno;
297                 goto fail;
298         }
299
300         return 0;
301
302 fail:
303         (void) unlink(s->state_file);
304
305         if (temp_path)
306                 (void) unlink(temp_path);
307
308         return log_error_errno(r, "Failed to save session data %s: %m", s->state_file);
309 }
310
311
312 int session_load(Session *s) {
313         _cleanup_free_ char *remote = NULL,
314                 *seat = NULL,
315                 *vtnr = NULL,
316                 *state = NULL,
317                 *position = NULL,
318                 *leader = NULL,
319                 *type = NULL,
320                 *class = NULL,
321                 *uid = NULL,
322                 *realtime = NULL,
323                 *monotonic = NULL,
324                 *controller = NULL;
325
326         int k, r;
327
328         assert(s);
329
330         r = parse_env_file(s->state_file, NEWLINE,
331                            "REMOTE",         &remote,
332                            "SCOPE",          &s->scope,
333 /// elogind does not support systemd scope_jobs
334 #if 0
335                            "SCOPE_JOB",      &s->scope_job,
336 #endif // 0
337                            "FIFO",           &s->fifo_path,
338                            "SEAT",           &seat,
339                            "TTY",            &s->tty,
340                            "DISPLAY",        &s->display,
341                            "REMOTE_HOST",    &s->remote_host,
342                            "REMOTE_USER",    &s->remote_user,
343                            "SERVICE",        &s->service,
344                            "DESKTOP",        &s->desktop,
345                            "VTNR",           &vtnr,
346                            "STATE",          &state,
347                            "POSITION",       &position,
348                            "LEADER",         &leader,
349                            "TYPE",           &type,
350                            "CLASS",          &class,
351                            "UID",            &uid,
352                            "REALTIME",       &realtime,
353                            "MONOTONIC",      &monotonic,
354                            "CONTROLLER",     &controller,
355                            NULL);
356
357         if (r < 0)
358                 return log_error_errno(r, "Failed to read %s: %m", s->state_file);
359
360         if (!s->user) {
361                 uid_t u;
362                 User *user;
363
364                 if (!uid) {
365                         log_error("UID not specified for session %s", s->id);
366                         return -ENOENT;
367                 }
368
369                 r = parse_uid(uid, &u);
370                 if (r < 0)  {
371                         log_error("Failed to parse UID value %s for session %s.", uid, s->id);
372                         return r;
373                 }
374
375                 user = hashmap_get(s->manager->users, UID_TO_PTR(u));
376                 if (!user) {
377                         log_error("User of session %s not known.", s->id);
378                         return -ENOENT;
379                 }
380
381                 session_set_user(s, user);
382         }
383
384         if (remote) {
385                 k = parse_boolean(remote);
386                 if (k >= 0)
387                         s->remote = k;
388         }
389
390         if (vtnr)
391                 safe_atou(vtnr, &s->vtnr);
392
393         if (seat && !s->seat) {
394                 Seat *o;
395
396                 o = hashmap_get(s->manager->seats, seat);
397                 if (o)
398                         r = seat_attach_session(o, s);
399                 if (!o || r < 0)
400                         log_error("Cannot attach session %s to seat %s", s->id, seat);
401         }
402
403         if (!s->seat || !seat_has_vts(s->seat))
404                 s->vtnr = 0;
405
406         if (position && s->seat) {
407                 unsigned int npos;
408
409                 safe_atou(position, &npos);
410                 seat_claim_position(s->seat, s, npos);
411         }
412
413         if (leader) {
414                 k = parse_pid(leader, &s->leader);
415                 if (k >= 0)
416                         audit_session_from_pid(s->leader, &s->audit_id);
417         }
418
419         if (type) {
420                 SessionType t;
421
422                 t = session_type_from_string(type);
423                 if (t >= 0)
424                         s->type = t;
425         }
426
427         if (class) {
428                 SessionClass c;
429
430                 c = session_class_from_string(class);
431                 if (c >= 0)
432                         s->class = c;
433         }
434
435         if (state && streq(state, "closing"))
436                 s->stopping = true;
437
438         if (s->fifo_path) {
439                 int fd;
440
441                 /* If we open an unopened pipe for reading we will not
442                    get an EOF. to trigger an EOF we hence open it for
443                    writing, but close it right away which then will
444                    trigger the EOF. This will happen immediately if no
445                    other process has the FIFO open for writing, i. e.
446                    when the session died before logind (re)started. */
447
448                 fd = session_create_fifo(s);
449                 safe_close(fd);
450         }
451
452         if (realtime) {
453                 unsigned long long l;
454                 if (sscanf(realtime, "%llu", &l) > 0)
455                         s->timestamp.realtime = l;
456         }
457
458         if (monotonic) {
459                 unsigned long long l;
460                 if (sscanf(monotonic, "%llu", &l) > 0)
461                         s->timestamp.monotonic = l;
462         }
463
464         if (controller) {
465                 if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0)
466                         session_set_controller(s, controller, false);
467                 else
468                         session_restore_vt(s);
469         }
470
471         return r;
472 }
473
474 int session_activate(Session *s) {
475         unsigned int num_pending;
476
477         assert(s);
478         assert(s->user);
479
480         if (!s->seat)
481                 return -EOPNOTSUPP;
482
483         if (s->seat->active == s)
484                 return 0;
485
486         /* on seats with VTs, we let VTs manage session-switching */
487         if (seat_has_vts(s->seat)) {
488                 if (!s->vtnr)
489                         return -EOPNOTSUPP;
490
491                 return chvt(s->vtnr);
492         }
493
494         /* On seats without VTs, we implement session-switching in logind. We
495          * try to pause all session-devices and wait until the session
496          * controller acknowledged them. Once all devices are asleep, we simply
497          * switch the active session and be done.
498          * We save the session we want to switch to in seat->pending_switch and
499          * seat_complete_switch() will perform the final switch. */
500
501         s->seat->pending_switch = s;
502
503         /* if no devices are running, immediately perform the session switch */
504         num_pending = session_device_try_pause_all(s);
505         if (!num_pending)
506                 seat_complete_switch(s->seat);
507
508         return 0;
509 }
510
511 static int session_start_scope(Session *s) {
512         int r = 0;
513
514         assert(s);
515         assert(s->user);
516         assert(s->user->slice);
517
518         if (!s->scope) {
519                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
520                 _cleanup_free_ char *description = NULL;
521                 char *scope = NULL; //, *job = NULL;
522
523                 description = strjoin("Session ", s->id, " of user ", s->user->name, NULL);
524                 if (!description)
525                         return log_oom();
526
527                 scope = strjoin("session-", s->id, ".scope", NULL);
528                 if (!scope)
529                         return log_oom();
530
531 /// elogind : Do not try to use dbus to call systemd
532 #if 0
533                 r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "logind.service", "systemd-user-sessions.service", &error, &job);
534 #endif // 0
535                 if (r < 0) {
536                         log_error("Failed to start session scope %s: %s %s",
537                                   scope, bus_error_message(&error, r), error.name);
538                         free(scope);
539                         return r;
540                 } else {
541                         s->scope = scope;
542 /// elogind does not support scope jobs
543 #if 0
544                         free(s->scope_job);
545                         s->scope_job = job;
546 #endif // 0
547                 }
548         }
549
550         if (s->scope)
551                 hashmap_put(s->manager->session_units, s->scope, s);
552
553         return 0;
554 }
555
556 int session_start(Session *s) {
557         int r;
558
559         assert(s);
560
561         if (!s->user)
562                 return -ESTALE;
563
564         if (s->started)
565                 return 0;
566
567         r = user_start(s->user);
568         if (r < 0)
569                 return r;
570
571         /* Create cgroup */
572         r = session_start_scope(s);
573         if (r < 0)
574                 return r;
575
576         log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
577                    LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START),
578                    "SESSION_ID=%s", s->id,
579                    "USER_ID=%s", s->user->name,
580                    "LEADER="PID_FMT, s->leader,
581                    LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name),
582                    NULL);
583
584         if (!dual_timestamp_is_set(&s->timestamp))
585                 dual_timestamp_get(&s->timestamp);
586
587         if (s->seat)
588                 seat_read_active_vt(s->seat);
589
590         s->started = true;
591
592         user_elect_display(s->user);
593
594         /* Save data */
595         session_save(s);
596         user_save(s->user);
597         if (s->seat)
598                 seat_save(s->seat);
599
600         /* Send signals */
601         session_send_signal(s, true);
602         user_send_changed(s->user, "Sessions", "Display", NULL);
603         if (s->seat) {
604                 if (s->seat->active == s)
605                         seat_send_changed(s->seat, "Sessions", "ActiveSession", NULL);
606                 else
607                         seat_send_changed(s->seat, "Sessions", NULL);
608         }
609
610         return 0;
611 }
612
613 /// UNNEEDED by elogind
614 #if 0
615 static int session_stop_scope(Session *s, bool force) {
616         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
617         char *job = NULL;
618         int r;
619
620         assert(s);
621
622         if (!s->scope)
623                 return 0;
624
625         if (force || manager_shall_kill(s->manager, s->user->name)) {
626                 r = manager_stop_unit(s->manager, s->scope, &error, &job);
627                 if (r < 0) {
628                         log_error("Failed to stop session scope: %s", bus_error_message(&error, r));
629                         return r;
630                 }
631
632                 free(s->scope_job);
633                 s->scope_job = job;
634         } else {
635                 r = manager_abandon_scope(s->manager, s->scope, &error);
636                 if (r < 0) {
637                         log_error("Failed to abandon session scope: %s", bus_error_message(&error, r));
638                         return r;
639                 }
640         }
641
642         return 0;
643 }
644 #endif // 0
645
646 int session_stop(Session *s, bool force) {
647         int r = 0;
648
649         assert(s);
650
651         if (!s->user)
652                 return -ESTALE;
653
654         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
655
656         if (s->seat)
657                 seat_evict_position(s->seat, s);
658
659         /* We are going down, don't care about FIFOs anymore */
660         session_remove_fifo(s);
661
662         /* Kill cgroup */
663 /// @todo : Currently elogind does not start scopes. It remains to be seen
664 ///         whether this is really not needed, but then, elogind is not a
665 ///         systemd cgroups manager.
666 #if 0
667         r = session_stop_scope(s, force);
668 #endif // 0
669
670         s->stopping = true;
671
672         user_elect_display(s->user);
673
674         session_save(s);
675         user_save(s->user);
676
677         return r;
678 }
679
680 int session_finalize(Session *s) {
681         SessionDevice *sd;
682
683         assert(s);
684
685         if (!s->user)
686                 return -ESTALE;
687
688         if (s->started)
689                 log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
690                            LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
691                            "SESSION_ID=%s", s->id,
692                            "USER_ID=%s", s->user->name,
693                            "LEADER="PID_FMT, s->leader,
694                            LOG_MESSAGE("Removed session %s.", s->id),
695                            NULL);
696
697         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
698
699         if (s->seat)
700                 seat_evict_position(s->seat, s);
701
702         /* Kill session devices */
703         while ((sd = hashmap_first(s->devices)))
704                 session_device_free(sd);
705
706         (void) unlink(s->state_file);
707         session_add_to_gc_queue(s);
708         user_add_to_gc_queue(s->user);
709
710         if (s->started) {
711                 session_send_signal(s, false);
712                 s->started = false;
713         }
714
715         if (s->seat) {
716                 if (s->seat->active == s)
717                         seat_set_active(s->seat, NULL);
718
719                 seat_save(s->seat);
720                 seat_send_changed(s->seat, "Sessions", NULL);
721         }
722
723         user_save(s->user);
724         user_send_changed(s->user, "Sessions", "Display", NULL);
725
726         return 0;
727 }
728
729 /// UNNEEDED by elogind
730 #if 0
731 static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) {
732         Session *s = userdata;
733
734         assert(es);
735         assert(s);
736
737         session_stop(s, false);
738         return 0;
739 }
740 #endif // 0
741
742 int session_release(Session *s) {
743         assert(s);
744
745         if (!s->started || s->stopping)
746                 return 0;
747
748         if (s->timer_event_source)
749                 return 0;
750
751         /* In systemd, session release is triggered by user jobs
752            dying.  In elogind we don't have that so go ahead and stop
753            now.  */
754 #if 0
755         return sd_event_add_time(s->manager->event,
756                                  &s->timer_event_source,
757                                  CLOCK_MONOTONIC,
758                                  now(CLOCK_MONOTONIC) + RELEASE_USEC, 0,
759                                  release_timeout_callback, s);
760
761 #else
762         return session_stop(s, false);
763 #endif // 0
764 }
765
766 bool session_is_active(Session *s) {
767         assert(s);
768
769         if (!s->seat)
770                 return true;
771
772         return s->seat->active == s;
773 }
774
775 static int get_tty_atime(const char *tty, usec_t *atime) {
776         _cleanup_free_ char *p = NULL;
777         struct stat st;
778
779         assert(tty);
780         assert(atime);
781
782         if (!path_is_absolute(tty)) {
783                 p = strappend("/dev/", tty);
784                 if (!p)
785                         return -ENOMEM;
786
787                 tty = p;
788         } else if (!path_startswith(tty, "/dev/"))
789                 return -ENOENT;
790
791         if (lstat(tty, &st) < 0)
792                 return -errno;
793
794         *atime = timespec_load(&st.st_atim);
795         return 0;
796 }
797
798 static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
799         _cleanup_free_ char *p = NULL;
800         int r;
801
802         assert(pid > 0);
803         assert(atime);
804
805         r = get_ctty(pid, NULL, &p);
806         if (r < 0)
807                 return r;
808
809         return get_tty_atime(p, atime);
810 }
811
812 int session_get_idle_hint(Session *s, dual_timestamp *t) {
813         usec_t atime = 0, n;
814         int r;
815
816         assert(s);
817
818         /* Explicit idle hint is set */
819         if (s->idle_hint) {
820                 if (t)
821                         *t = s->idle_hint_timestamp;
822
823                 return s->idle_hint;
824         }
825
826         /* Graphical sessions should really implement a real
827          * idle hint logic */
828         if (s->display)
829                 goto dont_know;
830
831         /* For sessions with an explicitly configured tty, let's check
832          * its atime */
833         if (s->tty) {
834                 r = get_tty_atime(s->tty, &atime);
835                 if (r >= 0)
836                         goto found_atime;
837         }
838
839         /* For sessions with a leader but no explicitly configured
840          * tty, let's check the controlling tty of the leader */
841         if (s->leader > 0) {
842                 r = get_process_ctty_atime(s->leader, &atime);
843                 if (r >= 0)
844                         goto found_atime;
845         }
846
847 dont_know:
848         if (t)
849                 *t = s->idle_hint_timestamp;
850
851         return 0;
852
853 found_atime:
854         if (t)
855                 dual_timestamp_from_realtime(t, atime);
856
857         n = now(CLOCK_REALTIME);
858
859         if (s->manager->idle_action_usec <= 0)
860                 return 0;
861
862         return atime + s->manager->idle_action_usec <= n;
863 }
864
865 void session_set_idle_hint(Session *s, bool b) {
866         assert(s);
867
868         if (s->idle_hint == b)
869                 return;
870
871         s->idle_hint = b;
872         dual_timestamp_get(&s->idle_hint_timestamp);
873
874         session_send_changed(s, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
875
876         if (s->seat)
877                 seat_send_changed(s->seat, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
878
879         user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
880         manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
881 }
882
883 static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
884         Session *s = userdata;
885
886         assert(s);
887         assert(s->fifo_fd == fd);
888
889         /* EOF on the FIFO means the session died abnormally. */
890
891         session_remove_fifo(s);
892         session_stop(s, false);
893
894         return 1;
895 }
896
897 int session_create_fifo(Session *s) {
898         int r;
899
900         assert(s);
901
902         /* Create FIFO */
903         if (!s->fifo_path) {
904                 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
905                 if (r < 0)
906                         return r;
907
908                 if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
909                         return -ENOMEM;
910
911                 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
912                         return -errno;
913         }
914
915         /* Open reading side */
916         if (s->fifo_fd < 0) {
917                 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
918                 if (s->fifo_fd < 0)
919                         return -errno;
920
921         }
922
923         if (!s->fifo_event_source) {
924                 r = sd_event_add_io(s->manager->event, &s->fifo_event_source, s->fifo_fd, 0, session_dispatch_fifo, s);
925                 if (r < 0)
926                         return r;
927
928                 r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_IDLE);
929                 if (r < 0)
930                         return r;
931         }
932
933         /* Open writing side */
934         r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
935         if (r < 0)
936                 return -errno;
937
938         return r;
939 }
940
941 static void session_remove_fifo(Session *s) {
942         assert(s);
943
944         s->fifo_event_source = sd_event_source_unref(s->fifo_event_source);
945         s->fifo_fd = safe_close(s->fifo_fd);
946
947         if (s->fifo_path) {
948                 unlink(s->fifo_path);
949                 free(s->fifo_path);
950                 s->fifo_path = NULL;
951         }
952 }
953
954 bool session_check_gc(Session *s, bool drop_not_started) {
955         assert(s);
956
957         if (drop_not_started && !s->started)
958                 return false;
959
960         if (!s->user)
961                 return false;
962
963         if (s->fifo_fd >= 0) {
964                 if (pipe_eof(s->fifo_fd) <= 0)
965                         return true;
966         }
967
968 /// elogind supports neither scopes nor jobs
969 #if 0
970         if (s->scope_job && manager_job_is_active(s->manager, s->scope_job))
971                 return true;
972
973         if (s->scope && manager_unit_is_active(s->manager, s->scope))
974                 return true;
975 #endif // 0
976
977         return false;
978 }
979
980 void session_add_to_gc_queue(Session *s) {
981         assert(s);
982
983         if (s->in_gc_queue)
984                 return;
985
986         LIST_PREPEND(gc_queue, s->manager->session_gc_queue, s);
987         s->in_gc_queue = true;
988 }
989
990 SessionState session_get_state(Session *s) {
991         assert(s);
992
993         /* always check closing first */
994         if (s->stopping || s->timer_event_source)
995                 return SESSION_CLOSING;
996
997 /// elogind does not support systemd scope_jobs
998 #if 0
999         if (s->scope_job || s->fifo_fd < 0)
1000 #else
1001         if (s->fifo_fd < 0)
1002 #endif // 0
1003                 return SESSION_OPENING;
1004
1005         if (session_is_active(s))
1006                 return SESSION_ACTIVE;
1007
1008         return SESSION_ONLINE;
1009 }
1010
1011 int session_kill(Session *s, KillWho who, int signo) {
1012         assert(s);
1013
1014 /// FIXME: Without direct cgroup support, elogind can not kill sessions
1015 #if 0
1016         if (!s->scope)
1017                 return -ESRCH;
1018
1019         return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
1020 #else
1021         return -ESRCH;
1022 #endif // 0
1023 }
1024
1025 static int session_open_vt(Session *s) {
1026         char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)];
1027
1028         if (s->vtnr < 1)
1029                 return -ENODEV;
1030
1031         if (s->vtfd >= 0)
1032                 return s->vtfd;
1033
1034         sprintf(path, "/dev/tty%u", s->vtnr);
1035         s->vtfd = open_terminal(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
1036         if (s->vtfd < 0)
1037                 return log_error_errno(errno, "cannot open VT %s of session %s: %m", path, s->id);
1038
1039         return s->vtfd;
1040 }
1041
1042 int session_prepare_vt(Session *s) {
1043         int vt, r;
1044         struct vt_mode mode = { 0 };
1045
1046         if (s->vtnr < 1)
1047                 return 0;
1048
1049         vt = session_open_vt(s);
1050         if (vt < 0)
1051                 return vt;
1052
1053         r = fchown(vt, s->user->uid, -1);
1054         if (r < 0) {
1055                 r = -errno;
1056                 log_error_errno(errno, "Cannot change owner of /dev/tty%u: %m", s->vtnr);
1057                 goto error;
1058         }
1059
1060         r = ioctl(vt, KDSKBMODE, K_OFF);
1061         if (r < 0) {
1062                 r = -errno;
1063                 log_error_errno(errno, "Cannot set K_OFF on /dev/tty%u: %m", s->vtnr);
1064                 goto error;
1065         }
1066
1067         r = ioctl(vt, KDSETMODE, KD_GRAPHICS);
1068         if (r < 0) {
1069                 r = -errno;
1070                 log_error_errno(errno, "Cannot set KD_GRAPHICS on /dev/tty%u: %m", s->vtnr);
1071                 goto error;
1072         }
1073
1074         /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS.
1075          * So we need a dummy handler here which just acknowledges *all* VT
1076          * switch requests. */
1077         mode.mode = VT_PROCESS;
1078         mode.relsig = SIGRTMIN;
1079         mode.acqsig = SIGRTMIN + 1;
1080         r = ioctl(vt, VT_SETMODE, &mode);
1081         if (r < 0) {
1082                 r = -errno;
1083                 log_error_errno(errno, "Cannot set VT_PROCESS on /dev/tty%u: %m", s->vtnr);
1084                 goto error;
1085         }
1086
1087         return 0;
1088
1089 error:
1090         session_restore_vt(s);
1091         return r;
1092 }
1093
1094 void session_restore_vt(Session *s) {
1095         _cleanup_free_ char *utf8 = NULL;
1096         int vt, kb = K_XLATE;
1097         struct vt_mode mode = { 0 };
1098
1099         /* We need to get a fresh handle to the virtual terminal,
1100          * since the old file-descriptor is potentially in a hung-up
1101          * state after the controlling process exited; we do a
1102          * little dance to avoid having the terminal be available
1103          * for reuse before we've cleaned it up.
1104          */
1105         int old_fd = s->vtfd;
1106         s->vtfd = -1;
1107
1108         vt = session_open_vt(s);
1109         safe_close(old_fd);
1110
1111         if (vt < 0)
1112                 return;
1113
1114         (void) ioctl(vt, KDSETMODE, KD_TEXT);
1115
1116         if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1')
1117                 kb = K_UNICODE;
1118
1119         (void) ioctl(vt, KDSKBMODE, kb);
1120
1121         mode.mode = VT_AUTO;
1122         (void) ioctl(vt, VT_SETMODE, &mode);
1123
1124         fchown(vt, 0, -1);
1125
1126         s->vtfd = safe_close(s->vtfd);
1127 }
1128
1129 void session_leave_vt(Session *s) {
1130         int r;
1131
1132         assert(s);
1133
1134         /* This is called whenever we get a VT-switch signal from the kernel.
1135          * We acknowledge all of them unconditionally. Note that session are
1136          * free to overwrite those handlers and we only register them for
1137          * sessions with controllers. Legacy sessions are not affected.
1138          * However, if we switch from a non-legacy to a legacy session, we must
1139          * make sure to pause all device before acknowledging the switch. We
1140          * process the real switch only after we are notified via sysfs, so the
1141          * legacy session might have already started using the devices. If we
1142          * don't pause the devices before the switch, we might confuse the
1143          * session we switch to. */
1144
1145         if (s->vtfd < 0)
1146                 return;
1147
1148         session_device_pause_all(s);
1149         r = ioctl(s->vtfd, VT_RELDISP, 1);
1150         if (r < 0)
1151                 log_debug_errno(errno, "Cannot release VT of session %s: %m", s->id);
1152 }
1153
1154 bool session_is_controller(Session *s, const char *sender) {
1155         assert(s);
1156
1157         return streq_ptr(s->controller, sender);
1158 }
1159
1160 static void session_release_controller(Session *s, bool notify) {
1161         _cleanup_free_ char *name = NULL;
1162         SessionDevice *sd;
1163
1164         if (!s->controller)
1165                 return;
1166
1167         name = s->controller;
1168
1169         /* By resetting the controller before releasing the devices, we won't
1170          * send notification signals. This avoids sending useless notifications
1171          * if the controller is released on disconnects. */
1172         if (!notify)
1173                 s->controller = NULL;
1174
1175         while ((sd = hashmap_first(s->devices)))
1176                 session_device_free(sd);
1177
1178         s->controller = NULL;
1179         s->track = sd_bus_track_unref(s->track);
1180 }
1181
1182 static int on_bus_track(sd_bus_track *track, void *userdata) {
1183         Session *s = userdata;
1184
1185         assert(track);
1186         assert(s);
1187
1188         session_drop_controller(s);
1189
1190         return 0;
1191 }
1192
1193 int session_set_controller(Session *s, const char *sender, bool force) {
1194         _cleanup_free_ char *name = NULL;
1195         int r;
1196
1197         assert(s);
1198         assert(sender);
1199
1200         if (session_is_controller(s, sender))
1201                 return 0;
1202         if (s->controller && !force)
1203                 return -EBUSY;
1204
1205         name = strdup(sender);
1206         if (!name)
1207                 return -ENOMEM;
1208
1209         s->track = sd_bus_track_unref(s->track);
1210         r = sd_bus_track_new(s->manager->bus, &s->track, on_bus_track, s);
1211         if (r < 0)
1212                 return r;
1213
1214         r = sd_bus_track_add_name(s->track, name);
1215         if (r < 0)
1216                 return r;
1217
1218         /* When setting a session controller, we forcibly mute the VT and set
1219          * it into graphics-mode. Applications can override that by changing
1220          * VT state after calling TakeControl(). However, this serves as a good
1221          * default and well-behaving controllers can now ignore VTs entirely.
1222          * Note that we reset the VT on ReleaseControl() and if the controller
1223          * exits.
1224          * If logind crashes/restarts, we restore the controller during restart
1225          * or reset the VT in case it crashed/exited, too. */
1226         r = session_prepare_vt(s);
1227         if (r < 0) {
1228                 s->track = sd_bus_track_unref(s->track);
1229                 return r;
1230         }
1231
1232         session_release_controller(s, true);
1233         s->controller = name;
1234         name = NULL;
1235         session_save(s);
1236
1237         return 0;
1238 }
1239
1240 void session_drop_controller(Session *s) {
1241         assert(s);
1242
1243         if (!s->controller)
1244                 return;
1245
1246         s->track = sd_bus_track_unref(s->track);
1247         session_release_controller(s, false);
1248         session_save(s);
1249         session_restore_vt(s);
1250 }
1251
1252 static const char* const session_state_table[_SESSION_STATE_MAX] = {
1253         [SESSION_OPENING] = "opening",
1254         [SESSION_ONLINE] = "online",
1255         [SESSION_ACTIVE] = "active",
1256         [SESSION_CLOSING] = "closing"
1257 };
1258
1259 DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState);
1260
1261 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
1262         [SESSION_UNSPECIFIED] = "unspecified",
1263         [SESSION_TTY] = "tty",
1264         [SESSION_X11] = "x11",
1265         [SESSION_WAYLAND] = "wayland",
1266         [SESSION_MIR] = "mir",
1267         [SESSION_WEB] = "web",
1268 };
1269
1270 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
1271
1272 static const char* const session_class_table[_SESSION_CLASS_MAX] = {
1273         [SESSION_USER] = "user",
1274         [SESSION_GREETER] = "greeter",
1275         [SESSION_LOCK_SCREEN] = "lock-screen",
1276         [SESSION_BACKGROUND] = "background"
1277 };
1278
1279 DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
1280
1281 static const char* const kill_who_table[_KILL_WHO_MAX] = {
1282         [KILL_LEADER] = "leader",
1283         [KILL_ALL] = "all"
1284 };
1285
1286 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);