blob: ade29c7c243f99e24d8a1ccfd64c867f2a3940ad [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>
16#include <sys/swap.h>
17#include <sys/sysmacros.h>
18
19const char init_usage[] = "Used internally by the system.";
20char console[16] = "";
21const char * default_console = "/dev/tty1";
22char * first_terminal = NULL;
23const char * second_terminal = "/dev/tty2";
24const char log[] = "/dev/tty3";
25char * term_ptr = NULL;
26
27static void
28message(const char * terminal, const char * pattern, ...)
29{
30 int fd;
31 FILE * con = 0;
32 va_list arguments;
33
34 /*
35 * Open the console device each time a message is printed. If the user
36 * has switched consoles, the open will get the new console. If we kept
37 * the console open, we'd always print to the same one.
38 */
39 if ( ((fd = open(terminal, O_WRONLY|O_NOCTTY)) < 0)
40 || ((con = fdopen(fd, "w")) == NULL) )
41 return;
42
43 va_start(arguments, pattern);
44 vfprintf(con, pattern, arguments);
45 va_end(arguments);
46 fclose(con);
47}
48
49static int
50waitfor(int pid)
51{
52 int status;
53 int wpid;
54
55 message(log, "Waiting for process %d.\n", pid);
56 while ( (wpid = wait(&status)) != pid ) {
57 if ( wpid > 0 ) {
58 message(
59 log
60 ,"pid %d exited, status=%x.\n"
61 ,wpid
62 ,status);
63 }
64 }
65 return wpid;
66}
67
68static int
Eric Andersen3c163821999-10-14 22:16:57 +000069run(const char* program, const char* const* arguments,
70 const char* terminal, int get_enter)
Eric Andersencc8ed391999-10-05 16:24:54 +000071{
72 static const char control_characters[] = {
73 '\003',
74 '\034',
75 '\177',
76 '\025',
77 '\004',
78 '\0',
79 '\1',
80 '\0',
81 '\021',
82 '\023',
83 '\032',
84 '\0',
85 '\022',
86 '\017',
87 '\027',
88 '\026',
89 '\0'
90 };
91
92 static char * environment[] = {
93 "HOME=/",
94 "PATH=/bin:/sbin:/usr/bin:/usr/sbin",
95 "SHELL=/bin/sh",
96 0,
97 "USER=root",
98 0
99 };
100
101 static const char press_enter[] =
Eric Andersencb6e2561999-10-16 15:48:40 +0000102 "\nPlease press Enter to activate this console. ";
Eric Andersencc8ed391999-10-05 16:24:54 +0000103
104 int pid;
105
106 environment[3]=term_ptr;
107
108 pid = fork();
109 if ( pid == 0 ) {
110 struct termios t;
111 const char * const * arg;
112
113 close(0);
114 close(1);
115 close(2);
116 setsid();
117
118 open(terminal, O_RDWR);
119 dup(0);
120 dup(0);
121 tcsetpgrp(0, getpgrp());
122
123 tcgetattr(0, &t);
124 memcpy(t.c_cc, control_characters, sizeof(control_characters));
125 t.c_line = 0;
126 t.c_iflag = ICRNL|IXON|IXOFF;
127 t.c_oflag = OPOST|ONLCR;
128 t.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN;
129 tcsetattr(0, TCSANOW, &t);
130
131 if ( get_enter ) {
132 /*
133 * Save memory by not exec-ing anything large (like a shell)
134 * before the user wants it. This is critical if swap is not
135 * enabled and the system has low memory. Generally this will
136 * be run on the second virtual console, and the first will
Eric Andersen3c163821999-10-14 22:16:57 +0000137 * be allowed to start a shell or whatever an init script
138 * specifies.
Eric Andersencc8ed391999-10-05 16:24:54 +0000139 */
140 char c;
141 write(1, press_enter, sizeof(press_enter) - 1);
142 read(0, &c, 1);
143 }
144
145 message(log, "Executing ");
146 arg = arguments;
147 while ( *arg != 0 )
148 message(log, "%s ", *arg++);
149 message(log, "\n");
150
151 execve(program, (char * *)arguments, (char * *)environment);
152 message(log, "%s: could not execute: %s.\r\n", program, strerror(errno));
153 exit(-1);
154 }
155 return pid;
156}
157
158static int
159mem_total()
160{
161 char s[80];
162 char *p;
163 FILE *f;
164 const char pattern[]="MemTotal:";
165
166 f=fopen("/proc/meminfo","r");
167 while (NULL != fgets(s,79,f)) {
168 p=strstr(s, pattern);
169 if (NULL != p) {
170 fclose(f);
171 return(atoi(p+strlen(pattern)));
172 }
173 }
174 return -1;
175}
176
177static void
178set_free_pages()
179{
180 char s[80];
181 FILE *f;
182
183 f=fopen("/proc/sys/vm/freepages","r");
184 fgets(s,79,f);
185 if (atoi(s) < 32) {
186 fclose(f);
187 f=fopen("/proc/sys/vm/freepages","w");
188 fprintf(f,"30\t40\t50\n");
189 printf("\nIncreased /proc/sys/vm/freepages values to 30/40/50\n");
190 }
191 fclose(f);
192}
193
194static void
195shutdown_system(int do_reboot)
196{
197 static const char * const umount_args[] = {"/bin/umount", "-a", "-n", 0};
198
199 sync();
200 /* Allow Ctrl-Alt-Del to reboot system. */
201 reboot(RB_ENABLE_CAD);
202
203 /* Send signals to every process _except_ pid 1 */
204 message(console, "Sending SIGHUP to all processes.\r\n");
205 kill(-1, SIGHUP);
206 sleep(2);
207 sync();
208 message(console, "Sending SIGKILL to all processes.\r\n");
209 kill(-1, SIGKILL);
210 sleep(1);
211 waitfor(run("/bin/umount", umount_args, console, 0));
212 sync();
213 bdflush(1, 0);
214 sync();
215 reboot(do_reboot ?RB_AUTOBOOT : RB_HALT_SYSTEM);
216 exit(0);
217}
218
219static void
220halt_signal(int sig)
221{
222 shutdown_system(0);
223}
224
225static void
226reboot_signal(int sig)
227{
228 shutdown_system(1);
229}
230
231static void
232exit_signal(int sig)
233{
234
235 /* initrd doesn't work anyway */
236
237 shutdown_system(1);
238
239 /* This is used on the initial ramdisk */
240
241 /* message(log, "Init exiting.");
242 exit(0);
243 */
244}
245
246void
Eric Andersen3c163821999-10-14 22:16:57 +0000247configure_terminals( int serial_cons )
248{
249 //struct stat statbuf;
250 char *tty;
251
252 switch (serial_cons) {
253 case 1:
254 strcpy( console, "/dev/ttyS0" );
255 break;
256 case 2:
257 strcpy( console, "/dev/ttyS1" );
258 break;
259 default:
260 tty = ttyname(0);
261 if (tty) {
262 strcpy( console, tty );
263 if (!strncmp( tty, "/dev/ttyS", 9 ))
264 serial_cons=1;
265 }
266 else
267 /* falls back to /dev/tty1 if an error occurs */
268 strcpy( console, default_console );
269 }
270 if (!first_terminal)
271 first_terminal = console;
272 if (serial_cons && !strncmp(term_ptr,"TERM=linux",10))
273 term_ptr = "TERM=vt100";
274}
Eric Andersencc8ed391999-10-05 16:24:54 +0000275
276extern int
Eric Andersen2c103011999-10-13 22:56:11 +0000277init_main(int argc, char * * argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000278{
Eric Andersen3c163821999-10-14 22:16:57 +0000279 static const char* const rc = "etc/rc";
280 const char * arguments[100];
281 int run_rc = 1;
282 int j;
283 int pid1 = 0;
284 int pid2 = 0;
285 int create_swap= -1;
286 struct stat statbuf;
287 const char * tty_commands[2] = { "bin/sh", "bin/sh"};
288 char swap[20];
289 int serial_console = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +0000290
291 /*
292 * If I am started as /linuxrc instead of /sbin/init, I don't have the
293 * environment that init expects. I can't fix the signal behavior. Try
294 * to divorce from the controlling terminal with setsid(). This won't work
295 * if I am the process group leader.
296 */
297 setsid();
298
299 signal(SIGUSR1, halt_signal);
300 signal(SIGUSR2, reboot_signal);
301 signal(SIGINT, reboot_signal);
302 signal(SIGTERM, exit_signal);
303
304 reboot(RB_DISABLE_CAD);
305
306 message(log, "%s: started. ", argv[0]);
307
308 for ( j = 1; j < argc; j++ ) {
309 if ( strcmp(argv[j], "single") == 0 ) {
310 run_rc = 0;
311 tty_commands[0] = "bin/sh";
312 tty_commands[1] = 0;
313 }
314 }
315 for ( j = 0; __environ[j] != 0; j++ ) {
316 if ( strncmp(__environ[j], "tty", 3) == 0
317 && __environ[j][3] >= '1'
318 && __environ[j][3] <= '2'
319 && __environ[j][4] == '=' ) {
320 const char * s = &__environ[j][5];
321
322 if ( *s == 0 || strcmp(s, "off") == 0 )
323 s = 0;
324
325 tty_commands[__environ[j][3] - '1'] = s;
326 }
327 /* Should catch the syntax of Sparc kernel console setting. */
328 /* The kernel does not recognize a serial console when getting*/
329 /* console=/dev/ttySX !! */
330 else if ( strcmp(__environ[j], "console=ttya") == 0 ) {
331 serial_console=1;
332 }
333 else if ( strcmp(__environ[j], "console=ttyb") == 0 ) {
334 serial_console=2;
335 }
336 /* standard console settings */
337 else if ( strncmp(__environ[j], "console=", 8) == 0 ) {
338 first_terminal=&(__environ[j][8]);
339 }
340 else if ( strncmp(__environ[j], "TERM=", 5) == 0) {
341 term_ptr=__environ[j];
342 }
343 }
344 configure_terminals( serial_console );
345
346 printf("mounting /proc ...\n");
347 if (mount("/proc","/proc","proc",0,0)) {
348 perror("mounting /proc failed\n");
349 }
350 printf("\tdone.\n");
351
352 set_free_pages();
353
Eric Andersen3c163821999-10-14 22:16:57 +0000354 /* not enough memory to do anything useful*/
355 if (mem_total() < 2000) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000356 int retval;
357 retval= stat("/etc/swappartition",&statbuf);
358 if (retval) {
Eric Andersen3c163821999-10-14 22:16:57 +0000359 printf("You do not have enough RAM, sorry.\n");
360 while (1) { sleep(1);}
Eric Andersencc8ed391999-10-05 16:24:54 +0000361 } else { /* everything OK */
362 FILE *f;
363
364 f=fopen("/etc/swappartition","r");
365 fgets(swap,19,f);
366 fclose(f);
367 *(strstr(swap,"\n"))='\0';
368
369 if (swapon(swap,0)) {
370 perror("swapon failed\n");
371 } else {
372 f=fopen("/etc/swaps","w");
373 fprintf(f,"%s none swap rw 0 0",swap);
374 fclose(f);
375 create_swap = 0;
376 }
377 }
378 }
379
380 /*
381 * Don't modify **argv directly, it would show up in the "ps" display.
382 * I don't want "init" to look like "rc".
383 */
384 arguments[0] = rc;
385 for ( j = 1; j < argc; j++ ) {
386 arguments[j] = argv[j];
387 }
388 arguments[j] = 0;
389
Eric Andersen2c103011999-10-13 22:56:11 +0000390 if ( run_rc ) {
Eric Andersencb6e2561999-10-16 15:48:40 +0000391 run(rc, arguments, console, 0);
392 //waitfor(run(rc, arguments, console, 0));
Eric Andersen2c103011999-10-13 22:56:11 +0000393 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000394
395 if ( 0 == create_swap) {
396 if (unlink("/etc/swappartition")) {
397 perror("unlinking /etc/swappartition");
398 }
399 }
400
401 arguments[0] = "-sh";
402 arguments[1] = 0;
403 for ( ; ; ) {
404 int wpid;
405 int status;
406
407 if ( pid1 == 0 && tty_commands[0] ) {
408 /* Ask before starting a shell */
409 /*
410 arguments[0] = tty_commands[0];
411 */
Eric Andersen3c163821999-10-14 22:16:57 +0000412 pid1 = run(tty_commands[0], arguments, first_terminal, 1);
Eric Andersencc8ed391999-10-05 16:24:54 +0000413 }
Eric Andersen3c163821999-10-14 22:16:57 +0000414 if ( pid2 == 0 && tty_commands[1] ) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000415 pid2 = run(tty_commands[1], arguments, second_terminal, 1);
Eric Andersen3c163821999-10-14 22:16:57 +0000416 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000417 wpid = wait(&status);
418 if ( wpid > 0 ) {
419 /* DEBUGGING */
420 message(log, "pid %d exited, status=%x.\n", wpid, status);
421 }
422 if ( wpid == pid1 ) {
423 pid1 = 0;
424 }
425 if ( wpid == pid2 )
426 pid2 = 0;
427 }
428}
429