Initial import from FreeBSD RELENG_4:
[dragonfly.git] / libexec / xtend / xtend.c
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
33 static 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
67 FILE *Log;                      /* Log file */
68 FILE *User;                     /* User connection */
69 STATUS Status[16][16];          /* Device status table */
70 int status;                     /* Status file descriptor */
71 int tw523;                      /* tw523 controller */
72 int sock;                       /* socket for user */
73 jmp_buf mainloop;               /* longjmp point after SIGHUP */
74 void onhup();                   /* SIGHUP handler */
75 void onterm();                  /* SIGTERM handler */
76 void onpipe();                  /* SIGPIPE handler */
77
78 void checkpoint_status __P((void));
79 void initstatus __P((void));
80 void logpacket __P((unsigned char *));
81 void processpacket __P((unsigned char *));
82 int user_command __P((void));
83
84 int
85 main(argc, argv)
86 int argc;
87 char *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
312 char *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
328 void 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
347 void 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
358 void 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 }