Home | History | Annotate | Line # | Download | only in rbootd
rbootd.c revision 1.2
      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  *	from: @(#)rbootd.c	8.1 (Berkeley) 6/4/93
     42  *	      $Id: rbootd.c,v 1.2 1994/01/11 16:41:47 brezak 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
     49 static 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
     55 /*static char sccsid[] = "@(#)rbootd.c	8.1 (Berkeley) 6/4/93";*/
     56 static char rcsid[] = "$Id: rbootd.c,v 1.2 1994/01/11 16:41:47 brezak Exp $";
     57 #endif /* not lint */
     58 
     59 #include <sys/param.h>
     60 #include <sys/ioctl.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
     77 typedef	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 
     87 int
     88 main(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 */
    359 void
    360 DoTimeout()
    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 
    396 CLIENT *
    397 FindClient(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 */
    422 void
    423 Exit(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 */
    451 void
    452 ReConfig(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 */
    478 void
    479 DebugOff(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 */
    501 void
    502 DebugOn(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