1 /*
2 * Copyright (c) 1988, 1992 The University of Utah and the Center
3 * for Software Science (CSS).
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * the Center for Software Science of the University of Utah Computer
9 * Science Department. CSS requests users of this software to return
10 * to css-dist@cs.utah.edu any improvements that they make and grant
11 * CSS redistribution rights.
12 *
13 * %sccs.include.redist.c%
14 *
15 * @(#)rbootd.c 8.2 (Berkeley) 02/22/94
16 *
17 * Utah $Hdr: rbootd.c 3.1 92/07/06$
18 * Author: Jeff Forys, University of Utah CSS
19 */
20
21 #ifndef lint
22 static char copyright[] =
23 "@(#) Copyright (c) 1992, 1993\n\
24 The Regents of the University of California. All rights reserved.\n";
25 #endif /* not lint */
26
27 #ifndef lint
28 static char sccsid[] = "@(#)rbootd.c 8.2 (Berkeley) 02/22/94";
29 #endif /* not lint */
30
31 #include <sys/param.h>
32 #include <sys/ioctl.h>
33 #include <sys/time.h>
34
35 #include <ctype.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <syslog.h>
43 #include <unistd.h>
44 #include "defs.h"
45
46
47 /* fd mask macros (backward compatibility with 4.2BSD) */
48 #ifndef FD_SET
49 #ifdef notdef
50 typedef struct fd_set { /* this should already be in 4.2 */
51 int fds_bits[1];
52 } fd_set;
53 #endif
54 #define FD_ZERO(p) ((p)->fds_bits[0] = 0)
55 #define FD_SET(n, p) ((p)->fds_bits[0] |= (1 << (n)))
56 #define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1 << (n)))
57 #define FD_ISSET(n, p) ((p)->fds_bits[0] & (1 << (n)))
58 #endif
59
60 int
main(argc,argv)61 main(argc, argv)
62 int argc;
63 char *argv[];
64 {
65 int c, fd, omask, maxfds;
66 fd_set rset;
67
68 /*
69 * Find what name we are running under.
70 */
71 ProgName = (ProgName = rindex(argv[0],'/')) ? ++ProgName : *argv;
72
73 /*
74 * Close any open file descriptors.
75 * Temporarily leave stdin & stdout open for `-d',
76 * and stderr open for any pre-syslog error messages.
77 */
78 {
79 int i, nfds = getdtablesize();
80
81 for (i = 0; i < nfds; i++)
82 if (i != fileno(stdin) && i != fileno(stdout) &&
83 i != fileno(stderr))
84 (void) close(i);
85 }
86
87 /*
88 * Parse any arguments.
89 */
90 while ((c = getopt(argc, argv, "adi:")) != EOF)
91 switch(c) {
92 case 'a':
93 BootAny++;
94 break;
95 case 'd':
96 DebugFlg++;
97 break;
98 case 'i':
99 IntfName = optarg;
100 break;
101 }
102 for (; optind < argc; optind++) {
103 if (ConfigFile == NULL)
104 ConfigFile = argv[optind];
105 else {
106 fprintf(stderr,
107 "%s: too many config files (`%s' ignored)\n",
108 ProgName, argv[optind]);
109 }
110 }
111
112 if (ConfigFile == NULL) /* use default config file */
113 ConfigFile = DfltConfig;
114
115 if (DebugFlg) {
116 DbgFp = stdout; /* output to stdout */
117
118 (void) signal(SIGUSR1, SIG_IGN); /* dont muck w/DbgFp */
119 (void) signal(SIGUSR2, SIG_IGN);
120 } else {
121 (void) fclose(stdin); /* dont need these */
122 (void) fclose(stdout);
123
124 /*
125 * Fork off a child to do the work & exit.
126 */
127 switch(fork()) {
128 case -1: /* fork failed */
129 fprintf(stderr, "%s: ", ProgName);
130 perror("fork");
131 Exit(0);
132 case 0: /* this is the CHILD */
133 break;
134 default: /* this is the PARENT */
135 _exit(0);
136 }
137
138 /*
139 * Try to disassociate from the current tty.
140 */
141 {
142 char *devtty = "/dev/tty";
143 int i;
144
145 if ((i = open(devtty, O_RDWR)) < 0) {
146 /* probably already disassociated */
147 if (setpgrp(0, 0) < 0) {
148 fprintf(stderr, "%s: ", ProgName);
149 perror("setpgrp");
150 }
151 } else {
152 if (ioctl(i, (u_long)TIOCNOTTY, (char *)0) < 0){
153 fprintf(stderr, "%s: ", ProgName);
154 perror("ioctl");
155 }
156 (void) close(i);
157 }
158 }
159
160 (void) signal(SIGUSR1, DebugOn);
161 (void) signal(SIGUSR2, DebugOff);
162 }
163
164 (void) fclose(stderr); /* finished with it */
165
166 #ifdef SYSLOG4_2
167 openlog(ProgName, LOG_PID);
168 #else
169 openlog(ProgName, LOG_PID, LOG_DAEMON);
170 #endif
171
172 /*
173 * If no interface was specified, get one now.
174 *
175 * This is convoluted because we want to get the default interface
176 * name for the syslog("restarted") message. If BpfGetIntfName()
177 * runs into an error, it will return a syslog-able error message
178 * (in `errmsg') which will be displayed here.
179 */
180 if (IntfName == NULL) {
181 char *errmsg;
182
183 if ((IntfName = BpfGetIntfName(&errmsg)) == NULL) {
184 syslog(LOG_NOTICE, "restarted (??)");
185 syslog(LOG_ERR, errmsg);
186 Exit(0);
187 }
188 }
189
190 syslog(LOG_NOTICE, "restarted (%s)", IntfName);
191
192 (void) signal(SIGHUP, ReConfig);
193 (void) signal(SIGINT, Exit);
194 (void) signal(SIGTERM, Exit);
195
196 /*
197 * Grab our host name and pid.
198 */
199 if (gethostname(MyHost, MAXHOSTNAMELEN) < 0) {
200 syslog(LOG_ERR, "gethostname: %m");
201 Exit(0);
202 }
203 MyHost[MAXHOSTNAMELEN] = '\0';
204
205 MyPid = getpid();
206
207 /*
208 * Write proc's pid to a file.
209 */
210 {
211 FILE *fp;
212
213 if ((fp = fopen(PidFile, "w")) != NULL) {
214 (void) fprintf(fp, "%d\n", MyPid);
215 (void) fclose(fp);
216 } else {
217 syslog(LOG_WARNING, "fopen: failed (%s)", PidFile);
218 }
219 }
220
221 /*
222 * All boot files are relative to the boot directory, we might
223 * as well chdir() there to make life easier.
224 */
225 if (chdir(BootDir) < 0) {
226 syslog(LOG_ERR, "chdir: %m (%s)", BootDir);
227 Exit(0);
228 }
229
230 /*
231 * Initial configuration.
232 */
233 omask = sigblock(sigmask(SIGHUP)); /* prevent reconfig's */
234 if (GetBootFiles() == 0) /* get list of boot files */
235 Exit(0);
236 if (ParseConfig() == 0) /* parse config file */
237 Exit(0);
238
239 /*
240 * Open and initialize a BPF device for the appropriate interface.
241 * If an error is encountered, a message is displayed and Exit()
242 * is called.
243 */
244 fd = BpfOpen();
245
246 (void) sigsetmask(omask); /* allow reconfig's */
247
248 /*
249 * Main loop: receive a packet, determine where it came from,
250 * and if we service this host, call routine to handle request.
251 */
252 maxfds = fd + 1;
253 FD_ZERO(&rset);
254 FD_SET(fd, &rset);
255 for (;;) {
256 struct timeval timeout;
257 fd_set r;
258 int nsel;
259
260 r = rset;
261
262 if (RmpConns == NULL) { /* timeout isnt necessary */
263 nsel = select(maxfds, &r, (fd_set *)0, (fd_set *)0,
264 (struct timeval *)0);
265 } else {
266 timeout.tv_sec = RMP_TIMEOUT;
267 timeout.tv_usec = 0;
268 nsel = select(maxfds, &r, (fd_set *)0, (fd_set *)0,
269 &timeout);
270 }
271
272 if (nsel < 0) {
273 if (errno == EINTR)
274 continue;
275 syslog(LOG_ERR, "select: %m");
276 Exit(0);
277 } else if (nsel == 0) { /* timeout */
278 DoTimeout(); /* clear stale conns */
279 continue;
280 }
281
282 if (FD_ISSET(fd, &r)) {
283 RMPCONN rconn;
284 CLIENT *client, *FindClient();
285 int doread = 1;
286
287 while (BpfRead(&rconn, doread)) {
288 doread = 0;
289
290 if (DbgFp != NULL) /* display packet */
291 DispPkt(&rconn,DIR_RCVD);
292
293 omask = sigblock(sigmask(SIGHUP));
294
295 /*
296 * If we do not restrict service, set the
297 * client to NULL (ProcessPacket() handles
298 * this). Otherwise, check that we can
299 * service this host; if not, log a message
300 * and ignore the packet.
301 */
302 if (BootAny) {
303 client = NULL;
304 } else if ((client=FindClient(&rconn))==NULL) {
305 syslog(LOG_INFO,
306 "%s: boot packet ignored",
307 EnetStr(&rconn));
308 (void) sigsetmask(omask);
309 continue;
310 }
311
312 ProcessPacket(&rconn,client);
313
314 (void) sigsetmask(omask);
315 }
316 }
317 }
318 }
319
320 /*
321 ** DoTimeout -- Free any connections that have timed out.
322 **
323 ** Parameters:
324 ** None.
325 **
326 ** Returns:
327 ** Nothing.
328 **
329 ** Side Effects:
330 ** - Timed out connections in `RmpConns' will be freed.
331 */
332 void
DoTimeout()333 DoTimeout()
334 {
335 register RMPCONN *rtmp;
336 struct timeval now;
337
338 (void) gettimeofday(&now, (struct timezone *)0);
339
340 /*
341 * For each active connection, if RMP_TIMEOUT seconds have passed
342 * since the last packet was sent, delete the connection.
343 */
344 for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next)
345 if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now.tv_sec) {
346 syslog(LOG_WARNING, "%s: connection timed out (%u)",
347 EnetStr(rtmp), rtmp->rmp.r_type);
348 RemoveConn(rtmp);
349 }
350 }
351
352 /*
353 ** FindClient -- Find client associated with a packet.
354 **
355 ** Parameters:
356 ** rconn - the new packet.
357 **
358 ** Returns:
359 ** Pointer to client info if found, NULL otherwise.
360 **
361 ** Side Effects:
362 ** None.
363 **
364 ** Warnings:
365 ** - This routine must be called with SIGHUP blocked since
366 ** a reconfigure can invalidate the information returned.
367 */
368
369 CLIENT *
FindClient(rconn)370 FindClient(rconn)
371 register RMPCONN *rconn;
372 {
373 register CLIENT *ctmp;
374
375 for (ctmp = Clients; ctmp != NULL; ctmp = ctmp->next)
376 if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0],
377 (char *)&ctmp->addr[0], RMP_ADDRLEN) == 0)
378 break;
379
380 return(ctmp);
381 }
382
383 /*
384 ** Exit -- Log an error message and exit.
385 **
386 ** Parameters:
387 ** sig - caught signal (or zero if not dying on a signal).
388 **
389 ** Returns:
390 ** Does not return.
391 **
392 ** Side Effects:
393 ** - This process ceases to exist.
394 */
395 void
Exit(sig)396 Exit(sig)
397 int sig;
398 {
399 if (sig > 0)
400 syslog(LOG_ERR, "going down on signal %d", sig);
401 else
402 syslog(LOG_ERR, "going down with fatal error");
403 BpfClose();
404 exit(1);
405 }
406
407 /*
408 ** ReConfig -- Get new list of boot files and reread config files.
409 **
410 ** Parameters:
411 ** None.
412 **
413 ** Returns:
414 ** Nothing.
415 **
416 ** Side Effects:
417 ** - All active connections are dropped.
418 ** - List of boot-able files is changed.
419 ** - List of clients is changed.
420 **
421 ** Warnings:
422 ** - This routine must be called with SIGHUP blocked.
423 */
424 void
ReConfig(signo)425 ReConfig(signo)
426 int signo;
427 {
428 syslog(LOG_NOTICE, "reconfiguring boot server");
429
430 FreeConns();
431
432 if (GetBootFiles() == 0)
433 Exit(0);
434
435 if (ParseConfig() == 0)
436 Exit(0);
437 }
438
439 /*
440 ** DebugOff -- Turn off debugging.
441 **
442 ** Parameters:
443 ** None.
444 **
445 ** Returns:
446 ** Nothing.
447 **
448 ** Side Effects:
449 ** - Debug file is closed.
450 */
451 void
DebugOff(signo)452 DebugOff(signo)
453 int signo;
454 {
455 if (DbgFp != NULL)
456 (void) fclose(DbgFp);
457
458 DbgFp = NULL;
459 }
460
461 /*
462 ** DebugOn -- Turn on debugging.
463 **
464 ** Parameters:
465 ** None.
466 **
467 ** Returns:
468 ** Nothing.
469 **
470 ** Side Effects:
471 ** - Debug file is opened/truncated if not already opened,
472 ** otherwise do nothing.
473 */
474 void
DebugOn(signo)475 DebugOn(signo)
476 int signo;
477 {
478 if (DbgFp == NULL) {
479 if ((DbgFp = fopen(DbgFile, "w")) == NULL)
480 syslog(LOG_ERR, "can't open debug file (%s)", DbgFile);
481 }
482 }
483