/* ************************************************************************* * * serdispproxy.c * proxy for gpi-events (ir-events, a.s.o.) generated by a serdisplib driver * * called by operating system (daemon) * ************************************************************************* * * copyright (C) 2008-2010 wolfgang astleitner * email mrwastl@users.sourceforge.net * * based on serdispd.c, copyright (C) 2006 //MAF * ************************************************************************* * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. Or, point your browser to * http://www.gnu.org/copyleft/gpl.html ************************************************************************* */ #include "../config.h" #include #include #include #include #include #include #include #include #include #include #include #include "serdisplib/serdisp.h" #include "serdisplib/serdisp_srvtools.h" #include "serdisplib/serdisp_gpevents.h" #include "serdisplib/serdisp_fctptr.h" #include "args.h" #include "conf.h" #include "lirc.h" #include "connections.h" /* version information */ #define SERDISPPROXY_VERSION_MAJOR 0 #define SERDISPPROXY_VERSION_MINOR 1 /* private symbols */ static volatile int stop_signal; static void sig_handler( int sig ); /* prototypes */ int check_sockets ( int fd_lst, lirctrans_t* lirc, int ms, int* fd_conn); /* custom signal handler */ static void sig_handler( int sig ) { /* what sort of signal is to be processed? ignore others than SIGINT and SIGTERM */ switch( sig ) { case SIGINT: case SIGTERM: stop_signal = sig; break; } } /* main function */ int main( int argc, char *argv[] ) { int help_flag = 0; int fg_flag = 0; int lirc_flag = 0; char* rcn_fname = "/etc/serdisplib/serdispd.rcn"; char* pid_fname = "/var/run/serdispproxy.pid"; char* proxy_fname = "/tmp/sdproxyd"; char* lircd_fname = "/tmp/lircd"; char* lircd_parg = "8765"; char* lircd_remname= "serdispproxy"; char* verb_arg = "0"; int verb_level = 0; char* vers_flag = NULL; char* debug_arg = "0"; char* eptr; int cpid; int lircd_port = 0; int fd; int fd_lst; FILE* fp; lirctrans_t* lirc = 0; int fd_conn = -1; /* set up commandline switches */ addarg( "--help", "-h", &help_flag, NULL, "Show this help message and quit" ); addarg( "--proxydev", "-pdev", &proxy_fname, "filename", "Name of proxy device" ); addarg( "--lirc", "-l", &lirc_flag, NULL, "Enable lirc emulation" ); addarg( "--rcnfile", "-rcn", &rcn_fname, "filename", "Name of translation file containing normalised rc-codes" ); addarg( "--lircdev", "-ldev", &lircd_fname, "filename", "Name of lircd device" ); addarg( "--lircport", "-lprt", &lircd_parg, "port", "Port for lirc listener" ); addarg( "--remname", "-rem", &lircd_remname, "name", "Remote name in lircd" ); addarg( "--fg", "-F", &fg_flag, NULL, "Run in foreground (no detach)" ); addarg( "--pfile", "-pid", &pid_fname, "filename", "Filename to store process ID" ); addarg( "--debug", "-d", &debug_arg, "level", "Debug level (0 .. no debugging, 2 .. max. debugging)" ); addarg( "--verbose", "-v", &verb_arg, "level", "Set verbosity level" ); addarg( "--version", "-V", &vers_flag, NULL, "Show program version" ); /* parse the arguments */ if( getargs(argc,argv) ) { usage( argv[0], 1 ); return 1; } /* set debug level */ if( debug_arg ) { int debug_level = (int) strtol( debug_arg, &eptr, 10 ); while( isspace(*eptr) ) eptr++; if( *eptr ) { fprintf( stderr, "Bad debug level: '%s'\n", debug_arg ); usage( argv[0], 1 ); return 1; } sd_setdebuglevel( debug_level ); } /* set verbosity level (print events to stdout) */ if( verb_arg ) { verb_level = (int) strtol( verb_arg, &eptr, 10 ); while( isspace(*eptr) ) eptr++; if( *eptr ) { fprintf( stderr, "Bad verbosity level: '%s'\n", verb_arg ); usage( argv[0], 1 ); return 1; } } /* show version and/or help */ if( vers_flag ) { printf( "%s version %d.%d (using serdisplib version %d.%d)\n", argv[0], SERDISPPROXY_VERSION_MAJOR, SERDISPPROXY_VERSION_MINOR, SERDISP_VERSION_GET_MAJOR(serdisp_getversioncode()), SERDISP_VERSION_GET_MINOR(serdisp_getversioncode()) ); printf( "(C) 2008-2010 by Wolfgang Astleitner\n\n" ); } if( help_flag ) { usage( argv[0], 0 ); return 0; } /* if foreground mode: log to stderr */ sd_setlogmedium( fg_flag ? SD_LOG_STDERR : SD_LOG_SYSLOG ); /* check min. req. flags */ if (!lirc_flag && !verb_level) { fprintf( stderr, "At least option -l or -v > 0 required\n"); usage( argv[0], 1 ); return 1; } if (!fg_flag && verb_level) { fprintf( stderr, "Option -v is only allowed when running in foreground\n"); return 1; } SDFCTPTR_init(); /* goto background */ if( ! fg_flag ) { cpid = fork(); if( cpid==-1 ) { perror( "Could not fork" ); return -2; } if( cpid ) /* Parent process exits ... */ return 0; if( setsid()==-1 ) { perror( "Could not create new session" ); return -2; } fd = open( "/dev/null", O_RDWR, 0 ); if( fd!=-1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); } } /* end of: if( ! fg_flag )*/ /* enable lirc-emulation */ if (lirc_flag) { lirc = sdtools_malloc( sizeof(lirctrans_t) ); if( !lirc ) { sd_error( SERDISP_ERUNTIME, "%s(): could not allocate memory, error: %s", __func__, strerror(errno) ); return -1; } lirc->in_fd = -1; lirc->un_fd = -1; lirc->input_fd = -1; lirc->rc5prev = 0; lirc->rc5reps = 0; lirc->remname = lircd_remname; /* read rcN-file */ if( lirc_read_rcnfile(lirc,rcn_fname) ) return -1; /* check if a lircd listener port is set */ if ( strlen(lircd_parg) > 0 ) { lircd_port = (int) strtol( lircd_parg, &eptr, 10 ); while( isspace(*eptr) ) eptr++; if( *eptr || lircd_port<=0 ) { fprintf( stderr, "Bad port number for lircd listener: '%s'\n", lircd_parg ); usage( argv[0], 1 ); return 1; } } else { lircd_port = 0; } } /* ... done reading all arguments ... */ /* setup PID file, ignore errors... */ fp = fopen( pid_fname, "w" ); if( fp ) { fprintf( fp, "%d\n", getpid() ); fclose( fp ); } /* OK, from here on we catch some terminating signals and ignore others */ signal( SIGINT, sig_handler ); signal( SIGTERM, sig_handler ); signal( SIGPIPE, SIG_IGN ); /* start up listener socket */ fd_lst = create_devicelistener( proxy_fname ); if( fd_lst < 0 ) return -1; /* start up lirc listeners */ if (lirc_flag) { int sock; if( lircd_fname && strlen(lircd_fname) > 0 ) { sock = create_devicelistener(lircd_fname); if (sock >= 0) { lirc->un_fd = sock; lirc->un_path = strdup( lircd_fname ); } } if( lircd_port > 0 ) { sock = create_portlistener(lircd_port); if (sock >= 0) { lirc->in_fd = sock; lirc->in_port = lircd_port; } } } stop_signal = 0; /* main loop */ while( !stop_signal ) { int retval; /* accept new connections (/tmp/lircd and /tmp/sdproxyd), get pending client requests */ retval = check_sockets(fd_lst, lirc, 500, &fd_conn); if (retval) break; if (fd_conn >= 0) { int retval = 0; if (lirc_flag) { retval = lirc_processinput( lirc, fd_conn, NULL ); } else { unsigned char buff[256]; int bytes = read( fd_conn, buff, sizeof(buff) ); if( bytes == -1 ) { retval = -2; } else if ( bytes == 0 ) { sd_srvmsg( LOG_ERR, "fd %d not fed any longer ...", fd_conn ); retval = -3; } else { SDGP_event_t event; memcpy(&event, buff, sizeof(event)); /* convert header from network byte order to network byte order */ SDGPT_event_header_ntoh(&event); if (SDGPT_GETCATEGORY(event.type) == SDGPT_CATEGORYVALUE) { printf("event: type %02x, cmd %02x, dev %02x/%02x, val %08x\n", event.type, event.cmdid, event.devid, event.subid, (int32_t)(event.value) ); } else { int l; printf("event: type %02x, cmd %02x, dev %02x/%02x, len %d: ", event.type, event.cmdid, event.devid, event.subid, event.length ); /* convert payload from network byte order to host byte order */ /* SDGPT_event_payload_ntoh(&buff[(int)sizeof(event)], bytes-sizeof(event) , event.word_size );*/ /* leave payload in network byte order for easier debug output */ for (l = 0; l < 0x10 && l < (bytes - sizeof(event)); l++) { printf("%02x", buff[l+sizeof(event)]); if ( ((l+1) % ((event.word_size) ? event.word_size : 1)) == 0) printf(" "); } printf("\n"); } retval = 0; } } if( retval ) { if (retval == -2) { if (errno == EAGAIN || errno == EBADF) { usleep(100); } } else if (retval == -3) { close(fd_conn); fd_conn = -1; } else break; } } } /* stop signal received: stop program */ if( stop_signal ) sd_debug( 1, "Exiting due to signal %d ...", stop_signal ); if (fd_conn) close (fd_conn); /* close listener and shutdown all connections */ close( fd_lst ); close_connection( NULL ); /* cleanup all displays and PID file */ unlink( pid_fname ); if (strncmp(proxy_fname, "/tmp/", 5) == 0) unlink (proxy_fname); if (lircd_fname && strncmp(lircd_fname, "/tmp/", 5) == 0) unlink (lircd_fname); return 0; } /* ********************************* int check_sockets(fd_lst, lirc, ms, *fd_conn) ********************************* checks for new connections on /tmp/lircd and /tmp/sdproxyd, get pending client requests ********************************* fd_lst ... file descriptor for listening device /tmp/sdproxyd lirc ... struct with lirc-items and connections ms ... timeout for select() in microseconds *fd_conn ... pointer to variable containing connection socket ********************************* returns 0 on success or -1 if a severe error occurred */ int check_sockets ( int fd_lst, lirctrans_t* lirc, int ms, int* fd_conn) { struct timeval timeout; fd_set readfds; fd_set writefds; fd_set exceptfds; lirccon_t *lcon, *lconnext; int fd_max = fd_lst; timeout.tv_sec = ms / 1000; timeout.tv_usec = ms - 1000*timeout.tv_sec; FD_ZERO( &readfds); FD_ZERO( &writefds); FD_ZERO( &exceptfds); FD_SET( fd_lst, &readfds); if ( *fd_conn >= 0) { FD_SET( *fd_conn, &readfds); FD_SET( *fd_conn, &exceptfds); } if (lirc) { if( lirc->in_fd >= 0 ) { FD_SET( lirc->in_fd, &readfds); if( lirc->in_fd > fd_max ) fd_max = lirc->in_fd; } if( lirc->un_fd >= 0 ) { FD_SET( lirc->un_fd, &readfds); if( lirc->un_fd > fd_max ) fd_max = lirc->un_fd; } for( lcon=lirc->connections; lcon; lcon=lcon->next ) { FD_SET( lcon->fd, &readfds ); if( lcon->fd > fd_max ) fd_max = lcon->fd; } } if( select(fd_max+1,&readfds,&writefds,&exceptfds,&timeout)<0 ) { sd_srvmsg( LOG_ERR, "%s(): error in select: %s", __func__, strerror(errno) ); return -1; } if( FD_ISSET(fd_lst,&readfds) ) { struct sockaddr rmadr; socklen_t rmadrl = sizeof(rmadr); char rmname[1024]; int flags; if (*fd_conn >= 0) close (*fd_conn); *fd_conn = fp_accept( fd_lst, &rmadr, &rmadrl ); if( *fd_conn < 0 ) { sd_srvmsg( LOG_ERR, "Error in accept: %s", strerror(errno) ); return -1; } if( *fd_conn >= FD_SETSIZE ) { sd_srvmsg( LOG_ERR, "File descriptor %d to large (too many connections?)", *fd_conn ); return -1; } if( fp_getnameinfo(&rmadr,rmadrl,rmname,sizeof(rmname),NULL,0,0) ) { sd_srvmsg( LOG_ERR, "Could not get peer name: %s", strerror(errno) ); return -1; } sd_srvmsg( LOG_NOTICE, "Connection initiated from %s", rmname ); flags = fcntl( *fd_conn, F_GETFL ); fcntl( *fd_conn, F_SETFL, flags|O_NONBLOCK ); sd_debug( 2, "New connection from %s with handle %d", rmname, *fd_conn ); } if (lirc) { if( lirc->in_fd >= 0 ) if( FD_ISSET(lirc->in_fd,&readfds) ) if( lirc_acceptconnection(lirc,lirc->in_fd, NULL) ) return -1; if( lirc->un_fd>=0 ) if( FD_ISSET(lirc->un_fd,&readfds) ) if( lirc_acceptconnection(lirc,lirc->un_fd, NULL) ) return -1; for( lcon=lirc->connections; lcon; lcon=lconnext ) { lconnext = lcon->next; /* we might loose con in loop body! */ if( FD_ISSET(lcon->fd,&readfds) ) if( lirc_readmsg(lirc,lcon) ) return -1; } } return 0; }