The X-10 demon. From 1.1.5.1.

This commit is contained in:
Jordan K. Hubbard 1994-10-22 09:53:33 +00:00
parent cd08c443af
commit 5b8113b5e8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/unlabeled-1.1.1/; revision=3779
9 changed files with 1228 additions and 0 deletions

12
libexec/xtend/Makefile Normal file
View File

@ -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 <bsd.prog.mk>

317
libexec/xtend/packet.c Normal file
View File

@ -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 <stdio.h>
#include <sys/time.h>
#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;
}
}
}
}

11
libexec/xtend/paths.h Normal file
View File

@ -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"

109
libexec/xtend/status.c Normal file
View File

@ -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 <stdio.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#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;
}

166
libexec/xtend/user.c Normal file
View File

@ -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 <stdio.h>
#include <sys/param.h>
#include <sys/time.h>
#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));
}

58
libexec/xtend/xten.h Normal file
View File

@ -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 */

174
libexec/xtend/xtend.8 Normal file
View File

@ -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)

304
libexec/xtend/xtend.c Normal file
View File

@ -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 <stdio.h>
#include <time.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/time.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;
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 */
}

77
libexec/xtend/xtend.h Normal file
View File

@ -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();