rbootd.c revision 18471
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$
43 *
44 * Utah $Hdr: rbootd.c 3.1 92/07/06$
45 * Author: Jeff Forys, University of Utah CSS
46 */
47
48#ifndef lint
49static 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
55static 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
77typedef	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
87int
88main(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:")) != EOF)
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*/
359void
360DoTimeout()
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
396CLIENT *
397FindClient(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*/
422void
423Exit(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*/
451void
452ReConfig(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*/
478void
479DebugOff(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*/
501void
502DebugOn(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