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.6 1993/04/26 14:54:43 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 pass = crypt(pass, passp->pw_passwd); 252 } while (strcmp(pass, passp->pw_passwd) != 0); 253 } 254 #endif 255 execl("/bin/sh", "-", (char *)0); 256 _exit(127); 257 } 258 while(wait(&status) != pid); 259 while(drain) /* 31 Jul 92*/ 260 pause(); 261 goto top; 262 } 263 264 /* multiuser mode, traipse through table */ 265 setttyent(); 266 for(tt = ttytab; (ty = getttyent()) && tt < &ttytab[NTTY]; tt++) { 267 tt->tt_name = newstring(ty->ty_name); 268 tt->tt_getty = newstring(ty->ty_getty); 269 tt->tt_type = newstring(ty->ty_type); 270 tt->tt_status = ty->ty_status; 271 } 272 ttytabend = tt; 273 endttyent(); 274 for(tt = ttytab; tt < ttytabend; getty(tt++)); 275 276 /* if we receive a request to reread the table, come here */ 277 if(setjmp(reread)) { 278 279 /* first pass. find and clean the entries that have changed */ 280 setttyent(); 281 while(ty = getttyent()) { 282 for(tt = ttytab; tt < ttytabend; tt++) 283 if(!strcmp(tt->tt_name, ty->ty_name)) { 284 /* if a process present, mark */ 285 if((tt->tt_status & ~TTY_LOGIN) !=ty->ty_status) 286 tt->tt_status = ty->ty_status |TTY_DIFF; 287 if(strcmp(tt->tt_getty, ty->ty_getty)) { 288 free(tt->tt_getty); 289 tt->tt_getty = newstring(ty->ty_getty); 290 tt->tt_status |= TTY_DIFF; 291 } 292 if(strcmp(tt->tt_type, ty->ty_type)) { 293 free(tt->tt_type); 294 tt->tt_type = newstring(ty->ty_type); 295 tt->tt_status |= TTY_DIFF; 296 } 297 if(((tt->tt_status |= TTY_SEEN) & TTY_DIFF) 298 && tt->tt_pid > 1) 299 kill(tt->tt_pid, 9); 300 break; 301 } 302 if(tt == ttytabend && tt < &ttytab[NTTY]) { 303 tt->tt_name = newstring(ty->ty_name); 304 tt->tt_getty = newstring(ty->ty_getty); 305 tt->tt_type = newstring(ty->ty_type); 306 tt->tt_status = ty->ty_status | 307 TTY_SEEN | TTY_DIFF; 308 ttytabend++; 309 } 310 } 311 endttyent(); 312 /* second pass. offer gettys on previously cleaned entries, 313 and garbage collect "dead" entries */ 314 for(tt = ttytab; tt < ttytabend; tt++) 315 if(tt->tt_status & TTY_SEEN) { 316 tt->tt_status &= ~TTY_SEEN; 317 if(tt->tt_status & TTY_DIFF) { 318 tt->tt_status &= ~TTY_DIFF; 319 getty(tt); 320 } 321 } 322 else { 323 if(tt->tt_pid > 1) 324 kill(tt->tt_pid, 9); 325 free(tt->tt_name); 326 free(tt->tt_getty); 327 free(tt->tt_type); 328 pid = tt - ttytab; 329 for(tt++; tt < ttytabend; tt++) 330 tt[-1] = *tt; 331 ttytabend--; 332 tt = &ttytab[pid]; 333 } 334 } 335 drain = 0; 336 337 /* listen for terminating gettys and sessions, and process them */ 338 while(1) { 339 sigsetmask(mask); 340 pid = wait(&status); 341 sigblock(sigmask(SIGHUP) | sigmask(SIGTERM)); 342 if(pid < 0) { 343 sleep(5); 344 continue; 345 } 346 for(tt = ttytab; tt < ttytabend; tt++) 347 if(pid == tt->tt_pid) { 348 /* 24 Jul 92*/ if (logout(tt->tt_name)) logwtmp(tt->tt_name,"",""); 349 if(drain && !(tt->tt_status & TTY_LOGIN)) { 350 free(tt->tt_name); 351 free(tt->tt_getty); 352 free(tt->tt_type); 353 for(tt++; tt < ttytabend; tt++) 354 tt[-1] = *tt; 355 ttytabend--; 356 } 357 else 358 getty(tt); 359 break; 360 } 361 } 362 } 363 364 /* process a getty for a "line". N.B. by having getty do open, init 365 is not limited by filedescriptors for number of possible users */ 366 getty(tt) 367 struct ttytab *tt; 368 { 369 char *sargv[NARG]; 370 register char *p = arg, **sp = sargv; 371 372 if(!(tt->tt_status & TTY_ON)) { 373 tt->tt_pid = -1; 374 return; 375 } 376 if((tt->tt_pid = fork()) < 0) 377 fatal("getty fork"); 378 else if(tt->tt_pid) { 379 if(tt->tt_status & TTY_LOGOUT) 380 tt->tt_status ^= TTY_LOGIN; 381 return; 382 } 383 signal(SIGHUP, SIG_DFL); 384 signal(SIGTERM, SIG_DFL); 385 signal(SIGTSTP, SIG_DFL); 386 sigsetmask(0); 387 strcpy(p, tt->tt_getty); 388 while(sp < &sargv[NARG - 2]) { 389 while(isspace(*p)) 390 p++; 391 if(!*p) 392 break; 393 *sp++ = p; 394 while(!isspace(*p) && *p) 395 p++; 396 if(!*p) 397 break; 398 *p++ = 0; 399 } 400 strcpy(nam, tt->tt_name); 401 *sp++ = nam; 402 *sp = 0; 403 p = *sargv; 404 strcpy(term, "TERM="); 405 strcat(term, tt->tt_type); 406 execve(p, sargv, env); 407 bad: 408 sleep(30); 409 fatal(tt->tt_name); 410 } 411 412 char * 413 newstring(s) 414 register char *s; 415 { 416 register char *n; 417 418 if(!(n = malloc(strlen(s) + 1))) 419 fatal("out of memory"); 420 strcpy(n, s); 421 return(n); 422 } 423 424 warn(s) 425 char *s; 426 { 427 register int pid; 428 int fd; 429 430 fd = open("/dev/console", 2); 431 writes(fd, "init WARNING: "); 432 writes(fd, s); 433 write(fd, "\n", 1); 434 close(fd); 435 } 436 437 fatal(s) 438 char *s; 439 { 440 login_tty(open("/dev/console", 2)); 441 writes(2, "init FATAL error: "); 442 perror(s); 443 _exit(1); /* 04 Sep 92*/ 444 /* panic: init died */ 445 } 446 447 writes(n, s) 448 char *s; 449 { 450 write(n, s, strlen(s)); 451 } 452