/*- * Copyright (c) 1992, 1993, 1995 Eugene W. Stark * All rights reserved. * * 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. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Eugene W. Stark. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``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 AUTHOR 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. */ #ifndef lint static const char rcsid[] = "$Id: xtend.c,v 1.7 1999/08/08 07:05:46 stark Exp $"; #endif /* not lint */ /* * xtend - X-10 daemon * Eugene W. Stark (stark@cs.sunysb.edu) * January 14, 1993 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xtend.h" #include "xten.h" #include "paths.h" FILE *Log; /* Log file */ FILE *User; /* User connection */ STATUS Status[16][16]; /* Device status table */ int status; /* Status file descriptor */ int tw523; /* tw523 controller */ int sock; /* socket for user */ jmp_buf mainloop; /* longjmp point after SIGHUP */ void onhup(); /* SIGHUP handler */ void onterm(); /* SIGTERM handler */ void onpipe(); /* SIGPIPE handler */ void checkpoint_status __P((void)); void initstatus __P((void)); void logpacket __P((unsigned char *)); void processpacket __P((unsigned char *)); int user_command __P((void)); int main(argc, argv) int argc; char *argv[]; { char *twpath = TWPATH; char *sockpath = SOCKPATH; char logpath[MAXPATHLEN+1]; char statpath[MAXPATHLEN+1]; struct sockaddr_un sa; struct timeval tv; struct passwd *pw; struct group *gr; struct stat sb; int user; FILE *pidf; /* * Make sure we start out running as root */ if(geteuid() != 0) errx(1, "you must be root"); /* * Find out what UID/GID we are to run as */ if((pw = getpwnam(XTENUNAME)) == NULL) errx(1, "no such user '%s'", XTENUNAME); if((gr = getgrnam(XTENGNAME)) == NULL) errx(1, "no such group '%s'", XTENGNAME); /* * Open the log file before doing anything else */ strcpy(logpath, X10DIR); if(stat(logpath, &sb) == -1 && errno == ENOENT) { if(mkdir(logpath, 0755) != -1) { chown(logpath, pw->pw_uid, gr->gr_gid); } else { errx(1, "can't create directory '%s'", logpath); } } strcat(logpath, "/"); strcat(logpath, X10LOGNAME); if((Log = fopen(logpath, "a")) == NULL) errx(1, "can't open log file '%s'", logpath); chown(logpath, pw->pw_uid, gr->gr_gid); /* * Become a daemon */ if(daemon(0, 0) == -1) { fprintf(Log, "%s: Unable to become a daemon\n", thedate()); fclose(Log); exit(1); } fprintf(Log, "%s: %s [%d] started\n", thedate(), argv[0], getpid()); /* * Get ahold of the TW523 device */ if((tw523 = open(twpath, O_RDWR)) < 0) { fprintf(Log, "%s: Can't open %s\n", thedate(), twpath); fclose(Log); exit(1); } fprintf(Log, "%s: %s successfully opened\n", thedate(), twpath); /* * Put our pid in a file so we can be signalled by shell scripts */ if((pidf = fopen(PIDPATH, "w")) == NULL) { fprintf(Log, "%s: Error writing pid file: %s\n", thedate(), PIDPATH); fclose(Log); exit(1); } fprintf(pidf, "%d\n", getpid()); fclose(pidf); /* * Set up socket to accept user commands */ if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { fprintf(Log, "%s: Can't create socket\n", thedate()); fclose(Log); exit(1); } strcpy(sa.sun_path, sockpath); sa.sun_family = AF_UNIX; unlink(sockpath); if(bind(sock, (struct sockaddr *)(&sa), strlen(sa.sun_path) + 2) < 0) { fprintf(Log, "%s: Can't bind socket to %s\n", thedate(), sockpath); fclose(Log); exit(1); } if(listen(sock, 5) < 0) { fprintf(Log, "%s: Can't listen on socket\n", thedate()); fclose(Log); exit(1); } /* * Set proper ownership and permissions on the socket */ if(chown(sockpath, pw->pw_uid, gr->gr_gid) == -1 || chmod(sockpath, 0660) == -1) { fprintf(Log, "%s: Can't set owner/permissions on socket\n", thedate()); fclose(Log); exit(1); } /* * Give up root privileges */ setgid(pw->pw_gid); setuid(pw->pw_uid); /* * Initialize the status table */ strcpy(statpath, X10DIR); strcat(statpath, "/"); strcat(statpath, X10STATNAME); if((status = open(statpath, O_RDWR)) < 0) { if((status = open(statpath, O_RDWR | O_CREAT, 0666)) < 0) { fprintf(Log, "%s: Can't open %s\n", thedate(), statpath); fclose(Log); exit(1); } if(write(status, Status, 16 * 16 * sizeof(STATUS)) != 16 * 16 * sizeof(STATUS)) { fprintf(Log, "%s: Error initializing status file\n", thedate()); fclose(Log); exit(1); } } initstatus(); /* * Return here on SIGHUP after closing and reopening log file. * Also on SIGPIPE after closing user connection. */ signal(SIGTERM, onterm); signal(SIGHUP, onhup); signal(SIGPIPE, onpipe); setjmp(mainloop); /* * Now start the main processing loop. */ tv.tv_sec = 0; tv.tv_usec = 250000; while(1) { fd_set fs; unsigned char rpkt[3]; int sel, h, k; STATUS *s; FD_ZERO(&fs); FD_SET(tw523, &fs); if(User != NULL) FD_SET(user, &fs); else FD_SET(sock, &fs); sel = select(FD_SETSIZE, &fs, 0, 0, &tv); if(sel == 0) { /* * Cancel brightening and dimming on ALL units on ALL house codes, * because the fact that we haven't gotten a packet for awhile means * that there was a gap in transmission. */ for(h = 0; h < 16; h++) { for(k = 0; k < 16; k++) { s = &Status[h][k]; if(s->selected == BRIGHTENING || s->selected == DIMMING) { s->selected = IDLE; s->lastchange = time(NULL); s->changed = 1; } } } fflush(Log); checkpoint_status(); /* * Now that we've done this stuff, we'll set the timeout a little * longer, so we don't keep looping too frequently. */ tv.tv_sec = 60; tv.tv_usec = 0; continue; } /* * While there is stuff happening, we keep a short timeout, so we * don't get stuck for some unknown reason, and so we can keep the * brightening and dimming data up-to-date. */ tv.tv_sec = 0; tv.tv_usec = 250000; if(FD_ISSET(tw523, &fs)) { /* X10 data arriving from TW523 */ if(read(tw523, rpkt, 3) < 3) { fprintf(Log, "%s: Error reading from TW523\n", thedate()); } else { logpacket(rpkt); processpacket(rpkt); } } else if(FD_ISSET(user, &fs) && User != NULL) { if(user_command()) { fprintf(Log, "%s: Closing user connection\n", thedate()); fclose(User); User = NULL; } } else if(FD_ISSET(sock, &fs)) { /* Accept a connection */ if (User == NULL) { int len = sizeof(struct sockaddr_un); if((user = accept(sock, (struct sockaddr *)(&sa), &len)) >= 0) { fprintf(Log, "%s: Accepting user connection\n", thedate()); if((User = fdopen(user, "w+")) == NULL) { fprintf(Log, "%s: Can't attach socket to stream\n", thedate()); } } else { fprintf(Log, "%s: Failure in attempt to accept connection\n", thedate()); } } else { /* "Can't happen */ } } } /* Not reached */ } char *thedate() { char *cp, *cp1; time_t tod; tod = time(NULL); cp = cp1 = ctime(&tod); while(*cp1 != '\n') cp1++; *cp1 = '\0'; return(cp); } /* * When SIGHUP received, close and reopen the Log file */ void onhup() { char logpath[MAXPATHLEN+1]; fprintf(Log, "%s: SIGHUP received, reopening Log\n", thedate()); fclose(Log); strcpy(logpath, X10DIR); strcat(logpath, "/"); strcat(logpath, X10LOGNAME); if((Log = fopen(logpath, "a")) == NULL) errx(1, "can't open log file '%s'", logpath); longjmp(mainloop, 1); /* No return */ } /* * When SIGTERM received, just exit normally */ void onterm() { fprintf(Log, "%s: SIGTERM received, shutting down\n", thedate()); fclose(Log); exit(0); } /* * When SIGPIPE received, reset user connection */ void onpipe() { fprintf(Log, "%s: SIGPIPE received, resetting user connection\n", thedate()); if(User != NULL) { fclose(User); User = NULL; } longjmp(mainloop, 1); /* No return */ }