rpc.rquotad(8): Remove bogus #include <varargs.h>.
[dragonfly.git] / libexec / xtend / xtend.c
... / ...
CommitLineData
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
65FILE *Log; /* Log file */
66FILE *User; /* User connection */
67STATUS Status[16][16]; /* Device status table */
68int status; /* Status file descriptor */
69int tw523; /* tw523 controller */
70int sock; /* socket for user */
71jmp_buf mainloop; /* longjmp point after SIGHUP */
72void onhup(); /* SIGHUP handler */
73void onterm(); /* SIGTERM handler */
74void onpipe(); /* SIGPIPE handler */
75
76void checkpoint_status (void);
77void initstatus (void);
78void logpacket (unsigned char *);
79void processpacket (unsigned char *);
80int user_command (void);
81
82int
83main(argc, argv)
84int argc;
85char *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
310char *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
326void 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
345void 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
356void 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}