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