kmscon: remove cdev sessions
[platform/upstream/kmscon.git] / src / kmscon_seat.c
1 /*
2  * Seats
3  *
4  * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /*
27  * Seats
28  * A seat is a single session that is self-hosting and provides all the
29  * interaction for a single logged-in user.
30  */
31
32 #include <errno.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include "conf.h"
36 #include "eloop.h"
37 #include "kmscon_conf.h"
38 #include "kmscon_dummy.h"
39 #include "kmscon_seat.h"
40 #include "kmscon_terminal.h"
41 #include "shl_dlist.h"
42 #include "shl_log.h"
43 #include "uterm_input.h"
44 #include "uterm_video.h"
45 #include "uterm_vt.h"
46
47 #define LOG_SUBSYSTEM "seat"
48
49 struct kmscon_session {
50         struct shl_dlist list;
51         unsigned long ref;
52         struct kmscon_seat *seat;
53
54         bool enabled;
55         bool foreground;
56         bool deactivating;
57
58         struct ev_timer *timer;
59
60         kmscon_session_cb_t cb;
61         void *data;
62 };
63
64 struct kmscon_display {
65         struct shl_dlist list;
66         struct kmscon_seat *seat;
67         struct uterm_display *disp;
68         bool activated;
69 };
70
71 enum kmscon_async_schedule {
72         SCHEDULE_SWITCH,
73         SCHEDULE_VT,
74         SCHEDULE_UNREGISTER,
75 };
76
77 struct kmscon_seat {
78         struct ev_eloop *eloop;
79         struct uterm_vt_master *vtm;
80         struct conf_ctx *conf_ctx;
81         struct kmscon_conf_t *conf;
82
83         char *name;
84         struct uterm_input *input;
85         struct uterm_vt *vt;
86         struct shl_dlist displays;
87
88         size_t session_count;
89         struct shl_dlist sessions;
90
91         bool awake;
92         bool foreground;
93         struct kmscon_session *current_sess;
94         struct kmscon_session *scheduled_sess;
95         struct kmscon_session *dummy_sess;
96
97         unsigned int async_schedule;
98
99         kmscon_seat_cb_t cb;
100         void *data;
101 };
102
103 static int session_call(struct kmscon_session *sess, unsigned int event,
104                         struct uterm_display *disp)
105 {
106         struct kmscon_session_event ev;
107
108         if (!sess->cb)
109                 return 0;
110
111         memset(&ev, 0, sizeof(ev));
112         ev.type = event;
113         ev.disp = disp;
114         return sess->cb(sess, &ev, sess->data);
115 }
116
117 static int session_call_activate(struct kmscon_session *sess)
118 {
119         log_debug("activate session %p", sess);
120         return session_call(sess, KMSCON_SESSION_ACTIVATE, NULL);
121 }
122
123 static int session_call_deactivate(struct kmscon_session *sess)
124 {
125         log_debug("deactivate session %p", sess);
126         return session_call(sess, KMSCON_SESSION_DEACTIVATE, NULL);
127 }
128
129 static void session_call_display_new(struct kmscon_session *sess,
130                                      struct uterm_display *disp)
131 {
132         session_call(sess, KMSCON_SESSION_DISPLAY_NEW, disp);
133 }
134
135 static void session_call_display_gone(struct kmscon_session *sess,
136                                       struct uterm_display *disp)
137 {
138         session_call(sess, KMSCON_SESSION_DISPLAY_GONE, disp);
139 }
140
141 static void session_call_display_refresh(struct kmscon_session *sess,
142                                          struct uterm_display *disp)
143 {
144         session_call(sess, KMSCON_SESSION_DISPLAY_REFRESH, disp);
145 }
146
147 static void activate_display(struct kmscon_display *d)
148 {
149         int ret;
150         struct shl_dlist *iter, *tmp;
151         struct kmscon_session *s;
152         struct kmscon_seat *seat = d->seat;
153
154         if (d->activated || !d->seat->awake || !d->seat->foreground)
155                 return;
156
157         /* TODO: We always use the default mode for new displays but we should
158          * rather allow the user to specify different modes in the configuration
159          * files. */
160         if (uterm_display_get_state(d->disp) == UTERM_DISPLAY_INACTIVE) {
161                 ret = uterm_display_activate(d->disp, NULL);
162                 if (ret)
163                         return;
164
165                 d->activated = true;
166
167                 ret = uterm_display_set_dpms(d->disp, UTERM_DPMS_ON);
168                 if (ret)
169                         log_warning("cannot set DPMS state to on for display: %d",
170                                     ret);
171
172                 shl_dlist_for_each_safe(iter, tmp, &seat->sessions) {
173                         s = shl_dlist_entry(iter, struct kmscon_session, list);
174                         session_call_display_new(s, d->disp);
175                 }
176         }
177 }
178
179 static int seat_go_foreground(struct kmscon_seat *seat, bool force)
180 {
181         int ret;
182         struct shl_dlist *iter;
183         struct kmscon_display *d;
184
185         if (seat->foreground)
186                 return 0;
187         if (!seat->awake || (!force && seat->current_sess))
188                 return -EBUSY;
189
190         if (seat->cb) {
191                 ret = seat->cb(seat, KMSCON_SEAT_FOREGROUND, seat->data);
192                 if (ret) {
193                         log_warning("cannot put seat %s into foreground: %d",
194                                     seat->name, ret);
195                         return ret;
196                 }
197         }
198
199         seat->foreground = true;
200
201         shl_dlist_for_each(iter, &seat->displays) {
202                 d = shl_dlist_entry(iter, struct kmscon_display, list);
203                 activate_display(d);
204         }
205
206         return 0;
207 }
208
209 static int seat_go_background(struct kmscon_seat *seat, bool force)
210 {
211         int ret;
212
213         if (!seat->foreground)
214                 return 0;
215         if (!seat->awake || (!force && seat->current_sess))
216                 return -EBUSY;
217
218         if (seat->cb) {
219                 ret = seat->cb(seat, KMSCON_SEAT_BACKGROUND, seat->data);
220                 if (ret) {
221                         log_warning("cannot put seat %s into background: %d",
222                                     seat->name, ret);
223                         return ret;
224                 }
225         }
226
227         seat->foreground = false;
228         return 0;
229 }
230
231 static int seat_go_asleep(struct kmscon_seat *seat, bool force)
232 {
233         int ret, err = 0;
234
235         if (!seat->awake)
236                 return 0;
237         if (seat->current_sess || seat->foreground) {
238                 if (force) {
239                         seat->foreground = false;
240                         seat->current_sess = NULL;
241                         err = -EBUSY;
242                 } else {
243                         return -EBUSY;
244                 }
245         }
246
247         if (seat->cb) {
248                 ret = seat->cb(seat, KMSCON_SEAT_SLEEP, seat->data);
249                 if (ret) {
250                         log_warning("cannot put seat %s asleep: %d",
251                                     seat->name, ret);
252                         if (!force)
253                                 return ret;
254                 }
255         }
256
257         seat->awake = false;
258         uterm_input_sleep(seat->input);
259
260         return err;
261 }
262
263 static int seat_go_awake(struct kmscon_seat *seat)
264 {
265         int ret;
266
267         if (seat->awake)
268                 return 0;
269
270         if (seat->cb) {
271                 ret = seat->cb(seat, KMSCON_SEAT_WAKE_UP, seat->data);
272                 if (ret) {
273                         log_warning("cannot wake up seat %s: %d", seat->name,
274                                     ret);
275                         return ret;
276                 }
277         }
278
279         seat->awake = true;
280         uterm_input_wake_up(seat->input);
281
282         return 0;
283 }
284
285 static int seat_run(struct kmscon_seat *seat)
286 {
287         int ret;
288         struct kmscon_session *session;
289
290         if (!seat->awake)
291                 return -EBUSY;
292         if (seat->current_sess)
293                 return 0;
294
295         if (!seat->scheduled_sess) {
296                 log_debug("no session scheduled to run (num %zu)",
297                           seat->session_count);
298                 return -ENOENT;
299         }
300         session = seat->scheduled_sess;
301
302         if (session->foreground && !seat->foreground) {
303                 ret = seat_go_foreground(seat, false);
304                 if (ret) {
305                         log_warning("cannot put seat %s into foreground for session %p",
306                                     seat->name, session);
307                         return ret;
308                 }
309         } else if (!session->foreground && seat->foreground) {
310                 ret = seat_go_background(seat, false);
311                 if (ret) {
312                         log_warning("cannot put seat %s into background for session %p",
313                                     seat->name, session);
314                         return ret;
315                 }
316         }
317
318         ret = session_call_activate(session);
319         if (ret) {
320                 log_warning("cannot activate session %p: %d", session, ret);
321                 return ret;
322         }
323
324         seat->current_sess = session;
325
326         return 0;
327 }
328
329 static void session_deactivate(struct kmscon_session *sess)
330 {
331         if (sess->seat->current_sess != sess)
332                 return;
333
334         sess->seat->async_schedule = SCHEDULE_SWITCH;
335         sess->deactivating = false;
336         sess->seat->current_sess = NULL;
337 }
338
339 static int seat_pause(struct kmscon_seat *seat, bool force)
340 {
341         int ret;
342
343         if (!seat->current_sess)
344                 return 0;
345
346         seat->current_sess->deactivating = true;
347         ret = session_call_deactivate(seat->current_sess);
348         if (ret) {
349                 if (ret == -EINPROGRESS)
350                         log_debug("pending deactivation for session %p",
351                                   seat->current_sess);
352                 else
353                         log_warning("cannot deactivate session %p: %d",
354                                     seat->current_sess, ret);
355                 if (!force)
356                         return ret;
357         }
358
359         session_deactivate(seat->current_sess);
360
361         return ret;
362 }
363
364 static void seat_reschedule(struct kmscon_seat *seat)
365 {
366         struct shl_dlist *iter, *start;
367         struct kmscon_session *sess;
368
369         if (seat->scheduled_sess && seat->scheduled_sess->enabled)
370                 return;
371
372         if (seat->current_sess && seat->current_sess->enabled) {
373                 seat->scheduled_sess = seat->current_sess;
374                 return;
375         }
376
377         if (seat->current_sess)
378                 start = &seat->current_sess->list;
379         else
380                 start = &seat->sessions;
381
382         shl_dlist_for_each_but_one(iter, start, &seat->sessions) {
383                 sess = shl_dlist_entry(iter, struct kmscon_session, list);
384                 if (sess == seat->dummy_sess || !sess->enabled)
385                         continue;
386                 seat->scheduled_sess = sess;
387                 return;
388         }
389
390         if (seat->dummy_sess && seat->dummy_sess->enabled)
391                 seat->scheduled_sess = seat->dummy_sess;
392         else
393                 seat->scheduled_sess = NULL;
394 }
395
396 static bool seat_has_schedule(struct kmscon_seat *seat)
397 {
398         return seat->scheduled_sess &&
399                seat->scheduled_sess != seat->current_sess;
400 }
401
402 static int seat_switch(struct kmscon_seat *seat)
403 {
404         int ret;
405
406         seat->async_schedule = SCHEDULE_SWITCH;
407         ret = seat_pause(seat, false);
408         if (ret)
409                 return ret;
410
411         return seat_run(seat);
412 }
413
414 static void seat_next(struct kmscon_seat *seat)
415 {
416         struct shl_dlist *cur, *iter;
417         struct kmscon_session *s, *next;
418
419         if (seat->current_sess)
420                 cur = &seat->current_sess->list;
421         else if (seat->session_count)
422                 cur = &seat->sessions;
423         else
424                 return;
425
426         next = NULL;
427         if (!seat->current_sess && seat->dummy_sess &&
428             seat->dummy_sess->enabled)
429                 next = seat->dummy_sess;
430
431         shl_dlist_for_each_but_one(iter, cur, &seat->sessions) {
432                 s = shl_dlist_entry(iter, struct kmscon_session, list);
433                 if (!s->enabled || seat->dummy_sess == s)
434                         continue;
435
436                 next = s;
437                 break;
438         }
439
440         if (!next)
441                 return;
442
443         seat->scheduled_sess = next;
444         seat_switch(seat);
445 }
446
447 static void seat_prev(struct kmscon_seat *seat)
448 {
449         struct shl_dlist *cur, *iter;
450         struct kmscon_session *s, *prev;
451
452         if (seat->current_sess)
453                 cur = &seat->current_sess->list;
454         else if (seat->session_count)
455                 cur = &seat->sessions;
456         else
457                 return;
458
459         prev = NULL;
460         if (!seat->current_sess && seat->dummy_sess &&
461             seat->dummy_sess->enabled)
462                 prev = seat->dummy_sess;
463
464         shl_dlist_for_each_reverse_but_one(iter, cur, &seat->sessions) {
465                 s = shl_dlist_entry(iter, struct kmscon_session, list);
466                 if (!s->enabled || seat->dummy_sess == s)
467                         continue;
468
469                 prev = s;
470                 break;
471         }
472
473         if (!prev)
474                 return;
475
476         seat->scheduled_sess = prev;
477         seat_switch(seat);
478 }
479
480 static int seat_add_display(struct kmscon_seat *seat,
481                             struct uterm_display *disp)
482 {
483         struct kmscon_display *d;
484
485         log_debug("add display %p to seat %s", disp, seat->name);
486
487         d = malloc(sizeof(*d));
488         if (!d)
489                 return -ENOMEM;
490         memset(d, 0, sizeof(*d));
491         d->disp = disp;
492         d->seat = seat;
493
494         uterm_display_ref(d->disp);
495         shl_dlist_link(&seat->displays, &d->list);
496         activate_display(d);
497         return 0;
498 }
499
500 static void seat_remove_display(struct kmscon_seat *seat,
501                                 struct kmscon_display *d)
502 {
503         struct shl_dlist *iter, *tmp;
504         struct kmscon_session *s;
505
506         log_debug("remove display %p from seat %s", d->disp, seat->name);
507
508         shl_dlist_unlink(&d->list);
509
510         if (d->activated) {
511                 shl_dlist_for_each_safe(iter, tmp, &seat->sessions) {
512                         s = shl_dlist_entry(iter, struct kmscon_session, list);
513                         session_call_display_gone(s, d->disp);
514                 }
515         }
516
517         uterm_display_unref(d->disp);
518         free(d);
519 }
520
521 static void seat_refresh_display(struct kmscon_seat *seat,
522                                  struct kmscon_display *d)
523 {
524         struct shl_dlist *iter;
525         struct kmscon_session *s;
526
527         log_debug("refresh display %p from seat %s", d->disp, seat->name);
528
529         if (d->activated) {
530                 shl_dlist_for_each(iter, &seat->sessions) {
531                         s = shl_dlist_entry(iter, struct kmscon_session, list);
532                         session_call_display_refresh(s, d->disp);
533                 }
534         }
535 }
536
537 static int seat_vt_event(struct uterm_vt *vt, struct uterm_vt_event *ev,
538                          void *data)
539 {
540         struct kmscon_seat *seat = data;
541         int ret;
542
543         switch (ev->action) {
544         case UTERM_VT_ACTIVATE:
545                 ret = seat_go_awake(seat);
546                 if (ret)
547                         return ret;
548                 seat_run(seat);
549                 break;
550         case UTERM_VT_DEACTIVATE:
551                 seat->async_schedule = SCHEDULE_VT;
552                 ret = seat_pause(seat, false);
553                 if (ret)
554                         return ret;
555                 ret = seat_go_background(seat, false);
556                 if (ret)
557                         return ret;
558                 ret = seat_go_asleep(seat, false);
559                 if (ret)
560                         return ret;
561                 break;
562         case UTERM_VT_HUP:
563                 if (seat->cb)
564                         seat->cb(seat, KMSCON_SEAT_HUP, seat->data);
565                 break;
566         }
567
568         return 0;
569 }
570
571 static void seat_input_event(struct uterm_input *input,
572                              struct uterm_input_event *ev,
573                              void *data)
574 {
575         struct kmscon_seat *seat = data;
576         struct kmscon_session *s;
577         int ret;
578
579         if (ev->handled || !seat->awake)
580                 return;
581
582         if (conf_grab_matches(seat->conf->grab_session_next,
583                               ev->mods, ev->num_syms, ev->keysyms)) {
584                 ev->handled = true;
585                 if (!seat->conf->session_control)
586                         return;
587                 seat_next(seat);
588                 return;
589         }
590         if (conf_grab_matches(seat->conf->grab_session_prev,
591                               ev->mods, ev->num_syms, ev->keysyms)) {
592                 ev->handled = true;
593                 if (!seat->conf->session_control)
594                         return;
595                 seat_prev(seat);
596                 return;
597         }
598         if (conf_grab_matches(seat->conf->grab_session_dummy,
599                               ev->mods, ev->num_syms, ev->keysyms)) {
600                 ev->handled = true;
601                 if (!seat->conf->session_control)
602                         return;
603                 seat->scheduled_sess = seat->dummy_sess;
604                 seat_switch(seat);
605                 return;
606         }
607         if (conf_grab_matches(seat->conf->grab_session_close,
608                               ev->mods, ev->num_syms, ev->keysyms)) {
609                 ev->handled = true;
610                 if (!seat->conf->session_control)
611                         return;
612                 s = seat->current_sess;
613                 if (!s)
614                         return;
615                 if (s == seat->dummy_sess)
616                         return;
617
618                 /* First time this is invoked on a session, we simply try
619                  * unloading it. If it fails, we give it some time. If this is
620                  * invoked a second time, we notice that we already tried
621                  * removing it and so we go straight to unregistering the
622                  * session unconditionally. */
623                 if (!s->deactivating) {
624                         seat->async_schedule = SCHEDULE_UNREGISTER;
625                         ret = seat_pause(seat, false);
626                         if (ret)
627                                 return;
628                 }
629
630                 kmscon_session_unregister(s);
631                 return;
632         }
633         if (conf_grab_matches(seat->conf->grab_terminal_new,
634                               ev->mods, ev->num_syms, ev->keysyms)) {
635                 ev->handled = true;
636                 if (!seat->conf->session_control)
637                         return;
638                 ret = kmscon_terminal_register(&s, seat,
639                                                uterm_vt_get_num(seat->vt));
640                 if (ret == -EOPNOTSUPP) {
641                         log_notice("terminal support not compiled in");
642                 } else if (ret) {
643                         log_error("cannot register terminal session: %d", ret);
644                 } else {
645                         s->enabled = true;
646                         seat->scheduled_sess = s;
647                         seat_switch(seat);
648                 }
649                 return;
650         }
651 }
652
653 int kmscon_seat_new(struct kmscon_seat **out,
654                     struct conf_ctx *main_conf,
655                     struct ev_eloop *eloop,
656                     struct uterm_vt_master *vtm,
657                     unsigned int vt_types,
658                     const char *seatname,
659                     kmscon_seat_cb_t cb,
660                     void *data)
661 {
662         struct kmscon_seat *seat;
663         int ret;
664         char *keymap;
665
666         if (!out || !eloop || !vtm || !seatname)
667                 return -EINVAL;
668
669         seat = malloc(sizeof(*seat));
670         if (!seat)
671                 return -ENOMEM;
672         memset(seat, 0, sizeof(*seat));
673         seat->eloop = eloop;
674         seat->vtm = vtm;
675         seat->cb = cb;
676         seat->data = data;
677         shl_dlist_init(&seat->displays);
678         shl_dlist_init(&seat->sessions);
679
680         seat->name = strdup(seatname);
681         if (!seat->name) {
682                 log_error("cannot copy string");
683                 ret = -ENOMEM;
684                 goto err_free;
685         }
686
687         ret = kmscon_conf_new(&seat->conf_ctx);
688         if (ret) {
689                 log_error("cannot create seat configuration object: %d", ret);
690                 goto err_name;
691         }
692         seat->conf = conf_ctx_get_mem(seat->conf_ctx);
693
694         ret = kmscon_conf_load_seat(seat->conf_ctx, main_conf, seat->name);
695         if (ret) {
696                 log_error("cannot parse seat configuration on seat %s: %d",
697                           seat->name, ret);
698                 goto err_conf;
699         }
700
701         /* TODO: The XKB-API currently requires zero-terminated strings as
702          * keymap input. Hence, we have to read it in instead of using mmap().
703          * We should fix this upstream! */
704         keymap = NULL;
705         if (seat->conf->xkb_keymap && *seat->conf->xkb_keymap) {
706                 ret = shl_read_file(seat->conf->xkb_keymap, &keymap, NULL);
707                 if (ret)
708                         log_error("cannot read keymap file %s: %d",
709                                   seat->conf->xkb_keymap, ret);
710         }
711
712         ret = uterm_input_new(&seat->input, seat->eloop,
713                               seat->conf->xkb_model,
714                               seat->conf->xkb_layout,
715                               seat->conf->xkb_variant,
716                               seat->conf->xkb_options,
717                               keymap,
718                               seat->conf->xkb_repeat_delay,
719                               seat->conf->xkb_repeat_rate);
720         free(keymap);
721
722         if (ret)
723                 goto err_conf;
724
725         ret = uterm_input_register_cb(seat->input, seat_input_event, seat);
726         if (ret)
727                 goto err_input;
728
729         ret = uterm_vt_allocate(seat->vtm, &seat->vt,
730                                 vt_types, seat->name,
731                                 seat->input, seat->conf->vt, seat_vt_event,
732                                 seat);
733         if (ret)
734                 goto err_input_cb;
735
736         ev_eloop_ref(seat->eloop);
737         uterm_vt_master_ref(seat->vtm);
738         *out = seat;
739         return 0;
740
741 err_input_cb:
742         uterm_input_unregister_cb(seat->input, seat_input_event, seat);
743 err_input:
744         uterm_input_unref(seat->input);
745 err_conf:
746         kmscon_conf_free(seat->conf_ctx);
747 err_name:
748         free(seat->name);
749 err_free:
750         free(seat);
751         return ret;
752 }
753
754 void kmscon_seat_free(struct kmscon_seat *seat)
755 {
756         struct kmscon_display *d;
757         struct kmscon_session *s;
758         int ret;
759
760         if (!seat)
761                 return;
762
763         ret = seat_pause(seat, true);
764         if (ret)
765                 log_warning("destroying seat %s while session %p is active",
766                             seat->name, seat->current_sess);
767
768         ret = seat_go_asleep(seat, true);
769         if (ret)
770                 log_warning("destroying seat %s while still awake: %d",
771                             seat->name, ret);
772
773         while (!shl_dlist_empty(&seat->sessions)) {
774                 s = shl_dlist_entry(seat->sessions.next,
775                                     struct kmscon_session,
776                                     list);
777                 kmscon_session_unregister(s);
778         }
779
780         while (!shl_dlist_empty(&seat->displays)) {
781                 d = shl_dlist_entry(seat->displays.next,
782                                     struct kmscon_display,
783                                     list);
784                 seat_remove_display(seat, d);
785         }
786
787         uterm_vt_deallocate(seat->vt);
788         uterm_input_unregister_cb(seat->input, seat_input_event, seat);
789         uterm_input_unref(seat->input);
790         kmscon_conf_free(seat->conf_ctx);
791         free(seat->name);
792         uterm_vt_master_unref(seat->vtm);
793         ev_eloop_unref(seat->eloop);
794         free(seat);
795 }
796
797 void kmscon_seat_startup(struct kmscon_seat *seat)
798 {
799         int ret;
800         struct kmscon_session *s;
801
802         if (!seat)
803                 return;
804
805         ret = kmscon_dummy_register(&s, seat);
806         if (ret == -EOPNOTSUPP) {
807                 log_notice("dummy sessions not compiled in");
808         } else if (ret) {
809                 log_error("cannot register dummy session: %d", ret);
810         } else {
811                 seat->dummy_sess = s;
812                 kmscon_session_enable(s);
813         }
814
815         if (seat->conf->terminal_session) {
816                 ret = kmscon_terminal_register(&s, seat,
817                                                uterm_vt_get_num(seat->vt));
818                 if (ret == -EOPNOTSUPP)
819                         log_notice("terminal support not compiled in");
820                 else if (ret)
821                         log_error("cannot register terminal session");
822                 else
823                         kmscon_session_enable(s);
824         }
825
826         if (seat->conf->switchvt ||
827             uterm_vt_get_type(seat->vt) == UTERM_VT_FAKE)
828                 uterm_vt_activate(seat->vt);
829 }
830
831 int kmscon_seat_add_display(struct kmscon_seat *seat,
832                             struct uterm_display *disp)
833 {
834         if (!seat || !disp)
835                 return -EINVAL;
836
837         return seat_add_display(seat, disp);
838 }
839
840 void kmscon_seat_remove_display(struct kmscon_seat *seat,
841                                 struct uterm_display *disp)
842 {
843         struct shl_dlist *iter;
844         struct kmscon_display *d;
845
846         if (!seat || !disp)
847                 return;
848
849         shl_dlist_for_each(iter, &seat->displays) {
850                 d = shl_dlist_entry(iter, struct kmscon_display, list);
851                 if (d->disp != disp)
852                         continue;
853
854                 seat_remove_display(seat, d);
855                 break;
856         }
857 }
858
859 void kmscon_seat_refresh_display(struct kmscon_seat *seat,
860                                  struct uterm_display *disp)
861 {
862         struct shl_dlist *iter;
863         struct kmscon_display *d;
864
865         if (!seat || !disp)
866                 return;
867
868         shl_dlist_for_each(iter, &seat->displays) {
869                 d = shl_dlist_entry(iter, struct kmscon_display, list);
870                 if (d->disp != disp)
871                         continue;
872
873                 seat_refresh_display(seat, d);
874                 break;
875         }
876 }
877
878 int kmscon_seat_add_input(struct kmscon_seat *seat, const char *node)
879 {
880         if (!seat || !node)
881                 return -EINVAL;
882
883         uterm_input_add_dev(seat->input, node);
884         return 0;
885 }
886
887 void kmscon_seat_remove_input(struct kmscon_seat *seat, const char *node)
888 {
889         if (!seat || !node)
890                 return;
891
892         uterm_input_remove_dev(seat->input, node);
893 }
894
895 const char *kmscon_seat_get_name(struct kmscon_seat *seat)
896 {
897         if (!seat)
898                 return NULL;
899
900         return seat->name;
901 }
902
903 struct uterm_input *kmscon_seat_get_input(struct kmscon_seat *seat)
904 {
905         if (!seat)
906                 return NULL;
907
908         return seat->input;
909 }
910
911 struct ev_eloop *kmscon_seat_get_eloop(struct kmscon_seat *seat)
912 {
913         if (!seat)
914                 return NULL;
915
916         return seat->eloop;
917 }
918
919 struct conf_ctx *kmscon_seat_get_conf(struct kmscon_seat *seat)
920 {
921         if (!seat)
922                 return NULL;
923
924         return seat->conf_ctx;
925 }
926
927 void kmscon_seat_schedule(struct kmscon_seat *seat, unsigned int id)
928 {
929         struct shl_dlist *iter;
930         struct kmscon_session *s, *next;
931
932         if (!seat)
933                 return;
934
935         next = seat->dummy_sess;
936         shl_dlist_for_each(iter, &seat->sessions) {
937                 s = shl_dlist_entry(iter, struct kmscon_session, list);
938                 if (!s->enabled || seat->dummy_sess == s ||
939                     seat->current_sess == s)
940                         continue;
941
942                 next = s;
943                 if (!id--)
944                         break;
945         }
946
947         seat->scheduled_sess = next;
948         if (seat_has_schedule(seat))
949                 seat_switch(seat);
950 }
951
952 int kmscon_seat_register_session(struct kmscon_seat *seat,
953                                  struct kmscon_session **out,
954                                  kmscon_session_cb_t cb,
955                                  void *data)
956 {
957         struct kmscon_session *sess;
958         struct shl_dlist *iter;
959         struct kmscon_display *d;
960
961         if (!seat || !out)
962                 return -EINVAL;
963
964         if (seat->conf->session_max &&
965             seat->session_count >= seat->conf->session_max) {
966                 log_warning("maximum number of sessions reached (%d), dropping new session",
967                             seat->conf->session_max);
968                 return -EOVERFLOW;
969         }
970
971         sess = malloc(sizeof(*sess));
972         if (!sess) {
973                 log_error("cannot allocate memory for new session on seat %s",
974                           seat->name);
975                 return -ENOMEM;
976         }
977
978         log_debug("register session %p", sess);
979
980         memset(sess, 0, sizeof(*sess));
981         sess->ref = 1;
982         sess->seat = seat;
983         sess->cb = cb;
984         sess->data = data;
985         sess->foreground = true;
986
987         /* register new sessions next to the current one */
988         if (seat->current_sess)
989                 shl_dlist_link(&seat->current_sess->list, &sess->list);
990         else
991                 shl_dlist_link_tail(&seat->sessions, &sess->list);
992
993         ++seat->session_count;
994         *out = sess;
995
996         shl_dlist_for_each(iter, &seat->displays) {
997                 d = shl_dlist_entry(iter, struct kmscon_display, list);
998                 session_call_display_new(sess, d->disp);
999         }
1000
1001         return 0;
1002 }
1003
1004 void kmscon_session_ref(struct kmscon_session *sess)
1005 {
1006         if (!sess || !sess->ref)
1007                 return;
1008
1009         ++sess->ref;
1010 }
1011
1012 void kmscon_session_unref(struct kmscon_session *sess)
1013 {
1014         if (!sess || !sess->ref || --sess->ref)
1015                 return;
1016
1017         kmscon_session_unregister(sess);
1018         free(sess);
1019 }
1020
1021 void kmscon_session_unregister(struct kmscon_session *sess)
1022 {
1023         struct kmscon_seat *seat;
1024         int ret;
1025         bool forced = false;
1026
1027         if (!sess || !sess->seat)
1028                 return;
1029
1030         log_debug("unregister session %p", sess);
1031
1032         seat = sess->seat;
1033         sess->enabled = false;
1034         if (seat->dummy_sess == sess)
1035                 seat->dummy_sess = NULL;
1036         seat_reschedule(seat);
1037
1038         if (seat->current_sess == sess) {
1039                 ret = seat_pause(seat, true);
1040                 if (ret) {
1041                         forced = true;
1042                         log_warning("unregistering active session %p; skipping automatic session-switch",
1043                                     sess);
1044                 }
1045         }
1046
1047         shl_dlist_unlink(&sess->list);
1048         --seat->session_count;
1049         sess->seat = NULL;
1050
1051         session_call(sess, KMSCON_SESSION_UNREGISTER, NULL);
1052         kmscon_session_unref(sess);
1053
1054         /* If this session was active and we couldn't deactivate it, then it
1055          * might still have resources allocated that couldn't get freed. In this
1056          * case we should not automatically switch to the next session as it is
1057          * very likely that it will not be able to start.
1058          * Instead, we stay inactive and wait for user/external input to switch
1059          * to another session. This delay will then hopefully be long enough so
1060          * all resources got freed. */
1061         if (!forced)
1062                 seat_run(seat);
1063 }
1064
1065 bool kmscon_session_is_registered(struct kmscon_session *sess)
1066 {
1067         return sess && sess->seat;
1068 }
1069
1070 bool kmscon_session_is_active(struct kmscon_session *sess)
1071 {
1072         return sess && sess->seat && sess->seat->current_sess == sess;
1073 }
1074
1075 int kmscon_session_set_foreground(struct kmscon_session *sess)
1076 {
1077         struct kmscon_seat *seat;
1078         int ret;
1079
1080         if (!sess)
1081                 return -EINVAL;
1082         if (sess->foreground)
1083                 return 0;
1084
1085         seat = sess->seat;
1086         if (seat && seat->current_sess == sess && !seat->foreground) {
1087                 ret = seat_go_foreground(seat, true);
1088                 if (ret)
1089                         return ret;
1090         }
1091
1092         sess->foreground = true;
1093         return 0;
1094 }
1095
1096 int kmscon_session_set_background(struct kmscon_session *sess)
1097 {
1098         struct kmscon_seat *seat;
1099         int ret;
1100
1101         if (!sess)
1102                 return -EINVAL;
1103         if (!sess->foreground)
1104                 return 0;
1105
1106         seat = sess->seat;
1107         if (seat && seat->current_sess == sess && seat->foreground) {
1108                 ret = seat_go_background(seat, true);
1109                 if (ret)
1110                         return ret;
1111         }
1112
1113         sess->foreground = false;
1114         return 0;
1115 }
1116
1117 void kmscon_session_schedule(struct kmscon_session *sess)
1118 {
1119         struct kmscon_seat *seat;
1120
1121         if (!sess || !sess->seat)
1122                 return;
1123
1124         seat = sess->seat;
1125         seat->scheduled_sess = sess;
1126         seat_reschedule(seat);
1127         if (seat_has_schedule(seat))
1128                 seat_switch(seat);
1129 }
1130
1131 void kmscon_session_enable(struct kmscon_session *sess)
1132 {
1133         if (!sess || sess->enabled)
1134                 return;
1135
1136         log_debug("enable session %p", sess);
1137         sess->enabled = true;
1138         if (sess->seat &&
1139             (!sess->seat->current_sess ||
1140              sess->seat->current_sess == sess->seat->dummy_sess)) {
1141                 sess->seat->scheduled_sess = sess;
1142                 if (seat_has_schedule(sess->seat))
1143                         seat_switch(sess->seat);
1144         }
1145 }
1146
1147 void kmscon_session_disable(struct kmscon_session *sess)
1148 {
1149         if (!sess || !sess->enabled)
1150                 return;
1151
1152         log_debug("disable session %p", sess);
1153         sess->enabled = false;
1154 }
1155
1156 bool kmscon_session_is_enabled(struct kmscon_session *sess)
1157 {
1158         return sess && sess->enabled;
1159 }
1160
1161 void kmscon_session_notify_deactivated(struct kmscon_session *sess)
1162 {
1163         struct kmscon_seat *seat;
1164         int ret;
1165         unsigned int sched;
1166
1167         if (!sess || !sess->seat)
1168                 return;
1169
1170         seat = sess->seat;
1171         if (seat->current_sess != sess)
1172                 return;
1173
1174         sched = seat->async_schedule;
1175         log_debug("session %p notified core about deactivation (schedule: %u)",
1176                   sess, sched);
1177         session_deactivate(sess);
1178         seat_reschedule(seat);
1179
1180         if (sched == SCHEDULE_VT) {
1181                 ret = seat_go_background(seat, false);
1182                 if (ret)
1183                         return;
1184                 ret = seat_go_asleep(seat, false);
1185                 if (ret)
1186                         return;
1187                 uterm_vt_retry(seat->vt);
1188         } else if (sched == SCHEDULE_UNREGISTER) {
1189                 kmscon_session_unregister(sess);
1190         } else {
1191                 seat_switch(seat);
1192         }
1193 }