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