Initial import from FreeBSD RELENG_4:
[dragonfly.git] / libexec / xtend / xtend.c
CommitLineData
984263bc
MD
1/*-
2 * Copyright (c) 1992, 1993, 1995 Eugene W. Stark
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Eugene W. Stark.
16 * 4. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#ifndef lint
33static const char rcsid[] =
34 "$FreeBSD: src/libexec/xtend/xtend.c,v 1.9 1999/08/28 00:10:31 peter Exp $";
35#endif /* not lint */
36
37/*
38 * xtend - X-10 daemon
39 * Eugene W. Stark (stark@cs.sunysb.edu)
40 * January 14, 1993
41 */
42
43#include <err.h>
44#include <pwd.h>
45#include <setjmp.h>
46#include <signal.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <time.h>
51#include <unistd.h>
52#include <sys/param.h>
53#include <sys/file.h>
54#include <sys/types.h>
55#include <sys/time.h>
56#include <sys/stat.h>
57#include <sys/errno.h>
58#include <sys/ioctl.h>
59#include <sys/socket.h>
60#include <sys/un.h>
61#include <grp.h>
62
63#include "xtend.h"
64#include "xten.h"
65#include "paths.h"
66
67FILE *Log; /* Log file */
68FILE *User; /* User connection */
69STATUS Status[16][16]; /* Device status table */
70int status; /* Status file descriptor */
71int tw523; /* tw523 controller */
72int sock; /* socket for user */
73jmp_buf mainloop; /* longjmp point after SIGHUP */
74void onhup(); /* SIGHUP handler */
75void onterm(); /* SIGTERM handler */
76void onpipe(); /* SIGPIPE handler */
77
78void checkpoint_status __P((void));
79void initstatus __P((void));
80void logpacket __P((unsigned char *));
81void processpacket __P((unsigned char *));
82int user_command __P((void));
83
84int
85main(argc, argv)
86int argc;
87char *argv[];
88{
89 char *twpath = TWPATH;
90 char *sockpath = SOCKPATH;
91 char logpath[MAXPATHLEN+1];
92 char statpath[MAXPATHLEN+1];
93 struct sockaddr_un sa;
94 struct timeval tv;
95 struct passwd *pw;
96 struct group *gr;
97 struct stat sb;
98 int user;
99 FILE *pidf;
100
101 /*
102 * Make sure we start out running as root
103 */
104 if(geteuid() != 0)
105 errx(1, "you must be root");
106
107 /*
108 * Find out what UID/GID we are to run as
109 */
110 if((pw = getpwnam(XTENUNAME)) == NULL)
111 errx(1, "no such user '%s'", XTENUNAME);
112 if((gr = getgrnam(XTENGNAME)) == NULL)
113 errx(1, "no such group '%s'", XTENGNAME);
114
115 /*
116 * Open the log file before doing anything else
117 */
118 strcpy(logpath, X10DIR);
119 if(stat(logpath, &sb) == -1 && errno == ENOENT) {
120 if(mkdir(logpath, 0755) != -1) {
121 chown(logpath, pw->pw_uid, gr->gr_gid);
122 } else {
123 errx(1, "can't create directory '%s'", logpath);
124 }
125 }
126 strcat(logpath, "/");
127 strcat(logpath, X10LOGNAME);
128 if((Log = fopen(logpath, "a")) == NULL)
129 errx(1, "can't open log file '%s'", logpath);
130 chown(logpath, pw->pw_uid, gr->gr_gid);
131
132 /*
133 * Become a daemon
134 */
135 if(daemon(0, 0) == -1) {
136 fprintf(Log, "%s: Unable to become a daemon\n", thedate());
137 fclose(Log);
138 exit(1);
139 }
140 fprintf(Log, "%s: %s [%d] started\n", thedate(), argv[0], getpid());
141
142 /*
143 * Get ahold of the TW523 device
144 */
145 if((tw523 = open(twpath, O_RDWR)) < 0) {
146 fprintf(Log, "%s: Can't open %s\n", thedate(), twpath);
147 fclose(Log);
148 exit(1);
149 }
150 fprintf(Log, "%s: %s successfully opened\n", thedate(), twpath);
151
152 /*
153 * Put our pid in a file so we can be signalled by shell scripts
154 */
155 if((pidf = fopen(PIDPATH, "w")) == NULL) {
156 fprintf(Log, "%s: Error writing pid file: %s\n", thedate(), PIDPATH);
157 fclose(Log);
158 exit(1);
159 }
160 fprintf(pidf, "%d\n", getpid());
161 fclose(pidf);
162
163 /*
164 * Set up socket to accept user commands
165 */
166 if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
167 fprintf(Log, "%s: Can't create socket\n", thedate());
168 fclose(Log);
169 exit(1);
170 }
171 strcpy(sa.sun_path, sockpath);
172 sa.sun_family = AF_UNIX;
173 unlink(sockpath);
174 if(bind(sock, (struct sockaddr *)(&sa), strlen(sa.sun_path) + 2) < 0) {
175 fprintf(Log, "%s: Can't bind socket to %s\n", thedate(), sockpath);
176 fclose(Log);
177 exit(1);
178 }
179 if(listen(sock, 5) < 0) {
180 fprintf(Log, "%s: Can't listen on socket\n", thedate());
181 fclose(Log);
182 exit(1);
183 }
184
185 /*
186 * Set proper ownership and permissions on the socket
187 */
188 if(chown(sockpath, pw->pw_uid, gr->gr_gid) == -1 ||
189 chmod(sockpath, 0660) == -1) {
190 fprintf(Log, "%s: Can't set owner/permissions on socket\n", thedate());
191 fclose(Log);
192 exit(1);
193 }
194
195 /*
196 * Give up root privileges
197 */
198 setgid(pw->pw_gid);
199 setuid(pw->pw_uid);
200
201 /*
202 * Initialize the status table
203 */
204 strcpy(statpath, X10DIR);
205 strcat(statpath, "/");
206 strcat(statpath, X10STATNAME);
207 if((status = open(statpath, O_RDWR)) < 0) {
208 if((status = open(statpath, O_RDWR | O_CREAT, 0666)) < 0) {
209 fprintf(Log, "%s: Can't open %s\n", thedate(), statpath);
210 fclose(Log);
211 exit(1);
212 }
213 if(write(status, Status, 16 * 16 * sizeof(STATUS))
214 != 16 * 16 * sizeof(STATUS)) {
215 fprintf(Log, "%s: Error initializing status file\n", thedate());
216 fclose(Log);
217 exit(1);
218 }
219 }
220 initstatus();
221
222 /*
223 * Return here on SIGHUP after closing and reopening log file.
224 * Also on SIGPIPE after closing user connection.
225 */
226 signal(SIGTERM, onterm);
227 signal(SIGHUP, onhup);
228 signal(SIGPIPE, onpipe);
229 setjmp(mainloop);
230
231 /*
232 * Now start the main processing loop.
233 */
234 tv.tv_sec = 0;
235 tv.tv_usec = 250000;
236 while(1) {
237 fd_set fs;
238 unsigned char rpkt[3];
239 int sel, h, k;
240 STATUS *s;
241
242 FD_ZERO(&fs);
243 FD_SET(tw523, &fs);
244 if(User != NULL) FD_SET(user, &fs);
245 else FD_SET(sock, &fs);
246 sel = select(FD_SETSIZE, &fs, 0, 0, &tv);
247 if(sel == 0) {
248 /*
249 * Cancel brightening and dimming on ALL units on ALL house codes,
250 * because the fact that we haven't gotten a packet for awhile means
251 * that there was a gap in transmission.
252 */
253 for(h = 0; h < 16; h++) {
254 for(k = 0; k < 16; k++) {
255 s = &Status[h][k];
256 if(s->selected == BRIGHTENING || s->selected == DIMMING) {
257 s->selected = IDLE;
258 s->lastchange = time(NULL);
259 s->changed = 1;
260 }
261 }
262 }
263 fflush(Log);
264 checkpoint_status();
265 /*
266 * Now that we've done this stuff, we'll set the timeout a little
267 * longer, so we don't keep looping too frequently.
268 */
269 tv.tv_sec = 60;
270 tv.tv_usec = 0;
271 continue;
272 }
273 /*
274 * While there is stuff happening, we keep a short timeout, so we
275 * don't get stuck for some unknown reason, and so we can keep the
276 * brightening and dimming data up-to-date.
277 */
278 tv.tv_sec = 0;
279 tv.tv_usec = 250000;
280 if(FD_ISSET(tw523, &fs)) { /* X10 data arriving from TW523 */
281 if(read(tw523, rpkt, 3) < 3) {
282 fprintf(Log, "%s: Error reading from TW523\n", thedate());
283 } else {
284 logpacket(rpkt);
285 processpacket(rpkt);
286 }
287 } else if(FD_ISSET(user, &fs) && User != NULL) {
288 if(user_command()) {
289 fprintf(Log, "%s: Closing user connection\n", thedate());
290 fclose(User);
291 User = NULL;
292 }
293 } else if(FD_ISSET(sock, &fs)) { /* Accept a connection */
294 if (User == NULL) {
295 int len = sizeof(struct sockaddr_un);
296 if((user = accept(sock, (struct sockaddr *)(&sa), &len)) >= 0) {
297 fprintf(Log, "%s: Accepting user connection\n", thedate());
298 if((User = fdopen(user, "w+")) == NULL) {
299 fprintf(Log, "%s: Can't attach socket to stream\n", thedate());
300 }
301 } else {
302 fprintf(Log, "%s: Failure in attempt to accept connection\n", thedate());
303 }
304 } else {
305 /* "Can't happen */
306 }
307 }
308 }
309 /* Not reached */
310}
311
312char *thedate()
313{
314 char *cp, *cp1;
315 time_t tod;
316
317 tod = time(NULL);
318 cp = cp1 = ctime(&tod);
319 while(*cp1 != '\n') cp1++;
320 *cp1 = '\0';
321 return(cp);
322}
323
324/*
325 * When SIGHUP received, close and reopen the Log file
326 */
327
328void onhup()
329{
330 char logpath[MAXPATHLEN+1];
331
332 fprintf(Log, "%s: SIGHUP received, reopening Log\n", thedate());
333 fclose(Log);
334 strcpy(logpath, X10DIR);
335 strcat(logpath, "/");
336 strcat(logpath, X10LOGNAME);
337 if((Log = fopen(logpath, "a")) == NULL)
338 errx(1, "can't open log file '%s'", logpath);
339 longjmp(mainloop, 1);
340 /* No return */
341}
342
343/*
344 * When SIGTERM received, just exit normally
345 */
346
347void onterm()
348{
349 fprintf(Log, "%s: SIGTERM received, shutting down\n", thedate());
350 fclose(Log);
351 exit(0);
352}
353
354/*
355 * When SIGPIPE received, reset user connection
356 */
357
358void onpipe()
359{
360 fprintf(Log, "%s: SIGPIPE received, resetting user connection\n",
361 thedate());
362 if(User != NULL) {
363 fclose(User);
364 User = NULL;
365 }
366 longjmp(mainloop, 1);
367 /* No return */
368}