📄 lircd.c
字号:
/* $Id: lircd.c,v 5.46 2002/09/21 15:25:28 lirc Exp $ *//**************************************************************************** ** lircd.c ***************************************************************** **************************************************************************** * * lircd - LIRC Decoder Daemon * * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de> * Copyright (C) 1998,99 Christoph Bartelmus <lirc@bartelmus.de> * * ======= * HISTORY * ======= * * 0.1: 03/27/96 decode SONY infra-red signals * create mousesystems mouse signals on pipe /dev/lircm * 04/07/96 send ir-codes to clients via socket (see irpty) * 05/16/96 now using ir_remotes for decoding * much easier now to describe new remotes * * 0.5: 09/02/98 finished (nearly) complete rewrite (Christoph) * */#ifdef HAVE_CONFIG_H# include <config.h>#endif/* disable daemonise if maintainer mode SIM_REC / SIM_SEND defined */#if defined(SIM_REC) || defined (SIM_SEND)# undef DAEMONIZE#endif#define _GNU_SOURCE#define _BSD_SOURCE#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <string.h>#include <signal.h>#include <unistd.h>#include <time.h>#include <getopt.h>#include <sys/time.h>#include <sys/socket.h>#include <sys/un.h>#include <netinet/in.h>#include <netdb.h>#include <arpa/inet.h>#include <sys/types.h>#include <sys/stat.h>#include <errno.h>#include <limits.h>#include <fcntl.h>#include <sys/file.h>#ifndef timersub#define timersub(a, b, result) \ do { \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((result)->tv_usec < 0) { \ --(result)->tv_sec; \ (result)->tv_usec += 1000000; \ } \ } while (0)#endif#include "lircd.h"#include "ir_remote.h"#include "config_file.h"#include "hardware.h"#include "hw-types.h"struct ir_remote *remotes;struct ir_remote *free_remotes=NULL;extern struct ir_remote *decoding;extern struct ir_remote *last_remote;extern struct ir_remote *repeat_remote;extern struct ir_ncode *repeat_code;static int repeat_fd=-1;static char *repeat_message=NULL;extern struct hardware hw;char *progname="lircd " VERSION;char *configfile=LIRCDCFGFILE;char *logfile=LOGFILE;FILE *pidfile;struct protocol_directive directives[] ={ {"LIST",list}, {"SEND_ONCE",send_once}, {"SEND_START",send_start}, {"SEND_STOP",send_stop}, {"VERSION",version}, {NULL,NULL} /* {"DEBUG",debug}, {"DEBUG_LEVEL",debug_level}, */};enum protocol_string_num { P_BEGIN=0, P_DATA, P_END, P_ERROR, P_SUCCESS, P_SIGHUP};char *protocol_string[] = { "BEGIN\n", "DATA\n", "END\n", "ERROR\n", "SUCCESS\n", "SIGHUP\n"};#ifndef USE_SYSLOG#define HOSTNAME_LEN 128char hostname[HOSTNAME_LEN+1];FILE *lf=NULL;#endif/* fixme: */#define MAX_PEERS 100int sockfd, sockinet;int clis[FD_SETSIZE-5-MAX_PEERS]; /* substract one for lirc, sockfd, sockinet, logfile, pidfile */#define CT_LOCAL 1#define CT_REMOTE 2int cli_type[FD_SETSIZE-5-MAX_PEERS];int clin=0;int listen_tcpip=0;unsigned short int port=LIRC_INET_PORT;struct peer_connection *peers[MAX_PEERS];int peern = 0;int debug=0;int daemonized=0;static sig_atomic_t term=0,hup=0,alrm=0;static int termsig;inline int max(int a,int b){ return(a>b ? a:b);}/* cut'n'paste from fileutils-3.16: */#define isodigit(c) ((c) >= '0' && (c) <= '7')/* Return a positive integer containing the value of the ASCII octal number S. If S is not an octal number, return -1. */static intoatoi (s) char *s;{ register int i; if (*s == 0) return -1; for (i = 0; isodigit (*s); ++s) i = i * 8 + *s - '0'; if (*s) return -1; return i;}/* A safer write(), since sockets might not write all but only some of the bytes requested */inline int write_socket(int fd, char *buf, int len){ int done,todo=len; while(todo) { done=write(fd,buf,todo); if(done<=0) return(done); buf+=done; todo-=done; } return(len);}inline int write_socket_len(int fd, char *buf){ int len; len=strlen(buf); if(write_socket(fd,buf,len)<len) return(0); return(1);}inline int read_timeout(int fd,char *buf,int len,int timeout){ fd_set fds; struct timeval tv; int ret,n; FD_ZERO(&fds); FD_SET(fd,&fds); tv.tv_sec=timeout; tv.tv_usec=0; /* CAVEAT: (from libc documentation) Any signal will cause `select' to return immediately. So if your program uses signals, you can't rely on `select' to keep waiting for the full time specified. If you want to be sure of waiting for a particular amount of time, you must check for `EINTR' and repeat the `select' with a newly calculated timeout based on the current time. See the example below. Obviously the timeout is not recalculated in the example because this is done automatically on Linux systems... */ do { ret=select(fd+1,&fds,NULL,NULL,&tv); } while(ret==-1 && errno==EINTR); if(ret==-1) { logprintf(LOG_ERR,"select() failed"); logperror(LOG_ERR,NULL); return(-1); } else if(ret==0) return(0); /* timeout */ n=read(fd,buf,len); if(n==-1) { logprintf(LOG_ERR,"read() failed"); logperror(LOG_ERR,NULL); return(-1); } return(n);}void sigterm(int sig){ /* all signals are blocked now */ if(term) return; term=1; termsig=sig;}void dosigterm(int sig){ int i; signal(SIGALRM,SIG_IGN); if(free_remotes!=NULL) { free_config(free_remotes); } free_config(remotes); logprintf(LOG_NOTICE,"caught signal"); for (i=0; i<clin; i++) { shutdown(clis[i],2); close(clis[i]); }; shutdown(sockfd,2); close(sockfd); if(listen_tcpip) { shutdown(sockinet,2); close(sockinet); } fclose(pidfile); (void) unlink(PIDFILE); if(clin>0 && hw.deinit_func) hw.deinit_func();#ifdef USE_SYSLOG closelog();#else if(lf) fclose(lf);#endif signal(sig,SIG_DFL); raise(sig);}void sighup(int sig){ hup=1;}void dosighup(int sig){#ifndef USE_SYSLOG struct stat s;#endif int i; /* reopen logfile first */#ifdef USE_SYSLOG /* we don't need to do anyting as this is syslogd's task */#else logprintf(LOG_INFO,"closing logfile"); if(-1==fstat(fileno(lf),&s)) { dosigterm(SIGTERM); /* shouldn't ever happen */ } fclose(lf); lf=fopen(logfile,"a"); if(lf==NULL) { /* can't print any error messagees */ dosigterm(SIGTERM); } logprintf(LOG_INFO,"reopened logfile"); if(-1==fchmod(fileno(lf),s.st_mode)) { logprintf(LOG_WARNING,"could not set file permissions"); logperror(0,NULL); }#endif config(); for (i=0; i<clin; i++) { if(!(write_socket_len(clis[i],protocol_string[P_BEGIN]) && write_socket_len(clis[i],protocol_string[P_SIGHUP]) && write_socket_len(clis[i],protocol_string[P_END]))) { remove_client(clis[i]); i--; } } /* restart all connection timers */ for (i=0; i<peern; i++) { if (peers[i]->socket == -1) { gettimeofday(&peers[i]->reconnect, NULL); peers[i]->connection_failure = 0; } }}void config(void){ FILE *fd; struct ir_remote *config_remotes; if(free_remotes!=NULL) { logprintf(LOG_ERR,"cannot read config file"); logprintf(LOG_ERR,"old config is still in use"); return; } fd=fopen(configfile,"r"); if(fd==NULL) { logprintf(LOG_ERR,"could not open config file '%s'", configfile); logperror(LOG_ERR,NULL); return; } config_remotes=read_config(fd); fclose(fd); if(config_remotes==(void *) -1) { logprintf(LOG_ERR,"reading of config file failed"); } else { LOGPRINTF(1,"config file read"); if(config_remotes==NULL) { logprintf(LOG_WARNING,"config file contains no " "valid remote control definition"); } /* I cannot free the data structure as they could still be in use */ free_remotes=remotes; remotes=config_remotes; if(hw.config_func) (void) hw.config_func(remotes); }}void nolinger(int sock){ static struct linger linger = {0, 0}; int lsize = sizeof(struct linger); setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, lsize);}void remove_client(int fd){ int i; for(i=0;i<clin;i++) { if(clis[i]==fd) { shutdown(clis[i],2); close(clis[i]); logprintf(LOG_INFO,"removed client"); clin--; if(clin==0 && repeat_remote==NULL && hw.deinit_func) { hw.deinit_func(); } for(;i<clin;i++) { clis[i]=clis[i+1]; } return; } } LOGPRINTF(1,"internal error in remove_client: no such fd");}void add_client(int sock){ int fd; int clilen; struct sockaddr client_addr; int flags; clilen=sizeof(client_addr); fd=accept(sock,(struct sockaddr *)&client_addr,&clilen); if(fd==-1) { logprintf(LOG_ERR,"accept() failed for new client"); logperror(LOG_ERR,NULL); dosigterm(SIGTERM); }; if(fd>=FD_SETSIZE) { logprintf(LOG_ERR,"connection rejected"); shutdown(fd,2); close(fd); return; } nolinger(fd); flags=fcntl(fd,F_GETFL,0); if(flags!=-1) { fcntl(fd,F_SETFL,flags|O_NONBLOCK); } if(client_addr.sa_family==AF_UNIX) { cli_type[clin]=CT_LOCAL; logprintf(LOG_NOTICE,"accepted new client on %s",LIRCD); } else if(client_addr.sa_family==AF_INET) { cli_type[clin]=CT_REMOTE; logprintf(LOG_NOTICE,"accepted new client from %s", inet_ntoa(((struct sockaddr_in *)&client_addr)->sin_addr)); } else { cli_type[clin]=0; /* what? */ } clis[clin++]=fd; if(clin==1 && repeat_remote==NULL) { if(hw.init_func) { if(!hw.init_func()) { shutdown(clis[0],2); close(clis[0]); clin=0; dosigterm(SIGTERM); } } }}int add_peer_connection(char *server){ char *sep; struct servent *service; if(peern<MAX_PEERS) { peers[peern]=malloc(sizeof(struct peer_connection)); if(peers[peern]!=NULL) { gettimeofday(&peers[peern]->reconnect,NULL); peers[peern]->connection_failure = 0; sep=strchr(server,':'); if(sep!=NULL) { *sep=0;sep++; peers[peern]->host=strdup(server); service=getservbyname(sep,"tcp"); if(service) { peers[peern]->port= ntohs(service->s_port); } else { long p; char *endptr; p=strtol(sep,&endptr,10); if(!*sep || *endptr || p<1 || p>USHRT_MAX) { fprintf(stderr, "%s: bad port number \"%s\"\n", progname,sep); return(0); } peers[peern]->port= (unsigned short int) p; } } else { peers[peern]->host=strdup(server); peers[peern]->port=LIRC_INET_PORT; } if(peers[peern]->host==NULL) { fprintf(stderr, "%s: out of memory\n",progname); } } else { fprintf(stderr, "%s: out of memory\n",progname); return(0); } peers[peern]->socket=-1; peern++; return(1); } else { fprintf(stderr,"%s: too many client connections\n", progname); } return(0);}void connect_to_peers(){ int i; struct hostent *host; struct sockaddr_in addr; struct timeval now; int enable=1; gettimeofday(&now,NULL); for(i=0;i<peern;i++) { if(peers[i]->socket!=-1) continue; /* some timercmp() definitions don't work with <= */ if(timercmp(&peers[i]->reconnect,&now,<)) { peers[i]->socket=socket(AF_INET, SOCK_STREAM,0); host=gethostbyname(peers[i]->host); if(host==NULL) { logprintf(LOG_ERR,"name lookup failure " "connecting to %s",peers[i]->host); peers[i]->connection_failure++; gettimeofday(&peers[i]->reconnect,NULL); peers[i]->reconnect.tv_sec+= 5*peers[i]->connection_failure; close(peers[i]->socket); peers[i]->socket=-1; continue; } (void) setsockopt(peers[i]->socket,SOL_SOCKET, SO_KEEPALIVE,&enable,sizeof(enable)); addr.sin_family=host->h_addrtype;; addr.sin_addr=*((struct in_addr *)host->h_addr); addr.sin_port=htons(peers[i]->port); if(connect(peers[i]->socket,(struct sockaddr *) &addr, sizeof(addr))==-1) { logprintf(LOG_ERR, "failure connecting to %s", peers[i]->host); logperror(LOG_ERR, NULL); peers[i]->connection_failure++; gettimeofday(&peers[i]->reconnect,NULL); peers[i]->reconnect.tv_sec+= 5*peers[i]->connection_failure; close(peers[i]->socket); peers[i]->socket=-1; continue; } logprintf(LOG_NOTICE, "connected to %s", peers[i]->host); peers[i]->connection_failure=0; } }}int get_peer_message(struct peer_connection *peer){ int length; char buffer[PACKET_SIZE+1]; char *end; int i; length=read_timeout(peer->socket,buffer,PACKET_SIZE,0); if(length) { buffer[length]=0; end=strchr(buffer,'\n'); if(end==NULL) { logprintf(LOG_ERR,"bad send packet: \"%s\"",buffer); /* remove clients that behave badly */ return(0); } end++; /* include the \n */ end[0]=0; LOGPRINTF(1,"received peer message: \"%s\"",buffer); for(i=0;i<clin;i++) { /* don't relay messages to remote clients */ if(cli_type[i]==CT_REMOTE) continue; LOGPRINTF(1,"writing to client %d",i); if(write_socket(clis[i],buffer,length)<length) { remove_client(clis[i]); i--; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -