summaryrefslogtreecommitdiffstats
path: root/libexec/xtend/xtend.c
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/xtend/xtend.c')
-rw-r--r--libexec/xtend/xtend.c370
1 files changed, 370 insertions, 0 deletions
diff --git a/libexec/xtend/xtend.c b/libexec/xtend/xtend.c
new file mode 100644
index 0000000..dd32129
--- /dev/null
+++ b/libexec/xtend/xtend.c
@@ -0,0 +1,370 @@
+/*-
+ * 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.
+ *
+ * $Id$
+ */
+
+/*
+ * xtend - X-10 daemon
+ * Eugene W. Stark (stark@cs.sunysb.edu)
+ * January 14, 1993
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#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 */
+
+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;
+ int fd;
+ FILE *pidf;
+
+ /*
+ * Make sure we start out running as root
+ */
+ if(geteuid() != 0) {
+ fprintf(stderr, "You must be root to run %s\n", argv[0]);
+ exit(1);
+ }
+
+ /*
+ * Find out what UID/GID we are to run as
+ */
+ if((pw = getpwnam(XTENUNAME)) == NULL) {
+ fprintf(stderr, "%s: No such user '%s'\n", argv[0], XTENUNAME);
+ exit(1);
+ }
+ if((gr = getgrnam(XTENGNAME)) == NULL) {
+ fprintf(stderr, "%s: No such group '%s'\n", argv[0], XTENGNAME);
+ exit(1);
+ }
+
+ /*
+ * 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 {
+ fprintf(stderr, "%s: Can't create directory '%s'\n", argv[0], logpath);
+ exit(1);
+ }
+ }
+ strcat(logpath, "/");
+ strcat(logpath, X10LOGNAME);
+ if((Log = fopen(logpath, "a")) == NULL) {
+ fprintf(stderr, "Can't open log file %s\n", logpath);
+ exit(1);
+ }
+ 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)) {
+ if(User != NULL) {
+ if(user_command()) {
+ fprintf(Log, "%s: Closing user connection\n", thedate());
+ fclose(User);
+ User = NULL;
+ }
+ } else {
+ /* "Can't" happen */
+ }
+ } 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) {
+ fprintf(stderr, "Can't open log file %s\n", logpath);
+ exit(1);
+ }
+ 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 */
+}
OpenPOWER on IntegriCloud