uvtd: vt: implement VT_GETMODE/SETMODE ioctl state-tracking
[platform/upstream/kmscon.git] / src / uvtd_main.c
1 /*
2  * uvtd - User-space VT daemon
3  *
4  * Copyright (c) 2012-2013 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 <signal.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/signalfd.h>
33 #include "eloop.h"
34 #include "shl_dlist.h"
35 #include "shl_log.h"
36 #include "uterm_input.h"
37 #include "uterm_monitor.h"
38 #include "uvt.h"
39 #include "uvtd_ctx.h"
40
41 struct app_seat {
42         struct shl_dlist list;
43         struct uvtd_app *app;
44         struct uterm_monitor_seat *useat;
45         struct uvtd_ctx *ctx;
46 };
47
48 struct uvtd_app {
49         struct ev_eloop *eloop;
50         struct uterm_monitor *mon;
51         struct uvt_ctx *ctx;
52         struct ev_fd *ctx_fd;
53         struct shl_dlist seats;
54 };
55
56 static int app_seat_new(struct uvtd_app *app, const char *sname,
57                         struct uterm_monitor_seat *useat)
58 {
59         struct app_seat *seat;
60         int ret;
61
62         seat = malloc(sizeof(*seat));
63         if (!seat)
64                 return -ENOMEM;
65
66         log_debug("new seat %p on %s", seat, sname);
67
68         memset(seat, 0, sizeof(*seat));
69         seat->app = app;
70         seat->useat = useat;
71
72         ret = uvtd_ctx_new(&seat->ctx, sname, app->eloop, app->ctx);
73         if (ret == -EEXIST) {
74                 log_debug("ignoring seat %s as it has real VTs", sname);
75                 goto err_free;
76         } else if (ret) {
77                 goto err_free;
78         }
79
80         uterm_monitor_set_seat_data(seat->useat, seat);
81         shl_dlist_link(&app->seats, &seat->list);
82         return 0;
83
84 err_free:
85         free(seat);
86         return ret;
87 }
88
89 static void app_seat_free(struct app_seat *seat)
90 {
91         log_debug("free seat %p", seat);
92
93         shl_dlist_unlink(&seat->list);
94         uterm_monitor_set_seat_data(seat->useat, NULL);
95         uvtd_ctx_free(seat->ctx);
96         free(seat);
97 }
98
99 static void app_monitor_event(struct uterm_monitor *mon,
100                               struct uterm_monitor_event *ev,
101                               void *data)
102 {
103         struct uvtd_app *app = data;
104         struct app_seat *seat;
105         int ret;
106
107         switch (ev->type) {
108         case UTERM_MONITOR_NEW_SEAT:
109                 ret = app_seat_new(app, ev->seat_name, ev->seat);
110                 if (ret)
111                         return;
112                 break;
113         case UTERM_MONITOR_FREE_SEAT:
114                 if (ev->seat_data)
115                         app_seat_free(ev->seat_data);
116                 break;
117         case UTERM_MONITOR_NEW_DEV:
118                 seat = ev->seat_data;
119                 if (!seat)
120                         return;
121
122                 switch (ev->dev_type) {
123                 case UTERM_MONITOR_INPUT:
124                         log_debug("new input device %s on seat %p",
125                                   ev->dev_node, seat);
126                         break;
127                 }
128                 break;
129         case UTERM_MONITOR_FREE_DEV:
130                 seat = ev->seat_data;
131                 if (!seat)
132                         return;
133
134                 switch (ev->dev_type) {
135                 case UTERM_MONITOR_INPUT:
136                         log_debug("free input device %s on seat %p",
137                                   ev->dev_node, seat);
138                         break;
139                 }
140                 break;
141         }
142 }
143
144 static void app_sig_generic(struct ev_eloop *eloop,
145                             struct signalfd_siginfo *info,
146                             void *data)
147 {
148         struct uvtd_app *app = data;
149
150         log_info("terminating due to caught signal %d", info->ssi_signo);
151         ev_eloop_exit(app->eloop);
152 }
153
154 static void app_sig_ignore(struct ev_eloop *eloop,
155                            struct signalfd_siginfo *info,
156                            void *data)
157 {
158 }
159
160 static void app_ctx_event(struct ev_fd *fd, int mask, void *data)
161 {
162         struct uvtd_app *app = data;
163
164         uvt_ctx_dispatch(app->ctx);
165
166         if (!(mask & EV_READABLE) && mask & (EV_HUP | EV_ERR)) {
167                 log_error("HUP on UVT ctx fd");
168                 ev_eloop_rm_fd(fd);
169                 app->ctx_fd = NULL;
170         }
171 }
172
173 static void destroy_app(struct uvtd_app *app)
174 {
175         ev_eloop_rm_fd(app->ctx_fd);
176         uvt_ctx_unref(app->ctx);
177         uterm_monitor_unref(app->mon);
178         ev_eloop_unregister_signal_cb(app->eloop, SIGPIPE, app_sig_ignore,
179                                       app);
180         ev_eloop_unregister_signal_cb(app->eloop, SIGINT, app_sig_generic,
181                                       app);
182         ev_eloop_unregister_signal_cb(app->eloop, SIGTERM, app_sig_generic,
183                                       app);
184         ev_eloop_unref(app->eloop);
185 }
186
187 static int setup_app(struct uvtd_app *app)
188 {
189         int ret, fd;
190
191         shl_dlist_init(&app->seats);
192
193         ret = ev_eloop_new(&app->eloop, log_llog, NULL);
194         if (ret) {
195                 log_error("cannot create eloop object: %d", ret);
196                 goto err_app;
197         }
198
199         ret = ev_eloop_register_signal_cb(app->eloop, SIGTERM,
200                                           app_sig_generic, app);
201         if (ret) {
202                 log_error("cannot register SIGTERM signal handler: %d", ret);
203                 goto err_app;
204         }
205
206         ret = ev_eloop_register_signal_cb(app->eloop, SIGINT,
207                                           app_sig_generic, app);
208         if (ret) {
209                 log_error("cannot register SIGINT signal handler: %d", ret);
210                 goto err_app;
211         }
212
213         ret = ev_eloop_register_signal_cb(app->eloop, SIGPIPE,
214                                           app_sig_ignore, app);
215         if (ret) {
216                 log_error("cannot register SIGPIPE signal handler: %d", ret);
217                 goto err_app;
218         }
219
220         ret = uterm_monitor_new(&app->mon, app->eloop, app_monitor_event, app);
221         if (ret) {
222                 log_error("cannot create device monitor: %d", ret);
223                 goto err_app;
224         }
225
226         ret = uvt_ctx_new(&app->ctx, log_llog, NULL);
227         if (ret) {
228                 log_error("cannot create UVT context: %d", ret);
229                 goto err_app;
230         }
231
232         fd = uvt_ctx_get_fd(app->ctx);
233         if (fd >= 0) {
234                 ret = ev_eloop_new_fd(app->eloop, &app->ctx_fd, fd,
235                                       EV_READABLE, app_ctx_event, app);
236                 if (ret) {
237                         log_error("cannot create UVT ctx efd: %d", ret);
238                         goto err_app;
239                 }
240         }
241
242         log_debug("scanning for devices...");
243         uterm_monitor_scan(app->mon);
244
245         return 0;
246
247 err_app:
248         destroy_app(app);
249         return ret;
250 }
251
252 int main(int argc, char **argv)
253 {
254         int ret;
255         struct uvtd_app app;
256
257         log_set_config(&LOG_CONFIG_INFO(1, 1));
258         log_print_init("uvtd");
259
260         memset(&app, 0, sizeof(app));
261
262         ret = setup_app(&app);
263         if (ret)
264                 goto err_out;
265
266         ev_eloop_run(app.eloop, -1);
267
268         ret = 0;
269         destroy_app(&app);
270 err_out:
271         if (ret)
272                 log_err("cannot initialize uvtd, errno %d: %s",
273                         ret, strerror(-ret));
274         log_info("exiting");
275         return -ret;
276 }