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