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