wsmoused.c revision 1.13 1 /* $NetBSD: wsmoused.c,v 1.13 2003/08/06 23:58:40 jmmv Exp $ */
2
3 /*
4 * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Julio M. Merino Vidal.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. The name authors may not be used to endorse or promote products
16 * derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33
34 #ifndef lint
35 __COPYRIGHT("@(#) Copyright (c) 2002, 2003\n"
36 "The NetBSD Foundation, Inc. All rights reserved.\n");
37 __RCSID("$NetBSD: wsmoused.c,v 1.13 2003/08/06 23:58:40 jmmv Exp $");
38 #endif /* not lint */
39
40 #include <sys/ioctl.h>
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <sys/tty.h>
44 #include <dev/wscons/wsconsio.h>
45
46 #include <err.h>
47 #include <fcntl.h>
48 #include <poll.h>
49 #include <signal.h>
50 #include <stdio.h>
51 #include <stdarg.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <syslog.h>
55 #include <unistd.h>
56 #include <util.h>
57
58 #include "pathnames.h"
59 #include "wsmoused.h"
60
61 /* --------------------------------------------------------------------- */
62
63 /*
64 * Global variables.
65 */
66
67 static struct mouse Mouse;
68 static char *Pid_File = NULL;
69 static int Foreground = 1;
70 static int X_Console = -1;
71
72 #ifdef WSMOUSED_SELECTION_MODE
73 extern struct mode_bootstrap Action_Mode;
74 #endif
75 #ifdef WSMOUSED_SELECTION_MODE
76 extern struct mode_bootstrap Selection_Mode;
77 #endif
78
79 #define MAX_MODES 2
80 static struct mode_bootstrap *Modes[MAX_MODES];
81 static struct mode_bootstrap *Avail_Modes[] = {
82 #ifdef WSMOUSED_ACTION_MODE
83 &Action_Mode,
84 #endif
85 #ifdef WSMOUSED_SELECTION_MODE
86 &Selection_Mode,
87 #endif
88 };
89
90 /* --------------------------------------------------------------------- */
91
92 /*
93 * Prototypes for functions private to this module.
94 */
95
96 static void usage(void);
97 static void open_device(unsigned int);
98 static void init_mouse(void);
99 static void event_loop(void);
100 static void generic_wscons_event(struct wscons_event);
101 static int attach_mode(const char *);
102 static void attach_modes(char *);
103 static void detach_mode(const char *);
104 static void detach_modes(void);
105 static void signal_terminate(int);
106 int main(int, char **);
107
108 /* --------------------------------------------------------------------- */
109
110 /* Shows program usage information and exits. */
111 static void
112 usage(void)
113 {
114
115 (void)fprintf(stderr,
116 "Usage: %s [-d device] [-f config_file] [-m modes] [-n]\n",
117 getprogname());
118 exit(EXIT_FAILURE);
119 }
120
121 /* --------------------------------------------------------------------- */
122
123 /* Logs the given error message to syslog if running in daemon mode, or
124 * to the console if running in the foreground. */
125 void
126 log_err(int e, const char *fmt, ...)
127 {
128 va_list ap;
129
130 va_start(ap, fmt);
131 if (Foreground)
132 verr(e, fmt, ap);
133 else {
134 int olderrno = errno;
135 vsyslog(LOG_DAEMON | LOG_ERR, fmt, ap);
136 errno = olderrno;
137 syslog(LOG_DAEMON | LOG_ERR, "%m");
138 exit(e);
139 }
140 /* NOTREACHED */
141 va_end(ap);
142 }
143
144 /* --------------------------------------------------------------------- */
145
146 /* Logs the given error message to syslog if running in daemon mode, or
147 * to the console if running in the foreground. */
148 void
149 log_errx(int e, const char *fmt, ...)
150 {
151 va_list ap;
152
153 va_start(ap, fmt);
154 if (Foreground)
155 verrx(e, fmt, ap);
156 else {
157 vsyslog(LOG_DAEMON | LOG_ERR, fmt, ap);
158 exit(e);
159 }
160 /* NOTREACHED */
161 va_end(ap);
162 }
163
164 /* --------------------------------------------------------------------- */
165
166 /* Logs the given info message to syslog if running in daemon mode, or
167 * to the console if running in the foreground. */
168 void
169 log_info(const char *fmt, ...)
170 {
171 va_list ap;
172
173 va_start(ap, fmt);
174 if (Foreground)
175 vfprintf(stderr, fmt, ap);
176 else
177 vsyslog(LOG_DAEMON | LOG_INFO, fmt, ap);
178 va_end(ap);
179 }
180
181 /* --------------------------------------------------------------------- */
182
183 /* Logs the given warning message to syslog if running in daemon mode, or
184 * to the console if running in the foreground. */
185 void
186 log_warn(const char *fmt, ...)
187 {
188 va_list ap;
189
190 va_start(ap, fmt);
191 if (Foreground)
192 vwarn(fmt, ap);
193 else {
194 int olderrno = errno;
195 vsyslog(LOG_DAEMON | LOG_WARNING, fmt, ap);
196 errno = olderrno;
197 syslog(LOG_DAEMON | LOG_WARNING, "%m");
198 }
199 va_end(ap);
200 }
201
202 /* --------------------------------------------------------------------- */
203
204 /* Logs the given warning message to syslog if running in daemon mode, or
205 * to the console if running in the foreground. */
206 void
207 log_warnx(const char *fmt, ...)
208 {
209 va_list ap;
210
211 va_start(ap, fmt);
212 if (Foreground)
213 vwarnx(fmt, ap);
214 else
215 vsyslog(LOG_DAEMON | LOG_WARNING, fmt, ap);
216 va_end(ap);
217 }
218
219 /* --------------------------------------------------------------------- */
220
221 /* Initializes mouse information. Basically, it opens required files
222 * for the daemon to work. */
223 static void
224 init_mouse(void)
225 {
226
227 Mouse.m_devfd = -1;
228 open_device(0);
229
230 /* Open FIFO, if wanted */
231 Mouse.m_fifofd = -1;
232 if (Mouse.m_fifoname != NULL) {
233 Mouse.m_fifofd = open(Mouse.m_fifoname,
234 O_RDWR | O_NONBLOCK, 0);
235 if (Mouse.m_fifofd == -1)
236 log_err(EXIT_FAILURE, "cannot open %s",
237 Mouse.m_fifoname);
238 }
239 }
240
241 /* --------------------------------------------------------------------- */
242
243 /* Opens the mouse device (if not already opened). The argument `secs'
244 * specifies how much seconds the function will wait before trying to
245 * open the device; this is used when returning from the X console. */
246 static void
247 open_device(unsigned int secs)
248 {
249
250 if (Mouse.m_devfd != -1)
251 return;
252
253 sleep(secs);
254
255 /* Open mouse file descriptor */
256 Mouse.m_devfd = open(Mouse.m_devname, O_RDONLY | O_NONBLOCK, 0);
257 if (Mouse.m_devfd == -1)
258 log_err(EXIT_FAILURE, "cannot open %s", Mouse.m_devname);
259 }
260
261 /* --------------------------------------------------------------------- */
262
263 /* Main program event loop. This function polls the wscons status
264 * device and the mouse device; whenever an event is received, the
265 * appropiate callback is fired for all attached modes. If the polls
266 * times out (which only appens when the mouse is disabled), another
267 * callback is launched. */
268 static void
269 event_loop(void)
270 {
271 int i, res;
272 struct pollfd fds[2];
273 struct wscons_event event;
274
275 fds[0].fd = Mouse.m_statfd;
276 fds[0].events = POLLIN;
277
278 for (;;) {
279 fds[1].fd = Mouse.m_devfd;
280 fds[1].events = POLLIN;
281 if (Mouse.m_disabled)
282 res = poll(fds, 1, INFTIM);
283 else
284 res = poll(fds, 2, 300);
285
286 if (res < 0)
287 log_warn("failed to read from devices");
288
289 if (fds[0].revents & POLLIN) {
290 res = read(Mouse.m_statfd, &event, sizeof(event));
291 if (res != sizeof(event))
292 log_warn("failed to read from mouse stat");
293
294 generic_wscons_event(event);
295
296 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
297 if (Modes[i]->mb_wscons_event != NULL)
298 Modes[i]->mb_wscons_event(event);
299
300 } else if (fds[1].revents & POLLIN) {
301 res = read(Mouse.m_devfd, &event, sizeof(event));
302 if (res != sizeof(event))
303 log_warn("failed to read from mouse");
304
305 if (Mouse.m_fifofd >= 0) {
306 res = write(Mouse.m_fifofd, &event,
307 sizeof(event));
308 if (res != sizeof(event))
309 log_warn("failed to write to fifo");
310 }
311
312 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
313 if (Modes[i]->mb_wsmouse_event != NULL)
314 Modes[i]->mb_wsmouse_event(event);
315 } else {
316 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
317 if (Modes[i]->mb_poll_timeout != NULL)
318 Modes[i]->mb_poll_timeout();
319 }
320 }
321 }
322
323 /* --------------------------------------------------------------------- */
324
325 /* This function parses generic wscons status events. Actually, it
326 * handles the screen switch event to enable or disable the mouse,
327 * depending if we are entering or leaving the X console. */
328 static void
329 generic_wscons_event(struct wscons_event evt)
330 {
331
332 switch (evt.type) {
333 case WSCONS_EVENT_SCREEN_SWITCH:
334 if (evt.value == X_Console) {
335 Mouse.m_disabled = 1;
336 (void)close(Mouse.m_devfd);
337 Mouse.m_devfd = -1;
338 } else {
339 if (Mouse.m_disabled) {
340 open_device(5);
341 Mouse.m_disabled = 0;
342 } else {
343 (void)close(Mouse.m_devfd);
344 Mouse.m_devfd = -1;
345 open_device(0);
346 }
347 }
348 break;
349 }
350 }
351
352 /* --------------------------------------------------------------------- */
353
354 /* Attaches a mode to the list of active modes, based on its name.
355 * Returns 1 on success or 0 if the mode fails to initialize or there is
356 * any other problem. */
357 static int
358 attach_mode(const char *name)
359 {
360 int i, pos;
361 struct mode_bootstrap *mb;
362
363 for (i = 0, pos = -1; i < MAX_MODES; i++)
364 if (Modes[i] == NULL) {
365 pos = i;
366 break;
367 }
368 if (pos == -1) {
369 log_warnx("modes table full; cannot register `%s'", name);
370 return 0;
371 }
372
373 for (i = 0; i < MAX_MODES; i++) {
374 mb = Avail_Modes[i];
375 if (mb != NULL && strcmp(name, mb->mb_name) == 0) {
376 int res;
377
378 res = mb->mb_startup(&Mouse);
379 if (res == 0) {
380 log_warnx("startup failed for `%s' mode",
381 mb->mb_name);
382 return 0;
383 } else {
384 Modes[pos] = mb;
385 return 1;
386 }
387 }
388 }
389
390 log_warnx("unknown mode `%s' (see the `modes' directive)", name);
391 return 0;
392 }
393
394 /* --------------------------------------------------------------------- */
395
396 /* Attaches all modes given in the whitespace separated string `list'.
397 * A fatal error is produced if no active modes can be attached. */
398 static void
399 attach_modes(char *list)
400 {
401 char *last, *p;
402 int count;
403
404 /* Attach all requested modes */
405 (void)memset(&Modes, 0, sizeof(struct mode_bootstrap) * MAX_MODES);
406 for (count = 0, (p = strtok_r(list, " ", &last)); p;
407 (p = strtok_r(NULL, " ", &last))) {
408 if (attach_mode(p))
409 count++;
410 }
411
412 if (count == 0)
413 log_errx(EXIT_FAILURE, "no active modes found; exiting...");
414 }
415
416 /* --------------------------------------------------------------------- */
417
418 /* Detaches a mode from the active modes list based on its name. */
419 static void
420 detach_mode(const char *name)
421 {
422 int i;
423 struct mode_bootstrap *mb;
424
425 for (i = 0; i < MAX_MODES; i++) {
426 mb = Modes[i];
427 if (mb != NULL && strcmp(name, mb->mb_name) == 0) {
428 int res;
429
430 res = mb->mb_cleanup();
431 if (res == 0) {
432 log_warnx("cleanup failed for `%s' mode",
433 mb->mb_name);
434 return;
435 } else {
436 Modes[i] = NULL;
437 return;
438 }
439 }
440 }
441
442 log_warnx("unknown mode `%s' (see the `modes' directive)", name);
443 }
444
445 /* --------------------------------------------------------------------- */
446
447 /* Detaches all active modes. */
448 static void
449 detach_modes(void)
450 {
451 int i;
452
453 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
454 detach_mode(Modes[i]->mb_name);
455 }
456
457 /* --------------------------------------------------------------------- */
458
459 /* Signal handler for close signals. The program can only be exited
460 * through this function. */
461 /* ARGSUSED */
462 static void
463 signal_terminate(int sig)
464 {
465
466 detach_modes();
467 config_free();
468 exit(EXIT_SUCCESS);
469 }
470
471 /* --------------------------------------------------------------------- */
472
473 /* Main program. Parses command line options, reads the configuration
474 * file, initializes the mouse and associated files and launches the main
475 * event loop. */
476 int
477 main(int argc, char **argv)
478 {
479 char *conffile, *modelist, *tstat;
480 int needconf, nodaemon, opt;
481 struct block *conf;
482
483 setprogname(argv[0]);
484
485 (void)memset(&Mouse, 0, sizeof(struct mouse));
486 conffile = _PATH_CONF;
487 modelist = NULL;
488 needconf = 0;
489 nodaemon = -1;
490
491 /* Parse command line options */
492 while ((opt = getopt(argc, argv, "d:f:m:n")) != -1) {
493 switch (opt) {
494 case 'd': /* Mouse device name */
495 Mouse.m_devname = optarg;
496 break;
497 case 'f': /* Configuration file name */
498 needconf = 1;
499 conffile = optarg;
500 break;
501 case 'm': /* List of modes to activate */
502 modelist = optarg;
503 break;
504 case 'n': /* No daemon */
505 nodaemon = 1;
506 break;
507 default:
508 usage();
509 /* NOTREACHED */
510 }
511 }
512
513 /* Read the configuration file and get some basic properties */
514 config_read(conffile, needconf);
515 conf = config_get_mode("Global");
516 if (nodaemon == -1)
517 nodaemon = block_get_propval_int(conf, "nodaemon", 0);
518 X_Console = block_get_propval_int(conf, "xconsole", -1);
519
520 /* Open wsdisplay status device */
521 tstat = block_get_propval(conf, "ttystat", _PATH_TTYSTAT);
522 Mouse.m_statfd = open(tstat, O_RDONLY | O_NONBLOCK, 0);
523 if (Mouse.m_statfd == -1)
524 log_err(EXIT_FAILURE, "cannot open %s", tstat);
525
526 /* Initialize mouse information and attach modes */
527 if (Mouse.m_devname == NULL)
528 Mouse.m_devname = block_get_propval(conf, "device",
529 _PATH_DEFAULT_MOUSE);
530 init_mouse();
531 if (modelist != NULL)
532 attach_modes(modelist);
533 else
534 attach_modes(block_get_propval(conf, "modes", "selection"));
535
536 /* Setup signal handlers */
537 (void)signal(SIGINT, signal_terminate);
538 (void)signal(SIGKILL, signal_terminate);
539 (void)signal(SIGQUIT, signal_terminate);
540 (void)signal(SIGTERM, signal_terminate);
541
542 if (!nodaemon) {
543 /* Become a daemon */
544 if (daemon(0, 0) == -1)
545 log_err(EXIT_FAILURE, "failed to become a daemon");
546
547 /* Create the pidfile, if wanted */
548 Pid_File = block_get_propval(conf, "pidfile", NULL);
549 if (pidfile(Pid_File) == -1)
550 log_warn("pidfile %s", Pid_File);
551
552 Foreground = 0;
553 }
554
555 event_loop();
556
557 /* NOTREACHED */
558 return EXIT_SUCCESS;
559 }
560