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