- Rewrite Buffer object to maintain only write pointer. There is no
[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  * $FreeBSD: src/libexec/xtend/xtend.c,v 1.9 1999/08/28 00:10:31 peter Exp $
32  * $DragonFly: src/libexec/xtend/xtend.c,v 1.3 2003/11/14 03:54:32 dillon Exp $
33  */
34
35 /*
36  * xtend - X-10 daemon
37  * Eugene W. Stark (stark@cs.sunysb.edu)
38  * January 14, 1993
39  */
40
41 #include <err.h>
42 #include <pwd.h>
43 #include <setjmp.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <time.h>
49 #include <unistd.h>
50 #include <sys/param.h>
51 #include <sys/file.h>
52 #include <sys/types.h>
53 #include <sys/time.h>
54 #include <sys/stat.h>
55 #include <sys/errno.h>
56 #include <sys/ioctl.h>
57 #include <sys/socket.h>
58 #include <sys/un.h>
59 #include <grp.h>
60
61 #include "xtend.h"
62 #include "xten.h"
63 #include "paths.h"
64
65 FILE *Log;                      /* Log file */
66 FILE *User;                     /* User connection */
67 STATUS Status[16][16];          /* Device status table */
68 int status;                     /* Status file descriptor */
69 int tw523;                      /* tw523 controller */
70 int sock;                       /* socket for user */
71 jmp_buf mainloop;               /* longjmp point after SIGHUP */
72 void onhup();                   /* SIGHUP handler */
73 void onterm();                  /* SIGTERM handler */
74 void onpipe();                  /* SIGPIPE handler */
75
76 void checkpoint_status (void);
77 void initstatus (void);
78 void logpacket (unsigned char *);
79 void processpacket (unsigned char *);
80 int user_command (void);
81
82 int
83 main(argc, argv)
84 int argc;
85 char *argv[];
86 {
87   char *twpath = TWPATH;
88   char *sockpath = SOCKPATH;
89   char logpath[MAXPATHLEN+1];
90   char statpath[MAXPATHLEN+1];
91   struct sockaddr_un sa;
92   struct timeval tv;
93   struct passwd *pw;
94   struct group *gr;
95   struct stat sb;
96   int user;
97   FILE *pidf;
98
99   /*
100    * Make sure we start out running as root
101    */
102   if(geteuid() != 0)
103     errx(1, "you must be root");
104
105   /*
106    * Find out what UID/GID we are to run as
107    */
108   if((pw = getpwnam(XTENUNAME)) == NULL)
109     errx(1, "no such user '%s'", XTENUNAME);
110   if((gr = getgrnam(XTENGNAME)) == NULL)
111     errx(1, "no such group '%s'", XTENGNAME);
112
113   /*
114    * Open the log file before doing anything else
115    */
116   strcpy(logpath, X10DIR);
117   if(stat(logpath, &sb) == -1 && errno == ENOENT) {
118     if(mkdir(logpath, 0755) != -1) {
119       chown(logpath, pw->pw_uid, gr->gr_gid);
120     } else {
121       errx(1, "can't create directory '%s'", logpath);
122     }
123   }
124   strcat(logpath, "/");
125   strcat(logpath, X10LOGNAME);
126   if((Log = fopen(logpath, "a")) == NULL)
127     errx(1, "can't open log file '%s'", logpath);
128   chown(logpath, pw->pw_uid, gr->gr_gid);
129
130   /*
131    * Become a daemon
132    */
133   if(daemon(0, 0) == -1) {
134     fprintf(Log, "%s:  Unable to become a daemon\n", thedate());
135     fclose(Log);
136     exit(1);
137   }
138   fprintf(Log, "%s:  %s [%d] started\n", thedate(), argv[0], getpid());
139
140   /*
141    * Get ahold of the TW523 device
142    */
143   if((tw523 = open(twpath, O_RDWR)) < 0) {
144     fprintf(Log, "%s:  Can't open %s\n", thedate(), twpath);
145     fclose(Log);
146     exit(1);
147   }
148   fprintf(Log, "%s:  %s successfully opened\n", thedate(), twpath);
149
150   /*
151    * Put our pid in a file so we can be signalled by shell scripts
152    */
153   if((pidf = fopen(PIDPATH, "w")) == NULL) {
154       fprintf(Log, "%s:  Error writing pid file: %s\n", thedate(), PIDPATH);
155       fclose(Log);
156       exit(1);
157   }
158   fprintf(pidf, "%d\n", getpid());
159   fclose(pidf);
160
161   /*
162    * Set up socket to accept user commands
163    */
164   if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
165     fprintf(Log, "%s:  Can't create socket\n", thedate());
166     fclose(Log);
167     exit(1);
168   }
169   strcpy(sa.sun_path, sockpath);
170   sa.sun_family = AF_UNIX;
171   unlink(sockpath);
172   if(bind(sock, (struct sockaddr *)(&sa), strlen(sa.sun_path) + 2) < 0) {
173     fprintf(Log, "%s:  Can't bind socket to %s\n", thedate(), sockpath);
174     fclose(Log);
175     exit(1);
176   }
177   if(listen(sock, 5) < 0) {
178     fprintf(Log, "%s:  Can't listen on socket\n", thedate());
179     fclose(Log);
180     exit(1);
181   }
182
183   /*
184    * Set proper ownership and permissions on the socket
185    */
186   if(chown(sockpath, pw->pw_uid, gr->gr_gid) == -1 ||
187      chmod(sockpath, 0660) == -1) {
188     fprintf(Log, "%s:  Can't set owner/permissions on socket\n", thedate());
189     fclose(Log);
190     exit(1);
191   }
192
193   /*
194    * Give up root privileges
195    */
196   setgid(pw->pw_gid);
197   setuid(pw->pw_uid);
198
199   /*
200    * Initialize the status table
201    */
202   strcpy(statpath, X10DIR);
203   strcat(statpath, "/");
204   strcat(statpath, X10STATNAME);
205   if((status = open(statpath, O_RDWR)) < 0) {
206     if((status = open(statpath, O_RDWR | O_CREAT, 0666)) < 0) {
207       fprintf(Log, "%s:  Can't open %s\n", thedate(), statpath);
208       fclose(Log);
209       exit(1);
210     }
211     if(write(status, Status, 16 * 16 * sizeof(STATUS))
212        != 16 * 16 * sizeof(STATUS)) {
213       fprintf(Log, "%s:  Error initializing status file\n", thedate());
214       fclose(Log);
215       exit(1);
216     }
217   }
218   initstatus();
219
220   /*
221    * Return here on SIGHUP after closing and reopening log file.
222    * Also on SIGPIPE after closing user connection.
223    */
224   signal(SIGTERM, onterm);
225   signal(SIGHUP, onhup);
226   signal(SIGPIPE, onpipe);
227   setjmp(mainloop);
228
229   /*
230    * Now start the main processing loop.
231    */
232   tv.tv_sec = 0;
233   tv.tv_usec = 250000;
234   while(1) {
235     fd_set fs;
236     unsigned char rpkt[3];
237     int sel, h, k;
238     STATUS *s;
239
240     FD_ZERO(&fs);
241     FD_SET(tw523, &fs);
242     if(User != NULL) FD_SET(user, &fs);
243     else FD_SET(sock, &fs);
244     sel = select(FD_SETSIZE, &fs, 0, 0, &tv);
245     if(sel == 0) {
246       /*
247        * Cancel brightening and dimming on ALL units on ALL house codes,
248        * because the fact that we haven't gotten a packet for awhile means
249        * that there was a gap in transmission.
250        */
251       for(h = 0; h < 16; h++) {
252         for(k = 0; k < 16; k++) {
253           s = &Status[h][k];
254           if(s->selected == BRIGHTENING || s->selected == DIMMING) {
255             s->selected = IDLE;
256             s->lastchange = time(NULL);
257             s->changed = 1;
258           }
259         }
260       }
261       fflush(Log);
262       checkpoint_status();
263       /*
264        * Now that we've done this stuff, we'll set the timeout a little
265        * longer, so we don't keep looping too frequently.
266        */
267       tv.tv_sec = 60;
268       tv.tv_usec = 0;
269       continue;
270     }
271     /*
272      * While there is stuff happening, we keep a short timeout, so we
273      * don't get stuck for some unknown reason, and so we can keep the
274      * brightening and dimming data up-to-date.
275      */
276     tv.tv_sec = 0;
277     tv.tv_usec = 250000;
278     if(FD_ISSET(tw523, &fs)) {  /* X10 data arriving from TW523 */
279       if(read(tw523, rpkt, 3) < 3) {
280         fprintf(Log, "%s:  Error reading from TW523\n", thedate());
281       } else {
282         logpacket(rpkt);
283         processpacket(rpkt);
284       }
285     } else if(FD_ISSET(user, &fs) && User != NULL) {
286       if(user_command()) {
287         fprintf(Log, "%s:  Closing user connection\n", thedate());
288         fclose(User);
289         User = NULL;
290       }
291     } else if(FD_ISSET(sock, &fs)) {  /* Accept a connection */
292       if (User == NULL) {
293         int len = sizeof(struct sockaddr_un);
294         if((user = accept(sock, (struct sockaddr *)(&sa), &len)) >= 0) {
295           fprintf(Log, "%s:  Accepting user connection\n", thedate());
296           if((User = fdopen(user, "w+")) == NULL) {
297             fprintf(Log, "%s:  Can't attach socket to stream\n", thedate());
298           }
299         } else {
300           fprintf(Log, "%s:  Failure in attempt to accept connection\n", thedate());
301         }
302       } else {
303         /* "Can't happen */
304       }
305     }
306   }
307   /* Not reached */
308 }
309
310 char *thedate()
311 {
312   char *cp, *cp1;
313   time_t tod;
314
315   tod = time(NULL);
316   cp = cp1 = ctime(&tod);
317   while(*cp1 != '\n') cp1++;
318   *cp1 = '\0';
319   return(cp);
320 }
321
322 /*
323  * When SIGHUP received, close and reopen the Log file
324  */
325
326 void onhup()
327 {
328   char logpath[MAXPATHLEN+1];
329
330   fprintf(Log, "%s:  SIGHUP received, reopening Log\n", thedate());
331   fclose(Log);
332   strcpy(logpath, X10DIR);
333   strcat(logpath, "/");
334   strcat(logpath, X10LOGNAME);
335   if((Log = fopen(logpath, "a")) == NULL)
336     errx(1, "can't open log file '%s'", logpath);
337   longjmp(mainloop, 1);
338   /* No return */
339 }
340
341 /*
342  * When SIGTERM received, just exit normally
343  */
344
345 void onterm()
346 {
347   fprintf(Log, "%s:  SIGTERM received, shutting down\n", thedate());
348   fclose(Log);
349   exit(0);
350 }
351
352 /*
353  * When SIGPIPE received, reset user connection
354  */
355
356 void onpipe()
357 {
358   fprintf(Log, "%s:  SIGPIPE received, resetting user connection\n",
359           thedate());
360   if(User != NULL) {
361     fclose(User);
362     User = NULL;
363   }
364   longjmp(mainloop, 1);
365   /* No return */
366 }