1 /* 2 * Copyright (c) 1986, 1987, 1992 Daniel D. Lanciani. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by 16 * Daniel D. Lanciani. 17 * 4. The name of the author may not 18 * be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY Daniel D. Lanciani ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL Daniel D. Lanciani BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 /* $Header: /cvsroot/src/sbin/init/init.c,v 1.5 1993/04/06 19:33:33 cgd Exp $ */ 35 36 37 #include <sys/types.h> 38 #include <sys/errno.h> 39 #include <sys/signal.h> 40 #include <sys/wait.h> 41 #include <setjmp.h> 42 #include <ttyent.h> 43 #include <unistd.h> 44 45 #ifdef SECURE_CONSOLE 46 #include <pwd.h> 47 #endif 48 49 #define NTTY 32 /* max ttys */ 50 #define NARG 16 /* max args to login/getty */ 51 52 /* internal flags */ 53 #define TTY_SEEN 0x8000 54 #define TTY_DIFF 0x4000 55 #define TTY_LOGIN 0x2000 56 57 /* non-standard tty_logout: rerun login/getty with -o switch to clean line */ 58 #ifndef TTY_LOGOUT 59 #define TTY_LOGOUT 0x1000 60 #endif 61 62 /* non-standard tty_open: open device for login/getty */ 63 #ifndef TTY_OPEN 64 #define TTY_OPEN 0x0800 65 #endif 66 67 #define isspace(c) ((c) == ' ' || (c) == '\t') 68 69 struct ttytab { 70 char *tt_name; 71 char *tt_getty; 72 char *tt_type; 73 int tt_status; 74 int tt_pid; 75 } ttytab[NTTY], *ttytabend = ttytab; 76 int drain, sflag; 77 char arg[128], nam[64], term[64], *env[] = { term, 0 }; 78 jmp_buf single, reread; 79 char *Reboot = "autoboot"; 80 81 char *newstring(), *malloc(); 82 extern int errno; 83 84 /* signal state of child process */ 85 #define SIGNALSFORCHILD \ 86 signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); \ 87 signal(SIGTERM, SIG_DFL); signal(SIGALRM, SIG_DFL); \ 88 signal(SIGTSTP, SIG_DFL); signal(SIGCHLD, SIG_DFL); \ 89 signal(SIGTTIN, SIG_DFL); signal(SIGTTOU, SIG_DFL); \ 90 sigsetmask( 0); /* 04 Sep 92*/ 91 92 /* SIGHUP: reread /etc/ttys */ 93 void 94 shup(sig) 95 { 96 longjmp(reread, 1); 97 } 98 99 /* SIGALRM: abort wait and go single user */ 100 void 101 salrm(sig) 102 { 103 signal(SIGALRM, SIG_DFL); 104 warn("process hung"); 105 longjmp(single, 1); 106 } 107 108 /* SIGTERM: go single user */ 109 void 110 sterm(sig) 111 { 112 register struct ttytab *tt; 113 114 if (!Reboot) { 115 for(tt = ttytab; tt < ttytabend; tt++) { 116 free(tt->tt_name); 117 free(tt->tt_getty); 118 free(tt->tt_type); 119 } 120 ttytabend = ttytab; 121 /* give processes time to exit cleanly */ /* 15 Aug 92*/ 122 kill(-1, SIGTERM); 123 sleep(10); 124 /* Now murder them */ 125 kill(-1, SIGKILL); 126 kill(-1, SIGCONT); 127 signal(SIGALRM, salrm); 128 alarm(30); 129 while(wait((int *)0) > 0); 130 alarm(0); 131 signal(SIGALRM, SIG_DFL); 132 longjmp(single, 1); 133 } 134 } 135 136 /* SIGTSTP: drain system */ 137 void 138 ststp(sig) 139 { 140 drain = 1; 141 } 142 143 /* init [-s] [-f] */ 144 145 main(argc, argv) 146 char **argv; 147 { 148 register int pid; 149 register struct ttytab *tt; 150 struct ttyent *ty; 151 int status; 152 long mask = sigblock(sigmask(SIGHUP) | sigmask(SIGTERM)); 153 154 /* did some idiot try to run us? */ 155 if(getpid() != 1) { 156 writes(2,"init: sorry, system daemon, runnable only by system\n"); 157 exit(0xff); 158 } 159 160 /* allocate a session for init */ 161 (void) setsid(); 162 163 /* protect against signals, listen for outside requests */ 164 signal(SIGHUP, shup); 165 signal(SIGTSTP, ststp); 166 167 signal (SIGTTIN, SIG_IGN); 168 signal (SIGTTOU, SIG_IGN); 169 signal (SIGCHLD, SIG_IGN); 170 signal (SIGINT, SIG_IGN); 171 172 /* handle arguments, if any */ 173 if(argc > 1) 174 if(!strcmp(argv[1], "-s")) 175 sflag++; 176 else if(!strcmp(argv[1], "-f")) 177 Reboot = 0; 178 top: 179 /* Single user mode? */ 180 if(sflag) { 181 sflag = 0; 182 status = 1; 183 } else { 184 /* otherwise, execute /etc/rc */ 185 if (access("/etc/rc", F_OK) == 0) { 186 187 signal(SIGTERM, SIG_IGN); /* XXX */ 188 if((pid = fork()) < 0) 189 fatal("fork"); 190 else if(!pid) { 191 /* signals, to default state */ 192 SIGNALSFORCHILD; 193 194 /* clean off console */ 195 revoke("/dev/console"); 196 197 /* create a shell */ 198 login_tty(open("/dev/console", 2)); 199 execl("/bin/sh", "sh", "/etc/rc", Reboot, (char *)0); 200 _exit(127); 201 } 202 Reboot = 0; /* 31 Jul 92*/ 203 while(wait(&status) != pid); 204 205 /* if we are about to be rebooted, then wait for it */ 206 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) 207 pause(); 208 } else { status = 1; sflag = 1; goto top; } 209 } 210 signal(SIGTERM, sterm); 211 Reboot = 0; 212 213 /* do single user shell on console */ 214 if (setjmp(single) || status) { 215 #ifdef SECURE_CONSOLE 216 struct ttyent *ttyp; 217 struct passwd *passp; 218 char *pass; 219 static const char banner[] = 220 "Enter root password, or Control-D to go multi-user\n"; 221 #endif 222 223 if((pid = fork()) < 0) 224 fatal("fork"); 225 else if(!pid) { 226 /* signals, to default state */ 227 SIGNALSFORCHILD; 228 229 /* clean off console */ 230 revoke("/dev/console"); 231 232 /* do open and configuration of console */ 233 login_tty(open("/dev/console", 2)); 234 #ifdef SECURE_CONSOLE 235 /* if the console isn't secure, check the root PW */ 236 ttyp = getttynam("console"); 237 if (!ttyp) { 238 /* don't have an entry for "console", probably 239 * have one for /dev/vga 240 */ 241 ttyp = getttynam("vga"); 242 } 243 passp = getpwnam("root"); 244 if (ttyp && ((ttyp->ty_status & TTY_SECURE) == 0) && 245 passp) { 246 write(2, banner, sizeof(banner) - 1); 247 do { 248 pass = getpass("Password:"); 249 if ((pass == 0) || (*pass == '\0')) 250 _exit(0); /* got control-d */ 251 #ifdef DES 252 pass = crypt(pass, passp->pw_passwd); 253 #endif 254 } while (strcmp(pass, passp->pw_passwd) != 0); 255 } 256 #endif 257 execl("/bin/sh", "-", (char *)0); 258 _exit(127); 259 } 260 while(wait(&status) != pid); 261 while(drain) /* 31 Jul 92*/ 262 pause(); 263 goto top; 264 } 265 266 /* multiuser mode, traipse through table */ 267 setttyent(); 268 for(tt = ttytab; (ty = getttyent()) && tt < &ttytab[NTTY]; tt++) { 269 tt->tt_name = newstring(ty->ty_name); 270 tt->tt_getty = newstring(ty->ty_getty); 271 tt->tt_type = newstring(ty->ty_type); 272 tt->tt_status = ty->ty_status; 273 } 274 ttytabend = tt; 275 endttyent(); 276 for(tt = ttytab; tt < ttytabend; getty(tt++)); 277 278 /* if we receive a request to reread the table, come here */ 279 if(setjmp(reread)) { 280 281 /* first pass. find and clean the entries that have changed */ 282 setttyent(); 283 while(ty = getttyent()) { 284 for(tt = ttytab; tt < ttytabend; tt++) 285 if(!strcmp(tt->tt_name, ty->ty_name)) { 286 /* if a process present, mark */ 287 if((tt->tt_status & ~TTY_LOGIN) !=ty->ty_status) 288 tt->tt_status = ty->ty_status |TTY_DIFF; 289 if(strcmp(tt->tt_getty, ty->ty_getty)) { 290 free(tt->tt_getty); 291 tt->tt_getty = newstring(ty->ty_getty); 292 tt->tt_status |= TTY_DIFF; 293 } 294 if(strcmp(tt->tt_type, ty->ty_type)) { 295 free(tt->tt_type); 296 tt->tt_type = newstring(ty->ty_type); 297 tt->tt_status |= TTY_DIFF; 298 } 299 if(((tt->tt_status |= TTY_SEEN) & TTY_DIFF) 300 && tt->tt_pid > 1) 301 kill(tt->tt_pid, 9); 302 break; 303 } 304 if(tt == ttytabend && tt < &ttytab[NTTY]) { 305 tt->tt_name = newstring(ty->ty_name); 306 tt->tt_getty = newstring(ty->ty_getty); 307 tt->tt_type = newstring(ty->ty_type); 308 tt->tt_status = ty->ty_status | 309 TTY_SEEN | TTY_DIFF; 310 ttytabend++; 311 } 312 } 313 endttyent(); 314 /* second pass. offer gettys on previously cleaned entries, 315 and garbage collect "dead" entries */ 316 for(tt = ttytab; tt < ttytabend; tt++) 317 if(tt->tt_status & TTY_SEEN) { 318 tt->tt_status &= ~TTY_SEEN; 319 if(tt->tt_status & TTY_DIFF) { 320 tt->tt_status &= ~TTY_DIFF; 321 getty(tt); 322 } 323 } 324 else { 325 if(tt->tt_pid > 1) 326 kill(tt->tt_pid, 9); 327 free(tt->tt_name); 328 free(tt->tt_getty); 329 free(tt->tt_type); 330 pid = tt - ttytab; 331 for(tt++; tt < ttytabend; tt++) 332 tt[-1] = *tt; 333 ttytabend--; 334 tt = &ttytab[pid]; 335 } 336 } 337 drain = 0; 338 339 /* listen for terminating gettys and sessions, and process them */ 340 while(1) { 341 sigsetmask(mask); 342 pid = wait(&status); 343 sigblock(sigmask(SIGHUP) | sigmask(SIGTERM)); 344 if(pid < 0) { 345 sleep(5); 346 continue; 347 } 348 for(tt = ttytab; tt < ttytabend; tt++) 349 if(pid == tt->tt_pid) { 350 /* 24 Jul 92*/ if (logout(tt->tt_name)) logwtmp(tt->tt_name,"",""); 351 if(drain && !(tt->tt_status & TTY_LOGIN)) { 352 free(tt->tt_name); 353 free(tt->tt_getty); 354 free(tt->tt_type); 355 for(tt++; tt < ttytabend; tt++) 356 tt[-1] = *tt; 357 ttytabend--; 358 } 359 else 360 getty(tt); 361 break; 362 } 363 } 364 } 365 366 /* process a getty for a "line". N.B. by having getty do open, init 367 is not limited by filedescriptors for number of possible users */ 368 getty(tt) 369 struct ttytab *tt; 370 { 371 char *sargv[NARG]; 372 register char *p = arg, **sp = sargv; 373 374 if(!(tt->tt_status & TTY_ON)) { 375 tt->tt_pid = -1; 376 return; 377 } 378 if((tt->tt_pid = fork()) < 0) 379 fatal("getty fork"); 380 else if(tt->tt_pid) { 381 if(tt->tt_status & TTY_LOGOUT) 382 tt->tt_status ^= TTY_LOGIN; 383 return; 384 } 385 signal(SIGHUP, SIG_DFL); 386 signal(SIGTERM, SIG_DFL); 387 signal(SIGTSTP, SIG_DFL); 388 sigsetmask(0); 389 strcpy(p, tt->tt_getty); 390 while(sp < &sargv[NARG - 2]) { 391 while(isspace(*p)) 392 p++; 393 if(!*p) 394 break; 395 *sp++ = p; 396 while(!isspace(*p) && *p) 397 p++; 398 if(!*p) 399 break; 400 *p++ = 0; 401 } 402 strcpy(nam, tt->tt_name); 403 *sp++ = nam; 404 *sp = 0; 405 p = *sargv; 406 strcpy(term, "TERM="); 407 strcat(term, tt->tt_type); 408 execve(p, sargv, env); 409 bad: 410 sleep(30); 411 fatal(tt->tt_name); 412 } 413 414 char * 415 newstring(s) 416 register char *s; 417 { 418 register char *n; 419 420 if(!(n = malloc(strlen(s) + 1))) 421 fatal("out of memory"); 422 strcpy(n, s); 423 return(n); 424 } 425 426 warn(s) 427 char *s; 428 { 429 register int pid; 430 int fd; 431 432 fd = open("/dev/console", 2); 433 writes(fd, "init WARNING: "); 434 writes(fd, s); 435 write(fd, "\n", 1); 436 close(fd); 437 } 438 439 fatal(s) 440 char *s; 441 { 442 login_tty(open("/dev/console", 2)); 443 writes(2, "init FATAL error: "); 444 perror(s); 445 _exit(1); /* 04 Sep 92*/ 446 /* panic: init died */ 447 } 448 449 writes(n, s) 450 char *s; 451 { 452 write(n, s, strlen(s)); 453 } 454