From d5c5338901425e12b9dc25280370cc41a721dfc7 Mon Sep 17 00:00:00 2001 From: jkh Date: Sat, 22 Oct 1994 09:53:33 +0000 Subject: The X-10 demon. From 1.1.5.1. --- libexec/xtend/Makefile | 12 ++ libexec/xtend/packet.c | 317 +++++++++++++++++++++++++++++++++++++++++++++++++ libexec/xtend/paths.h | 11 ++ libexec/xtend/status.c | 109 +++++++++++++++++ libexec/xtend/user.c | 166 ++++++++++++++++++++++++++ libexec/xtend/xten.h | 58 +++++++++ libexec/xtend/xtend.8 | 174 +++++++++++++++++++++++++++ libexec/xtend/xtend.c | 304 +++++++++++++++++++++++++++++++++++++++++++++++ libexec/xtend/xtend.h | 77 ++++++++++++ 9 files changed, 1228 insertions(+) create mode 100644 libexec/xtend/Makefile create mode 100644 libexec/xtend/packet.c create mode 100644 libexec/xtend/paths.h create mode 100644 libexec/xtend/status.c create mode 100644 libexec/xtend/user.c create mode 100644 libexec/xtend/xten.h create mode 100644 libexec/xtend/xtend.8 create mode 100644 libexec/xtend/xtend.c create mode 100644 libexec/xtend/xtend.h (limited to 'libexec/xtend') diff --git a/libexec/xtend/Makefile b/libexec/xtend/Makefile new file mode 100644 index 0000000..2164a73 --- /dev/null +++ b/libexec/xtend/Makefile @@ -0,0 +1,12 @@ +# Makefile for xtend (Stark) 10/30/93 + +BINMODE= 4555 + +PROG= xtend +SRCS= xtend.c status.c packet.c user.c +CFLAGS+=-I. +LDADD+= -lutil + +MAN8= xtend.8 + +.include diff --git a/libexec/xtend/packet.c b/libexec/xtend/packet.c new file mode 100644 index 0000000..bc75fcb --- /dev/null +++ b/libexec/xtend/packet.c @@ -0,0 +1,317 @@ +/*- + * Copyright (c) 1992, 1993 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. + */ + +#include +#include +#include "xtend.h" +#include "xten.h" + +char *X10housenames[] = { + "A", "B", "C", "D", "E", "F", "G", "H", + "I", "J", "K", "L", "M", "N", "O", "P", + NULL +}; + +char *X10cmdnames[] = { + "1", "2", "3", "4", "5", "6", "7", "8", + "9", "10", "11", "12", "13", "14", "15", "16", + "AllUnitsOff", "AllLightsOn", "On", "Off", "Dim", "Bright", "AllLightsOff", + "ExtendedCode", "HailRequest", "HailAcknowledge", "PreSetDim0", "PreSetDim1", + "ExtendedData", "StatusOn", "StatusOff", "StatusRequest", + NULL +}; + +/* + * Log a packet and update device status accordingly + */ + +logpacket(p) +unsigned char *p; +{ + fprintf(Log, "%s: %s %s ", thedate(), + X10housenames[p[1]], X10cmdnames[p[2]]); + if(p[0] & TW_RCV_LOCAL) fprintf(Log, "(loc,"); + else fprintf(Log, "(rem,"); + if(p[0] & TW_RCV_ERROR) fprintf(Log, "err)"); + else fprintf(Log, " ok)"); + fprintf(Log, "\n"); +} + +/* + * Process a received packet p, updating device status information both + * in core and on disk. + */ + +processpacket(p) +unsigned char *p; +{ + int i, j, h, k; + STATUS *s; + + /* + * If the packet had the error flag set, there is no other useful info. + */ + if(p[0] & TW_RCV_ERROR) return; + /* + * First update in-core status information for the device. + */ + h = p[1]; k = p[2]; + if(k < 16) { /* We received a unit code, to select a particular device */ + s = &Status[h][k]; + s->selected = SELECTED; + s->lastchange = time(NULL); + s->changed = 1; + } else { /* We received a key code, to execute some function */ + /* + * Change in status depends on the key code received + */ + if(k == DIM) { + /* + * We can't really track DIM/BRIGHT properly the way things are right + * now. The TW523 reports the first, fourth, seventh, etc. Dim packet. + * We don't really have any way to tell when gaps occur, to cancel + * selection. For now, we'll assume that successive sequences of + * Dim/Bright commands are never transmitted without some other + * intervening command, and we make a good guess about how many units of + * dim/bright are represented by each packet actually reported by the + * TW523. + */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + switch(s->selected) { + case SELECTED: /* Selected, but not being dimmed or brightened */ + if(s->onoff == 0) { + s->onoff = 1; + s->brightness = 15; + } + s->brightness -= 2; + if(s->brightness < 0) s->brightness = 0; + s->selected = DIMMING; + s->lastchange = time(NULL); + s->changed = 1; + break; + case DIMMING: /* Selected and being dimmed */ + s->brightness -=3; + if(s->brightness < 0) s->brightness = 0; + s->lastchange = time(NULL); + s->changed = 1; + break; + case BRIGHTENING: /* Selected and being brightened (an error) */ + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + break; + default: + break; + } + } + } else if(k == BRIGHT) { + /* + * Same problem here... + */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + switch(s->selected) { + case SELECTED: /* Selected, but not being dimmed or brightened */ + if(s->onoff == 0) { + s->onoff = 1; + s->brightness = 15; + } + s->brightness += 2; + if(s->brightness > 15) s->brightness = 15; + s->selected = BRIGHTENING; + s->lastchange = time(NULL); + s->changed = 1; + break; + case DIMMING: /* Selected and being dimmed (an error) */ + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + break; + case BRIGHTENING: /* Selected and being brightened */ + s->brightness +=3; + if(s->brightness > 15) s->brightness = 15; + s->lastchange = time(NULL); + s->changed = 1; + break; + default: + break; + } + } + } else { /* Other key codes besides Bright and Dim */ + /* + * We cancel brightening and dimming on ALL units on ALL house codes, + * because the arrival of a different packet indicates a gap that + * terminates any prior sequence of brightening and dimming + */ + for(j = 0; j < 16; j++) { + for(i = 0; i < 16; i++) { + s = &Status[j][i]; + if(s->selected == BRIGHTENING || s->selected == DIMMING) { + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + } + switch(k) { + case ALLUNITSOFF: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + s->onoff = 0; + s->selected = IDLE; + s->brightness = 0; + s->lastchange = time(NULL); + s->changed = 1; + } + break; + case ALLLIGHTSON: + /* Does AllLightsOn cancel selectedness of non-lights? */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->devcap & ISLIGHT) { + s->onoff = 1; + s->selected = IDLE; + s->brightness = 15; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case UNITON: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->onoff = 1; + s->selected = IDLE; + s->brightness = 15; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case UNITOFF: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->onoff = 0; + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case ALLLIGHTSOFF: + /* Does AllLightsOff cancel selectedness of non-lights? */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->devcap & ISLIGHT) { + s->onoff = 0; + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case EXTENDEDCODE: + break; + case HAILREQUEST: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->selected = HAILED; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case HAILACKNOWLEDGE: + /* Do these commands cancel selection of devices not affected? */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == HAILED) { + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case PRESETDIM0: + case PRESETDIM1: + /* I don't really understand these */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case EXTENDEDDATA: + /* Who knows? The TW523 can't receive these anyway. */ + break; + case STATUSON: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == REQUESTED) { + s->onoff = 1; + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case STATUSOFF: + for(i = 0; i < 16; i++) { + if(s->selected == REQUESTED) { + s = &Status[h][i]; + s->onoff = 0; + s->selected = IDLE; + s->brightness = 0; + s->lastchange = time(NULL); + s->changed = 1; + } + } + case STATUSREQUEST: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected) { + s->selected = REQUESTED; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + } + } + } +} diff --git a/libexec/xtend/paths.h b/libexec/xtend/paths.h new file mode 100644 index 0000000..ad49443 --- /dev/null +++ b/libexec/xtend/paths.h @@ -0,0 +1,11 @@ +/* + * Pathnames for files used by xtend + */ + +#define X10DIR "/var/spool/xten/" +#define X10LOGNAME "Log" +#define X10STATNAME "Status" +#define X10DUMPNAME "status.out" +#define TWPATH "/dev/tw0" +#define SOCKPATH "/var/run/tw523" +#define PIDPATH "/var/run/xtend.pid" diff --git a/libexec/xtend/status.c b/libexec/xtend/status.c new file mode 100644 index 0000000..6249577 --- /dev/null +++ b/libexec/xtend/status.c @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 1992, 1993 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. + */ + +#include +#include +#include +#include +#include "xtend.h" +#include "xten.h" +#include "paths.h" + +/* + * Initialize the status table from the status files + */ + +initstatus() +{ + int h, i; + + if(lseek(status, 0, SEEK_SET) != 0) { + fprintf(Log, "%s: Seek error on status file\n", thedate()); + return; + } + if(read(status, Status, 16*16*sizeof(STATUS)) != 16*16*sizeof(STATUS)) { + fprintf(Log, "%s: Read error on status file\n", thedate()); + return; + } +} + +/* + * Checkpoint status of any devices whose status has changed + * and notify anyone monitoring those devices. + */ + +checkpoint_status() +{ + int h, i, k, offset; + + offset = 0; + for(h = 0; h < 16; h++) { + for(i = 0; i < 16; i++) { + if(Status[h][i].changed) { + if(lseek(status, offset, SEEK_SET) != offset) { + fprintf(Log, "%s: Seek error on status file\n", thedate()); + } else { + if(write(status, &Status[h][i], sizeof(STATUS)) != sizeof(STATUS)) { + fprintf(Log, "%s: Write error on status file\n", thedate()); + } + } + Status[h][i].changed = 0; + for(k = 0; k < MAXMON; k++) { + if(Monitor[k].inuse + && Monitor[k].house == h && Monitor[k].unit == i) { + /* + * Arrange to catch SIGPIPE in case client has gone away. + */ + extern int client; + extern void clientgone(); + void (*prev)(); + + client = k; + prev = signal(SIGPIPE, clientgone); + printstatus(Monitor[k].user, &Status[h][i]); + fflush(Monitor[k].user); + signal(SIGPIPE, prev); + } + } + } + offset += sizeof(STATUS); + } + } +} + +int client; + +void clientgone() +{ + fprintf(Log, "%s: Deleting monitor table entry %d, client gone\n", thedate(), client); + fclose(Monitor[client].user); + Monitor[client].inuse = 0; +} diff --git a/libexec/xtend/user.c b/libexec/xtend/user.c new file mode 100644 index 0000000..effce38 --- /dev/null +++ b/libexec/xtend/user.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1992, 1993 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. + */ + +#include +#include +#include +#include "xtend.h" +#include "xten.h" +#include "paths.h" + +MONENTRY Monitor[MAXMON]; + +/* + * Process a user command + */ + +user_command() +{ + char h; + int i, k, c, n, error; + char cmd[512], dumppath[MAXPATHLEN+1], pkt[3]; + FILE *dumpf; + + error = 0; + if(fgets(cmd, 512, User) != NULL) { + if(sscanf(cmd, "status %c %d", &h, &i) == 2 + && h >= 'A' && h <= 'P' && i >= 1 && i <= 16) { + h -= 'A'; + i--; + printstatus(User, &Status[h][i]); + } else if(sscanf(cmd, "send %c %s %d", &h, cmd, &n) == 3 + && h >= 'A' && h <= 'P' && (i = find(cmd, X10cmdnames)) >= 0) { + h -= 'A'; + pkt[0] = h; + pkt[1] = i; + pkt[2] = n; + if(write(tw523, pkt, 3) != 3) { + fprintf(Log, "%s: Transmission error (packet [%s %s]:%d).\n", + thedate(), X10housenames[h], X10cmdnames[i], n); + error++; + } else { + fprintf(User, "OK\n"); + } + } else if(!strcmp("dump\n", cmd)) { + strcpy(dumppath, X10DIR); + strcat(dumppath, X10DUMPNAME); + if((dumpf = fopen(dumppath, "w")) != NULL) { + for(h = 0; h < 16; h++) { + for(i = 0; i < 16; i++) { + if(Status[h][i].lastchange) { + fprintf(dumpf, "%s%d\t", X10housenames[h], i+1); + printstatus(dumpf, &Status[h][i]); + } + } + } + fclose(dumpf); + fprintf(User, "OK\n"); + } else { + error++; + } + } else if(sscanf(cmd, "monitor %c %d", &h, &i) == 2 + && h >= 'A' && h <= 'P' && i >= 1 && i <= 16) { + h -= 'A'; + i--; + for(k = 0; k < MAXMON; k++) { + if(!Monitor[k].inuse) break; + } + if(k == MAXMON) { + error++; + } else { + Monitor[k].house = h; + Monitor[k].unit = i; + Monitor[k].user = User; + Monitor[k].inuse = 1; + fprintf(Log, "%s: Adding %c %d to monitor list (entry %d)\n", + thedate(), h+'A', i+1, k); + fprintf(User, "OK\n"); + fflush(User); + User = NULL; + return(0); /* We don't want caller to close stream */ + } + } else if(!strcmp("done\n", cmd)) { + fprintf(User, "OK\n"); + fflush(User); + return(1); + } else { + if(feof(User)) { + return(1); + } else { + error++; + } + } + } else { + error++; + } + if(error) { + fprintf(User, "ERROR\n"); + } + fflush(User); + return(0); +} + +find(s, tab) +char *s; +char *tab[]; +{ + int i; + + for(i = 0; tab[i] != NULL; i++) { + if(strcmp(s, tab[i]) == 0) return(i); + } + return(-1); +} + +printstatus(f, s) +FILE *f; +STATUS *s; +{ + fprintf(f, "%s:%d", s->onoff ? "On" : "Off", s->brightness); + switch(s->selected) { + case IDLE: + fprintf(f, " (normal) "); break; + case SELECTED: + fprintf(f, " (selected) "); break; + case DIMMING: + fprintf(f, " (dimming) "); break; + case BRIGHTENING: + fprintf(f, " (brightening) "); break; + case REQUESTED: + fprintf(f, " (requested) "); break; + case HAILED: + fprintf(f, " (hailed) "); break; + default: + fprintf(f, " (bogus) "); break; + } + fprintf(f, "%s", ctime(&s->lastchange)); +} + diff --git a/libexec/xtend/xten.h b/libexec/xtend/xten.h new file mode 100644 index 0000000..fea7e17 --- /dev/null +++ b/libexec/xtend/xten.h @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 1992, 1993 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. + */ + +extern char *X10housenames[]; +extern char *X10cmdnames[]; + +#define ALLUNITSOFF 16 +#define ALLLIGHTSON 17 +#define UNITON 18 +#define UNITOFF 19 +#define DIM 20 +#define BRIGHT 21 +#define ALLLIGHTSOFF 22 +#define EXTENDEDCODE 23 +#define HAILREQUEST 24 +#define HAILACKNOWLEDGE 25 +#define PRESETDIM0 26 +#define PRESETDIM1 27 +#define EXTENDEDDATA 28 +#define STATUSON 29 +#define STATUSOFF 30 +#define STATUSREQUEST 31 + +/* + * Flags for first byte of received packet + */ + +#define TW_RCV_LOCAL 1 /* The packet arrived during a local transmission */ +#define TW_RCV_ERROR 2 /* An invalid/corrupted packet was received */ + diff --git a/libexec/xtend/xtend.8 b/libexec/xtend/xtend.8 new file mode 100644 index 0000000..e6ac50a --- /dev/null +++ b/libexec/xtend/xtend.8 @@ -0,0 +1,174 @@ +.\" Copyright (c) 1992, 1993 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. +.\" +.Th XTEND 8 "30 Oct 1993" +.Dd Oct 30, 1993 +.Dt XTEND 8 +.Os BSD FreeBSD +.Sh NAME +xtend \- X-10 daemon +.Sh SYNOPSIS +.Nm xtend +.Sh DESCRIPTION +.Nm Xtend +interfaces between user-level programs and the TW523 X-10 controller. +It logs all packets received from the TW523, attempts to track the +status of all X-10 devices, and accepts socket connections from user-level +client programs that need to manipulate X-10 devices. +.Pp +When +.Nm xtend +is started, it forks, releases the controlling terminal, then opens +its log file, where it subsequently records all X-10 activity and +diagnostic messages. It then begins processing packets received from +the TW523 and accepting connections one at a time from clients +wishing to issue X-10 commands. The usual place to start xtend would +be from the +.Pa /etc/rc.local +startup script. +.Pp +Sending +.Nm xtend +a SIGHUP causes it to close and reopen its log file. This is useful +in shell scripts that rotate the log files to keep them from growing +indefinitely. +If +.Nm xtend +receives a SIGTERM, it shuts down gracefully and exits. +A SIGPIPE causes +.Nm xtend +to abort the current client connection. +.Pp +.Nm Xtend +communicates with client processes by a simple protocol in which a one-line +command is sent by the client, and is acknowledged by a one-line response +from the daemon. +.Pp +.Nm Xtend +understands four types of commands. The command +.Bl -tag +.It status H U +.El +.Pp +where H is a single letter house code, and U is a numeric unit code, +causes +.Nm xtend +to respond with one line of status information about the specified device. +The command +.Bl -tag +.It send H U N +.El +.Pp +where H is a single-letter house code, U is either a numeric unit code +or a function code (see source file +.Pa xtend/packet.c +) for a list, and N is a number indicating the number of times (usually 2) +the packet is to be transmitted without gaps, +causes +.Nm xtend +to perform the specified X-10 transmission. If the transmission was apparently +successful, a single-line response containing +.B +OK +is issued, otherwise a single-line response containing +.B +ERROR +is produced. +The command +.Bl -tag +.It dump +.El +.Pp +causes +.Nm xtend +to dump the current status of all devices to an ASCII file in the spool +directory. The response +.B +OK +is issued, regardless of whether the status dump was successful. +The command +.Bl -tag +.It monitor H U +.El +.Pp +causes +.Nm xtend +to add the current client socket connection to a list of clients that are to +be notified about activity concerning the specified X-10 device. +The single-line acknowledgement +.B +OK +is returned if the maximum (currently 5) number of such clients was not +exceeded, otherwise +.B +ERROR +is returned. +.Nm Xtend +then returns to its normal mode of accepting connections from clients. +However, each subsequent change in the status of the specified device will +cause +.Nm xtend +to write one line of status information for the device (in the same +format as produced by the +.B +status +command) to the saved socket. This feature is useful for writing programs +that need to monitor the activity of devices, like motion detectors, that can +perform X-10 transmissions. +.Sh OPTIONS +None. +.Sh SEE ALSO +.Xr xten 1 +.Xr tw 4 +.Sh FILES +.Bl -tag -width /var/spool/xten/Status -compact +.It Pa /dev/tw0 +the TW523 special file +.It Pa /var/run/tw523 +socket for client connections +.It Pa /var/run/xtend.pid +pid file +.It Pa /var/spool/xten/Log +log file +.It Pa /var/spool/xten/Status +device status file (binary) +.It Pa /var/spool/status.out +ASCII dump of device status +.El +.Sh BUGS +There is currently no timeout on client socket connections, so a hung +client program can prevent other clients from accessing the daemon. +.Pp +.Nm Xtend +does the best it can at trying to track device status, but there is +usually no way it can tell when a device has been operated manually. +This is due to the fact that most X-10 devices are not able to +respond to queries about their status. +.Sh AUTHOR +Eugene W. Stark (stark@cs.sunysb.edu) diff --git a/libexec/xtend/xtend.c b/libexec/xtend/xtend.c new file mode 100644 index 0000000..08d5d50 --- /dev/null +++ b/libexec/xtend/xtend.c @@ -0,0 +1,304 @@ +/*- + * Copyright (c) 1992, 1993 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. + */ + +/* + * 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 "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; + int user; + int fd; + FILE *pidf; + + /* + * Open the log file before doing anything + */ + strcpy(logpath, X10DIR); + strcat(logpath, X10LOGNAME); + if((Log = fopen(logpath, "a")) == NULL) { + fprintf(stderr, "Can't open log file %s\n", logpath); + exit(1); + } + + /* + * Become a daemon + */ + if(daemon(0, 0) == -1) { + fprintf(Log, "%s: %s unable to become a daemon\n", thedate(), argv[0]); + 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); + exit(1); + } + fprintf(Log, "%s: %s successfully opened\n", thedate(), twpath); + + /* + * Initialize the status table + */ + strcpy(statpath, X10DIR); + 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); + exit(1); + } + if(write(status, Status, 16 * 16 * sizeof(STATUS)) + != 16 * 16 * sizeof(STATUS)) { + fprintf(Log, "%s: Error initializing status file\n", thedate()); + exit(1); + } + } + initstatus(); + + /* + * 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); + 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()); + 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); + exit(1); + } + if(listen(sock, 5) < 0) { + fprintf(Log, "%s: Can't listen on socket\n", thedate()); + exit(1); + } + + signal(SIGHUP, onhup); + signal(SIGTERM, onterm); + signal(SIGPIPE, onpipe); + /* + * Return here on SIGHUP after closing and reopening log file. + * Also on SIGPIPE after closing user connection. + */ + 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, 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()); + 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 */ +} diff --git a/libexec/xtend/xtend.h b/libexec/xtend/xtend.h new file mode 100644 index 0000000..d52ea9b --- /dev/null +++ b/libexec/xtend/xtend.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1992, 1993 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. + */ + +/* + * Device capabilities + */ + +#define ISLIGHT 1 /* Is device a light? */ +#define CANQUERY 2 /* Responds to status query */ + +/* + * Device status + */ + +typedef enum { + IDLE, + SELECTED, + DIMMING, + BRIGHTENING, + REQUESTED, + HAILED + } SELECT; + +typedef struct { + unsigned int devcap; /* device capabilities */ + unsigned int changed; /* status changed since last checkpoint? */ + time_t lastchange; /* time status last changed */ + SELECT selected; /* select status of device */ + unsigned int onoff; /* nonzero if on */ + unsigned int brightness; /* value in range 0-15 */ +} STATUS; + +typedef struct { + int inuse; /* Is entry in use? */ + FILE *user; /* Socket to notify user */ + int house; /* House code of device to monitor */ + int unit; /* Unit code of device to monitor */ +} MONENTRY; + +#define MAXMON 5 /* Maximum number of monitor entries */ + +extern FILE *Log; /* Log file */ +extern FILE *User; /* User connection */ +extern STATUS Status[16][16]; /* Device status table */ +extern int status; /* Status file descriptor */ +extern int tw523; /* tw523 controller */ +extern MONENTRY Monitor[MAXMON];/* Monitor table */ + +extern char *thedate(); -- cgit v1.1