build: replace genshader by binary linker
[platform/upstream/kmscon.git] / src / kmscon_main.c
1 /*
2  * kmscon - KMS Console
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 #include <errno.h>
27 #include <paths.h>
28 #include <signal.h>
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/signalfd.h>
34 #include "conf.h"
35 #include "eloop.h"
36 #include "kmscon_conf.h"
37 #include "kmscon_module.h"
38 #include "kmscon_seat.h"
39 #include "shl_dlist.h"
40 #include "shl_log.h"
41 #include "shl_misc.h"
42 #include "text.h"
43 #include "uterm_input.h"
44 #include "uterm_monitor.h"
45 #include "uterm_video.h"
46 #include "uterm_vt.h"
47
48 struct app_video {
49         struct shl_dlist list;
50         struct app_seat *seat;
51         struct uterm_monitor_dev *udev;
52
53         char *node;
54         struct uterm_video *video;
55 };
56
57 struct app_seat {
58         struct shl_dlist list;
59         struct kmscon_app *app;
60         struct uterm_monitor_seat *useat;
61
62         bool awake;
63         char *name;
64         struct kmscon_seat *seat;
65         struct conf_ctx *conf_ctx;
66         struct kmscon_conf_t *conf;
67         struct shl_dlist videos;
68 };
69
70 struct kmscon_app {
71         struct conf_ctx *conf_ctx;
72         struct kmscon_conf_t *conf;
73         bool exiting;
74
75         struct ev_eloop *eloop;
76         unsigned int vt_exit_count;
77
78         struct uterm_vt_master *vtm;
79         struct uterm_monitor *mon;
80         struct shl_dlist seats;
81         unsigned int running_seats;
82 };
83
84 static int app_seat_event(struct kmscon_seat *s, unsigned int event,
85                           void *data)
86 {
87         struct app_seat *seat = data;
88         struct kmscon_app *app = seat->app;
89         struct shl_dlist *iter;
90         struct app_video *vid;
91
92         switch (event) {
93         case KMSCON_SEAT_FOREGROUND:
94                 seat->awake = true;
95
96                 shl_dlist_for_each(iter, &seat->videos) {
97                         vid = shl_dlist_entry(iter, struct app_video, list);
98                         uterm_video_wake_up(vid->video);
99                 }
100                 break;
101         case KMSCON_SEAT_BACKGROUND:
102                 shl_dlist_for_each(iter, &seat->videos) {
103                         vid = shl_dlist_entry(iter, struct app_video, list);
104                         uterm_video_sleep(vid->video);
105                 }
106
107                 seat->awake = false;
108                 break;
109         case KMSCON_SEAT_SLEEP:
110                 if (app->vt_exit_count > 0) {
111                         log_debug("deactivating VT on exit, %d to go",
112                                   app->vt_exit_count - 1);
113                         if (!--app->vt_exit_count)
114                                 ev_eloop_exit(app->eloop);
115                 }
116                 break;
117         case KMSCON_SEAT_WAKE_UP:
118                 if (app->exiting)
119                         return -EBUSY;
120                 break;
121         case KMSCON_SEAT_HUP:
122                 kmscon_seat_free(seat->seat);
123                 seat->seat = NULL;
124
125                 if (!app->conf->listen) {
126                         --app->running_seats;
127                         if (!app->running_seats) {
128                                 log_debug("seat HUP on %s in default-mode; exiting...",
129                                           seat->name);
130                                 ev_eloop_exit(app->eloop);
131                         } else {
132                                 log_debug("seat HUP on %s in default-mode; %u more running seats",
133                                           seat->name, app->running_seats);
134                         }
135                 } else {
136                         /* Seat HUP here means that we are running in
137                          * listen-mode on a modular-VT like kmscon-fake-VTs. But
138                          * this is an invalid setup. In listen-mode we
139                          * exclusively run as seat-VT-master without a
140                          * controlling VT and we effectively prevent other
141                          * setups during startup. Hence, we can safely drop the
142                          * seat here and ignore it.
143                          * You can destroy and recreate the seat to make kmscon
144                          * pick it up again in listen-mode. */
145                         log_warning("seat HUP on %s in listen-mode; dropping seat...",
146                                     seat->name);
147                 }
148
149                 break;
150         }
151
152         return 0;
153 }
154
155 static int app_seat_new(struct kmscon_app *app, const char *sname,
156                         struct uterm_monitor_seat *useat)
157 {
158         struct app_seat *seat;
159         int ret;
160         unsigned int i, types;
161         bool found;
162         char *cseat;
163
164         if (app->exiting)
165                 return -EBUSY;
166
167         found = false;
168         if (kmscon_conf_is_all_seats(app->conf)) {
169                 found = true;
170         } else if (kmscon_conf_is_current_seat(app->conf)) {
171                 cseat = getenv("XDG_SEAT");
172                 if (!cseat)
173                         cseat = "seat0";
174                 if (!strcmp(cseat, sname))
175                         found = true;
176         } else {
177                 for (i = 0; app->conf->seats[i]; ++i) {
178                         if (!strcmp(app->conf->seats[i], sname)) {
179                                 found = true;
180                                 break;
181                         }
182                 }
183         }
184
185         if (!found) {
186                 log_info("ignoring new seat %s as not specified in seat-list",
187                          sname);
188                 return -ERANGE;
189         }
190
191         log_debug("new seat %s", sname);
192
193         seat = malloc(sizeof(*seat));
194         if (!seat) {
195                 log_error("cannot allocate memory for seat %s", sname);
196                 return -ENOMEM;
197         }
198         memset(seat, 0, sizeof(*seat));
199         seat->app = app;
200         seat->useat = useat;
201         shl_dlist_init(&seat->videos);
202
203         seat->name = strdup(sname);
204         if (!seat->name) {
205                 log_error("cannot copy seat name on seat %s", sname);
206                 ret = -ENOMEM;
207                 goto err_free;
208         }
209
210         types = UTERM_VT_FAKE;
211         if (!app->conf->listen)
212                 types |= UTERM_VT_REAL;
213
214         ret = kmscon_seat_new(&seat->seat, app->conf_ctx, app->eloop, app->vtm,
215                               types, sname, app_seat_event, seat);
216         if (ret) {
217                 if (ret == -ERANGE)
218                         log_debug("ignoring seat %s as it already has a seat manager",
219                                   sname);
220                 else
221                         log_error("cannot create seat object on seat %s: %d",
222                                   sname, ret);
223                 goto err_name;
224         }
225         seat->conf_ctx = kmscon_seat_get_conf(seat->seat);
226         seat->conf = conf_ctx_get_mem(seat->conf_ctx);
227
228         uterm_monitor_set_seat_data(seat->useat, seat);
229         shl_dlist_link(&app->seats, &seat->list);
230         ++app->running_seats;
231
232         kmscon_seat_startup(seat->seat);
233
234         return 0;
235
236 err_name:
237         free(seat->name);
238 err_free:
239         free(seat);
240         return ret;
241 }
242
243 static void app_seat_free(struct app_seat *seat)
244 {
245         log_debug("free seat %s", seat->name);
246
247         shl_dlist_unlink(&seat->list);
248         uterm_monitor_set_seat_data(seat->useat, NULL);
249         kmscon_seat_free(seat->seat);
250         free(seat->name);
251         free(seat);
252 }
253
254 static void app_seat_video_event(struct uterm_video *video,
255                                  struct uterm_video_hotplug *ev,
256                                  void *data)
257 {
258         struct app_video *vid = data;
259
260         switch (ev->action) {
261         case UTERM_NEW:
262                 if (!vid->seat->app->exiting)
263                         kmscon_seat_add_display(vid->seat->seat, ev->display);
264                 break;
265         case UTERM_GONE:
266                 kmscon_seat_remove_display(vid->seat->seat, ev->display);
267                 break;
268         case UTERM_REFRESH:
269                 if (!vid->seat->app->exiting)
270                         kmscon_seat_refresh_display(vid->seat->seat,
271                                                     ev->display);
272                 break;
273         }
274 }
275
276 static bool app_seat_gpu_is_ignored(struct app_seat *seat,
277                                     unsigned int type,
278                                     bool drm_backed,
279                                     bool primary,
280                                     bool aux,
281                                     const char *node)
282 {
283         switch (type) {
284         case UTERM_MONITOR_FBDEV:
285                 if (seat->conf->drm) {
286                         if (drm_backed) {
287                                 log_info("ignoring video device %s on seat %s as it is a DRM-fbdev device",
288                                          node, seat->name);
289                                 return true;
290                         }
291                 }
292                 break;
293         case UTERM_MONITOR_DRM:
294                 if (!seat->conf->drm) {
295                         log_info("ignoring video device %s on seat %s as it is a DRM device",
296                                   node, seat->name);
297                         return true;
298                 }
299                 break;
300         default:
301                 log_info("ignoring unknown video device %s on seat %s",
302                          node, seat->name);
303                 return true;
304         }
305
306         if (seat->conf->gpus == KMSCON_GPU_PRIMARY && !primary) {
307                 log_info("ignoring video device %s on seat %s as it is no primary GPU",
308                          node, seat->name);
309                 return true;
310         }
311
312         if (seat->conf->gpus == KMSCON_GPU_AUX && !primary && !aux) {
313                 log_info("ignoring video device %s on seat %s as it is neither a primary nor auxiliary GPU",
314                          node, seat->name);
315                 return true;
316         }
317
318         return false;
319 }
320
321 static int app_seat_add_video(struct app_seat *seat,
322                               unsigned int type,
323                               unsigned int flags,
324                               const char *node,
325                               struct uterm_monitor_dev *udev)
326 {
327         int ret;
328         const struct uterm_video_module *mode;
329         struct app_video *vid;
330
331         if (seat->app->exiting)
332                 return -EBUSY;
333
334         if (app_seat_gpu_is_ignored(seat, type,
335                                     flags & UTERM_MONITOR_DRM_BACKED,
336                                     flags & UTERM_MONITOR_PRIMARY,
337                                     flags & UTERM_MONITOR_AUX,
338                                     node))
339                 return -ERANGE;
340
341         log_debug("new video device %s on seat %s", node, seat->name);
342
343         vid = malloc(sizeof(*vid));
344         if (!vid) {
345                 log_error("cannot allocate memory for video device %s on seat %s",
346                           node, seat->name);
347                 return -ENOMEM;
348         }
349         memset(vid, 0, sizeof(*vid));
350         vid->seat = seat;
351         vid->udev = udev;
352
353         vid->node = strdup(node);
354         if (!vid->node) {
355                 log_error("cannot copy video device name %s on seat %s",
356                           node, seat->name);
357                 ret = -ENOMEM;
358                 goto err_free;
359         }
360
361         if (type == UTERM_MONITOR_DRM) {
362                 if (seat->conf->hwaccel)
363                         mode = UTERM_VIDEO_DRM3D;
364                 else
365                         mode = UTERM_VIDEO_DRM2D;
366         } else {
367                 mode = UTERM_VIDEO_FBDEV;
368         }
369
370         ret = uterm_video_new(&vid->video, seat->app->eloop, node, mode);
371         if (ret) {
372                 if (mode == UTERM_VIDEO_DRM3D) {
373                         log_info("cannot create drm3d device %s on seat %s (%d); trying drm2d mode",
374                                  vid->node, seat->name, ret);
375                         ret = uterm_video_new(&vid->video, seat->app->eloop,
376                                               node, UTERM_VIDEO_DRM2D);
377                         if (ret)
378                                 goto err_node;
379                 } else {
380                         goto err_node;
381                 }
382         }
383
384         ret = uterm_video_register_cb(vid->video, app_seat_video_event, vid);
385         if (ret) {
386                 log_error("cannot register video callback for device %s on seat %s: %d",
387                           vid->node, seat->name, ret);
388                 goto err_video;
389         }
390
391         if (seat->awake)
392                 uterm_video_wake_up(vid->video);
393
394         uterm_monitor_set_dev_data(vid->udev, vid);
395         shl_dlist_link(&seat->videos, &vid->list);
396         return 0;
397
398 err_video:
399         uterm_video_unref(vid->video);
400 err_node:
401         free(vid->node);
402 err_free:
403         free(vid);
404         return ret;
405 }
406
407 static void app_seat_remove_video(struct app_seat *seat, struct app_video *vid)
408 {
409         struct uterm_display *disp;
410
411         log_debug("free video device %s on seat %s", vid->node, seat->name);
412
413         shl_dlist_unlink(&vid->list);
414         uterm_monitor_set_dev_data(vid->udev, NULL);
415         uterm_video_unregister_cb(vid->video, app_seat_video_event, vid);
416
417         disp = uterm_video_get_displays(vid->video);
418         while (disp) {
419                 kmscon_seat_remove_display(seat->seat, disp);
420                 disp = uterm_display_next(disp);
421         }
422
423         uterm_video_unref(vid->video);
424         free(vid->node);
425         free(vid);
426 }
427
428 static void app_monitor_event(struct uterm_monitor *mon,
429                               struct uterm_monitor_event *ev,
430                               void *data)
431 {
432         struct kmscon_app *app = data;
433         struct app_seat *seat;
434         struct app_video *vid;
435         int ret;
436
437         switch (ev->type) {
438         case UTERM_MONITOR_NEW_SEAT:
439                 ret = app_seat_new(app, ev->seat_name, ev->seat);
440                 if (ret)
441                         return;
442                 break;
443         case UTERM_MONITOR_FREE_SEAT:
444                 if (ev->seat_data)
445                         app_seat_free(ev->seat_data);
446                 break;
447         case UTERM_MONITOR_NEW_DEV:
448                 seat = ev->seat_data;
449                 if (!seat)
450                         return;
451
452                 switch (ev->dev_type) {
453                 case UTERM_MONITOR_DRM:
454                 case UTERM_MONITOR_FBDEV:
455                         ret = app_seat_add_video(seat, ev->dev_type,
456                                                  ev->dev_flags,
457                                                  ev->dev_node, ev->dev);
458                         if (ret)
459                                 return;
460                         break;
461                 case UTERM_MONITOR_INPUT:
462                         log_debug("new input device %s on seat %s",
463                                   ev->dev_node, seat->name);
464                         kmscon_seat_add_input(seat->seat, ev->dev_node);
465                         break;
466                 }
467                 break;
468         case UTERM_MONITOR_FREE_DEV:
469                 seat = ev->seat_data;
470                 if (!seat)
471                         return;
472
473                 switch (ev->dev_type) {
474                 case UTERM_MONITOR_DRM:
475                 case UTERM_MONITOR_FBDEV:
476                         if (ev->dev_data)
477                                 app_seat_remove_video(seat, ev->dev_data);
478                         break;
479                 case UTERM_MONITOR_INPUT:
480                         log_debug("free input device %s on seat %s",
481                                   ev->dev_node, seat->name);
482                         kmscon_seat_remove_input(seat->seat, ev->dev_node);
483                         break;
484                 }
485                 break;
486         case UTERM_MONITOR_HOTPLUG_DEV:
487                 seat = ev->seat_data;
488                 if (!seat)
489                         return;
490
491                 switch (ev->dev_type) {
492                 case UTERM_MONITOR_DRM:
493                 case UTERM_MONITOR_FBDEV:
494                         vid = ev->dev_data;
495                         if (!vid)
496                                 return;
497
498                         log_debug("video hotplug event on device %s on seat %s",
499                                   vid->node, seat->name);
500                         uterm_video_poll(vid->video);
501                         break;
502                 }
503                 break;
504         }
505 }
506
507 static void app_sig_generic(struct ev_eloop *eloop,
508                             struct signalfd_siginfo *info,
509                             void *data)
510 {
511         struct kmscon_app *app = data;
512
513         log_info("terminating due to caught signal %d", info->ssi_signo);
514         ev_eloop_exit(app->eloop);
515 }
516
517 static void app_sig_ignore(struct ev_eloop *eloop,
518                            struct signalfd_siginfo *info,
519                            void *data)
520 {
521 }
522
523 static void destroy_app(struct kmscon_app *app)
524 {
525         uterm_monitor_unref(app->mon);
526         uterm_vt_master_unref(app->vtm);
527         ev_eloop_unregister_signal_cb(app->eloop, SIGPIPE, app_sig_ignore,
528                                       app);
529         ev_eloop_unregister_signal_cb(app->eloop, SIGINT, app_sig_generic,
530                                       app);
531         ev_eloop_unregister_signal_cb(app->eloop, SIGTERM, app_sig_generic,
532                                       app);
533         ev_eloop_unref(app->eloop);
534 }
535
536 static int setup_app(struct kmscon_app *app)
537 {
538         int ret;
539
540         shl_dlist_init(&app->seats);
541
542         ret = ev_eloop_new(&app->eloop, log_llog, NULL);
543         if (ret) {
544                 log_error("cannot create eloop object: %d", ret);
545                 goto err_app;
546         }
547
548         ret = ev_eloop_register_signal_cb(app->eloop, SIGTERM,
549                                           app_sig_generic, app);
550         if (ret) {
551                 log_error("cannot register SIGTERM signal handler: %d", ret);
552                 goto err_app;
553         }
554
555         ret = ev_eloop_register_signal_cb(app->eloop, SIGINT,
556                                           app_sig_generic, app);
557         if (ret) {
558                 log_error("cannot register SIGINT signal handler: %d", ret);
559                 goto err_app;
560         }
561
562         ret = ev_eloop_register_signal_cb(app->eloop, SIGPIPE,
563                                           app_sig_ignore, app);
564         if (ret) {
565                 log_error("cannot register SIGPIPE signal handler: %d", ret);
566                 goto err_app;
567         }
568
569         ret = uterm_vt_master_new(&app->vtm, app->eloop);
570         if (ret) {
571                 log_error("cannot create VT master: %d", ret);
572                 goto err_app;
573         }
574
575         ret = uterm_monitor_new(&app->mon, app->eloop, app_monitor_event, app);
576         if (ret) {
577                 log_error("cannot create device monitor: %d", ret);
578                 goto err_app;
579         }
580
581         log_debug("scanning for devices...");
582         uterm_monitor_scan(app->mon);
583
584         return 0;
585
586 err_app:
587         destroy_app(app);
588         return ret;
589 }
590
591 int main(int argc, char **argv)
592 {
593         int ret;
594         struct conf_ctx *conf_ctx;
595         struct kmscon_conf_t *conf;
596         struct kmscon_app app;
597
598         ret = kmscon_conf_new(&conf_ctx);
599         if (ret) {
600                 log_error("cannot create configuration: %d", ret);
601                 goto err_out;
602         }
603         conf = conf_ctx_get_mem(conf_ctx);
604
605         ret = kmscon_conf_load_main(conf_ctx, argc, argv);
606         if (ret) {
607                 log_error("cannot load configuration: %d", ret);
608                 goto err_conf;
609         }
610
611         if (conf->exit) {
612                 kmscon_conf_free(conf_ctx);
613                 return 0;
614         }
615
616         kmscon_load_modules();
617         kmscon_font_register(&kmscon_font_8x16_ops);
618         kmscon_text_register(&kmscon_text_bblit_ops);
619
620         memset(&app, 0, sizeof(app));
621         app.conf_ctx = conf_ctx;
622         app.conf = conf;
623
624         ret = setup_app(&app);
625         if (ret)
626                 goto err_unload;
627
628         if (!app.conf->listen && !app.running_seats) {
629                 log_notice("no running seats; exiting");
630         } else {
631                 log_debug("%u running seats after startup", app.running_seats);
632                 ev_eloop_run(app.eloop, -1);
633         }
634
635         app.exiting = true;
636
637         if (app.conf->switchvt) {
638                 /* The VT subsystem needs to acknowledge the VT-leave so if it
639                  * returns -EINPROGRESS we need to wait for the VT-leave SIGUSR2
640                  * signal to arrive. Therefore, we use a separate eloop object
641                  * which is used by the VT system only. Therefore, waiting on
642                  * this eloop allows us to safely wait 50ms for the SIGUSR2 to
643                  * arrive.
644                  * We use a timeout of 100ms to avoid hanging on exit. */
645                 log_debug("deactivating VTs during shutdown");
646                 ret = uterm_vt_master_deactivate_all(app.vtm);
647                 if (ret > 0) {
648                         log_debug("waiting for %d VTs to deactivate", ret);
649                         app.vt_exit_count = ret;
650                         ev_eloop_run(app.eloop, 50);
651                 }
652         }
653
654         ret = 0;
655
656         destroy_app(&app);
657 err_unload:
658         kmscon_text_unregister(kmscon_text_bblit_ops.name);
659         kmscon_font_unregister(kmscon_font_8x16_ops.name);
660         kmscon_unload_modules();
661 err_conf:
662         kmscon_conf_free(conf_ctx);
663 err_out:
664         if (ret)
665                 log_err("cannot initialize kmscon, errno %d: %s",
666                         ret, strerror(-ret));
667         log_info("exiting");
668         return -ret;
669 }