spacenavd/src/spnavd.c

276 wiersze
5.3 KiB
C

/*
spacenavd - a free software replacement driver for 6dof space-mice.
Copyright (C) 2007-2010 John Tsiombikas <nuclear@member.fsf.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include "spnavd.h"
#include "dev.h"
#include "client.h"
#include "proto_unix.h"
#ifdef USE_X11
#include "proto_x11.h"
#endif
static void cleanup(void);
static void daemonize(void);
static int write_pid_file(void);
static void handle_events(fd_set *rset);
static void sig_handler(int s);
int main(int argc, char **argv)
{
int i, ret, become_daemon = 1;
for(i=1; i<argc; i++) {
if(argv[i][0] == '-' && argv[i][2] == 0) {
switch(argv[i][1]) {
case 'd':
become_daemon = !become_daemon;
break;
case 'v':
verbose = 1;
break;
case 'h':
printf("usage: %s [options]\n", argv[0]);
printf("options:\n");
printf(" -d\tdo not daemonize\n");
printf(" -v\tverbose output\n");
printf(" -h\tprint this usage information\n");
return 0;
default:
fprintf(stderr, "unrecognized argument: %s\n", argv[i]);
return 1;
}
} else {
fprintf(stderr, "unexpected argument: %s\n", argv[i]);
return 1;
}
}
if(become_daemon) {
daemonize();
}
write_pid_file();
puts("Spacenav daemon " VERSION);
read_cfg("/etc/spnavrc", &cfg);
if(init_clients() == -1) {
return 1;
}
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
signal(SIGSEGV, sig_handler);
signal(SIGHUP, sig_handler);
signal(SIGUSR1, sig_handler);
signal(SIGUSR2, sig_handler);
if(init_dev() == -1) {
init_hotplug();
}
init_unix();
#ifdef USE_X11
init_x11();
#endif
atexit(cleanup);
for(;;) {
fd_set rset;
int fd, max_fd = 0;
struct client *c;
FD_ZERO(&rset);
/* set the device fd if it's open, otherwise set the hotplug fd */
if((fd = get_dev_fd()) != -1 || (fd = get_hotplug_fd()) != -1) {
FD_SET(fd, &rset);
if(fd > max_fd) max_fd = fd;
}
/* the UNIX domain socket listening for connections */
if((fd = get_unix_socket()) != -1) {
FD_SET(fd, &rset);
if(fd > max_fd) max_fd = fd;
}
/* all the UNIX socket clients */
c = first_client();
while(c) {
if(get_client_type(c) == CLIENT_UNIX) {
int s = get_client_socket(c);
assert(s >= 0);
FD_SET(s, &rset);
if(s > max_fd) max_fd = s;
}
c = next_client();
}
/* and the X server socket */
#ifdef USE_X11
if((fd = get_x11_socket()) != -1) {
FD_SET(fd, &rset);
if(fd > max_fd) max_fd = fd;
}
#endif
do {
ret = select(max_fd + 1, &rset, 0, 0, 0);
} while(ret == -1 && errno == EINTR);
if(ret > 0) {
handle_events(&rset);
}
}
return 0; /* unreachable */
}
static void cleanup(void)
{
#ifdef USE_X11
close_x11(); /* call to avoid leaving garbage in the X server's root windows */
#endif
close_unix();
shutdown_dev();
remove(PIDFILE);
}
static void daemonize(void)
{
int i, pid;
if((pid = fork()) == -1) {
perror("failed to fork");
exit(1);
} else if(pid) {
exit(0);
}
setsid();
chdir("/");
/* redirect standard input/output/error */
for(i=0; i<3; i++) {
close(i);
}
open("/dev/zero", O_RDONLY);
if(open(LOGFILE, O_WRONLY | O_CREAT | O_TRUNC, 0644) == -1) {
open("/dev/null", O_WRONLY);
}
dup(1);
setvbuf(stdout, 0, _IOLBF, 0);
setvbuf(stderr, 0, _IONBF, 0);
}
static int write_pid_file(void)
{
FILE *fp;
int pid = getpid();
if(!(fp = fopen(PIDFILE, "w"))) {
return -1;
}
fprintf(fp, "%d\n", pid);
fclose(fp);
return 0;
}
static void handle_events(fd_set *rset)
{
int dev_fd, hotplug_fd;
/* handle anything coming through the UNIX socket */
handle_uevents(rset);
#ifdef USE_X11
/* handle any X11 events (magellan protocol) */
handle_xevents(rset);
#endif
/* finally read any pending device input data */
if((dev_fd = get_dev_fd()) != -1) {
if(FD_ISSET(dev_fd, rset)) {
struct dev_input inp;
/* read an event from the device ... */
while(read_dev(&inp) != -1) {
/* ... and process it, possibly dispatching a spacenav event to clients */
process_input(&inp);
}
}
} else if((hotplug_fd = get_hotplug_fd()) != -1) {
if(FD_ISSET(hotplug_fd, rset)) {
handle_hotplug();
}
}
}
/* signals usr1 & usr2 are sent by the spnav_x11 script to start/stop the
* daemon's connection to the X server.
*/
static void sig_handler(int s)
{
int tmp;
switch(s) {
case SIGHUP:
tmp = cfg.led;
read_cfg("/etc/spnavrc", &cfg);
if(cfg.led != tmp) {
set_led(cfg.led);
}
break;
case SIGSEGV:
fprintf(stderr, "Segmentation fault caught, trying to exit gracefully\n");
case SIGINT:
case SIGTERM:
exit(0);
#ifdef USE_X11
case SIGUSR1:
init_x11();
break;
case SIGUSR2:
close_x11();
break;
#endif
default:
break;
}
}