1/* $NetBSD: wsmoused.c,v 1.29 2022/05/20 21:31:24 andvar 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.29 2022/05/20 21:31:24 andvar 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 68static struct mouse Mouse; 69static char *Pid_File = NULL; 70static int Foreground = 1; 71static int X_Console = -1; 72static int X_Console_Delay = 5; 73 74#ifdef WSMOUSED_ACTION_MODE 75extern struct mode_bootstrap Action_Mode; 76#endif 77#ifdef WSMOUSED_SELECTION_MODE 78extern struct mode_bootstrap Selection_Mode; 79#endif 80 81#define MAX_MODES 2 82static struct mode_bootstrap *Modes[MAX_MODES]; 83static 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 98static void usage(void) __attribute__((__noreturn__)); 99static void open_device(unsigned int); 100static void init_mouse(void); 101static void event_loop(void); 102static void generic_wscons_event(struct wscons_event); 103static int attach_mode(const char *); 104static void attach_modes(char *); 105static void detach_mode(const char *); 106static void detach_modes(void); 107static void signal_terminate(int); 108 109static int debug; 110 111/* --------------------------------------------------------------------- */ 112 113/* Shows program usage information and exits. */ 114static void 115usage(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. */ 128void 129log_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. */ 151void 152log_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. */ 171void 172log_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. */ 189void 190log_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. */ 210void 211log_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. */ 227static void 228init_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. */ 250static void 251open_device(unsigned int secs) 252{ 253 int status; 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 const int version = WSMOUSE_EVENT_VERSION; 266 status = ioctl(Mouse.m_devfd, WSMOUSEIO_SETVERSION, &version); 267 if (status == -1) 268 log_err(EXIT_FAILURE, "cannot set version %s", Mouse.m_devname); 269 270 271 /* 272 * Get calibration data for touch panel. Not fatal if we can't. 273 */ 274 Mouse.m_doabs = 0; 275 276 unsigned int mouse_type = 0; /* defined WSMOUSE_TYPE_* start at 1 */ 277 status = ioctl(Mouse.m_devfd, WSMOUSEIO_GTYPE, &mouse_type); 278 if (status == -1) { 279 log_warn("WSMOUSEIO_GTYPE"); 280 return; 281 } 282 283 /* absolute position events make no sense for free-ranging mice */ 284 if (mouse_type != WSMOUSE_TYPE_TPANEL) 285 return; 286 287 status = ioctl(Mouse.m_devfd, WSMOUSEIO_GCALIBCOORDS, &Mouse.m_calib); 288 if (status == -1) { 289 log_warn("WSMOUSEIO_GCALIBCOORDS"); 290 return; 291 } 292 293 Mouse.m_doabs = 1; 294} 295 296 297/* --------------------------------------------------------------------- */ 298 299/* Main program event loop. This function polls the wscons status 300 * device and the mouse device; whenever an event is received, the 301 * appropriate callback is fired for all attached modes. If the polls 302 * times out (which only happens when the mouse is disabled), another 303 * callback is launched. */ 304static void 305event_loop(void) 306{ 307 int i, res; 308 struct pollfd fds[2]; 309 struct wscons_event event; 310 311 fds[0].fd = Mouse.m_statfd; 312 fds[0].events = POLLIN; 313 314 for (;;) { 315 fds[1].fd = Mouse.m_devfd; 316 fds[1].events = POLLIN; 317 if (Mouse.m_disabled) 318 res = poll(fds, 1, INFTIM); 319 else 320 res = poll(fds, 2, 300); 321 322 if (res < 0) 323 log_warn("failed to read from devices"); 324 325 if (fds[0].revents & POLLIN) { 326 res = read(Mouse.m_statfd, &event, sizeof(event)); 327 if (debug) 328 (void)fprintf(stderr, "event [type=%u,value=%d," 329 "time=[%lld,%ld]\n", event.type, 330 event.value, (long long)event.time.tv_sec, 331 (long)event.time.tv_nsec); 332 if (res != sizeof(event)) 333 log_warn("failed to read from mouse stat"); 334 335 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++) 336 if (Modes[i]->mb_wscons_event != NULL) 337 Modes[i]->mb_wscons_event(event, 1); 338 339 generic_wscons_event(event); 340 341 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++) 342 if (Modes[i]->mb_wscons_event != NULL) 343 Modes[i]->mb_wscons_event(event, 0); 344 345 } else if (fds[1].revents & POLLIN) { 346 res = read(Mouse.m_devfd, &event, sizeof(event)); 347 if (res != sizeof(event)) 348 log_warn("failed to read from mouse"); 349 350 if (debug) 351 (void)fprintf(stderr, "event [type=%u,value=%d," 352 "time=[%lld,%ld]\n", event.type, 353 event.value, (long long)event.time.tv_sec, 354 (long)event.time.tv_nsec); 355 if (Mouse.m_fifofd >= 0) { 356 res = write(Mouse.m_fifofd, &event, 357 sizeof(event)); 358 if (res != sizeof(event)) 359 log_warn("failed to write to fifo"); 360 } 361 362 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++) 363 if (Modes[i]->mb_wsmouse_event != NULL) 364 Modes[i]->mb_wsmouse_event(event); 365 } else { 366 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++) 367 if (Modes[i]->mb_poll_timeout != NULL) 368 Modes[i]->mb_poll_timeout(); 369 } 370 } 371} 372 373/* --------------------------------------------------------------------- */ 374 375/* This function parses generic wscons status events. Actually, it 376 * handles the screen switch event to enable or disable the mouse, 377 * depending if we are entering or leaving the X console. */ 378static void 379generic_wscons_event(struct wscons_event evt) 380{ 381 382 switch (evt.type) { 383 case WSCONS_EVENT_SCREEN_SWITCH: 384 if (evt.value == X_Console) { 385 Mouse.m_disabled = 1; 386 (void)close(Mouse.m_devfd); 387 Mouse.m_devfd = -1; 388 } else { 389 if (Mouse.m_disabled) { 390 open_device(X_Console_Delay); 391 Mouse.m_disabled = 0; 392 } else { 393 (void)close(Mouse.m_devfd); 394 Mouse.m_devfd = -1; 395 open_device(0); 396 } 397 } 398 break; 399 } 400} 401 402/* --------------------------------------------------------------------- */ 403 404/* Attaches a mode to the list of active modes, based on its name. 405 * Returns 1 on success or 0 if the mode fails to initialize or there is 406 * any other problem. */ 407static int 408attach_mode(const char *name) 409{ 410 int i, pos; 411 struct mode_bootstrap *mb; 412 413 for (i = 0, pos = -1; i < MAX_MODES; i++) 414 if (Modes[i] == NULL) { 415 pos = i; 416 break; 417 } 418 if (pos == -1) { 419 log_warnx("modes table full; cannot register `%s'", name); 420 return 0; 421 } 422 423 for (i = 0; i < MAX_MODES; i++) { 424 mb = Avail_Modes[i]; 425 if (mb != NULL && strcmp(name, mb->mb_name) == 0) { 426 int res; 427 428 res = mb->mb_startup(&Mouse); 429 if (res == 0) { 430 log_warnx("startup failed for `%s' mode", 431 mb->mb_name); 432 return 0; 433 } else { 434 Modes[pos] = mb; 435 return 1; 436 } 437 } 438 } 439 440 log_warnx("unknown mode `%s' (see the `modes' directive)", name); 441 return 0; 442} 443 444/* --------------------------------------------------------------------- */ 445 446/* Attaches all modes given in the whitespace separated string `list'. 447 * A fatal error is produced if no active modes can be attached. */ 448static void 449attach_modes(char *list) 450{ 451 char *last, *p; 452 int count; 453 454 /* Attach all requested modes */ 455 (void)memset(&Modes, 0, sizeof(struct mode_bootstrap *) * MAX_MODES); 456 for (count = 0, (p = strtok_r(list, " ", &last)); p; 457 (p = strtok_r(NULL, " ", &last))) { 458 if (attach_mode(p)) 459 count++; 460 } 461 462 if (count == 0) 463 log_errx(EXIT_FAILURE, "no active modes found; exiting..."); 464} 465 466/* --------------------------------------------------------------------- */ 467 468/* Detaches a mode from the active modes list based on its name. */ 469static void 470detach_mode(const char *name) 471{ 472 int i; 473 struct mode_bootstrap *mb; 474 475 for (i = 0; i < MAX_MODES; i++) { 476 mb = Modes[i]; 477 if (mb != NULL && strcmp(name, mb->mb_name) == 0) { 478 int res; 479 480 res = mb->mb_cleanup(); 481 if (res == 0) { 482 log_warnx("cleanup failed for `%s' mode", 483 mb->mb_name); 484 return; 485 } else { 486 Modes[i] = NULL; 487 return; 488 } 489 } 490 } 491 492 log_warnx("unknown mode `%s' (see the `modes' directive)", name); 493} 494 495/* --------------------------------------------------------------------- */ 496 497/* Detaches all active modes. */ 498static void 499detach_modes(void) 500{ 501 int i; 502 503 for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++) 504 detach_mode(Modes[i]->mb_name); 505} 506 507/* --------------------------------------------------------------------- */ 508 509/* Signal handler for close signals. The program can only be exited 510 * through this function. */ 511/* ARGSUSED */ 512static void 513signal_terminate(int sig) 514{ 515 516 detach_modes(); 517 config_free(); 518 exit(EXIT_SUCCESS); 519} 520 521/* --------------------------------------------------------------------- */ 522 523/* Main program. Parses command line options, reads the configuration 524 * file, initializes the mouse and associated files and launches the main 525 * event loop. */ 526int 527main(int argc, char **argv) 528{ 529 char *conffile, *modelist, *tstat; 530 int needconf, nodaemon, opt; 531 struct block *conf; 532 533 setprogname(argv[0]); 534 535 (void)memset(&Mouse, 0, sizeof(struct mouse)); 536 conffile = _PATH_CONF; 537 modelist = NULL; 538 needconf = 0; 539 nodaemon = -1; 540 541 /* Parse command line options */ 542 while ((opt = getopt(argc, argv, "Dd:f:m:n")) != -1) { 543 switch (opt) { 544 case 'D': 545 debug++; 546 break; 547 case 'd': /* Mouse device name */ 548 Mouse.m_devname = optarg; 549 break; 550 case 'f': /* Configuration file name */ 551 needconf = 1; 552 conffile = optarg; 553 break; 554 case 'm': /* List of modes to activate */ 555 modelist = optarg; 556 break; 557 case 'n': /* No daemon */ 558 nodaemon = 1; 559 break; 560 default: 561 usage(); 562 /* NOTREACHED */ 563 } 564 } 565 566 /* Read the configuration file and get some basic properties */ 567 config_read(conffile, needconf); 568 conf = config_get_mode("Global"); 569 if (nodaemon == -1) 570 nodaemon = block_get_propval_int(conf, "nodaemon", 0); 571 X_Console = block_get_propval_int(conf, "xconsole", -1); 572 X_Console_Delay = block_get_propval_int(conf, "xconsole_delay", 573 X_Console_Delay); 574 575 /* Open wsdisplay status device */ 576 tstat = block_get_propval(conf, "ttystat", _PATH_TTYSTAT); 577 Mouse.m_statfd = open(tstat, O_RDONLY | O_NONBLOCK, 0); 578 if (Mouse.m_statfd == -1) 579 log_err(EXIT_FAILURE, "cannot open %s", tstat); 580 581 /* Initialize mouse information and attach modes */ 582 if (Mouse.m_devname == NULL) 583 Mouse.m_devname = block_get_propval(conf, "device", 584 _PATH_DEFAULT_MOUSE); 585 Mouse.m_fifoname = block_get_propval(conf, "fifo", NULL); 586 init_mouse(); 587 if (modelist != NULL) 588 attach_modes(modelist); 589 else 590 attach_modes(block_get_propval(conf, "modes", "selection")); 591 592 /* Setup signal handlers */ 593 (void)signal(SIGINT, signal_terminate); 594 (void)signal(SIGKILL, signal_terminate); 595 (void)signal(SIGQUIT, signal_terminate); 596 (void)signal(SIGTERM, signal_terminate); 597 598 if (!nodaemon) { 599 /* Become a daemon */ 600 if (daemon(0, 0) == -1) 601 log_err(EXIT_FAILURE, "failed to become a daemon"); 602 603 /* Create the pidfile, if wanted */ 604 Pid_File = block_get_propval(conf, "pidfile", NULL); 605 if (pidfile(Pid_File) == -1) 606 log_warn("pidfile %s", Pid_File); 607 608 Foreground = 0; 609 } 610 611 event_loop(); 612 613 /* NOTREACHED */ 614 return EXIT_SUCCESS; 615} 616