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