xref: /openbsd-src/usr.sbin/rbootd/rbootd.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: rbootd.c,v 1.22 2005/09/22 14:24:51 jmc Exp $	*/
2 /*	$NetBSD: rbootd.c,v 1.5 1995/10/06 05:12:17 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1988, 1992 The University of Utah and the Center
6  *	for Software Science (CSS).
7  * Copyright (c) 1992, 1993
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * This code is derived from software contributed to Berkeley by
11  * the Center for Software Science of the University of Utah Computer
12  * Science Department.  CSS requests users of this software to return
13  * to css-dist@cs.utah.edu any improvements that they make and grant
14  * CSS redistribution rights.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *	from: @(#)rbootd.c	8.1 (Berkeley) 6/4/93
41  *
42  * From: Utah Hdr: rbootd.c 3.1 92/07/06
43  * Author: Jeff Forys, University of Utah CSS
44  */
45 
46 #ifndef lint
47 static char copyright[] =
48 "@(#) Copyright (c) 1992, 1993\n\
49 	The Regents of the University of California.  All rights reserved.\n";
50 #endif /* not lint */
51 
52 #ifndef lint
53 /*static char sccsid[] = "@(#)rbootd.c	8.1 (Berkeley) 6/4/93";*/
54 static char rcsid[] = "$OpenBSD: rbootd.c,v 1.22 2005/09/22 14:24:51 jmc Exp $";
55 #endif /* not lint */
56 
57 #include <sys/param.h>
58 #include <sys/time.h>
59 
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 <util.h>
71 #include <pwd.h>
72 
73 #include "defs.h"
74 
75 extern	char *__progname;	/* from crt0.o */
76 
77 volatile sig_atomic_t	dodebugoff;
78 volatile sig_atomic_t	dodebugon;
79 volatile sig_atomic_t	doreconfig;
80 
81 void DebugOff(int);
82 void DebugOn(int);
83 void ReConfig(int);
84 void Exit(int);
85 
86 void DoDebugOff(void);
87 void DoDebugOn(void);
88 void DoReConfig(void);
89 
90 void DoTimeout(void);
91 CLIENT *FindClient(RMPCONN *);
92 
93 int
94 main(int argc, char *argv[])
95 {
96 	int c, fd, maxfds;
97 	sigset_t hmask, omask;
98 	struct passwd *pw;
99 	fd_set rset;
100 
101 	closefrom(STDERR_FILENO + 1);
102 
103 	if ((pw = getpwnam("_rbootd")) == NULL)
104 		err(1, "getpwnam");
105 
106 	while ((c = getopt(argc, argv, "adi:")) != -1)
107 		switch (c) {
108 		case 'a':
109 			BootAny++;
110 			break;
111 		case 'd':
112 			DebugFlg++;
113 			break;
114 		case 'i':
115 			IntfName = optarg;
116 			break;
117 		}
118 	for (; optind < argc; optind++) {
119 		if (ConfigFile == NULL)
120 			ConfigFile = argv[optind];
121 		else {
122 			warnx("too many config files (`%s' ignored)",
123 			    argv[optind]);
124 		}
125 	}
126 
127 	if (ConfigFile == NULL)			/* use default config file */
128 		ConfigFile = DfltConfig;
129 
130 	if (DebugFlg) {
131 		DbgFp = stdout;				/* output to stdout */
132 
133 		(void) signal(SIGUSR1, SIG_IGN);	/* dont muck w/DbgFp */
134 		(void) signal(SIGUSR2, SIG_IGN);
135 		(void) fclose(stderr);			/* finished with it */
136 	} else {
137 		if (daemon(0, 0))
138 			err(1, "can't detach from terminal");
139 
140 		(void) signal(SIGUSR1, DebugOn);
141 		(void) signal(SIGUSR2, DebugOff);
142 	}
143 
144 	/*
145 	 *  If no interface was specified, get one now.
146 	 *
147 	 *  This is convoluted because we want to get the default interface
148 	 *  name for the syslog("restarted") message.  If BpfGetIntfName()
149 	 *  runs into an error, it will return a syslog-able error message
150 	 *  (in `errmsg') which will be displayed here.
151 	 */
152 	if (IntfName == NULL) {
153 		char *errmsg;
154 
155 		if ((IntfName = BpfGetIntfName(&errmsg)) == NULL) {
156 			syslog(LOG_NOTICE, "restarted (??)");
157 			/* BpfGetIntfName() returns safe names, using %m */
158 			syslog(LOG_ERR, "%s", errmsg);
159 			DoExit();
160 		}
161 	}
162 
163 	openlog(__progname, LOG_PID, LOG_DAEMON);
164 	fd = BpfOpen();
165 	syslog(LOG_NOTICE, "restarted (%s)", IntfName);
166 
167 	(void) signal(SIGHUP, ReConfig);
168 	(void) signal(SIGINT, Exit);
169 	(void) signal(SIGTERM, Exit);
170 
171 	gethostname(MyHost, MAXHOSTNAMELEN);
172 
173 	if (pidfile(NULL) < 0)
174 		syslog(LOG_WARNING, "pidfile: failed");
175 
176 	/*
177 	 *  All boot files are relative to the boot directory, we might
178 	 *  as well chdir() there to make life easier.
179 	 */
180 	if (chdir(BootDir) < 0) {
181 		syslog(LOG_ERR, "chdir: %m (%s)", BootDir);
182 		DoExit();
183 	}
184 
185 	/*
186 	 *  Initial configuration.
187 	 */
188 	sigemptyset(&hmask);
189 	sigaddset(&hmask, SIGHUP);
190 	sigprocmask(SIG_BLOCK, &hmask, &omask);	/* prevent reconfig's */
191 	if (GetBootFiles() == 0)		/* get list of boot files */
192 		DoExit();
193 	if (ParseConfig() == 0)			/* parse config file */
194 		DoExit();
195 
196 	if (chroot(BootDir) == -1) {
197 		syslog(LOG_CRIT, "chroot %s: %m", BootDir);
198 		exit(1);
199 	}
200 	if (chdir("/") == -1) {
201 		syslog(LOG_CRIT, "chdir(\"/\"): %m");
202 		exit(1);
203 	}
204 	if (setgroups(1, &pw->pw_gid) ||
205 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
206 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
207 		syslog(LOG_CRIT, "can't drop privileges: %m");
208 		exit(1);
209 	}
210 	endpwent();
211 
212 	sigprocmask(SIG_SETMASK, &omask, NULL);	/* allow reconfig's */
213 
214 	/*
215 	 *  Main loop: receive a packet, determine where it came from,
216 	 *  and if we service this host, call routine to handle request.
217 	 */
218 	maxfds = fd + 1;
219 	FD_ZERO(&rset);
220 	FD_SET(fd, &rset);
221 	for (;;) {
222 		struct timeval timeout;
223 		fd_set r;
224 		int nsel;
225 
226 		/*
227 		 * Check pending actions
228 		 */
229 		if (dodebugoff) {
230 			DoDebugOff();
231 			dodebugoff = 0;
232 		}
233 		if (dodebugon) {
234 			DoDebugOn();
235 			dodebugon = 0;
236 		}
237 		if (doreconfig) {
238 			DoReConfig();
239 			doreconfig = 0;
240 		}
241 
242 		r = rset;
243 
244 		if (RmpConns == NULL) {		/* timeout isnt necessary */
245 			nsel = select(maxfds, &r, NULL, NULL, NULL);
246 		} else {
247 			timeout.tv_sec = RMP_TIMEOUT;
248 			timeout.tv_usec = 0;
249 			nsel = select(maxfds, &r, NULL, NULL, &timeout);
250 		}
251 
252 		if (nsel < 0) {
253 			if (errno == EINTR)
254 				continue;
255 			syslog(LOG_ERR, "select: %m");
256 			DoExit();
257 		} else if (nsel == 0) {		/* timeout */
258 			DoTimeout();		/* clear stale conns */
259 			continue;
260 		}
261 
262 		if (FD_ISSET(fd, &r)) {
263 			RMPCONN rconn;
264 			CLIENT *client;
265 			int doread = 1;
266 
267 			while (BpfRead(&rconn, doread)) {
268 				doread = 0;
269 
270 				if (DbgFp != NULL)	/* display packet */
271 					DispPkt(&rconn,DIR_RCVD);
272 
273 				sigprocmask(SIG_BLOCK, &hmask, &omask);
274 
275 				/*
276 				 *  If we do not restrict service, set the
277 				 *  client to NULL (ProcessPacket() handles
278 				 *  this).  Otherwise, check that we can
279 				 *  service this host; if not, log a message
280 				 *  and ignore the packet.
281 				 */
282 				if (BootAny) {
283 					client = NULL;
284 				} else if ((client=FindClient(&rconn))==NULL) {
285 					syslog(LOG_INFO,
286 					    "%s: boot packet ignored",
287 					    EnetStr(&rconn));
288 					sigprocmask(SIG_SETMASK, &omask, NULL);
289 					continue;
290 				}
291 
292 				ProcessPacket(&rconn,client);
293 
294 				sigprocmask(SIG_SETMASK, &omask, NULL);
295 			}
296 		}
297 	}
298 }
299 
300 /*
301 **  DoTimeout -- Free any connections that have timed out.
302 **
303 **	Parameters:
304 **		None.
305 **
306 **	Returns:
307 **		Nothing.
308 **
309 **	Side Effects:
310 **		- Timed out connections in `RmpConns' will be freed.
311 */
312 void
313 DoTimeout(void)
314 {
315 	RMPCONN *rtmp;
316 	struct timeval now;
317 
318 	(void) gettimeofday(&now, (struct timezone *)0);
319 
320 	/*
321 	 *  For each active connection, if RMP_TIMEOUT seconds have passed
322 	 *  since the last packet was sent, delete the connection.
323 	 */
324 	for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next)
325 		if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now.tv_sec) {
326 			syslog(LOG_WARNING, "%s: connection timed out (%u)",
327 			    EnetStr(rtmp), rtmp->rmp.r_type);
328 			RemoveConn(rtmp);
329 		}
330 }
331 
332 /*
333 **  FindClient -- Find client associated with a packet.
334 **
335 **	Parameters:
336 **		rconn - the new packet.
337 **
338 **	Returns:
339 **		Pointer to client info if found, NULL otherwise.
340 **
341 **	Side Effects:
342 **		None.
343 **
344 **	Warnings:
345 **		- This routine must be called with SIGHUP blocked since
346 **		  a reconfigure can invalidate the information returned.
347 */
348 CLIENT *
349 FindClient(RMPCONN *rconn)
350 {
351 	CLIENT *ctmp;
352 
353 	for (ctmp = Clients; ctmp != NULL; ctmp = ctmp->next)
354 		if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0],
355 		    (char *)&ctmp->addr[0], RMP_ADDRLEN) == 0)
356 			break;
357 
358 	return(ctmp);
359 }
360 
361 /*
362 **  Exit -- Log an error message and exit.
363 **
364 **	Parameters:
365 **		sig - caught signal (or zero if not dying on a signal).
366 **
367 **	Returns:
368 **		Does not return.
369 **
370 **	Side Effects:
371 **		- This process ceases to exist.
372 */
373 void
374 Exit(int sig)
375 {
376 	struct syslog_data sdata = SYSLOG_DATA_INIT;
377 
378 	syslog_r(LOG_ERR, &sdata, "going down on signal %d", sig);
379 	_exit(1);
380 }
381 
382 void
383 DoExit(void)
384 {
385 	syslog(LOG_ERR, "going down on fatal error");
386 	exit(1);
387 }
388 
389 /*
390 **  ReConfig -- Get new list of boot files and reread config files.
391 **
392 **	Parameters:
393 **		None.
394 **
395 **	Returns:
396 **		Nothing.
397 **
398 **	Side Effects:
399 **		- All active connections are dropped.
400 **		- List of bootable files is changed.
401 **		- List of clients is changed.
402 **
403 **	Warnings:
404 **		- This routine must be called with SIGHUP blocked.
405 */
406 void
407 ReConfig(int signo)
408 {
409 	doreconfig = 1;
410 }
411 
412 void
413 DoReConfig(void)
414 {
415 	syslog(LOG_NOTICE, "reconfiguring boot server");
416 
417 	FreeConns();
418 
419 	if (GetBootFiles() == 0)
420 		DoExit();
421 
422 	if (ParseConfig() == 0)
423 		DoExit();
424 }
425 
426 /*
427 **  DebugOff -- Turn off debugging.
428 **
429 **	Parameters:
430 **		None.
431 **
432 **	Returns:
433 **		Nothing.
434 **
435 **	Side Effects:
436 **		- Debug file is closed.
437 */
438 void
439 DebugOff(int signo)
440 {
441 	dodebugoff = 1;
442 }
443 
444 void
445 DoDebugOff(void)
446 {
447 	if (DbgFp != NULL)
448 		(void) fclose(DbgFp);
449 
450 	DbgFp = NULL;
451 }
452 
453 /*
454 **  DebugOn -- Turn on debugging.
455 **
456 **	Parameters:
457 **		None.
458 **
459 **	Returns:
460 **		Nothing.
461 **
462 **	Side Effects:
463 **		- Debug file is opened/truncated if not already opened,
464 **		  otherwise do nothing.
465 */
466 void
467 DebugOn(int signo)
468 {
469 	dodebugon = 1;
470 }
471 
472 void
473 DoDebugOn(void)
474 {
475 	if (DbgFp == NULL) {
476 		if ((DbgFp = fopen(DbgFile, "w")) == NULL)
477 			syslog(LOG_ERR, "can't open debug file (%s)", DbgFile);
478 	}
479 }
480