blob: ca194dd352417316e6dddf9fcf47314f455b0ea3 [file] [log] [blame]
Eric Andersencc8ed391999-10-05 16:24:54 +00001#include "internal.h"
2#include <stdio.h>
3#include <stdlib.h>
4#include <stdarg.h>
5#include <unistd.h>
6#include <errno.h>
7#include <signal.h>
8#include <termios.h>
9#include <sys/types.h>
10#include <sys/fcntl.h>
11#include <sys/wait.h>
12#include <string.h>
13#include <sys/mount.h>
14#include <sys/reboot.h>
15#include <sys/kdaemon.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000016#include <sys/sysmacros.h>
Eric Andersenabc7d591999-10-19 00:27:50 +000017#include <linux/serial.h> /* for serial_struct */
18#include <sys/vt.h> /* for vt_stat */
19#include <sys/ioctl.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000020
Eric Andersenabc7d591999-10-19 00:27:50 +000021static const char init_usage[] = "Used internally by the system.";
22static char console[16] = "";
23static const char* default_console = "/dev/tty2";
24static char* first_terminal = NULL;
25static const char* second_terminal = "/dev/tty2";
26static const char* log = "/dev/tty3";
27static char* term_ptr = NULL;
Eric Andersencc8ed391999-10-05 16:24:54 +000028
29static void
Eric Andersenabc7d591999-10-19 00:27:50 +000030message(const char* terminal, const char * pattern, ...)
Eric Andersencc8ed391999-10-05 16:24:54 +000031{
32 int fd;
33 FILE * con = 0;
34 va_list arguments;
35
36 /*
37 * Open the console device each time a message is printed. If the user
38 * has switched consoles, the open will get the new console. If we kept
39 * the console open, we'd always print to the same one.
40 */
Eric Andersenabc7d591999-10-19 00:27:50 +000041 if ( !terminal
42 || ((fd = open(terminal, O_WRONLY|O_NOCTTY)) < 0)
Eric Andersencc8ed391999-10-05 16:24:54 +000043 || ((con = fdopen(fd, "w")) == NULL) )
44 return;
45
46 va_start(arguments, pattern);
47 vfprintf(con, pattern, arguments);
48 va_end(arguments);
49 fclose(con);
50}
51
52static int
53waitfor(int pid)
54{
55 int status;
56 int wpid;
57
58 message(log, "Waiting for process %d.\n", pid);
59 while ( (wpid = wait(&status)) != pid ) {
60 if ( wpid > 0 ) {
61 message(
62 log
63 ,"pid %d exited, status=%x.\n"
64 ,wpid
65 ,status);
66 }
67 }
68 return wpid;
69}
70
71static int
Eric Andersen3c163821999-10-14 22:16:57 +000072run(const char* program, const char* const* arguments,
73 const char* terminal, int get_enter)
Eric Andersencc8ed391999-10-05 16:24:54 +000074{
75 static const char control_characters[] = {
76 '\003',
77 '\034',
78 '\177',
79 '\025',
80 '\004',
81 '\0',
82 '\1',
83 '\0',
84 '\021',
85 '\023',
86 '\032',
87 '\0',
88 '\022',
89 '\017',
90 '\027',
91 '\026',
92 '\0'
93 };
94
95 static char * environment[] = {
96 "HOME=/",
97 "PATH=/bin:/sbin:/usr/bin:/usr/sbin",
98 "SHELL=/bin/sh",
99 0,
100 "USER=root",
101 0
102 };
103
104 static const char press_enter[] =
Eric Andersencb6e2561999-10-16 15:48:40 +0000105 "\nPlease press Enter to activate this console. ";
Eric Andersencc8ed391999-10-05 16:24:54 +0000106
107 int pid;
108
109 environment[3]=term_ptr;
110
111 pid = fork();
112 if ( pid == 0 ) {
113 struct termios t;
114 const char * const * arg;
115
116 close(0);
117 close(1);
118 close(2);
119 setsid();
120
121 open(terminal, O_RDWR);
122 dup(0);
123 dup(0);
124 tcsetpgrp(0, getpgrp());
125
126 tcgetattr(0, &t);
127 memcpy(t.c_cc, control_characters, sizeof(control_characters));
128 t.c_line = 0;
129 t.c_iflag = ICRNL|IXON|IXOFF;
130 t.c_oflag = OPOST|ONLCR;
131 t.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN;
132 tcsetattr(0, TCSANOW, &t);
133
134 if ( get_enter ) {
135 /*
136 * Save memory by not exec-ing anything large (like a shell)
137 * before the user wants it. This is critical if swap is not
138 * enabled and the system has low memory. Generally this will
139 * be run on the second virtual console, and the first will
Eric Andersen3c163821999-10-14 22:16:57 +0000140 * be allowed to start a shell or whatever an init script
141 * specifies.
Eric Andersencc8ed391999-10-05 16:24:54 +0000142 */
143 char c;
144 write(1, press_enter, sizeof(press_enter) - 1);
145 read(0, &c, 1);
146 }
147
148 message(log, "Executing ");
149 arg = arguments;
150 while ( *arg != 0 )
151 message(log, "%s ", *arg++);
152 message(log, "\n");
153
154 execve(program, (char * *)arguments, (char * *)environment);
155 message(log, "%s: could not execute: %s.\r\n", program, strerror(errno));
156 exit(-1);
157 }
158 return pid;
159}
160
161static int
162mem_total()
163{
Eric Andersenabc7d591999-10-19 00:27:50 +0000164 char s[80];
165 char *p;
166 FILE *f;
167 const char pattern[]="MemTotal:";
Eric Andersencc8ed391999-10-05 16:24:54 +0000168
Eric Andersenabc7d591999-10-19 00:27:50 +0000169 f=fopen("/proc/meminfo","r");
170 while (NULL != fgets(s,79,f)) {
171 p=strstr(s, pattern);
172 if (NULL != p) {
173 fclose(f);
174 return(atoi(p+strlen(pattern)));
175 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000176 }
Eric Andersenabc7d591999-10-19 00:27:50 +0000177 return -1;
Eric Andersencc8ed391999-10-05 16:24:54 +0000178}
179
180static void
181set_free_pages()
182{
Eric Andersenabc7d591999-10-19 00:27:50 +0000183 char s[80];
184 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000185
Eric Andersenabc7d591999-10-19 00:27:50 +0000186 f=fopen("/proc/sys/vm/freepages","r");
187 fgets(s,79,f);
188 if (atoi(s) < 32) {
189 fclose(f);
190 f=fopen("/proc/sys/vm/freepages","w");
191 fprintf(f,"30\t40\t50\n");
192 printf("\nIncreased /proc/sys/vm/freepages values to 30/40/50\n");
193 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000194 fclose(f);
Eric Andersencc8ed391999-10-05 16:24:54 +0000195}
196
Eric Andersenabc7d591999-10-19 00:27:50 +0000197static int
198get_kernel_revision()
199{
200 FILE *f;
201 int major=0, minor=0, patch=0;
202
203 f = fopen("/proc/sys/kernel/osrelease","r");
204 fscanf(f,"%d.%d.%d",&major,&minor,&patch);
205 fclose(f);
206 return major*65536 + minor*256 + patch;
207}
208
209
Eric Andersencc8ed391999-10-05 16:24:54 +0000210static void
Eric Andersenabc7d591999-10-19 00:27:50 +0000211shutdown_system(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000212{
213 static const char * const umount_args[] = {"/bin/umount", "-a", "-n", 0};
Eric Andersenabc7d591999-10-19 00:27:50 +0000214 static const char * const swapoff_args[] = {"/bin/swapoff", "-a", 0};
Eric Andersencc8ed391999-10-05 16:24:54 +0000215
Eric Andersenabc7d591999-10-19 00:27:50 +0000216 message(console, "The system is going down NOW !!");
Eric Andersencc8ed391999-10-05 16:24:54 +0000217 sync();
218 /* Allow Ctrl-Alt-Del to reboot system. */
219 reboot(RB_ENABLE_CAD);
220
221 /* Send signals to every process _except_ pid 1 */
222 message(console, "Sending SIGHUP to all processes.\r\n");
223 kill(-1, SIGHUP);
224 sleep(2);
225 sync();
226 message(console, "Sending SIGKILL to all processes.\r\n");
227 kill(-1, SIGKILL);
228 sleep(1);
Eric Andersenabc7d591999-10-19 00:27:50 +0000229 waitfor(run("/bin/swapoff", swapoff_args, console, 0));
Eric Andersencc8ed391999-10-05 16:24:54 +0000230 waitfor(run("/bin/umount", umount_args, console, 0));
231 sync();
Eric Andersenabc7d591999-10-19 00:27:50 +0000232 if (get_kernel_revision() <= 2*65536+2*256+11) {
233 /* Removed bdflush call, kupdate in kernels >2.2.11 */
234 bdflush(1, 0);
235 sync();
236 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000237}
238
239static void
240halt_signal(int sig)
241{
Eric Andersenabc7d591999-10-19 00:27:50 +0000242 shutdown_system();
243 message(console, "The system is halted. Press CTRL-ALT-DEL or turn off power\r\n");
244 reboot( RB_HALT_SYSTEM);
245 exit(0);
Eric Andersencc8ed391999-10-05 16:24:54 +0000246}
247
248static void
249reboot_signal(int sig)
250{
Eric Andersenabc7d591999-10-19 00:27:50 +0000251 shutdown_system();
252 message(console, "Please stand by while rebooting the system.\r\n");
253 reboot( RB_AUTOBOOT);
254 exit(0);
Eric Andersencc8ed391999-10-05 16:24:54 +0000255}
256
257static void
Eric Andersenabc7d591999-10-19 00:27:50 +0000258configure_terminals( int serial_cons, int single_user_mode )
Eric Andersencc8ed391999-10-05 16:24:54 +0000259{
Eric Andersen3c163821999-10-14 22:16:57 +0000260 char *tty;
Eric Andersenabc7d591999-10-19 00:27:50 +0000261 struct serial_struct sr;
262 struct vt_stat vt;
263
Eric Andersen3c163821999-10-14 22:16:57 +0000264
265 switch (serial_cons) {
Eric Andersenabc7d591999-10-19 00:27:50 +0000266 case -1:
267 /* 2.2 kernels:
268 * identify the real console backend and try to make use of it */
269 if (ioctl(0,TIOCGSERIAL,&sr) == 0) {
270 sprintf( console, "/dev/ttyS%d", sr.line );
271 serial_cons = sr.line+1;
272 }
273 else if (ioctl(0, VT_GETSTATE, &vt) == 0) {
274 sprintf( console, "/dev/tty%d", vt.v_active );
275 serial_cons = 0;
276 }
277 else {
278 /* unknown backend: fallback to /dev/console */
279 strcpy( console, "/dev/console" );
280 serial_cons = 0;
281 }
282 break;
283
Eric Andersen3c163821999-10-14 22:16:57 +0000284 case 1:
Eric Andersenabc7d591999-10-19 00:27:50 +0000285 strcpy( console, "/dev/cua0" );
Eric Andersen3c163821999-10-14 22:16:57 +0000286 break;
287 case 2:
Eric Andersenabc7d591999-10-19 00:27:50 +0000288 strcpy( console, "/dev/cua1" );
Eric Andersen3c163821999-10-14 22:16:57 +0000289 break;
290 default:
291 tty = ttyname(0);
292 if (tty) {
293 strcpy( console, tty );
Eric Andersenabc7d591999-10-19 00:27:50 +0000294 if (!strncmp( tty, "/dev/cua", 8 ))
Eric Andersen3c163821999-10-14 22:16:57 +0000295 serial_cons=1;
296 }
297 else
298 /* falls back to /dev/tty1 if an error occurs */
299 strcpy( console, default_console );
300 }
301 if (!first_terminal)
302 first_terminal = console;
Eric Andersenabc7d591999-10-19 00:27:50 +0000303#if #cpu (sparc)
304 if (serial_cons > 0 && !strncmp(term_ptr,"TERM=linux",10))
305 term_ptr = "TERM=vt100";
306#endif
307 if (serial_cons) {
308 /* disable other but the first terminal:
309 * VT is not initialized anymore on 2.2 kernel when booting from
310 * serial console, therefore modprobe is flooding the display with
311 * "can't locate module char-major-4" messages. */
312 log = 0;
313 second_terminal = 0;
314 }
Eric Andersen3c163821999-10-14 22:16:57 +0000315}
Eric Andersencc8ed391999-10-05 16:24:54 +0000316
317extern int
Eric Andersen2c103011999-10-13 22:56:11 +0000318init_main(int argc, char * * argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000319{
Eric Andersenabc7d591999-10-19 00:27:50 +0000320 const char * rc_arguments[100];
Eric Andersen3c163821999-10-14 22:16:57 +0000321 const char * arguments[100];
Eric Andersenabc7d591999-10-19 00:27:50 +0000322 int run_rc = TRUE;
Eric Andersen3c163821999-10-14 22:16:57 +0000323 int j;
324 int pid1 = 0;
325 int pid2 = 0;
Eric Andersen3c163821999-10-14 22:16:57 +0000326 struct stat statbuf;
Eric Andersenabc7d591999-10-19 00:27:50 +0000327 const char * tty_commands[3] = { "etc/init.d/rcS", "bin/sh"};
Eric Andersen3c163821999-10-14 22:16:57 +0000328 int serial_console = 0;
Eric Andersenabc7d591999-10-19 00:27:50 +0000329 int retval;
Eric Andersencc8ed391999-10-05 16:24:54 +0000330
331 /*
332 * If I am started as /linuxrc instead of /sbin/init, I don't have the
333 * environment that init expects. I can't fix the signal behavior. Try
334 * to divorce from the controlling terminal with setsid(). This won't work
335 * if I am the process group leader.
336 */
337 setsid();
338
339 signal(SIGUSR1, halt_signal);
340 signal(SIGUSR2, reboot_signal);
341 signal(SIGINT, reboot_signal);
Eric Andersenabc7d591999-10-19 00:27:50 +0000342 signal(SIGTERM, reboot_signal);
Eric Andersencc8ed391999-10-05 16:24:54 +0000343
344 reboot(RB_DISABLE_CAD);
345
346 message(log, "%s: started. ", argv[0]);
347
348 for ( j = 1; j < argc; j++ ) {
349 if ( strcmp(argv[j], "single") == 0 ) {
Eric Andersenabc7d591999-10-19 00:27:50 +0000350 run_rc = FALSE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000351 tty_commands[0] = "bin/sh";
352 tty_commands[1] = 0;
353 }
354 }
355 for ( j = 0; __environ[j] != 0; j++ ) {
356 if ( strncmp(__environ[j], "tty", 3) == 0
357 && __environ[j][3] >= '1'
358 && __environ[j][3] <= '2'
359 && __environ[j][4] == '=' ) {
360 const char * s = &__environ[j][5];
361
362 if ( *s == 0 || strcmp(s, "off") == 0 )
363 s = 0;
364
365 tty_commands[__environ[j][3] - '1'] = s;
366 }
367 /* Should catch the syntax of Sparc kernel console setting. */
368 /* The kernel does not recognize a serial console when getting*/
369 /* console=/dev/ttySX !! */
370 else if ( strcmp(__environ[j], "console=ttya") == 0 ) {
371 serial_console=1;
372 }
373 else if ( strcmp(__environ[j], "console=ttyb") == 0 ) {
374 serial_console=2;
375 }
376 /* standard console settings */
377 else if ( strncmp(__environ[j], "console=", 8) == 0 ) {
378 first_terminal=&(__environ[j][8]);
379 }
380 else if ( strncmp(__environ[j], "TERM=", 5) == 0) {
381 term_ptr=__environ[j];
382 }
383 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000384
385 printf("mounting /proc ...\n");
386 if (mount("/proc","/proc","proc",0,0)) {
387 perror("mounting /proc failed\n");
388 }
389 printf("\tdone.\n");
390
Eric Andersenabc7d591999-10-19 00:27:50 +0000391 if (get_kernel_revision() >= 2*65536+1*256+71) {
392 /* if >= 2.1.71 kernel, /dev/console is not a symlink anymore:
393 * use it as primary console */
394 serial_console=-1;
395 }
396
397 /* Make sure /etc/init.d/rc exists */
398 retval= stat(tty_commands[0],&statbuf);
399 if (retval)
400 run_rc = FALSE;
401
402 configure_terminals( serial_console, run_rc);
403
Eric Andersencc8ed391999-10-05 16:24:54 +0000404 set_free_pages();
405
Eric Andersen3c163821999-10-14 22:16:57 +0000406 /* not enough memory to do anything useful*/
407 if (mem_total() < 2000) {
Eric Andersenabc7d591999-10-19 00:27:50 +0000408 retval= stat("/etc/fstab",&statbuf);
409 if (retval) {
410 printf("You do not have enough RAM, sorry.\n");
411 while (1) { sleep(1);}
412 } else {
413 /* Try to turn on swap */
414 static const char * const swapon_args[] = {"/bin/swapon", "-a", 0};
415 waitfor(run("/bin/swapon", swapon_args, console, 0));
416 if (mem_total() < 2000) {
417 printf("You do not have enough RAM, sorry.\n");
418 while (1) { sleep(1);}
419 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000420 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000421 }
422
423 /*
424 * Don't modify **argv directly, it would show up in the "ps" display.
425 * I don't want "init" to look like "rc".
426 */
Eric Andersenabc7d591999-10-19 00:27:50 +0000427 rc_arguments[0] = tty_commands[0];
Eric Andersencc8ed391999-10-05 16:24:54 +0000428 for ( j = 1; j < argc; j++ ) {
Eric Andersenabc7d591999-10-19 00:27:50 +0000429 rc_arguments[j] = argv[j];
Eric Andersencc8ed391999-10-05 16:24:54 +0000430 }
Eric Andersenabc7d591999-10-19 00:27:50 +0000431 rc_arguments[j] = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +0000432
433 arguments[0] = "-sh";
434 arguments[1] = 0;
Eric Andersenabc7d591999-10-19 00:27:50 +0000435
436 /* Ok, now launch the rc script /etc/init.d/rcS and prepare to start up
437 * some VTs on tty1 and tty2 if somebody hits enter
438 */
Eric Andersencc8ed391999-10-05 16:24:54 +0000439 for ( ; ; ) {
440 int wpid;
441 int status;
442
Eric Andersenabc7d591999-10-19 00:27:50 +0000443 if ( pid1 == 0 && tty_commands[0] ) {
444 if ( run_rc == TRUE ) {
445 pid1 = run(tty_commands[0], rc_arguments, first_terminal, 0);
446 } else {
447 pid2 = run(tty_commands[1], arguments, first_terminal, 1);
448 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000449 }
Eric Andersenabc7d591999-10-19 00:27:50 +0000450 if ( pid2 == 0 && second_terminal && tty_commands[1] ) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000451 pid2 = run(tty_commands[1], arguments, second_terminal, 1);
Eric Andersen3c163821999-10-14 22:16:57 +0000452 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000453 wpid = wait(&status);
Eric Andersenabc7d591999-10-19 00:27:50 +0000454 if ( wpid > 0 && wpid != pid1) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000455 message(log, "pid %d exited, status=%x.\n", wpid, status);
456 }
Eric Andersenabc7d591999-10-19 00:27:50 +0000457 if ( wpid == pid2 ) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000458 pid2 = 0;
Eric Andersenabc7d591999-10-19 00:27:50 +0000459 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000460 }
461}
462