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