155575Sbostic /* 255575Sbostic * Copyright (c) 1992 Regents of the University of California. 355575Sbostic * Copyright (c) 1988, 1992 The University of Utah and the Center 455575Sbostic * for Software Science (CSS). 555575Sbostic * All rights reserved. 655575Sbostic * 755575Sbostic * This code is derived from software contributed to Berkeley by 855575Sbostic * the Center for Software Science of the University of Utah Computer 955575Sbostic * Science Department. CSS requests users of this software to return 1055575Sbostic * to css-dist@cs.utah.edu any improvements that they make and grant 1155575Sbostic * CSS redistribution rights. 1255575Sbostic * 1355575Sbostic * %sccs.include.redist.c% 1455575Sbostic * 15*55600Sbostic * @(#)rbootd.c 5.2 (Berkeley) 07/23/92 1655575Sbostic * 1755575Sbostic * Utah $Hdr: rbootd.c 3.1 92/07/06$ 1855575Sbostic * Author: Jeff Forys, University of Utah CSS 1955575Sbostic */ 2055575Sbostic 2155575Sbostic #ifndef lint 22*55600Sbostic static char sccsid[] = "@(#)rbootd.c 5.2 (Berkeley) 07/23/92"; 2355575Sbostic #endif /* not lint */ 2455575Sbostic 25*55600Sbostic #include <sys/param.h> 2655575Sbostic #include <sys/ioctl.h> 2755575Sbostic 28*55600Sbostic #include <ctype.h> 2955575Sbostic #include <errno.h> 30*55600Sbostic #include <fcntl.h> 31*55600Sbostic #include <signal.h> 32*55600Sbostic #include <stdio.h> 33*55600Sbostic #include <stdlib.h> 34*55600Sbostic #include <string.h> 3555575Sbostic #include <syslog.h> 36*55600Sbostic #include <unistd.h> 37*55600Sbostic #include "defs.h" 3855575Sbostic 39*55600Sbostic 4055575Sbostic /* fd mask macros (backward compatibility with 4.2BSD) */ 4155575Sbostic #ifndef FD_SET 4255575Sbostic #ifdef notdef 4355575Sbostic typedef struct fd_set { /* this should already be in 4.2 */ 4455575Sbostic int fds_bits[1]; 4555575Sbostic } fd_set; 4655575Sbostic #endif 4755575Sbostic #define FD_ZERO(p) ((p)->fds_bits[0] = 0) 4855575Sbostic #define FD_SET(n, p) ((p)->fds_bits[0] |= (1 << (n))) 4955575Sbostic #define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1 << (n))) 5055575Sbostic #define FD_ISSET(n, p) ((p)->fds_bits[0] & (1 << (n))) 5155575Sbostic #endif 5255575Sbostic 53*55600Sbostic int 5455575Sbostic main(argc, argv) 55*55600Sbostic int argc; 56*55600Sbostic char *argv[]; 5755575Sbostic { 5855575Sbostic int c, fd, omask, maxfds; 5955575Sbostic fd_set rset; 6055575Sbostic 6155575Sbostic /* 6255575Sbostic * Find what name we are running under. 6355575Sbostic */ 6455575Sbostic ProgName = (ProgName = rindex(argv[0],'/')) ? ++ProgName : *argv; 6555575Sbostic 6655575Sbostic /* 6755575Sbostic * Close any open file descriptors. 6855575Sbostic * Temporarily leave stdin & stdout open for `-d', 6955575Sbostic * and stderr open for any pre-syslog error messages. 7055575Sbostic */ 7155575Sbostic { 7255575Sbostic int i, nfds = getdtablesize(); 7355575Sbostic 7455575Sbostic for (i = 0; i < nfds; i++) 7555575Sbostic if (i != fileno(stdin) && i != fileno(stdout) && 7655575Sbostic i != fileno(stderr)) 7755575Sbostic (void) close(i); 7855575Sbostic } 7955575Sbostic 8055575Sbostic /* 8155575Sbostic * Parse any arguments. 8255575Sbostic */ 8355575Sbostic while ((c = getopt(argc, argv, "adi:")) != EOF) 8455575Sbostic switch(c) { 8555575Sbostic case 'a': 8655575Sbostic BootAny++; 8755575Sbostic break; 8855575Sbostic case 'd': 8955575Sbostic DebugFlg++; 9055575Sbostic break; 9155575Sbostic case 'i': 9255575Sbostic IntfName = optarg; 9355575Sbostic break; 9455575Sbostic } 9555575Sbostic for (; optind < argc; optind++) { 9655575Sbostic if (ConfigFile == NULL) 9755575Sbostic ConfigFile = argv[optind]; 9855575Sbostic else { 9955575Sbostic fprintf(stderr, 10055575Sbostic "%s: too many config files (`%s' ignored)\n", 10155575Sbostic ProgName, argv[optind]); 10255575Sbostic } 10355575Sbostic } 10455575Sbostic 10555575Sbostic if (ConfigFile == NULL) /* use default config file */ 10655575Sbostic ConfigFile = DfltConfig; 10755575Sbostic 10855575Sbostic if (DebugFlg) { 10955575Sbostic DbgFp = stdout; /* output to stdout */ 11055575Sbostic 11155575Sbostic (void) signal(SIGUSR1, SIG_IGN); /* dont muck w/DbgFp */ 11255575Sbostic (void) signal(SIGUSR2, SIG_IGN); 11355575Sbostic } else { 11455575Sbostic (void) fclose(stdin); /* dont need these */ 11555575Sbostic (void) fclose(stdout); 11655575Sbostic 11755575Sbostic /* 11855575Sbostic * Fork off a child to do the work & exit. 11955575Sbostic */ 12055575Sbostic switch(fork()) { 12155575Sbostic case -1: /* fork failed */ 12255575Sbostic fprintf(stderr, "%s: ", ProgName); 12355575Sbostic perror("fork"); 12455575Sbostic Exit(0); 12555575Sbostic case 0: /* this is the CHILD */ 12655575Sbostic break; 12755575Sbostic default: /* this is the PARENT */ 12855575Sbostic _exit(0); 12955575Sbostic } 13055575Sbostic 13155575Sbostic /* 13255575Sbostic * Try to disassociate from the current tty. 13355575Sbostic */ 13455575Sbostic { 13555575Sbostic char *devtty = "/dev/tty"; 13655575Sbostic int i; 13755575Sbostic 13855575Sbostic if ((i = open(devtty, O_RDWR)) < 0) { 13955575Sbostic /* probably already disassociated */ 14055575Sbostic if (setpgrp(0, 0) < 0) { 14155575Sbostic fprintf(stderr, "%s: ", ProgName); 14255575Sbostic perror("setpgrp"); 14355575Sbostic } 14455575Sbostic } else { 14555575Sbostic if (ioctl(i, (u_long)TIOCNOTTY, (char *)0) < 0){ 14655575Sbostic fprintf(stderr, "%s: ", ProgName); 14755575Sbostic perror("ioctl"); 14855575Sbostic } 14955575Sbostic (void) close(i); 15055575Sbostic } 15155575Sbostic } 15255575Sbostic 15355575Sbostic (void) signal(SIGUSR1, DebugOn); 15455575Sbostic (void) signal(SIGUSR2, DebugOff); 15555575Sbostic } 15655575Sbostic 15755575Sbostic (void) fclose(stderr); /* finished with it */ 15855575Sbostic 15955575Sbostic #ifdef SYSLOG4_2 16055575Sbostic openlog(ProgName, LOG_PID); 16155575Sbostic #else 16255575Sbostic openlog(ProgName, LOG_PID, LOG_DAEMON); 16355575Sbostic #endif 16455575Sbostic 16555575Sbostic /* 16655575Sbostic * If no interface was specified, get one now. 16755575Sbostic * 16855575Sbostic * This is convoluted because we want to get the default interface 16955575Sbostic * name for the syslog("restarted") message. If BpfGetIntfName() 17055575Sbostic * runs into an error, it will return a syslog-able error message 17155575Sbostic * (in `errmsg') which will be displayed here. 17255575Sbostic */ 17355575Sbostic if (IntfName == NULL) { 174*55600Sbostic char *errmsg; 17555575Sbostic 17655575Sbostic if ((IntfName = BpfGetIntfName(&errmsg)) == NULL) { 17755575Sbostic syslog(LOG_NOTICE, "restarted (??)"); 17855575Sbostic syslog(LOG_ERR, errmsg); 17955575Sbostic Exit(0); 18055575Sbostic } 18155575Sbostic } 18255575Sbostic 18355575Sbostic syslog(LOG_NOTICE, "restarted (%s)", IntfName); 18455575Sbostic 18555575Sbostic (void) signal(SIGHUP, ReConfig); 18655575Sbostic (void) signal(SIGINT, Exit); 18755575Sbostic (void) signal(SIGTERM, Exit); 18855575Sbostic 18955575Sbostic /* 19055575Sbostic * Grab our host name and pid. 19155575Sbostic */ 19255575Sbostic if (gethostname(MyHost, MAXHOSTNAMELEN) < 0) { 19355575Sbostic syslog(LOG_ERR, "gethostname: %m"); 19455575Sbostic Exit(0); 19555575Sbostic } 19655575Sbostic MyHost[MAXHOSTNAMELEN] = '\0'; 19755575Sbostic 19855575Sbostic MyPid = getpid(); 19955575Sbostic 20055575Sbostic /* 20155575Sbostic * Write proc's pid to a file. 20255575Sbostic */ 20355575Sbostic { 20455575Sbostic FILE *fp; 20555575Sbostic 20655575Sbostic if ((fp = fopen(PidFile, "w")) != NULL) { 20755575Sbostic (void) fprintf(fp, "%d\n", MyPid); 20855575Sbostic (void) fclose(fp); 20955575Sbostic } else { 21055575Sbostic syslog(LOG_WARNING, "fopen: failed (%s)", PidFile); 21155575Sbostic } 21255575Sbostic } 21355575Sbostic 21455575Sbostic /* 21555575Sbostic * All boot files are relative to the boot directory, we might 21655575Sbostic * as well chdir() there to make life easier. 21755575Sbostic */ 21855575Sbostic if (chdir(BootDir) < 0) { 21955575Sbostic syslog(LOG_ERR, "chdir: %m (%s)", BootDir); 22055575Sbostic Exit(0); 22155575Sbostic } 22255575Sbostic 22355575Sbostic /* 22455575Sbostic * Initial configuration. 22555575Sbostic */ 22655575Sbostic omask = sigblock(sigmask(SIGHUP)); /* prevent reconfig's */ 22755575Sbostic if (GetBootFiles() == 0) /* get list of boot files */ 22855575Sbostic Exit(0); 22955575Sbostic if (ParseConfig() == 0) /* parse config file */ 23055575Sbostic Exit(0); 23155575Sbostic 23255575Sbostic /* 23355575Sbostic * Open and initialize a BPF device for the appropriate interface. 23455575Sbostic * If an error is encountered, a message is displayed and Exit() 23555575Sbostic * is called. 23655575Sbostic */ 23755575Sbostic fd = BpfOpen(); 23855575Sbostic 23955575Sbostic (void) sigsetmask(omask); /* allow reconfig's */ 24055575Sbostic 24155575Sbostic /* 24255575Sbostic * Main loop: receive a packet, determine where it came from, 24355575Sbostic * and if we service this host, call routine to handle request. 24455575Sbostic */ 24555575Sbostic maxfds = fd + 1; 24655575Sbostic FD_ZERO(&rset); 24755575Sbostic FD_SET(fd, &rset); 24855575Sbostic for (;;) { 24955575Sbostic struct timeval timeout; 25055575Sbostic fd_set r; 25155575Sbostic int nsel; 25255575Sbostic 25355575Sbostic r = rset; 25455575Sbostic 25555575Sbostic if (RmpConns == NULL) { /* timeout isnt necessary */ 25655575Sbostic nsel = select(maxfds, &r, (fd_set *)0, (fd_set *)0, 25755575Sbostic (struct timeval *)0); 25855575Sbostic } else { 25955575Sbostic timeout.tv_sec = RMP_TIMEOUT; 26055575Sbostic timeout.tv_usec = 0; 26155575Sbostic nsel = select(maxfds, &r, (fd_set *)0, (fd_set *)0, 26255575Sbostic &timeout); 26355575Sbostic } 26455575Sbostic 26555575Sbostic if (nsel < 0) { 26655575Sbostic if (errno == EINTR) 26755575Sbostic continue; 26855575Sbostic syslog(LOG_ERR, "select: %m"); 26955575Sbostic Exit(0); 27055575Sbostic } else if (nsel == 0) { /* timeout */ 27155575Sbostic DoTimeout(); /* clear stale conns */ 27255575Sbostic continue; 27355575Sbostic } 27455575Sbostic 27555575Sbostic if (FD_ISSET(fd, &r)) { 27655575Sbostic RMPCONN rconn; 27755575Sbostic CLIENT *client, *FindClient(); 27855575Sbostic int doread = 1; 27955575Sbostic 28055575Sbostic while (BpfRead(&rconn, doread)) { 28155575Sbostic doread = 0; 28255575Sbostic 28355575Sbostic if (DbgFp != NULL) /* display packet */ 28455575Sbostic DispPkt(&rconn,DIR_RCVD); 28555575Sbostic 28655575Sbostic omask = sigblock(sigmask(SIGHUP)); 28755575Sbostic 28855575Sbostic /* 28955575Sbostic * If we do not restrict service, set the 29055575Sbostic * client to NULL (ProcessPacket() handles 29155575Sbostic * this). Otherwise, check that we can 29255575Sbostic * service this host; if not, log a message 29355575Sbostic * and ignore the packet. 29455575Sbostic */ 29555575Sbostic if (BootAny) { 29655575Sbostic client = NULL; 29755575Sbostic } else if ((client=FindClient(&rconn))==NULL) { 29855575Sbostic syslog(LOG_INFO, 29955575Sbostic "%s: boot packet ignored", 30055575Sbostic EnetStr(&rconn)); 30155575Sbostic (void) sigsetmask(omask); 30255575Sbostic continue; 30355575Sbostic } 30455575Sbostic 30555575Sbostic ProcessPacket(&rconn,client); 30655575Sbostic 30755575Sbostic (void) sigsetmask(omask); 30855575Sbostic } 30955575Sbostic } 31055575Sbostic } 31155575Sbostic } 31255575Sbostic 31355575Sbostic /* 31455575Sbostic ** DoTimeout -- Free any connections that have timed out. 31555575Sbostic ** 31655575Sbostic ** Parameters: 31755575Sbostic ** None. 31855575Sbostic ** 31955575Sbostic ** Returns: 32055575Sbostic ** Nothing. 32155575Sbostic ** 32255575Sbostic ** Side Effects: 32355575Sbostic ** - Timed out connections in `RmpConns' will be freed. 32455575Sbostic */ 325*55600Sbostic void 32655575Sbostic DoTimeout() 32755575Sbostic { 32855575Sbostic register RMPCONN *rtmp; 32955575Sbostic struct timeval now; 33055575Sbostic 33155575Sbostic (void) gettimeofday(&now, (struct timezone *)0); 33255575Sbostic 33355575Sbostic /* 33455575Sbostic * For each active connection, if RMP_TIMEOUT seconds have passed 33555575Sbostic * since the last packet was sent, delete the connection. 33655575Sbostic */ 33755575Sbostic for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next) 33855575Sbostic if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now.tv_sec) { 33955575Sbostic syslog(LOG_WARNING, "%s: connection timed out (%u)", 34055575Sbostic EnetStr(rtmp), rtmp->rmp.r_type); 34155575Sbostic RemoveConn(rtmp); 34255575Sbostic } 34355575Sbostic } 34455575Sbostic 34555575Sbostic /* 34655575Sbostic ** FindClient -- Find client associated with a packet. 34755575Sbostic ** 34855575Sbostic ** Parameters: 34955575Sbostic ** rconn - the new packet. 35055575Sbostic ** 35155575Sbostic ** Returns: 35255575Sbostic ** Pointer to client info if found, NULL otherwise. 35355575Sbostic ** 35455575Sbostic ** Side Effects: 35555575Sbostic ** None. 35655575Sbostic ** 35755575Sbostic ** Warnings: 35855575Sbostic ** - This routine must be called with SIGHUP blocked since 35955575Sbostic ** a reconfigure can invalidate the information returned. 36055575Sbostic */ 36155575Sbostic 36255575Sbostic CLIENT * 36355575Sbostic FindClient(rconn) 364*55600Sbostic register RMPCONN *rconn; 36555575Sbostic { 36655575Sbostic register CLIENT *ctmp; 36755575Sbostic 36855575Sbostic for (ctmp = Clients; ctmp != NULL; ctmp = ctmp->next) 36955575Sbostic if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0], 37055575Sbostic (char *)&ctmp->addr[0], RMP_ADDRLEN) == 0) 37155575Sbostic break; 37255575Sbostic 37355575Sbostic return(ctmp); 37455575Sbostic } 37555575Sbostic 37655575Sbostic /* 37755575Sbostic ** Exit -- Log an error message and exit. 37855575Sbostic ** 37955575Sbostic ** Parameters: 38055575Sbostic ** sig - caught signal (or zero if not dying on a signal). 38155575Sbostic ** 38255575Sbostic ** Returns: 38355575Sbostic ** Does not return. 38455575Sbostic ** 38555575Sbostic ** Side Effects: 38655575Sbostic ** - This process ceases to exist. 38755575Sbostic */ 388*55600Sbostic void 38955575Sbostic Exit(sig) 39055575Sbostic int sig; 39155575Sbostic { 39255575Sbostic if (sig > 0) 39355575Sbostic syslog(LOG_ERR, "going down on signal %d", sig); 39455575Sbostic else 39555575Sbostic syslog(LOG_ERR, "going down with fatal error"); 39655575Sbostic BpfClose(); 39755575Sbostic exit(1); 39855575Sbostic } 39955575Sbostic 40055575Sbostic /* 40155575Sbostic ** ReConfig -- Get new list of boot files and reread config files. 40255575Sbostic ** 40355575Sbostic ** Parameters: 40455575Sbostic ** None. 40555575Sbostic ** 40655575Sbostic ** Returns: 40755575Sbostic ** Nothing. 40855575Sbostic ** 40955575Sbostic ** Side Effects: 41055575Sbostic ** - All active connections are dropped. 41155575Sbostic ** - List of boot-able files is changed. 41255575Sbostic ** - List of clients is changed. 41355575Sbostic ** 41455575Sbostic ** Warnings: 41555575Sbostic ** - This routine must be called with SIGHUP blocked. 41655575Sbostic */ 417*55600Sbostic void 418*55600Sbostic ReConfig(signo) 419*55600Sbostic int signo; 42055575Sbostic { 42155575Sbostic syslog(LOG_NOTICE, "reconfiguring boot server"); 42255575Sbostic 42355575Sbostic FreeConns(); 42455575Sbostic 42555575Sbostic if (GetBootFiles() == 0) 42655575Sbostic Exit(0); 42755575Sbostic 42855575Sbostic if (ParseConfig() == 0) 42955575Sbostic Exit(0); 43055575Sbostic } 43155575Sbostic 43255575Sbostic /* 43355575Sbostic ** DebugOff -- Turn off debugging. 43455575Sbostic ** 43555575Sbostic ** Parameters: 43655575Sbostic ** None. 43755575Sbostic ** 43855575Sbostic ** Returns: 43955575Sbostic ** Nothing. 44055575Sbostic ** 44155575Sbostic ** Side Effects: 44255575Sbostic ** - Debug file is closed. 44355575Sbostic */ 444*55600Sbostic void 445*55600Sbostic DebugOff(signo) 446*55600Sbostic int signo; 44755575Sbostic { 44855575Sbostic if (DbgFp != NULL) 44955575Sbostic (void) fclose(DbgFp); 45055575Sbostic 45155575Sbostic DbgFp = NULL; 45255575Sbostic } 45355575Sbostic 45455575Sbostic /* 45555575Sbostic ** DebugOn -- Turn on debugging. 45655575Sbostic ** 45755575Sbostic ** Parameters: 45855575Sbostic ** None. 45955575Sbostic ** 46055575Sbostic ** Returns: 46155575Sbostic ** Nothing. 46255575Sbostic ** 46355575Sbostic ** Side Effects: 46455575Sbostic ** - Debug file is opened/truncated if not already opened, 46555575Sbostic ** otherwise do nothing. 46655575Sbostic */ 467*55600Sbostic void 468*55600Sbostic DebugOn(signo) 469*55600Sbostic int signo; 47055575Sbostic { 47155575Sbostic if (DbgFp == NULL) { 47255575Sbostic if ((DbgFp = fopen(DbgFile, "w")) == NULL) 47355575Sbostic syslog(LOG_ERR, "can't open debug file (%s)", DbgFile); 47455575Sbostic } 47555575Sbostic } 476