]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights | |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.0 (the 'License'). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License." | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | /*- | |
25 | * Copyright (c) 1991, 1993 | |
26 | * The Regents of the University of California. All rights reserved. | |
27 | * | |
28 | * This code is derived from software contributed to Berkeley by | |
29 | * Donn Seeley at Berkeley Software Design, Inc. | |
30 | * | |
31 | * Redistribution and use in source and binary forms, with or without | |
32 | * modification, are permitted provided that the following conditions | |
33 | * are met: | |
34 | * 1. Redistributions of source code must retain the above copyright | |
35 | * notice, this list of conditions and the following disclaimer. | |
36 | * 2. Redistributions in binary form must reproduce the above copyright | |
37 | * notice, this list of conditions and the following disclaimer in the | |
38 | * documentation and/or other materials provided with the distribution. | |
39 | * 3. All advertising materials mentioning features or use of this software | |
40 | * must display the following acknowledgement: | |
41 | * This product includes software developed by the University of | |
42 | * California, Berkeley and its contributors. | |
43 | * 4. Neither the name of the University nor the names of its contributors | |
44 | * may be used to endorse or promote products derived from this software | |
45 | * without specific prior written permission. | |
46 | * | |
47 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
48 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
49 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
50 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
51 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
52 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
53 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
54 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
55 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
56 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
57 | * SUCH DAMAGE. | |
58 | */ | |
59 | ||
60 | #include <sys/param.h> | |
61 | #include <sys/sysctl.h> | |
62 | #include <sys/wait.h> | |
63 | ||
64 | #include <db.h> | |
65 | #include <errno.h> | |
66 | #include <fcntl.h> | |
67 | #include <signal.h> | |
68 | #include <stdio.h> | |
69 | #include <stdlib.h> | |
70 | #include <string.h> | |
71 | #include <syslog.h> | |
72 | #include <time.h> | |
73 | #include <ttyent.h> | |
74 | #include <unistd.h> | |
75 | #include <paths.h> | |
76 | ||
77 | #include <stdarg.h> | |
78 | ||
79 | #ifdef SECURE | |
80 | #include <pwd.h> | |
81 | #endif | |
82 | ||
83 | #include "pathnames.h" | |
84 | ||
85 | /* | |
86 | * Until the mythical util.h arrives... | |
87 | */ | |
88 | extern int login_tty __P((int)); | |
89 | extern int logout __P((const char *)); | |
90 | extern void logwtmp __P((const char *, const char *, const char *)); | |
91 | ||
92 | /* | |
93 | * Sleep times; used to prevent thrashing. | |
94 | */ | |
95 | #define GETTY_SPACING 5 /* N secs minimum getty spacing */ | |
96 | #define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ | |
97 | #define WINDOW_WAIT 3 /* wait N secs after starting window */ | |
98 | #define STALL_TIMEOUT 30 /* wait N secs after warning */ | |
99 | #define DEATH_WATCH 10 /* wait N secs for procs to die */ | |
100 | #define FAILED_HW_PASS 5 /* wait N secs before croaking user */ | |
101 | ||
102 | void handle __P((sig_t, ...)); | |
103 | void delset __P((sigset_t *, ...)); | |
104 | ||
105 | void stall __P((char *, ...)); | |
106 | void warning __P((char *, ...)); | |
107 | void emergency __P((char *, ...)); | |
108 | void disaster __P((int)); | |
109 | void badsys __P((int)); | |
110 | ||
111 | /* | |
112 | * We really need a recursive typedef... | |
113 | * The following at least guarantees that the return type of (*state_t)() | |
114 | * is sufficiently wide to hold a function pointer. | |
115 | */ | |
116 | typedef long (*state_func_t) __P((void)); | |
117 | typedef state_func_t (*state_t) __P((void)); | |
118 | ||
119 | state_func_t single_user __P((void)); | |
120 | state_func_t runcom __P((void)); | |
121 | state_func_t read_ttys __P((void)); | |
122 | state_func_t multi_user __P((void)); | |
123 | state_func_t clean_ttys __P((void)); | |
124 | state_func_t catatonia __P((void)); | |
125 | state_func_t death __P((void)); | |
126 | ||
127 | enum { AUTOBOOT, FASTBOOT, BOOT_SCRIPT } runcom_mode = AUTOBOOT; | |
128 | int runcom_boot = 1; /* Run the rc.boot script */ | |
129 | int runcom_verbose = 0; | |
130 | int runcom_safe = 0; | |
131 | ||
132 | void transition __P((state_t)); | |
133 | state_t requested_transition = runcom; | |
134 | ||
135 | void setctty __P((char *, int)); | |
136 | ||
137 | ||
138 | // gvdl@next.com 14 Aug 1995 | |
139 | // - from ~apps/loginwindow_proj/loginwindow/common.h | |
140 | #define REALLY_EXIT_TO_CONSOLE 229 | |
141 | ||
142 | // From old init.c | |
143 | // These flags are used in the se_flags field of the init_session structure | |
144 | #define SE_SHUTDOWN 0x1 /* session won't be restarted */ | |
145 | ||
146 | // The flags below control what sort of getty is launched. | |
147 | #define SE_GETTY_LAUNCH 0x30 /* What type of getty to launch */ | |
148 | #define SE_COMMON 0x00 /* Usual command that is run - getty */ | |
149 | #define SE_ONERROR 0x10 /* Command to run if error condition occurs. | |
150 | * This will almost always be the windowserver | |
151 | * and loginwindow. This is so if the w.s. | |
152 | * ever dies, that the naive user (stan) | |
153 | * doesn't ever see the console window. */ | |
154 | #define SE_ONOPTION 0x20 /* Command to run when loginwindow exits with | |
155 | * special error code (229). This signifies | |
156 | * that the user typed "console" at l.w. and | |
157 | * l.w. wants to exit and have init run getty | |
158 | * which will then put up a console window. */ | |
159 | ||
160 | typedef struct _se_command { | |
161 | char *path; /* what to run on that port */ | |
162 | char **argv; /* pre-parsed argument array */ | |
163 | } se_cmd_t; | |
164 | ||
165 | typedef struct init_session { | |
166 | int se_index; /* index of entry in ttys file */ | |
167 | pid_t se_process; /* controlling process */ | |
168 | time_t se_started; /* used to avoid thrashing */ | |
169 | int se_flags; /* status of session */ | |
170 | char *se_device; /* filename of port */ | |
171 | se_cmd_t se_getty; /* what to run on that port */ | |
172 | se_cmd_t se_window; /* window system (started only once) */ | |
173 | se_cmd_t se_onerror; /* See SE_ONERROR above */ | |
174 | se_cmd_t se_onoption; /* See SE_ONOPTION above */ | |
175 | struct init_session *se_prev; | |
176 | struct init_session *se_next; | |
177 | } session_t; | |
178 | ||
179 | void free_session __P((session_t *)); | |
180 | session_t *new_session __P((session_t *, int, struct ttyent *)); | |
181 | session_t *sessions; | |
182 | ||
183 | char **construct_argv __P((char *)); | |
184 | void collect_child __P((pid_t)); | |
185 | pid_t start_getty __P((session_t *)); | |
186 | void transition_handler __P((int)); | |
187 | void alrm_handler __P((int)); | |
188 | void setsecuritylevel __P((int)); | |
189 | int getsecuritylevel __P((void)); | |
190 | int setupargv __P((session_t *, struct ttyent *)); | |
191 | int clang; | |
192 | ||
193 | void clear_session_logs __P((session_t *)); | |
194 | ||
195 | int start_session_db __P((void)); | |
196 | void add_session __P((session_t *)); | |
197 | void del_session __P((session_t *)); | |
198 | session_t *find_session __P((pid_t)); | |
199 | DB *session_db; | |
200 | ||
201 | /* | |
202 | * The mother of all processes. | |
203 | */ | |
204 | int | |
205 | main(argc, argv) | |
206 | int argc; | |
207 | char **argv; | |
208 | { | |
209 | int c; | |
210 | struct sigaction sa; | |
211 | sigset_t mask; | |
212 | ||
213 | ||
214 | /* Dispose of random users. */ | |
215 | if (getuid() != 0) { | |
216 | (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); | |
217 | exit (1); | |
218 | } | |
219 | ||
220 | /* System V users like to reexec init. */ | |
221 | if (getpid() != 1) { | |
222 | (void)fprintf(stderr, "init: already running\n"); | |
223 | exit (1); | |
224 | } | |
225 | ||
226 | /* | |
227 | * Note that this does NOT open a file... | |
228 | * Does 'init' deserve its own facility number? | |
229 | */ | |
230 | openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); | |
231 | ||
232 | /* | |
233 | * Create an initial session. | |
234 | */ | |
235 | if (setsid() < 0) | |
236 | warning("initial setsid() failed: %m"); | |
237 | ||
238 | /* | |
239 | * Establish an initial user so that programs running | |
240 | * single user do not freak out and die (like passwd). | |
241 | */ | |
242 | if (setlogin("root") < 0) | |
243 | warning("setlogin() failed: %m"); | |
244 | ||
245 | /* | |
246 | * This code assumes that we always get arguments through flags, | |
247 | * never through bits set in some random machine register. | |
248 | */ | |
249 | ||
250 | #ifdef DEBUG | |
251 | { | |
252 | int i; | |
253 | for (i = 0; i <= argc; i++) { | |
254 | if (argv[i]) | |
255 | warning("init argument %d: '%s'", i, argv[i]); | |
256 | else | |
257 | warning("init argument %d: ***NULL***", i); | |
258 | } | |
259 | } | |
260 | #endif | |
261 | ||
262 | while ((c = getopt(argc, argv, "sfbvx")) != -1) { | |
263 | #ifdef DEBUG | |
264 | warning("handling init argument '-%c'", c); | |
265 | #endif | |
266 | switch (c) { | |
267 | case 's': | |
268 | requested_transition = single_user; | |
269 | break; | |
270 | case 'f': | |
271 | runcom_mode = FASTBOOT; | |
272 | break; | |
273 | case 'b': | |
274 | runcom_boot = 0; // Don't runcom rc.boot | |
275 | break; | |
276 | case 'v': | |
277 | runcom_verbose = 1; | |
278 | break; | |
279 | case 'x': | |
280 | runcom_safe = 1; | |
281 | break; | |
282 | default: | |
283 | warning("unrecognized flag '-%c'", c); | |
284 | break; | |
285 | } | |
286 | } | |
287 | ||
288 | if (optind != argc) | |
289 | warning("ignoring excess arguments"); | |
290 | ||
291 | /* | |
292 | * We catch or block signals rather than ignore them, | |
293 | * so that they get reset on exec. | |
294 | */ | |
295 | handle(badsys, SIGSYS, 0); | |
296 | handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, | |
297 | SIGBUS, SIGXCPU, SIGXFSZ, 0); | |
298 | handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); | |
299 | handle(alrm_handler, SIGALRM, 0); | |
300 | sigfillset(&mask); | |
301 | delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, | |
302 | SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); | |
303 | sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); | |
304 | sigemptyset(&sa.sa_mask); | |
305 | sa.sa_flags = 0; | |
306 | sa.sa_handler = SIG_IGN; | |
307 | (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); | |
308 | (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); | |
309 | ||
310 | /* | |
311 | * Paranoia. | |
312 | */ | |
313 | close(0); | |
314 | close(1); | |
315 | close(2); | |
316 | ||
317 | if (runcom_boot) | |
318 | { | |
319 | int old_rc_mode = runcom_mode; | |
320 | ||
321 | runcom_mode = BOOT_SCRIPT; | |
322 | if (runcom() == (state_func_t) single_user) | |
323 | requested_transition = single_user; // Error in script | |
324 | runcom_mode = old_rc_mode; | |
325 | } | |
326 | ||
327 | /* | |
328 | * Start the state machine. | |
329 | */ | |
330 | transition(requested_transition); | |
331 | ||
332 | /* | |
333 | * Should never reach here. | |
334 | */ | |
335 | return 1; | |
336 | } | |
337 | ||
338 | /* | |
339 | * Associate a function with a signal handler. | |
340 | */ | |
341 | void | |
342 | handle(sig_t handler, ...) | |
343 | { | |
344 | int sig; | |
345 | struct sigaction sa; | |
346 | int mask_everything; | |
347 | va_list ap; | |
348 | va_start(ap, handler); | |
349 | ||
350 | sa.sa_handler = handler; | |
351 | sigfillset(&mask_everything); | |
352 | ||
353 | while (sig = va_arg(ap, int)) { | |
354 | sa.sa_mask = mask_everything; | |
355 | /* XXX SA_RESTART? */ | |
356 | sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; | |
357 | sigaction(sig, &sa, (struct sigaction *) 0); | |
358 | } | |
359 | va_end(ap); | |
360 | } | |
361 | ||
362 | /* | |
363 | * Delete a set of signals from a mask. | |
364 | */ | |
365 | void | |
366 | delset(sigset_t *maskp, ...) | |
367 | { | |
368 | int sig; | |
369 | va_list ap; | |
370 | va_start(ap, maskp); | |
371 | ||
372 | while (sig = va_arg(ap, int)) | |
373 | sigdelset(maskp, sig); | |
374 | va_end(ap); | |
375 | } | |
376 | ||
377 | /* | |
378 | * Log a message and sleep for a while (to give someone an opportunity | |
379 | * to read it and to save log or hardcopy output if the problem is chronic). | |
380 | * NB: should send a message to the session logger to avoid blocking. | |
381 | */ | |
382 | void | |
383 | stall(char *message, ...) | |
384 | { | |
385 | va_list ap; | |
386 | va_start(ap, message); | |
387 | ||
388 | vsyslog(LOG_ALERT, message, ap); | |
389 | va_end(ap); | |
390 | sleep(STALL_TIMEOUT); | |
391 | } | |
392 | ||
393 | /* | |
394 | * Like stall(), but doesn't sleep. | |
395 | * If cpp had variadic macros, the two functions could be #defines for another. | |
396 | * NB: should send a message to the session logger to avoid blocking. | |
397 | */ | |
398 | void | |
399 | warning(char *message, ...) | |
400 | { | |
401 | va_list ap; | |
402 | va_start(ap, message); | |
403 | ||
404 | vsyslog(LOG_ALERT, message, ap); | |
405 | va_end(ap); | |
406 | } | |
407 | ||
408 | /* | |
409 | * Log an emergency message. | |
410 | * NB: should send a message to the session logger to avoid blocking. | |
411 | */ | |
412 | void | |
413 | emergency(char *message, ...) | |
414 | { | |
415 | va_list ap; | |
416 | va_start(ap, message); | |
417 | ||
418 | vsyslog(LOG_EMERG, message, ap); | |
419 | va_end(ap); | |
420 | } | |
421 | ||
422 | /* | |
423 | * Catch a SIGSYS signal. | |
424 | * | |
425 | * These may arise if a system does not support sysctl. | |
426 | * We tolerate up to 25 of these, then throw in the towel. | |
427 | */ | |
428 | void | |
429 | badsys(sig) | |
430 | int sig; | |
431 | { | |
432 | static int badcount = 0; | |
433 | ||
434 | if (badcount++ < 25) | |
435 | return; | |
436 | disaster(sig); | |
437 | } | |
438 | ||
439 | /* | |
440 | * Catch an unexpected signal. | |
441 | */ | |
442 | void | |
443 | disaster(sig) | |
444 | int sig; | |
445 | { | |
446 | emergency("fatal signal: %s", | |
447 | sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); | |
448 | ||
449 | sleep(STALL_TIMEOUT); | |
450 | _exit(sig); /* reboot */ | |
451 | } | |
452 | ||
453 | /* | |
454 | * Get the security level of the kernel. | |
455 | */ | |
456 | int | |
457 | getsecuritylevel() | |
458 | { | |
459 | #ifdef KERN_SECURELVL | |
460 | int name[2], curlevel; | |
461 | size_t len; | |
462 | extern int errno; | |
463 | ||
464 | name[0] = CTL_KERN; | |
465 | name[1] = KERN_SECURELVL; | |
466 | len = sizeof curlevel; | |
467 | if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { | |
468 | emergency("cannot get kernel security level: %s", | |
469 | strerror(errno)); | |
470 | return (-1); | |
471 | } | |
472 | return (curlevel); | |
473 | #else | |
474 | return (-1); | |
475 | #endif | |
476 | } | |
477 | ||
478 | /* | |
479 | * Set the security level of the kernel. | |
480 | */ | |
481 | void | |
482 | setsecuritylevel(newlevel) | |
483 | int newlevel; | |
484 | { | |
485 | #ifdef KERN_SECURELVL | |
486 | int name[2], curlevel; | |
487 | extern int errno; | |
488 | ||
489 | curlevel = getsecuritylevel(); | |
490 | if (newlevel == curlevel) | |
491 | return; | |
492 | name[0] = CTL_KERN; | |
493 | name[1] = KERN_SECURELVL; | |
494 | if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { | |
495 | emergency( | |
496 | "cannot change kernel security level from %d to %d: %s", | |
497 | curlevel, newlevel, strerror(errno)); | |
498 | return; | |
499 | } | |
500 | #ifdef SECURE | |
501 | warning("kernel security level changed from %d to %d", | |
502 | curlevel, newlevel); | |
503 | #endif | |
504 | #endif | |
505 | } | |
506 | ||
507 | /* | |
508 | * Change states in the finite state machine. | |
509 | * The initial state is passed as an argument. | |
510 | */ | |
511 | void | |
512 | transition(s) | |
513 | state_t s; | |
514 | { | |
515 | for (;;) | |
516 | s = (state_t) (*s)(); | |
517 | } | |
518 | ||
519 | /* | |
520 | * Close out the accounting files for a login session. | |
521 | * NB: should send a message to the session logger to avoid blocking. | |
522 | */ | |
523 | void | |
524 | clear_session_logs(sp) | |
525 | session_t *sp; | |
526 | { | |
527 | char *line = sp->se_device + sizeof(_PATH_DEV) - 1; | |
528 | ||
529 | if (logout(line)) | |
530 | logwtmp(line, "", ""); | |
531 | } | |
532 | ||
533 | /* | |
534 | * Start a session and allocate a controlling terminal. | |
535 | * Only called by children of init after forking. | |
536 | */ | |
537 | void | |
538 | setctty(name, flags) | |
539 | char *name; | |
540 | int flags; | |
541 | { | |
542 | int fd; | |
543 | ||
544 | (void) revoke(name); | |
545 | sleep (2); /* leave DTR low */ | |
546 | if ((fd = open(name, flags | O_RDWR)) == -1) { | |
547 | stall("can't open %s: %m", name); | |
548 | _exit(1); | |
549 | } | |
550 | if (login_tty(fd) == -1) { | |
551 | stall("can't get %s for controlling terminal: %m", name); | |
552 | _exit(1); | |
553 | } | |
554 | } | |
555 | ||
556 | #if m68k | |
557 | /* | |
558 | * Taken from etc/halt/halt.c | |
559 | */ | |
560 | ||
561 | #include <stdio.h> | |
562 | #include <signal.h> | |
563 | #include <sgtty.h> | |
564 | ||
565 | static void shutend(void) | |
566 | { | |
567 | register i; | |
568 | ||
569 | acct(0); | |
570 | for (i = 0; i < 10; i++) | |
571 | close(i); | |
572 | ||
573 | logwtmp("~", "shutdown", ""); | |
574 | } | |
575 | ||
576 | static void do_halt(void) | |
577 | { | |
578 | char sbuf [40]; | |
579 | int halthowto = RB_HALT; | |
580 | ||
581 | (void) kill(-1, SIGTERM); /* one chance to catch it */ | |
582 | ||
583 | sprintf (sbuf, "Invalid hardware password, halting machine...\n"); | |
584 | write (1, sbuf, strlen (sbuf)); | |
585 | ||
586 | signal(SIGALRM, SIG_DFL); | |
587 | shutend(); | |
588 | sync(); | |
589 | ||
590 | signal(SIGALRM, alrm_handler); | |
591 | alarm(FAILED_HW_PASS); | |
592 | pause(); | |
593 | ||
594 | syscall(SYS_reboot, halthowto); | |
595 | } | |
596 | ||
597 | /* | |
598 | * Taken from lib/gen/getpass.c | |
599 | */ | |
600 | ||
601 | static char *gethwpasswd(char *prompt) | |
602 | { | |
603 | struct termios term; | |
604 | register char *p; | |
605 | register c; | |
606 | static char pbuf[9]; | |
607 | int echo; | |
608 | ||
609 | (void) tcgetattr(1, &term); | |
610 | if (echo = (term.c_lflag & ECHO)) | |
611 | { | |
612 | term.c_lflag &= ~ECHO; | |
613 | (void) tcsetattr(1, TCSAFLUSH|TCSASOFT, &term); | |
614 | } | |
615 | ||
616 | write(2, prompt, strlen(prompt)); | |
617 | ||
618 | for (p = pbuf; (c = getchar()) != '\n' && c != EOF; ) | |
619 | if (p < &pbuf[8]) | |
620 | *p++ = c; | |
621 | *p = '\0'; | |
622 | ||
623 | p = "\n"; | |
624 | write(2, p, strlen(p)); | |
625 | ||
626 | if (echo) | |
627 | { | |
628 | term.c_lflag |= ECHO; | |
629 | (void) tcsetattr(1, TCSAFLUSH|TCSASOFT, &term); | |
630 | } | |
631 | ||
632 | return(pbuf); | |
633 | } | |
634 | ||
635 | ||
636 | static char *hw_passwd (void) | |
637 | { | |
638 | char sbuf[40]; | |
639 | static char buffer [12]; | |
640 | struct nvram_info nvi; | |
641 | int vidfd, count; | |
642 | ||
643 | if ((vidfd = open ("/dev/vid0", O_RDONLY, 0)) == -1) | |
644 | return NULL; | |
645 | ||
646 | if (ioctl (vidfd, DKIOCGNVRAM, &nvi) == -1) | |
647 | return NULL; | |
648 | ||
649 | if (nvi.ni_hw_pwd != HW_PWD) | |
650 | return NULL; | |
651 | else | |
652 | { | |
653 | ||
654 | for (count = 0; count < NVRAM_HW_PASSWD; count++) | |
655 | nvi.ni_ep[count] ^= 'N'; | |
656 | strncpy(buffer, nvi.ni_ep, NVRAM_HW_PASSWD); | |
657 | /* ni_ep is not necessarily null terminated */ | |
658 | ||
659 | // gvdl I sure hope it is 'cause bad things will happen otherwise | |
660 | ||
661 | return buffer; | |
662 | } | |
663 | } | |
664 | ||
665 | ||
666 | #endif m68k | |
667 | ||
668 | ||
669 | static void | |
670 | do_security_check(void) | |
671 | { | |
672 | #if m68k | |
673 | char sbuf[128]; | |
674 | char *try, *passwd; | |
675 | int retries = 0; | |
676 | ||
677 | /* | |
678 | * If there is a hardware passwd, we want to | |
679 | * prompt the user for it. The write will be | |
680 | * to the console window because of the O_POPUP flag. | |
681 | */ | |
682 | passwd = hw_passwd(); | |
683 | write (1, "\n\n", 2); | |
684 | ||
685 | if (passwd != NULL) | |
686 | { | |
687 | do | |
688 | { | |
689 | try = gethwpasswd ("Enter hardware password:"); | |
690 | if (strncmp (try, passwd, NVRAM_HW_PASSWD) == 0) | |
691 | { | |
692 | execl(shell, minus, (char *)0); | |
693 | exit (0); | |
694 | } | |
695 | else | |
696 | { | |
697 | sprintf (sbuf, "Password incorrect.\n\n"); | |
698 | write (1, sbuf, strlen (sbuf)); | |
699 | } | |
700 | } | |
701 | while (++retries < 3); | |
702 | do_halt(); | |
703 | } | |
704 | #elif defined(SECURE) | |
705 | struct ttyent *typ; | |
706 | struct passwd *pp; | |
707 | static const char banner[] = | |
708 | "Enter root password, or ^D to go multi-user\n"; | |
709 | char *clear, *password; | |
710 | ||
711 | /* | |
712 | * Check the root password. | |
713 | * We don't care if the console is 'on' by default; | |
714 | * it's the only tty that can be 'off' and 'secure'. | |
715 | */ | |
716 | typ = getttynam("console"); | |
717 | pp = getpwnam("root"); | |
718 | if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) | |
719 | { | |
720 | write(2, banner, sizeof banner - 1); | |
721 | for (;;) | |
722 | { | |
723 | clear = getpass("Password:"); | |
724 | if (clear == 0 || *clear == '\0') | |
725 | _exit(0); | |
726 | password = crypt(clear, pp->pw_passwd); | |
727 | memset(clear, 0, _PASSWORD_LEN); | |
728 | if (strcmp(password, pp->pw_passwd) == 0) | |
729 | break; | |
730 | warning("single-user login failed\n"); | |
731 | } | |
732 | } | |
733 | endttyent(); | |
734 | endpwent(); | |
735 | #endif /* SECURE */ | |
736 | } | |
737 | ||
738 | /* | |
739 | * Bring the system up single user. | |
740 | */ | |
741 | state_func_t | |
742 | single_user() | |
743 | { | |
744 | pid_t pid, wpid; | |
745 | int status; | |
746 | sigset_t mask; | |
747 | char *shell = _PATH_BSHELL; | |
748 | char *argv[2]; | |
749 | /* | |
750 | * If the kernel is in secure mode, downgrade it to insecure mode. | |
751 | */ | |
752 | if (getsecuritylevel() > 0) | |
753 | setsecuritylevel(0); | |
754 | ||
755 | if ((pid = fork()) == 0) { | |
756 | /* | |
757 | * Start the single user session. | |
758 | */ | |
759 | setctty(_PATH_CONSOLE, O_POPUP); | |
760 | ||
761 | do_security_check(); | |
762 | ||
763 | #ifdef DEBUGSHELL | |
764 | { | |
765 | char altshell[128], *cp = altshell; | |
766 | int num; | |
767 | ||
768 | #define SHREQUEST \ | |
769 | "Enter pathname of shell or RETURN for sh: " | |
770 | (void)write(STDERR_FILENO, | |
771 | SHREQUEST, sizeof(SHREQUEST) - 1); | |
772 | while ((num = read(STDIN_FILENO, cp, 1)) != -1 && | |
773 | num != 0 && *cp != '\n' && cp < &altshell[127]) | |
774 | cp++; | |
775 | *cp = '\0'; | |
776 | if (altshell[0] != '\0') | |
777 | shell = altshell; | |
778 | } | |
779 | #endif /* DEBUGSHELL */ | |
780 | ||
781 | /* | |
782 | * Unblock signals. | |
783 | * We catch all the interesting ones, | |
784 | * and those are reset to SIG_DFL on exec. | |
785 | */ | |
786 | sigemptyset(&mask); | |
787 | sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); | |
788 | ||
789 | /* | |
790 | * Set up the PATH to be approriate for the root user. | |
791 | */ | |
792 | setenv("PATH", _PATH_STDPATH, 1); | |
793 | ||
794 | /* | |
795 | * We're dropping into the console; set TERM appropriately. | |
796 | */ | |
797 | setenv("TERM", "vt100", 1); | |
798 | ||
799 | /* | |
800 | * Fire off a shell. | |
801 | * If the default one doesn't work, try the Bourne shell. | |
802 | */ | |
803 | argv[0] = "-sh"; | |
804 | argv[1] = 0; | |
805 | execv(shell, argv); | |
806 | emergency("can't exec %s for single user: %m", shell); | |
807 | execv(_PATH_BSHELL, argv); | |
808 | emergency("can't exec %s for single user: %m", _PATH_BSHELL); | |
809 | sleep(STALL_TIMEOUT); | |
810 | _exit(1); | |
811 | } | |
812 | ||
813 | if (pid == -1) { | |
814 | /* | |
815 | * We are seriously hosed. Do our best. | |
816 | */ | |
817 | emergency("can't fork single-user shell, trying again"); | |
818 | while (waitpid(-1, (int *) 0, WNOHANG) > 0) | |
819 | continue; | |
820 | return (state_func_t) single_user; | |
821 | } | |
822 | ||
823 | requested_transition = 0; | |
824 | do { | |
825 | if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) | |
826 | collect_child(wpid); | |
827 | if (wpid == -1) { | |
828 | if (errno == EINTR) | |
829 | continue; | |
830 | warning("wait for single-user shell failed: %m; restarting"); | |
831 | return (state_func_t) single_user; | |
832 | } | |
833 | if (wpid == pid && WIFSTOPPED(status)) { | |
834 | warning("init: shell stopped, restarting\n"); | |
835 | kill(pid, SIGCONT); | |
836 | wpid = -1; | |
837 | } | |
838 | } while (wpid != pid && !requested_transition); | |
839 | ||
840 | if (requested_transition) | |
841 | return (state_func_t) requested_transition; | |
842 | ||
843 | if (!WIFEXITED(status)) { | |
844 | if (WTERMSIG(status) == SIGKILL) { | |
845 | /* | |
846 | * reboot(8) killed shell? | |
847 | */ | |
848 | warning("single user shell terminated."); | |
849 | sleep(STALL_TIMEOUT); | |
850 | _exit(0); | |
851 | } else { | |
852 | warning("single user shell terminated, restarting"); | |
853 | return (state_func_t) single_user; | |
854 | } | |
855 | } | |
856 | ||
857 | runcom_mode = FASTBOOT; | |
858 | return (state_func_t) runcom; | |
859 | } | |
860 | ||
861 | /* | |
862 | * Run the system startup script. | |
863 | */ | |
864 | state_func_t | |
865 | runcom() | |
866 | { | |
867 | pid_t pid, wpid; | |
868 | int status; | |
869 | char *argv[4]; | |
870 | char options[4]; | |
871 | struct sigaction sa; | |
872 | ||
873 | if ((pid = fork()) == 0) { | |
874 | sigemptyset(&sa.sa_mask); | |
875 | sa.sa_flags = 0; | |
876 | sa.sa_handler = SIG_IGN; | |
877 | (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); | |
878 | (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); | |
879 | ||
880 | setctty(_PATH_CONSOLE, 0); | |
881 | ||
882 | argv[0] = "sh"; | |
883 | ||
884 | if (runcom_mode == BOOT_SCRIPT) | |
885 | { | |
886 | argv[1] = _PATH_RUNCOM_BOOT; | |
887 | argv[2] = requested_transition == single_user | |
888 | ? "singleuser" : 0; | |
889 | } | |
890 | else /* runcom_mode != BOOT_SCRIPT */ | |
891 | { | |
892 | argv[1] = _PATH_RUNCOM; | |
893 | ||
894 | switch(runcom_mode) { | |
895 | case AUTOBOOT: | |
896 | argv[2] = "autoboot"; | |
897 | break; | |
898 | default: | |
899 | argv[2] = "multiuser"; | |
900 | break; | |
901 | } | |
902 | } | |
903 | ||
904 | if (runcom_verbose || runcom_safe) | |
905 | { | |
906 | int i = 0; | |
907 | ||
908 | options[i++] = '-'; | |
909 | if (runcom_verbose) options[i++] = 'v'; | |
910 | if (runcom_safe ) options[i++] = 'x'; | |
911 | options[i] = '\0'; | |
912 | ||
913 | argv[3] = options; | |
914 | } | |
915 | else | |
916 | { | |
917 | argv[3] = 0; | |
918 | } | |
919 | ||
920 | argv[4] = 0; | |
921 | ||
922 | #ifdef DEBUG | |
923 | { | |
924 | int i; | |
925 | for (i = 0; i <= 4; i++) { | |
926 | if (argv[i]) | |
927 | warning("%s argument: %s", _PATH_RUNCOM, argv[i]); | |
928 | } | |
929 | } | |
930 | #endif | |
931 | ||
932 | sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); | |
933 | ||
934 | execv(_PATH_BSHELL, argv); | |
935 | stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); | |
936 | _exit(1); /* force single user mode */ | |
937 | } | |
938 | ||
939 | if (pid == -1) { | |
940 | emergency("can't fork for %s on %s: %m", | |
941 | _PATH_BSHELL, _PATH_RUNCOM); | |
942 | while (waitpid(-1, (int *) 0, WNOHANG) > 0) | |
943 | continue; | |
944 | sleep(STALL_TIMEOUT); | |
945 | return (state_func_t) single_user; | |
946 | } | |
947 | ||
948 | /* | |
949 | * Copied from single_user(). This is a bit paranoid. | |
950 | */ | |
951 | do { | |
952 | if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) | |
953 | collect_child(wpid); | |
954 | if (wpid == -1) { | |
955 | if (errno == EINTR) | |
956 | continue; | |
957 | warning("wait for %s on %s failed: %m; going to single user mode", | |
958 | _PATH_BSHELL, _PATH_RUNCOM); | |
959 | return (state_func_t) single_user; | |
960 | } | |
961 | if (wpid == pid && WIFSTOPPED(status)) { | |
962 | warning("init: %s on %s stopped, restarting\n", | |
963 | _PATH_BSHELL, _PATH_RUNCOM); | |
964 | kill(pid, SIGCONT); | |
965 | wpid = -1; | |
966 | } | |
967 | } while (wpid != pid); | |
968 | ||
969 | if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && | |
970 | requested_transition == catatonia) { | |
971 | /* /etc/rc executed /sbin/reboot; wait for the end quietly */ | |
972 | sigset_t s; | |
973 | ||
974 | sigfillset(&s); | |
975 | for (;;) | |
976 | sigsuspend(&s); | |
977 | } | |
978 | ||
979 | if (!WIFEXITED(status)) { | |
980 | warning("%s on %s terminated abnormally, going to single user mode", | |
981 | _PATH_BSHELL, _PATH_RUNCOM); | |
982 | return (state_func_t) single_user; | |
983 | } | |
984 | ||
985 | if (WEXITSTATUS(status)) | |
986 | return (state_func_t) single_user; | |
987 | ||
988 | runcom_mode = AUTOBOOT; /* the default */ | |
989 | /* NB: should send a message to the session logger to avoid blocking. */ | |
990 | logwtmp("~", "reboot", ""); | |
991 | return (state_func_t) read_ttys; | |
992 | } | |
993 | ||
994 | /* | |
995 | * Open the session database. | |
996 | * | |
997 | * NB: We could pass in the size here; is it necessary? | |
998 | */ | |
999 | int | |
1000 | start_session_db() | |
1001 | { | |
1002 | if (session_db && (*session_db->close)(session_db)) | |
1003 | emergency("session database close: %s", strerror(errno)); | |
1004 | if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { | |
1005 | emergency("session database open: %s", strerror(errno)); | |
1006 | return (1); | |
1007 | } | |
1008 | return (0); | |
1009 | ||
1010 | } | |
1011 | ||
1012 | /* | |
1013 | * Add a new login session. | |
1014 | */ | |
1015 | void | |
1016 | add_session(sp) | |
1017 | session_t *sp; | |
1018 | { | |
1019 | DBT key; | |
1020 | DBT data; | |
1021 | ||
1022 | key.data = &sp->se_process; | |
1023 | key.size = sizeof sp->se_process; | |
1024 | data.data = &sp; | |
1025 | data.size = sizeof sp; | |
1026 | ||
1027 | if ((*session_db->put)(session_db, &key, &data, 0)) | |
1028 | emergency("insert %d: %s", sp->se_process, strerror(errno)); | |
1029 | } | |
1030 | ||
1031 | /* | |
1032 | * Delete an old login session. | |
1033 | */ | |
1034 | void | |
1035 | del_session(sp) | |
1036 | session_t *sp; | |
1037 | { | |
1038 | DBT key; | |
1039 | ||
1040 | key.data = &sp->se_process; | |
1041 | key.size = sizeof sp->se_process; | |
1042 | ||
1043 | if ((*session_db->del)(session_db, &key, 0)) | |
1044 | emergency("delete %d: %s", sp->se_process, strerror(errno)); | |
1045 | } | |
1046 | ||
1047 | /* | |
1048 | * Look up a login session by pid. | |
1049 | */ | |
1050 | session_t * | |
1051 | find_session(pid_t pid) | |
1052 | { | |
1053 | DBT key; | |
1054 | DBT data; | |
1055 | session_t *ret; | |
1056 | ||
1057 | key.data = &pid; | |
1058 | key.size = sizeof pid; | |
1059 | if ((*session_db->get)(session_db, &key, &data, 0) != 0) | |
1060 | return 0; | |
1061 | memmove(&ret, data.data, sizeof(ret)); | |
1062 | return ret; | |
1063 | } | |
1064 | ||
1065 | /* | |
1066 | * Construct an argument vector from a command line. | |
1067 | */ | |
1068 | char ** | |
1069 | construct_argv(command) | |
1070 | char *command; | |
1071 | { | |
1072 | register int argc = 0; | |
1073 | register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) | |
1074 | * sizeof (char *)); | |
1075 | static const char separators[] = " \t"; | |
1076 | ||
1077 | if ((argv[argc++] = strtok(command, separators)) == 0) | |
1078 | return 0; | |
1079 | while (argv[argc++] = strtok((char *) 0, separators)) | |
1080 | continue; | |
1081 | return argv; | |
1082 | } | |
1083 | ||
1084 | /* | |
1085 | * Deallocate a session descriptor. | |
1086 | */ | |
1087 | ||
1088 | static __inline__ void free_command(se_cmd_t *se_cmd) | |
1089 | { | |
1090 | if (se_cmd->path) | |
1091 | { | |
1092 | free(se_cmd->path); | |
1093 | free(se_cmd->argv); | |
1094 | } | |
1095 | } | |
1096 | ||
1097 | void | |
1098 | free_session(sp) | |
1099 | register session_t *sp; | |
1100 | { | |
1101 | free(sp->se_device); | |
1102 | free_command(&sp->se_getty); | |
1103 | free_command(&sp->se_window); | |
1104 | free_command(&sp->se_onerror); | |
1105 | free_command(&sp->se_onoption); | |
1106 | memset(sp, '\0', sizeof(*sp)); // a bit of defensive programming | |
1107 | ||
1108 | free(sp); | |
1109 | } | |
1110 | ||
1111 | static int setup_command(se_cmd_t *se_cmd, char *command, char *arg ) | |
1112 | { | |
1113 | ||
1114 | char *commandWithArg; | |
1115 | ||
1116 | commandWithArg = malloc( strlen( command) + strlen( arg) + 2); | |
1117 | (void) sprintf(commandWithArg, "%s %s", command, arg); | |
1118 | ||
1119 | free_command(se_cmd); | |
1120 | ||
1121 | se_cmd->path = commandWithArg; | |
1122 | se_cmd->argv = construct_argv(commandWithArg); | |
1123 | if (se_cmd->argv == NULL) | |
1124 | { | |
1125 | free(se_cmd->path); | |
1126 | se_cmd->path = NULL; | |
1127 | return 0; | |
1128 | } | |
1129 | return 1; | |
1130 | } | |
1131 | ||
1132 | /* | |
1133 | * Calculate getty and if useful window argv vectors. | |
1134 | */ | |
1135 | int | |
1136 | setupargv(sp, typ) | |
1137 | session_t *sp; | |
1138 | struct ttyent *typ; | |
1139 | { | |
1140 | char *type; | |
1141 | ||
1142 | if ( !setup_command(&sp->se_getty, typ->ty_getty, typ->ty_name) ) | |
1143 | { | |
1144 | type = "getty"; | |
1145 | goto bad_args; | |
1146 | } | |
1147 | ||
1148 | if (typ->ty_window | |
1149 | && !setup_command(&sp->se_window, typ->ty_window, typ->ty_name) ) | |
1150 | { | |
1151 | type = "window"; | |
1152 | goto bad_args; | |
1153 | } | |
1154 | ||
1155 | if (typ->ty_onerror | |
1156 | && !setup_command(&sp->se_onerror, typ->ty_onerror, typ->ty_name) ) | |
1157 | { | |
1158 | type = "onerror"; | |
1159 | goto bad_args; | |
1160 | } | |
1161 | ||
1162 | if (typ->ty_onoption | |
1163 | && !setup_command(&sp->se_onoption, typ->ty_onoption, typ->ty_name) ) | |
1164 | { | |
1165 | type = "onoption"; | |
1166 | goto bad_args; | |
1167 | } | |
1168 | ||
1169 | return 1; | |
1170 | ||
1171 | bad_args: | |
1172 | warning("can't parse %s for port %s", type, sp->se_device); | |
1173 | return 0; | |
1174 | } | |
1175 | ||
1176 | ||
1177 | /* | |
1178 | * Allocate a new session descriptor. | |
1179 | */ | |
1180 | session_t * | |
1181 | new_session(sprev, session_index, typ) | |
1182 | session_t *sprev; | |
1183 | int session_index; | |
1184 | register struct ttyent *typ; | |
1185 | { | |
1186 | register session_t *sp; | |
1187 | ||
1188 | if ((typ->ty_status & TTY_ON) == 0 || | |
1189 | typ->ty_name == 0 || | |
1190 | typ->ty_getty == 0) | |
1191 | return 0; | |
1192 | ||
1193 | sp = (session_t *) malloc(sizeof (session_t)); | |
1194 | memset(sp, 0, sizeof *sp); | |
1195 | ||
1196 | sp->se_index = session_index; | |
1197 | ||
1198 | sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); | |
1199 | (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); | |
1200 | ||
1201 | if (setupargv(sp, typ) == 0) { | |
1202 | free_session(sp); | |
1203 | return (0); | |
1204 | } | |
1205 | ||
1206 | sp->se_next = 0; | |
1207 | if (sprev == 0) { | |
1208 | sessions = sp; | |
1209 | sp->se_prev = 0; | |
1210 | } else { | |
1211 | sprev->se_next = sp; | |
1212 | sp->se_prev = sprev; | |
1213 | } | |
1214 | ||
1215 | return sp; | |
1216 | } | |
1217 | ||
1218 | /* | |
1219 | * Walk the list of ttys and create sessions for each active line. | |
1220 | */ | |
1221 | state_func_t | |
1222 | read_ttys() | |
1223 | { | |
1224 | int session_index = 0; | |
1225 | register session_t *sp, *snext; | |
1226 | register struct ttyent *typ; | |
1227 | ||
1228 | /* | |
1229 | * Destroy any previous session state. | |
1230 | * There shouldn't be any, but just in case... | |
1231 | */ | |
1232 | for (sp = sessions; sp; sp = snext) { | |
1233 | if (sp->se_process) | |
1234 | clear_session_logs(sp); | |
1235 | snext = sp->se_next; | |
1236 | free_session(sp); | |
1237 | } | |
1238 | sessions = 0; | |
1239 | if (start_session_db()) | |
1240 | return (state_func_t) single_user; | |
1241 | ||
1242 | /* | |
1243 | * Allocate a session entry for each active port. | |
1244 | * Note that sp starts at 0. | |
1245 | */ | |
1246 | while (typ = getttyent()) | |
1247 | if (snext = new_session(sp, ++session_index, typ)) | |
1248 | sp = snext; | |
1249 | ||
1250 | endttyent(); | |
1251 | ||
1252 | return (state_func_t) multi_user; | |
1253 | } | |
1254 | ||
1255 | /* | |
1256 | * Start a window system running. | |
1257 | */ | |
1258 | static pid_t | |
1259 | start_window_system(session_t *sp) | |
1260 | { | |
1261 | pid_t pid; | |
1262 | sigset_t mask; | |
1263 | ||
1264 | if ((pid = fork()) == -1) { | |
1265 | emergency("can't fork for window system on port %s: %m", | |
1266 | sp->se_device); | |
1267 | /* hope that getty fails and we can try again */ | |
1268 | return -1; | |
1269 | } | |
1270 | ||
1271 | if (pid) | |
1272 | return pid; | |
1273 | ||
1274 | sigemptyset(&mask); | |
1275 | sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); | |
1276 | ||
1277 | if (setsid() < 0) | |
1278 | emergency("setsid failed (window) %m"); | |
1279 | ||
1280 | execv(sp->se_window.argv[0], sp->se_window.argv); | |
1281 | stall("can't exec window system '%s' for port %s: %m", | |
1282 | sp->se_window.argv[0], sp->se_device); | |
1283 | _exit(1); | |
1284 | } | |
1285 | ||
1286 | /* | |
1287 | * Start a login session running. | |
1288 | */ | |
1289 | pid_t | |
1290 | start_getty(sp) | |
1291 | session_t *sp; | |
1292 | { | |
1293 | pid_t pid; | |
1294 | sigset_t mask; | |
1295 | se_cmd_t *se_cmd; | |
1296 | const char *session_type = NULL; | |
1297 | time_t current_time = time((time_t *) 0); | |
1298 | ||
1299 | // Setup the default values; | |
1300 | switch (sp->se_flags & SE_GETTY_LAUNCH) | |
1301 | { | |
1302 | case SE_ONOPTION: | |
1303 | if (sp->se_onoption.path) | |
1304 | { | |
1305 | se_cmd = &sp->se_onoption; | |
1306 | session_type = "onoption"; | |
1307 | break; | |
1308 | } | |
1309 | /* No break */ | |
1310 | ||
1311 | case SE_ONERROR: | |
1312 | if (sp->se_onerror.path) | |
1313 | { | |
1314 | se_cmd = &sp->se_onerror; | |
1315 | session_type = "onerror"; | |
1316 | break; | |
1317 | } | |
1318 | /* No break */ | |
1319 | ||
1320 | case SE_COMMON: | |
1321 | default: | |
1322 | se_cmd = &sp->se_getty; | |
1323 | session_type = "getty"; | |
1324 | break; | |
1325 | } | |
1326 | ||
1327 | if (sp->se_window.path | |
1328 | && ((sp->se_flags & SE_GETTY_LAUNCH) != SE_ONOPTION)) | |
1329 | { | |
1330 | if (start_window_system(sp) == -1) | |
1331 | return -1; | |
1332 | } | |
1333 | ||
1334 | /* | |
1335 | * fork(), not vfork() -- we can't afford to block. | |
1336 | */ | |
1337 | if ((pid = fork()) == -1) { | |
1338 | emergency("can't fork for %s on port %s: %m", | |
1339 | session_type, sp->se_device); | |
1340 | return -1; | |
1341 | } | |
1342 | ||
1343 | if (pid) | |
1344 | return pid; | |
1345 | ||
1346 | if (current_time > sp->se_started && | |
1347 | current_time - sp->se_started < GETTY_SPACING) { | |
1348 | warning("%s repeating too quickly on port %s, sleeping", | |
1349 | session_type, sp->se_device); | |
1350 | sleep((unsigned) GETTY_SLEEP); | |
1351 | } | |
1352 | ||
1353 | sigemptyset(&mask); | |
1354 | sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); | |
1355 | ||
1356 | execv(se_cmd->argv[0], se_cmd->argv); | |
1357 | stall("can't exec %s '%s' for port %s: %m", session_type, | |
1358 | se_cmd->argv[0], sp->se_device); | |
1359 | _exit(1); | |
1360 | } | |
1361 | ||
1362 | /* | |
1363 | * Collect exit status for a child. | |
1364 | * If an exiting login, start a new login running. | |
1365 | */ | |
1366 | void | |
1367 | collect_child(pid_t pid) | |
1368 | { | |
1369 | register session_t *sp, *sprev, *snext; | |
1370 | ||
1371 | if ( !sessions) | |
1372 | return; | |
1373 | ||
1374 | if ( !(sp = find_session(pid)) ) | |
1375 | return; | |
1376 | ||
1377 | clear_session_logs(sp); | |
1378 | del_session(sp); | |
1379 | sp->se_process = 0; | |
1380 | ||
1381 | if (sp->se_flags & SE_SHUTDOWN) { | |
1382 | if (sprev = sp->se_prev) | |
1383 | sprev->se_next = sp->se_next; | |
1384 | else | |
1385 | sessions = sp->se_next; | |
1386 | if (snext = sp->se_next) | |
1387 | snext->se_prev = sp->se_prev; | |
1388 | free_session(sp); | |
1389 | return; | |
1390 | } | |
1391 | ||
1392 | if ((pid = start_getty(sp)) == -1) { | |
1393 | /* serious trouble */ | |
1394 | requested_transition = clean_ttys; | |
1395 | return; | |
1396 | } | |
1397 | ||
1398 | sp->se_process = pid; | |
1399 | sp->se_started = time((time_t *) 0); | |
1400 | sp->se_flags &= ~SE_GETTY_LAUNCH; // clear down getty launch type | |
1401 | add_session(sp); | |
1402 | } | |
1403 | ||
1404 | /* | |
1405 | * Catch a signal and request a state transition. | |
1406 | */ | |
1407 | void | |
1408 | transition_handler(sig) | |
1409 | int sig; | |
1410 | { | |
1411 | ||
1412 | switch (sig) { | |
1413 | case SIGHUP: | |
1414 | requested_transition = clean_ttys; | |
1415 | break; | |
1416 | case SIGTERM: | |
1417 | requested_transition = death; | |
1418 | break; | |
1419 | case SIGTSTP: | |
1420 | requested_transition = catatonia; | |
1421 | break; | |
1422 | default: | |
1423 | requested_transition = 0; | |
1424 | break; | |
1425 | } | |
1426 | } | |
1427 | ||
1428 | /* | |
1429 | * Take the system multiuser. | |
1430 | */ | |
1431 | state_func_t | |
1432 | multi_user() | |
1433 | { | |
1434 | pid_t pid; | |
1435 | register session_t *sp; | |
1436 | ||
1437 | requested_transition = 0; | |
1438 | ||
1439 | /* | |
1440 | * If the administrator has not set the security level to -1 | |
1441 | * to indicate that the kernel should not run multiuser in secure | |
1442 | * mode, and the run script has not set a higher level of security | |
1443 | * than level 1, then put the kernel into secure mode. | |
1444 | */ | |
1445 | if (getsecuritylevel() == 0) | |
1446 | setsecuritylevel(1); | |
1447 | ||
1448 | for (sp = sessions; sp; sp = sp->se_next) { | |
1449 | if (sp->se_process) | |
1450 | continue; | |
1451 | if ((pid = start_getty(sp)) == -1) { | |
1452 | /* serious trouble */ | |
1453 | requested_transition = clean_ttys; | |
1454 | break; | |
1455 | } | |
1456 | sp->se_process = pid; | |
1457 | sp->se_started = time((time_t *) 0); | |
1458 | add_session(sp); | |
1459 | } | |
1460 | ||
1461 | while (!requested_transition) | |
1462 | { | |
1463 | int status; | |
1464 | session_t *sp; | |
1465 | ||
1466 | pid = waitpid(-1, &status, 0); | |
1467 | if (!sessions || !(sp = find_session(pid))) | |
1468 | continue; | |
1469 | ||
1470 | if (WIFSIGNALED(status)) | |
1471 | sp->se_flags |= SE_ONERROR; | |
1472 | else if (WEXITSTATUS(status) == REALLY_EXIT_TO_CONSOLE) | |
1473 | { /* WIFEXITED(status) assumed */ | |
1474 | sp->se_flags |= SE_ONOPTION; | |
1475 | } | |
1476 | else | |
1477 | sp->se_flags |= SE_ONERROR; | |
1478 | ||
1479 | if (pid != -1) | |
1480 | collect_child(pid); | |
1481 | } | |
1482 | ||
1483 | return (state_func_t) requested_transition; | |
1484 | } | |
1485 | ||
1486 | /* | |
1487 | * This is an n-squared algorithm. We hope it isn't run often... | |
1488 | */ | |
1489 | state_func_t | |
1490 | clean_ttys() | |
1491 | { | |
1492 | register session_t *sp, *sprev; | |
1493 | register struct ttyent *typ; | |
1494 | register int session_index = 0; | |
1495 | register int devlen; | |
1496 | ||
1497 | if (! sessions) | |
1498 | return (state_func_t) multi_user; | |
1499 | ||
1500 | devlen = sizeof(_PATH_DEV) - 1; | |
1501 | while (typ = getttyent()) { | |
1502 | ++session_index; | |
1503 | ||
1504 | for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) | |
1505 | if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) | |
1506 | break; | |
1507 | ||
1508 | if (sp) { | |
1509 | if (sp->se_index != session_index) { | |
1510 | warning("port %s changed utmp index from %d to %d", | |
1511 | sp->se_device, sp->se_index, | |
1512 | session_index); | |
1513 | sp->se_index = session_index; | |
1514 | } | |
1515 | if ((typ->ty_status & TTY_ON) == 0 || | |
1516 | typ->ty_getty == 0) { | |
1517 | sp->se_flags |= SE_SHUTDOWN; | |
1518 | kill(sp->se_process, SIGHUP); | |
1519 | continue; | |
1520 | } | |
1521 | sp->se_flags &= ~SE_SHUTDOWN; | |
1522 | if (setupargv(sp, typ) == 0) { | |
1523 | warning("can't parse getty for port %s", | |
1524 | sp->se_device); | |
1525 | sp->se_flags |= SE_SHUTDOWN; | |
1526 | kill(sp->se_process, SIGHUP); | |
1527 | } | |
1528 | continue; | |
1529 | } | |
1530 | ||
1531 | new_session(sprev, session_index, typ); | |
1532 | } | |
1533 | ||
1534 | endttyent(); | |
1535 | ||
1536 | return (state_func_t) multi_user; | |
1537 | } | |
1538 | ||
1539 | /* | |
1540 | * Block further logins. | |
1541 | */ | |
1542 | state_func_t | |
1543 | catatonia() | |
1544 | { | |
1545 | register session_t *sp; | |
1546 | ||
1547 | for (sp = sessions; sp; sp = sp->se_next) | |
1548 | sp->se_flags |= SE_SHUTDOWN; | |
1549 | ||
1550 | return (state_func_t) multi_user; | |
1551 | } | |
1552 | ||
1553 | /* | |
1554 | * Note SIGALRM. | |
1555 | */ | |
1556 | void | |
1557 | alrm_handler(sig) | |
1558 | int sig; | |
1559 | { | |
1560 | clang = 1; | |
1561 | } | |
1562 | ||
1563 | /* | |
1564 | * Bring the system down to single user. | |
1565 | */ | |
1566 | state_func_t | |
1567 | death() | |
1568 | { | |
1569 | register session_t *sp; | |
1570 | register int i; | |
1571 | pid_t pid; | |
1572 | static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; | |
1573 | ||
1574 | for (sp = sessions; sp; sp = sp->se_next) | |
1575 | sp->se_flags |= SE_SHUTDOWN; | |
1576 | ||
1577 | /* NB: should send a message to the session logger to avoid blocking. */ | |
1578 | logwtmp("~", "shutdown", ""); | |
1579 | ||
1580 | for (i = 0; i < 3; ++i) { | |
1581 | if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) | |
1582 | return (state_func_t) single_user; | |
1583 | ||
1584 | clang = 0; | |
1585 | alarm(DEATH_WATCH); | |
1586 | do | |
1587 | if ((pid = waitpid(-1, (int *)0, 0)) != -1) | |
1588 | collect_child(pid); | |
1589 | while (clang == 0 && errno != ECHILD); | |
1590 | ||
1591 | if (errno == ECHILD) | |
1592 | return (state_func_t) single_user; | |
1593 | } | |
1594 | ||
1595 | warning("some processes would not die; ps axl advised"); | |
1596 | ||
1597 | return (state_func_t) single_user; | |
1598 | } |