/* $NetBSD: wsmoused.c,v 1.26 2011/05/31 03:37:02 christos Exp $ */ /* * Copyright (c) 2002, 2003, 2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Julio M. Merino Vidal. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name authors may not be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifndef lint __COPYRIGHT("@(#) Copyright (c) 2002, 2003\ The NetBSD Foundation, Inc. All rights reserved."); __RCSID("$NetBSD: wsmoused.c,v 1.26 2011/05/31 03:37:02 christos Exp $"); #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "wsmoused.h" /* --------------------------------------------------------------------- */ /* * Global variables. */ static struct mouse Mouse; static char *Pid_File = NULL; static int Foreground = 1; static int X_Console = -1; static int X_Console_Delay = 5; #ifdef WSMOUSED_SELECTION_MODE extern struct mode_bootstrap Action_Mode; #endif #ifdef WSMOUSED_SELECTION_MODE extern struct mode_bootstrap Selection_Mode; #endif #define MAX_MODES 2 static struct mode_bootstrap *Modes[MAX_MODES]; static struct mode_bootstrap *Avail_Modes[] = { #ifdef WSMOUSED_ACTION_MODE &Action_Mode, #endif #ifdef WSMOUSED_SELECTION_MODE &Selection_Mode, #endif }; /* --------------------------------------------------------------------- */ /* * Prototypes for functions private to this module. */ static void usage(void) __attribute__((__noreturn__)); static void open_device(unsigned int); static void init_mouse(void); static void event_loop(void); static void generic_wscons_event(struct wscons_event); static int attach_mode(const char *); static void attach_modes(char *); static void detach_mode(const char *); static void detach_modes(void); static void signal_terminate(int); static int debug; /* --------------------------------------------------------------------- */ /* Shows program usage information and exits. */ static void usage(void) { (void)fprintf(stderr, "usage: %s [-d device] [-f config_file] [-m modes] [-n]\n", getprogname()); exit(EXIT_FAILURE); } /* --------------------------------------------------------------------- */ /* Logs the given error message to syslog if running in daemon mode, or * to the console if running in the foreground. */ void log_err(int e, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (Foreground) verr(e, fmt, ap); else { int olderrno = errno; vsyslog(LOG_DAEMON | LOG_ERR, fmt, ap); errno = olderrno; syslog(LOG_DAEMON | LOG_ERR, "%m"); exit(e); } /* NOTREACHED */ va_end(ap); } /* --------------------------------------------------------------------- */ /* Logs the given error message to syslog if running in daemon mode, or * to the console if running in the foreground. */ void log_errx(int e, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (Foreground) verrx(e, fmt, ap); else { vsyslog(LOG_DAEMON | LOG_ERR, fmt, ap); exit(e); } /* NOTREACHED */ va_end(ap); } /* --------------------------------------------------------------------- */ /* Logs the given info message to syslog if running in daemon mode, or * to the console if running in the foreground. */ void log_info(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (Foreground) { vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); } else vsyslog(LOG_DAEMON | LOG_INFO, fmt, ap); va_end(ap); } /* --------------------------------------------------------------------- */ /* Logs the given warning message to syslog if running in daemon mode, or * to the console if running in the foreground. */ void log_warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (Foreground) vwarn(fmt, ap); else { int olderrno = errno; vsyslog(LOG_DAEMON | LOG_WARNING, fmt, ap); errno = olderrno; syslog(LOG_DAEMON | LOG_WARNING, "%m"); } va_end(ap); } /* --------------------------------------------------------------------- */ /* Logs the given warning message to syslog if running in daemon mode, or * to the console if running in the foreground. */ void log_warnx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (Foreground) vwarnx(fmt, ap); else vsyslog(LOG_DAEMON | LOG_WARNING, fmt, ap); va_end(ap); } /* --------------------------------------------------------------------- */ /* Initializes mouse information. Basically, it opens required files * for the daemon to work. */ static void init_mouse(void) { Mouse.m_devfd = -1; open_device(0); /* Open FIFO, if wanted */ Mouse.m_fifofd = -1; if (Mouse.m_fifoname != NULL) { Mouse.m_fifofd = open(Mouse.m_fifoname, O_RDWR | O_NONBLOCK, 0); if (Mouse.m_fifofd == -1) log_err(EXIT_FAILURE, "cannot open %s", Mouse.m_fifoname); } } /* --------------------------------------------------------------------- */ /* Opens the mouse device (if not already opened). The argument `secs' * specifies how much seconds the function will wait before trying to * open the device; this is used when returning from the X console. */ static void open_device(unsigned int secs) { int version = WSMOUSE_EVENT_VERSION; if (Mouse.m_devfd != -1) return; sleep(secs); /* Open mouse file descriptor */ Mouse.m_devfd = open(Mouse.m_devname, O_RDONLY | O_NONBLOCK, 0); if (Mouse.m_devfd == -1) log_err(EXIT_FAILURE, "cannot open %s", Mouse.m_devname); if (ioctl(Mouse.m_devfd, WSMOUSEIO_SETVERSION, &version) == -1) log_err(EXIT_FAILURE, "cannot set version %s", Mouse.m_devname); } /* --------------------------------------------------------------------- */ /* Main program event loop. This function polls the wscons status * device and the mouse device; whenever an event is received, the * appropiate callback is fired for all attached modes. If the polls * times out (which only appens when the mouse is disabled), another * callback is launched. */ static void event_loop(void) { int i, res; struct pollfd fds[2]; struct wscons_event event; fds[0].fd = Mouse.m_statfd; fds[0].events = POLLIN; for (;;) { fds[1].fd = Mouse.m_devfd; fds[1].events = POLLIN; if (Mouse.m_disabled) res = poll(fds, 1, INFTIM); else res = poll(fds, 2, 300); if (res < 0) log_warn("failed to read from devices"); if (fds[0].revents & POLLIN) { res = read(Mouse.m_statfd, &event, sizeof(event)); if (debug) (void)fprintf(stderr, "event [type=%u,value=%d," "time=[%lld,%ld]\n", event.type, event.value, (long long)event.time.tv_sec, (long)event.time.tv_nsec); if (res != sizeof(event)) log_warn("failed to read from mouse stat"); for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++) if (Modes[i]->mb_wscons_event != NULL) Modes[i]->mb_wscons_event(event, 1); generic_wscons_event(event); for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++) if (Modes[i]->mb_wscons_event != NULL) Modes[i]->mb_wscons_event(event, 0); } else if (fds[1].revents & POLLIN) { res = read(Mouse.m_devfd, &event, sizeof(event)); if (res != sizeof(event)) log_warn("failed to read from mouse"); if (debug) (void)fprintf(stderr, "event [type=%u,value=%d," "time=[%lld,%ld]\n", event.type, event.value, (long long)event.time.tv_sec, (long)event.time.tv_nsec); if (Mouse.m_fifofd >= 0) { res = write(Mouse.m_fifofd, &event, sizeof(event)); if (res != sizeof(event)) log_warn("failed to write to fifo"); } for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++) if (Modes[i]->mb_wsmouse_event != NULL) Modes[i]->mb_wsmouse_event(event); } else { for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++) if (Modes[i]->mb_poll_timeout != NULL) Modes[i]->mb_poll_timeout(); } } } /* --------------------------------------------------------------------- */ /* This function parses generic wscons status events. Actually, it * handles the screen switch event to enable or disable the mouse, * depending if we are entering or leaving the X console. */ static void generic_wscons_event(struct wscons_event evt) { switch (evt.type) { case WSCONS_EVENT_SCREEN_SWITCH: if (evt.value == X_Console) { Mouse.m_disabled = 1; (void)close(Mouse.m_devfd); Mouse.m_devfd = -1; } else { if (Mouse.m_disabled) { open_device(X_Console_Delay); Mouse.m_disabled = 0; } else { (void)close(Mouse.m_devfd); Mouse.m_devfd = -1; open_device(0); } } break; } } /* --------------------------------------------------------------------- */ /* Attaches a mode to the list of active modes, based on its name. * Returns 1 on success or 0 if the mode fails to initialize or there is * any other problem. */ static int attach_mode(const char *name) { int i, pos; struct mode_bootstrap *mb; for (i = 0, pos = -1; i < MAX_MODES; i++) if (Modes[i] == NULL) { pos = i; break; } if (pos == -1) { log_warnx("modes table full; cannot register `%s'", name); return 0; } for (i = 0; i < MAX_MODES; i++) { mb = Avail_Modes[i]; if (mb != NULL && strcmp(name, mb->mb_name) == 0) { int res; res = mb->mb_startup(&Mouse); if (res == 0) { log_warnx("startup failed for `%s' mode", mb->mb_name); return 0; } else { Modes[pos] = mb; return 1; } } } log_warnx("unknown mode `%s' (see the `modes' directive)", name); return 0; } /* --------------------------------------------------------------------- */ /* Attaches all modes given in the whitespace separated string `list'. * A fatal error is produced if no active modes can be attached. */ static void attach_modes(char *list) { char *last, *p; int count; /* Attach all requested modes */ (void)memset(&Modes, 0, sizeof(struct mode_bootstrap *) * MAX_MODES); for (count = 0, (p = strtok_r(list, " ", &last)); p; (p = strtok_r(NULL, " ", &last))) { if (attach_mode(p)) count++; } if (count == 0) log_errx(EXIT_FAILURE, "no active modes found; exiting..."); } /* --------------------------------------------------------------------- */ /* Detaches a mode from the active modes list based on its name. */ static void detach_mode(const char *name) { int i; struct mode_bootstrap *mb; for (i = 0; i < MAX_MODES; i++) { mb = Modes[i]; if (mb != NULL && strcmp(name, mb->mb_name) == 0) { int res; res = mb->mb_cleanup(); if (res == 0) { log_warnx("cleanup failed for `%s' mode", mb->mb_name); return; } else { Modes[i] = NULL; return; } } } log_warnx("unknown mode `%s' (see the `modes' directive)", name); } /* --------------------------------------------------------------------- */ /* Detaches all active modes. */ static void detach_modes(void) { int i; for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++) detach_mode(Modes[i]->mb_name); } /* --------------------------------------------------------------------- */ /* Signal handler for close signals. The program can only be exited * through this function. */ /* ARGSUSED */ static void signal_terminate(int sig) { detach_modes(); config_free(); exit(EXIT_SUCCESS); } /* --------------------------------------------------------------------- */ /* Main program. Parses command line options, reads the configuration * file, initializes the mouse and associated files and launches the main * event loop. */ int main(int argc, char **argv) { char *conffile, *modelist, *tstat; int needconf, nodaemon, opt; struct block *conf; setprogname(argv[0]); (void)memset(&Mouse, 0, sizeof(struct mouse)); conffile = _PATH_CONF; modelist = NULL; needconf = 0; nodaemon = -1; /* Parse command line options */ while ((opt = getopt(argc, argv, "Dd:f:m:n")) != -1) { switch (opt) { case 'D': debug++; break; case 'd': /* Mouse device name */ Mouse.m_devname = optarg; break; case 'f': /* Configuration file name */ needconf = 1; conffile = optarg; break; case 'm': /* List of modes to activate */ modelist = optarg; break; case 'n': /* No daemon */ nodaemon = 1; break; default: usage(); /* NOTREACHED */ } } /* Read the configuration file and get some basic properties */ config_read(conffile, needconf); conf = config_get_mode("Global"); if (nodaemon == -1) nodaemon = block_get_propval_int(conf, "nodaemon", 0); X_Console = block_get_propval_int(conf, "xconsole", -1); X_Console_Delay = block_get_propval_int(conf, "xconsole_delay", X_Console_Delay); /* Open wsdisplay status device */ tstat = block_get_propval(conf, "ttystat", _PATH_TTYSTAT); Mouse.m_statfd = open(tstat, O_RDONLY | O_NONBLOCK, 0); if (Mouse.m_statfd == -1) log_err(EXIT_FAILURE, "cannot open %s", tstat); /* Initialize mouse information and attach modes */ if (Mouse.m_devname == NULL) Mouse.m_devname = block_get_propval(conf, "device", _PATH_DEFAULT_MOUSE); Mouse.m_fifoname = block_get_propval(conf, "fifo", NULL); init_mouse(); if (modelist != NULL) attach_modes(modelist); else attach_modes(block_get_propval(conf, "modes", "selection")); /* Setup signal handlers */ (void)signal(SIGINT, signal_terminate); (void)signal(SIGKILL, signal_terminate); (void)signal(SIGQUIT, signal_terminate); (void)signal(SIGTERM, signal_terminate); if (!nodaemon) { /* Become a daemon */ if (daemon(0, 0) == -1) log_err(EXIT_FAILURE, "failed to become a daemon"); /* Create the pidfile, if wanted */ Pid_File = block_get_propval(conf, "pidfile", NULL); if (pidfile(Pid_File) == -1) log_warn("pidfile %s", Pid_File); Foreground = 0; } event_loop(); /* NOTREACHED */ return EXIT_SUCCESS; }