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