blob: 4771722b9a5b2ce2cc6fc06108b3c66015107b0a [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
69run(
70 const char * program
71,const char * const * arguments
72,const char * terminal
73,int get_enter)
74{
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[] =
105 "Please press Enter to activate this console. ";
106
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
140 * be allowed to start a shell or the installation system.
141 */
142 char c;
143 write(1, press_enter, sizeof(press_enter) - 1);
144 read(0, &c, 1);
145 }
146
147 message(log, "Executing ");
148 arg = arguments;
149 while ( *arg != 0 )
150 message(log, "%s ", *arg++);
151 message(log, "\n");
152
153 execve(program, (char * *)arguments, (char * *)environment);
154 message(log, "%s: could not execute: %s.\r\n", program, strerror(errno));
155 exit(-1);
156 }
157 return pid;
158}
159
160static int
161mem_total()
162{
163 char s[80];
164 char *p;
165 FILE *f;
166 const char pattern[]="MemTotal:";
167
168 f=fopen("/proc/meminfo","r");
169 while (NULL != fgets(s,79,f)) {
170 p=strstr(s, pattern);
171 if (NULL != p) {
172 fclose(f);
173 return(atoi(p+strlen(pattern)));
174 }
175 }
176 return -1;
177}
178
179static void
180set_free_pages()
181{
182 char s[80];
183 FILE *f;
184
185 f=fopen("/proc/sys/vm/freepages","r");
186 fgets(s,79,f);
187 if (atoi(s) < 32) {
188 fclose(f);
189 f=fopen("/proc/sys/vm/freepages","w");
190 fprintf(f,"30\t40\t50\n");
191 printf("\nIncreased /proc/sys/vm/freepages values to 30/40/50\n");
192 }
193 fclose(f);
194}
195
196static void
197shutdown_system(int do_reboot)
198{
199 static const char * const umount_args[] = {"/bin/umount", "-a", "-n", 0};
200
201 sync();
202 /* Allow Ctrl-Alt-Del to reboot system. */
203 reboot(RB_ENABLE_CAD);
204
205 /* Send signals to every process _except_ pid 1 */
206 message(console, "Sending SIGHUP to all processes.\r\n");
207 kill(-1, SIGHUP);
208 sleep(2);
209 sync();
210 message(console, "Sending SIGKILL to all processes.\r\n");
211 kill(-1, SIGKILL);
212 sleep(1);
213 waitfor(run("/bin/umount", umount_args, console, 0));
214 sync();
215 bdflush(1, 0);
216 sync();
217 reboot(do_reboot ?RB_AUTOBOOT : RB_HALT_SYSTEM);
218 exit(0);
219}
220
221static void
222halt_signal(int sig)
223{
224 shutdown_system(0);
225}
226
227static void
228reboot_signal(int sig)
229{
230 shutdown_system(1);
231}
232
233static void
234exit_signal(int sig)
235{
236
237 /* initrd doesn't work anyway */
238
239 shutdown_system(1);
240
241 /* This is used on the initial ramdisk */
242
243 /* message(log, "Init exiting.");
244 exit(0);
245 */
246}
247
248void
249configure_terminals( int serial_cons );
250
251extern int
252init_main(struct FileInfo * i, int argc, char * * argv)
253{
254 static const char * const rc = "etc/rc";
255 const char * arguments[100];
256 int run_rc = 1;
257 int j;
258 int pid1 = 0;
259 int pid2 = 0;
260 int create_swap= -1;
261 struct stat statbuf;
262#ifndef INCLUDE_DINSTALL
263 const char * tty_commands[2] = { "bin/sh", "bin/sh"};
264#else
265 const char * tty_commands[2] = { "sbin/dinstall", "bin/sh"};
266#endif
267 char swap[20];
268 int serial_console = 0;
269
270 /*
271 * If I am started as /linuxrc instead of /sbin/init, I don't have the
272 * environment that init expects. I can't fix the signal behavior. Try
273 * to divorce from the controlling terminal with setsid(). This won't work
274 * if I am the process group leader.
275 */
276 setsid();
277
278 signal(SIGUSR1, halt_signal);
279 signal(SIGUSR2, reboot_signal);
280 signal(SIGINT, reboot_signal);
281 signal(SIGTERM, exit_signal);
282
283 reboot(RB_DISABLE_CAD);
284
285 message(log, "%s: started. ", argv[0]);
286
287 for ( j = 1; j < argc; j++ ) {
288 if ( strcmp(argv[j], "single") == 0 ) {
289 run_rc = 0;
290 tty_commands[0] = "bin/sh";
291 tty_commands[1] = 0;
292 }
293 }
294 for ( j = 0; __environ[j] != 0; j++ ) {
295 if ( strncmp(__environ[j], "tty", 3) == 0
296 && __environ[j][3] >= '1'
297 && __environ[j][3] <= '2'
298 && __environ[j][4] == '=' ) {
299 const char * s = &__environ[j][5];
300
301 if ( *s == 0 || strcmp(s, "off") == 0 )
302 s = 0;
303
304 tty_commands[__environ[j][3] - '1'] = s;
305 }
306 /* Should catch the syntax of Sparc kernel console setting. */
307 /* The kernel does not recognize a serial console when getting*/
308 /* console=/dev/ttySX !! */
309 else if ( strcmp(__environ[j], "console=ttya") == 0 ) {
310 serial_console=1;
311 }
312 else if ( strcmp(__environ[j], "console=ttyb") == 0 ) {
313 serial_console=2;
314 }
315 /* standard console settings */
316 else if ( strncmp(__environ[j], "console=", 8) == 0 ) {
317 first_terminal=&(__environ[j][8]);
318 }
319 else if ( strncmp(__environ[j], "TERM=", 5) == 0) {
320 term_ptr=__environ[j];
321 }
322 }
323 configure_terminals( serial_console );
324
325 printf("mounting /proc ...\n");
326 if (mount("/proc","/proc","proc",0,0)) {
327 perror("mounting /proc failed\n");
328 }
329 printf("\tdone.\n");
330
331 set_free_pages();
332
333 if (mem_total() < 3500) { /* not enough memory for standard install */
334 int retval;
335 retval= stat("/etc/swappartition",&statbuf);
336 if (retval) {
337 printf("
338You do not have enough RAM, hence you must boot using the Boot Disk
339for Low Memory systems.
340
341Read the instructions in the install.html file.
342");
343 while (1) {;}
344 } else { /* everything OK */
345 FILE *f;
346
347 f=fopen("/etc/swappartition","r");
348 fgets(swap,19,f);
349 fclose(f);
350 *(strstr(swap,"\n"))='\0';
351
352 if (swapon(swap,0)) {
353 perror("swapon failed\n");
354 } else {
355 f=fopen("/etc/swaps","w");
356 fprintf(f,"%s none swap rw 0 0",swap);
357 fclose(f);
358 create_swap = 0;
359 }
360 }
361 }
362
363 /*
364 * Don't modify **argv directly, it would show up in the "ps" display.
365 * I don't want "init" to look like "rc".
366 */
367 arguments[0] = rc;
368 for ( j = 1; j < argc; j++ ) {
369 arguments[j] = argv[j];
370 }
371 arguments[j] = 0;
372
373 if ( run_rc )
374 waitfor(run(rc, arguments, console, 0));
375
376 if ( 0 == create_swap) {
377 if (unlink("/etc/swappartition")) {
378 perror("unlinking /etc/swappartition");
379 }
380 }
381
382 arguments[0] = "-sh";
383 arguments[1] = 0;
384 for ( ; ; ) {
385 int wpid;
386 int status;
387
388 if ( pid1 == 0 && tty_commands[0] ) {
389 /* Ask before starting a shell */
390 /*
391 arguments[0] = tty_commands[0];
392 */
393 pid1 = run(tty_commands[0], arguments, first_terminal, 0);
394 }
395 if ( pid2 == 0 && tty_commands[1] )
396 pid2 = run(tty_commands[1], arguments, second_terminal, 1);
397 wpid = wait(&status);
398 if ( wpid > 0 ) {
399 /* DEBUGGING */
400 message(log, "pid %d exited, status=%x.\n", wpid, status);
401 }
402 if ( wpid == pid1 ) {
403 pid1 = 0;
404 }
405 if ( wpid == pid2 )
406 pid2 = 0;
407 }
408}
409
410void
411configure_terminals( int serial_cons )
412{
413 //struct stat statbuf;
414 char *tty;
415
416 switch (serial_cons) {
417 case 1:
418 strcpy( console, "/dev/ttyS0" );
419 break;
420 case 2:
421 strcpy( console, "/dev/ttyS1" );
422 break;
423 default:
424 tty = ttyname(0);
425 if (tty) {
426 strcpy( console, tty );
427 if (!strncmp( tty, "/dev/ttyS", 9 ))
428 serial_cons=1;
429 }
430 else
431 /* falls back to /dev/tty1 if an error occurs */
432 strcpy( console, default_console );
433 }
434 if (!first_terminal)
435 first_terminal = console;
436 if (serial_cons && !strncmp(term_ptr,"TERM=linux",10))
437 term_ptr = "TERM=vt100";
438}