]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/simpleinit.c
Imported from util-linux-2.11x tarball.
[thirdparty/util-linux.git] / login-utils / simpleinit.c
1 /* simpleinit.c - poe@daimi.aau.dk */
2 /* Version 2.0.2 */
3
4 /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
5 * - added Native Language Support
6 * 2001-01-25 Richard Gooch <rgooch@atnf.csiro.au>
7 * - fixed bug with failed services so they may be later "reclaimed"
8 * 2001-02-02 Richard Gooch <rgooch@atnf.csiro.au>
9 * - fixed race when reading from pipe and reaping children
10 * 2001-02-18 sam@quux.dropbear.id.au
11 * - fixed bug in <get_path>: multiple INIT_PATH components did not work
12 * 2001-02-21 Richard Gooch <rgooch@atnf.csiro.au>
13 * - block signals in handlers, so that longjmp() doesn't kill context
14 * 2001-02-25 Richard Gooch <rgooch@atnf.csiro.au>
15 * - make default INIT_PATH the boot_prog (if it is a directory) - YECCH
16 */
17
18 #include <sys/types.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <ctype.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <signal.h>
26 #include <pwd.h>
27 #include <sys/file.h>
28 #include <sys/wait.h>
29 #include <sys/stat.h>
30 #include <sys/sysmacros.h>
31 #include <sys/time.h>
32 #include <sys/ioctl.h>
33 #include <dirent.h>
34 #include <termios.h>
35 #include <utmp.h>
36 #include <setjmp.h>
37 #include <sched.h>
38 #ifdef SHADOW_PWD
39 # include <shadow.h>
40 #endif
41 #include "my_crypt.h"
42 #include "pathnames.h"
43 #include "linux_reboot.h"
44 #include "xstrncpy.h"
45 #include "nls.h"
46 #include "simpleinit.h"
47
48 #define CMDSIZ 150 /* max size of a line in inittab */
49 #define NUMCMD 30 /* max number of lines in inittab */
50 #define NUMTOK 20 /* max number of tokens in inittab command */
51 #define PATH_SIZE (CMDSIZ+CMDSIZ+1)
52
53 #define MAX_RESPAWN_RATE 5 /* number of respawns per 100 seconds */
54
55 #define TZFILE "/etc/TZ"
56 char tzone[CMDSIZ];
57 /* #define DEBUGGING */
58
59 /* Define this if you want init to ignore the termcap field in inittab for
60 console ttys. */
61 /* #define SPECIAL_CONSOLE_TERM */
62
63 #define ever (;;)
64
65 struct initline {
66 pid_t pid;
67 char tty[10];
68 char termcap[30];
69 char *toks[NUMTOK];
70 char line[CMDSIZ];
71 struct timeval last_start;
72 signed long rate;
73 };
74
75 struct initline inittab[NUMCMD];
76 int numcmd;
77 int stopped = 0; /* are we stopped */
78 static char boot_prog[PATH_SIZE] = _PATH_RC;
79 static char script_prefix[PATH_SIZE] = "\0";
80 static char final_prog[PATH_SIZE] = "\0";
81 static char init_path[PATH_SIZE] = "\0";
82 static int caught_sigint = 0;
83 static int no_reboot = 0;
84 static pid_t rc_child = -1;
85 static const char *initctl_name = "/dev/initctl";
86 static int initctl_fd = -1;
87 static volatile int do_longjmp = 0;
88 static sigjmp_buf jmp_env;
89
90
91 static void do_single (void);
92 static int do_rc_tty (const char *path);
93 static int process_path (const char *path, int (*func) (const char *path),
94 int ignore_dangling_symlink);
95 static int preload_file (const char *path);
96 static int run_file (const char *path);
97 static void spawn (int i), read_inittab (void);
98 static void sighup_handler (int sig);
99 static void sigtstp_handler (int sig);
100 static void sigint_handler (int sig);
101 static void sigchild_handler (int sig);
102 static void sigquit_handler (int sig);
103 static void sigterm_handler (int sig);
104 #ifdef SET_TZ
105 static void set_tz (void);
106 #endif
107 static void write_wtmp (void);
108 static pid_t mywait (int *status);
109 static int run_command (const char *file, const char *name, pid_t pid);
110
111
112 static void err (char *s)
113 {
114 int fd;
115
116 if((fd = open("/dev/console", O_WRONLY)) < 0) return;
117
118 write(fd, "init: ", 6);
119 write(fd, s, strlen(s));
120 close(fd);
121 }
122
123 static void enter_single (void)
124 {
125 pid_t pid;
126 int i;
127
128 err(_("Booting to single user mode.\n"));
129 if((pid = fork()) == 0) {
130 /* the child */
131 execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
132 err(_("exec of single user shell failed\n"));
133 } else if(pid > 0) {
134 while (waitpid (pid, &i, 0) != pid) /* Nothing */;
135 } else if(pid < 0) {
136 err(_("fork of single user shell failed\n"));
137 }
138 unlink(_PATH_SINGLE);
139 }
140
141 int main(int argc, char *argv[])
142 {
143 int vec, i;
144 int want_single = 0;
145 pid_t pid;
146 struct sigaction sa;
147
148
149 #ifdef SET_TZ
150 set_tz();
151 #endif
152 sigfillset (&sa.sa_mask); /* longjmp and nested signals don't mix */
153 sa.sa_flags = SA_ONESHOT;
154 sa.sa_handler = sigint_handler;
155 sigaction (SIGINT, &sa, NULL);
156 sa.sa_flags = 0;
157 sa.sa_handler = sigtstp_handler;
158 sigaction (SIGTSTP, &sa, NULL);
159 sa.sa_handler = sigterm_handler;
160 sigaction (SIGTERM, &sa, NULL);
161 sa.sa_handler = sigchild_handler;
162 sigaction (SIGCHLD, &sa, NULL);
163 sa.sa_handler = sigquit_handler;
164 sigaction (SIGQUIT, &sa, NULL);
165
166 setlocale(LC_ALL, "");
167 bindtextdomain(PACKAGE, LOCALEDIR);
168 textdomain(PACKAGE);
169
170 my_reboot (LINUX_REBOOT_CMD_CAD_OFF);
171 /* Find script to run. Command-line overrides config file overrides
172 built-in default */
173 for (i = 0; i < NUMCMD; i++) inittab[i].pid = -1;
174 read_inittab ();
175 for (i = 1; i < argc; i++) {
176 if (strcmp (argv[i], "single") == 0)
177 want_single = 1;
178 else if (strcmp (argv[i], "-noreboot") == 0)
179 no_reboot = 1;
180 else if (strlen(script_prefix) + strlen(argv[i]) < PATH_SIZE) {
181 char path[PATH_SIZE];
182
183 strcpy (path, script_prefix);
184 strcat (path, argv[i]);
185 if (access (path, R_OK | X_OK) == 0)
186 strcpy (boot_prog, path);
187 }
188 }
189 if (init_path[0] == '\0')
190 {
191 struct stat statbuf;
192
193 if ( (stat (boot_prog, &statbuf) == 0) && S_ISDIR (statbuf.st_mode) )
194 {
195 strcpy (init_path, boot_prog);
196 i = strlen (init_path);
197 if (init_path[i - 1] == '/') init_path[i - 1] = '\0';
198 }
199 }
200
201 if ( ( initctl_fd = open (initctl_name, O_RDWR, 0) ) < 0 ) {
202 mkfifo (initctl_name, S_IRUSR | S_IWUSR);
203 if ( ( initctl_fd = open (initctl_name, O_RDWR, 0) ) < 0 )
204 err ( _("error opening fifo\n") );
205 }
206
207 if ( want_single || (access (_PATH_SINGLE, R_OK) == 0) ) do_single ();
208
209 /*If we get a SIGTSTP before multi-user mode, do nothing*/
210 while (stopped)
211 pause();
212
213 if ( do_rc_tty (boot_prog) ) do_single ();
214
215 while (stopped) /* Also if /etc/rc fails & we get SIGTSTP */
216 pause();
217
218 write_wtmp(); /* write boottime record */
219 #ifdef DEBUGGING
220 for(i = 0; i < numcmd; i++) {
221 char **p;
222 p = inittab[i].toks;
223 printf("toks= %s %s %s %s\n",p[0], p[1], p[2], p[3]);
224 printf("tty= %s\n", inittab[i].tty);
225 printf("termcap= %s\n", inittab[i].termcap);
226 }
227 exit(0);
228 #endif
229 signal (SIGHUP, sighup_handler); /* Better semantics with signal(2) */
230
231 for (i = 0; i < getdtablesize (); i++)
232 if (i != initctl_fd) close (i);
233
234 for(i = 0; i < numcmd; i++)
235 spawn(i);
236
237 if (final_prog[0] != '\0') {
238 switch ( fork () )
239 {
240 case 0: /* Child */
241 execl (final_prog, final_prog, "start", NULL);
242 err ( _("error running finalprog\n") );
243 _exit (1);
244 break;
245 case -1: /* Error */
246 err ( _("error forking finalprog\n") );
247 break;
248 default: /* Parent */
249 break;
250 }
251 }
252
253 for ever {
254 pid = mywait (&vec);
255 if (pid < 1) continue;
256
257 /* clear utmp entry, and append to wtmp if possible */
258 {
259 struct utmp *ut;
260 int ut_fd, lf;
261
262 utmpname(_PATH_UTMP);
263 setutent();
264 while((ut = getutent())) {
265 if(ut->ut_pid == pid) {
266 time(&ut->ut_time);
267 memset(&ut->ut_user, 0, UT_NAMESIZE);
268 memset(&ut->ut_host, 0, sizeof(ut->ut_host));
269 ut->ut_type = DEAD_PROCESS;
270 ut->ut_pid = 0;
271 ut->ut_addr = 0;
272 /*endutent();*/
273 pututline(ut);
274
275 if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
276 flock(lf, LOCK_EX|LOCK_NB);
277 if((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
278 write(ut_fd, ut, sizeof(struct utmp));
279 close(ut_fd);
280 }
281 flock(lf, LOCK_UN|LOCK_NB);
282 close(lf);
283 }
284 break;
285 }
286 }
287 endutent();
288 }
289
290 for(i = 0; i < numcmd; i++) {
291 if(pid == inittab[i].pid || inittab[i].pid < 0) {
292 if (stopped)
293 inittab[i].pid = -1;
294 else
295 spawn(i);
296 if (pid == inittab[i].pid)
297 break;
298 }
299 }
300 }
301 }
302
303 #define MAXTRIES 3 /* number of tries allowed when giving the password */
304
305 /*
306 * return true if singleuser mode is allowed.
307 * If /etc/securesingle exists ask for root password, otherwise always OK.
308 */
309 static int check_single_ok (void)
310 {
311 char *pass, *rootpass = NULL;
312 struct passwd *pwd;
313 int i;
314
315 if (access (_PATH_SECURE, R_OK) != 0) return 1;
316 if ( ( pwd = getpwnam ("root") ) || ( pwd = getpwuid (0) ) )
317 rootpass = pwd->pw_passwd;
318 else
319 return 1; /* a bad /etc/passwd should not lock out */
320
321 for (i = 0; i < MAXTRIES; i++)
322 {
323 pass = getpass (_ ("Password: ") );
324 if (pass == NULL) continue;
325
326 if ( !strcmp (crypt (pass, rootpass), rootpass) ) return 1;
327
328 puts (_ ("\nWrong password.\n") );
329 }
330 return 0;
331 }
332
333 static void do_single (void)
334 {
335 char path[PATH_SIZE];
336
337 if (caught_sigint) return;
338 strcpy (path, script_prefix);
339 strcat (path, "single");
340 if (access (path, R_OK | X_OK) == 0)
341 if (do_rc_tty (path) == 0) return;
342 if ( check_single_ok () ) enter_single ();
343 } /* End Function do_single */
344
345 /*
346 * run boot script(s). The environment is passed to the script(s), so the RC
347 * environment variable can be used to decide what to do.
348 * RC may be set from LILO.
349 * [RETURNS] 0 on success (exit status convention), otherwise error.
350 */
351 static int do_rc_tty (const char *path)
352 {
353 int status;
354 pid_t pid;
355 sigset_t ss;
356
357 if (caught_sigint) return 0;
358 process_path (path, preload_file, 0);
359 /* Launch off a subprocess to start a new session (required for frobbing
360 the TTY) and capture control-C */
361 switch ( rc_child = fork () )
362 {
363 case 0: /* Child */
364 for (status = 1; status < NSIG; status++) signal (status, SIG_DFL);
365 sigfillset (&ss);
366 sigprocmask (SIG_UNBLOCK, &ss, NULL);
367 sigdelset (&ss, SIGINT);
368 sigdelset (&ss, SIGQUIT);
369 setsid ();
370 ioctl (0, TIOCSCTTY, 0); /* I want my control-C */
371 sigsuspend (&ss); /* Should never return, should just be killed */
372 break; /* No-one else is controlled by this TTY now */
373 case -1: /* Error */
374 return (1);
375 /*break;*/
376 default: /* Parent */
377 break;
378 }
379 /* Parent */
380 process_path (path, run_file, 0);
381 while (1)
382 {
383 if ( ( pid = mywait (&status) ) == rc_child )
384 return (WTERMSIG (status) == SIGINT) ? 0 : 1;
385 if (pid < 0) break;
386 }
387 kill (rc_child, SIGKILL);
388 while (waitpid (rc_child, NULL, 0) != rc_child) /* Nothing */;
389 return 0;
390 } /* End Function do_rc_tty */
391
392 static int process_path (const char *path, int (*func) (const char *path),
393 int ignore_dangling_symlink)
394 {
395 struct stat statbuf;
396 DIR *dp;
397 struct dirent *de;
398
399 if (lstat (path, &statbuf) != 0)
400 {
401 err (_ ("lstat of path failed\n") );
402 return 1;
403 }
404 if ( S_ISLNK (statbuf.st_mode) )
405 {
406 if (stat (path, &statbuf) != 0)
407 {
408 if ( (errno == ENOENT) && ignore_dangling_symlink ) return 0;
409 err (_ ("stat of path failed\n") );
410 return 1;
411 }
412 }
413 if ( !( statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH) ) ) return 0;
414 if ( !S_ISDIR (statbuf.st_mode) ) return (*func) (path);
415 if ( ( dp = opendir (path) ) == NULL )
416 {
417 err (_ ("open of directory failed\n") );
418 return 1;
419 }
420 while ( ( de = readdir (dp) ) != NULL )
421 {
422 int retval;
423 char newpath[PATH_SIZE];
424
425 if (de->d_name[0] == '.') continue;
426 retval = sprintf (newpath, "%s/%s", path, de->d_name);
427 if (newpath[retval - 1] == '~') continue; /* Common mistake */
428 if ( ( retval = process_path (newpath, func, 1) ) ) return retval;
429 }
430 closedir (dp);
431 return 0;
432 } /* End Function process_path */
433
434 static int preload_file (const char *path)
435 {
436 int fd;
437 char ch;
438
439 if ( ( fd = open (path, O_RDONLY, 0) ) < 0) return 0;
440 while (read (fd, &ch, 1) == 1) lseek (fd, 1024, SEEK_CUR);
441 close (fd);
442 return 0;
443 } /* End Function preload_file */
444
445 static int run_file (const char *path)
446 {
447 const char *ptr;
448
449 if ( ( ptr = strrchr ( (char *) path, '/' ) ) == NULL ) ptr = path;
450 else ++ptr;
451 return (run_command (path, ptr, 0) == SIG_FAILED) ? 1 : 0;
452 } /* End Function run_file */
453
454 static void spawn (int i)
455 {
456 pid_t pid;
457 int j;
458 signed long ds_taken;
459 struct timeval ct;
460
461 if (inittab[i].toks[0] == NULL) return;
462
463 /* Check if respawning too fast */
464 gettimeofday (&ct, NULL);
465 ds_taken = ct.tv_sec - inittab[i].last_start.tv_sec;
466
467 /* On the first iteration last_start==0 and ds_taken
468 may be very large. Avoid overflow. -- Denis Vlasenko */
469 if (ds_taken > 10000)
470 ds_taken = 10000;
471
472 ds_taken *= 10;
473 ds_taken += (ct.tv_usec - inittab[i].last_start.tv_usec) / 100000;
474 if (ds_taken < 1)
475 ds_taken = 1;
476 inittab[i].rate = (9 * inittab[i].rate + 1000 / ds_taken) / 10;
477 if (inittab[i].rate > MAX_RESPAWN_RATE) {
478 char txt[256];
479
480 inittab[i].toks[0] = NULL;
481 inittab[i].pid = -1;
482 inittab[i].rate = 0;
483 sprintf (txt,"respawning: \"%s\" too fast: quenching entry\n",
484 inittab[i].tty);
485 err (_(txt));
486 return;
487 }
488
489 if((pid = fork()) < 0) {
490 inittab[i].pid = -1;
491 err(_("fork failed\n"));
492 return;
493 }
494 if(pid) {
495 /* this is the parent */
496 inittab[i].pid = pid;
497 inittab[i].last_start = ct;
498 sched_yield ();
499 return;
500 } else {
501 /* this is the child */
502 char term[40];
503 #ifdef SET_TZ
504 char tz[CMDSIZ];
505 #endif
506 char *env[3];
507
508 setsid();
509 for(j = 0; j < getdtablesize(); j++)
510 (void) close(j);
511
512 (void) sprintf(term, "TERM=%s", inittab[i].termcap);
513 env[0] = term;
514 env[1] = (char *)0;
515 #ifdef SET_TZ
516 (void) sprintf(tz, "TZ=%s", tzone);
517 env[1] = tz;
518 #endif
519 env[2] = (char *)0;
520
521 execve(inittab[i].toks[0], inittab[i].toks, env);
522 err(_("exec failed\n"));
523 sleep(5);
524 _exit(1);
525 }
526 }
527
528 static void read_inittab (void)
529 {
530 FILE *f;
531 char buf[CMDSIZ];
532 int i,j,k;
533 int has_prog = 0;
534 char *ptr, *getty;
535 char prog[PATH_SIZE];
536 #ifdef SPECIAL_CONSOLE_TERM
537 char tty[50];
538 struct stat stb;
539 #endif
540 char *termenv;
541
542 termenv = getenv("TERM"); /* set by kernel */
543 /* termenv = "vt100"; */
544
545 if(!(f = fopen(_PATH_INITTAB, "r"))) {
546 err(_("cannot open inittab\n"));
547 return;
548 }
549
550 prog[0] = '\0';
551 i = 0;
552 while(!feof(f) && i < NUMCMD - 2) {
553 if(fgets(buf, CMDSIZ - 1, f) == 0) break;
554 buf[CMDSIZ-1] = 0;
555
556 for(k = 0; k < CMDSIZ && buf[k]; k++) {
557 if ((buf[k] == '#') || (buf[k] == '\n')) {
558 buf[k] = 0; break;
559 }
560 }
561
562 if(buf[0] == 0 || buf[0] == '\n') continue;
563 ptr = strchr (buf, '=');
564 if (ptr) {
565 ptr++;
566 if ( !strncmp (buf, "bootprog", 8) ) {
567 while ( isspace (*ptr) ) ++ptr;
568 strcpy (prog, ptr);
569 has_prog = 1;
570 continue;
571 }
572 if ( !strncmp (buf, "fileprefix", 10) ) {
573 while ( isspace (*ptr) ) ++ptr;
574 strcpy (script_prefix, ptr);
575 continue;
576 }
577 if ( !strncmp (buf, "PATH", 4) ) {
578 while ( isspace (*ptr) ) ++ptr;
579 setenv ("PATH", ptr, 1);
580 continue;
581 }
582 if ( !strncmp (buf, "INIT_PATH", 9) ) {
583 while ( isspace (*ptr) ) ++ptr;
584 strcpy (init_path, ptr);
585 continue;
586 }
587 if ( !strncmp (buf, "finalprog", 8) ) {
588 while ( isspace (*ptr) ) ++ptr;
589 strcpy (final_prog, ptr);
590 continue;
591 }
592 }
593
594
595 (void) strcpy(inittab[i].line, buf);
596
597 (void) strtok(inittab[i].line, ":");
598 xstrncpy(inittab[i].tty, inittab[i].line, 10);
599 xstrncpy(inittab[i].termcap, strtok((char *)0, ":"), 30);
600
601 getty = strtok((char *)0, ":");
602 (void) strtok(getty, " \t\n");
603 inittab[i].toks[0] = getty;
604 j = 1;
605 while((ptr = strtok((char *)0, " \t\n")))
606 inittab[i].toks[j++] = ptr;
607 inittab[i].toks[j] = (char *)0;
608
609 #ifdef SPECIAL_CONSOLE_TERM
610 /* special-case termcap for the console ttys */
611 (void) sprintf(tty, "/dev/%s", inittab[i].tty);
612 if(!termenv || stat(tty, &stb) < 0) {
613 err(_("no TERM or cannot stat tty\n"));
614 } else {
615 /* is it a console tty? */
616 if(major(stb.st_rdev) == 4 && minor(stb.st_rdev) < 64)
617 xstrncpy(inittab[i].termcap, termenv, 30);
618 }
619 #endif
620
621 i++;
622 }
623 fclose(f);
624 numcmd = i;
625 if (has_prog) {
626 int len;
627 char path[PATH_SIZE];
628
629 strcpy (path, script_prefix);
630 strcat (path, prog);
631 len = strlen (path);
632 if (path[len - 1] == '/') path[len - 1] = '\0';
633 if (access (path, R_OK | X_OK) == 0)
634 strcpy (boot_prog, path);
635 }
636 } /* End Function read_inittab */
637
638 static void sighup_handler (int sig)
639 {
640 int i,j;
641 int oldnum;
642 struct initline savetab[NUMCMD];
643 int had_already;
644
645 signal (SIGHUP, SIG_IGN);
646 memcpy(savetab, inittab, NUMCMD * sizeof(struct initline));
647 oldnum = numcmd;
648 read_inittab ();
649
650 for(i = 0; i < numcmd; i++) {
651 had_already = 0;
652 for(j = 0; j < oldnum; j++) {
653 if(!strcmp(savetab[j].tty, inittab[i].tty)) {
654 had_already = 1;
655 if((inittab[i].pid = savetab[j].pid) < 0)
656 spawn(i);
657 }
658 }
659 if (!had_already) spawn (i);
660 }
661 signal (SIGHUP, sighup_handler);
662 } /* End Function sighup_handler */
663
664 static void sigtstp_handler (int sig)
665 {
666 stopped = ~stopped;
667 if (!stopped) sighup_handler (sig);
668 } /* End Function sigtstp_handler */
669
670 static void sigterm_handler (int sig)
671 {
672 int i;
673
674 for (i = 0; i < numcmd; i++)
675 if (inittab[i].pid > 0) kill (inittab[i].pid, SIGTERM);
676 } /* End Function sigterm_handler */
677
678 static void sigint_handler (int sig)
679 {
680 pid_t pid;
681
682 caught_sigint = 1;
683 kill (rc_child, SIGKILL);
684 if (no_reboot) _exit (1) /*kill (0, SIGKILL)*/;
685 sync ();
686 sync ();
687 pid = fork ();
688 if (pid > 0) return; /* Parent */
689 if (pid == 0) /* Child: reboot properly... */
690 execl (_PATH_REBOOT, _PATH_REBOOT, (char *) 0);
691
692 /* fork or exec failed, try the hard way... */
693 my_reboot (LINUX_REBOOT_CMD_RESTART);
694 } /* End Function sigint_handler */
695
696 static void sigchild_handler (int sig)
697 {
698 if (!do_longjmp) return;
699 siglongjmp (jmp_env, 1);
700 }
701
702 static void sigquit_handler (int sig)
703 {
704 execl (_PATH_REBOOT, _PATH_REBOOT, NULL); /* It knows pid=1 must sleep */
705 }
706
707 #ifdef SET_TZ
708 static void set_tz (void)
709 {
710 FILE *f;
711 int len;
712
713 if((f=fopen(TZFILE, "r")) == (FILE *)NULL) return;
714 fgets(tzone, CMDSIZ-2, f);
715 fclose(f);
716 if((len=strlen(tzone)) < 2) return;
717 tzone[len-1] = 0; /* get rid of the '\n' */
718 setenv("TZ", tzone, 0);
719 }
720 #endif
721
722 static void write_wtmp (void)
723 {
724 int fd, lf;
725 struct utmp ut;
726
727 memset((char *)&ut, 0, sizeof(ut));
728 strcpy(ut.ut_line, "~");
729 memset(ut.ut_name, 0, sizeof(ut.ut_name));
730 time(&ut.ut_time);
731 ut.ut_type = BOOT_TIME;
732
733 if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
734 flock(lf, LOCK_EX|LOCK_NB); /* make sure init won't hang */
735 if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) >= 0) {
736 write(fd, (char *)&ut, sizeof(ut));
737 close(fd);
738 }
739 flock(lf, LOCK_UN|LOCK_NB);
740 close(lf);
741 }
742 } /* End Function write_wtmp */
743
744
745 struct needer_struct
746 {
747 struct needer_struct *next;
748 pid_t pid;
749 };
750
751 struct service_struct
752 {
753 struct service_struct *prev, *next; /* Script services chain */
754 struct needer_struct *needers; /* Needers waiting for service */
755 struct script_struct *attempting_providers;
756 int failed; /* TRUE if attempting provider failed badly */
757 char name[1];
758 };
759
760 struct script_struct
761 {
762 pid_t pid;
763 struct script_struct *prev, *next; /* For the list */
764 struct service_struct *first_service, *last_service; /*First is true name*/
765 struct script_struct *next_attempting_provider; /* Provider chain */
766 };
767
768 struct list_head
769 {
770 struct script_struct *first, *last;
771 unsigned int num_entries;
772 };
773
774
775 static struct list_head available_list = {NULL, NULL, 0};
776 static struct list_head starting_list = {NULL, NULL, 0};
777 static struct service_struct *unavailable_services = NULL; /* For needers */
778 static int num_needers = 0;
779
780
781 static int process_pidstat (pid_t pid, int status);
782 static void process_command (const struct command_struct *command);
783 static struct service_struct *find_service_in_list (const char *name,
784 struct service_struct *sv);
785 static struct script_struct *find_script_byname
786 (const char *name,struct list_head *head, struct service_struct **service);
787 static struct script_struct *find_script_bypid (pid_t pid,
788 struct list_head *head);
789 static void insert_entry (struct list_head *head, struct script_struct *entry);
790 static void remove_entry (struct list_head *head, struct script_struct *entry);
791 static void signal_needers (struct service_struct *service, int sig);
792 static void handle_nonworking (struct script_struct *script);
793 static int force_progress (void);
794 static void show_scripts (FILE *fp, const struct script_struct *script,
795 const char *type);
796 static const char *get_path (const char *file);
797
798
799 static pid_t mywait (int *status)
800 /* [RETURNS] The pid for a process to be reaped, 0 if no process is to be
801 reaped, and less than 0 if the boot scripts appear to have finished.
802 */
803 {
804 pid_t pid;
805 sigset_t ss;
806 long buffer[COMMAND_SIZE / sizeof (long)];
807 struct command_struct *command = (struct command_struct *) buffer;
808
809 if (initctl_fd < 0) return wait (status);
810 /* Some magic to avoid races which can result in lost signals */
811 command->command = -1;
812 if ( sigsetjmp (jmp_env, 1) )
813 { /* Jump from signal handler */
814 do_longjmp = 0;
815 process_command (command);
816 return 0;
817 }
818 sigemptyset (&ss); /* Block SIGCHLD so wait status cannot be lost */
819 sigaddset (&ss, SIGCHLD);
820 sigprocmask (SIG_BLOCK, &ss, NULL);
821 if ( ( pid = waitpid (-1, status, WNOHANG) ) > 0 )
822 {
823 sigprocmask (SIG_UNBLOCK, &ss, NULL);
824 return process_pidstat (pid, *status);
825 }
826 do_longjmp = 1; /* After this, SIGCHLD will cause a jump backwards */
827 sigprocmask (SIG_UNBLOCK, &ss, NULL);
828 read (initctl_fd, buffer, COMMAND_SIZE);
829 do_longjmp = 0;
830 process_command (command);
831 return 0;
832 } /* End Function mywait */
833
834 static pid_t process_pidstat (pid_t pid, int status)
835 /* [RETURNS] The pid for a process to be reaped, 0 if no process is to be
836 reaped, and less than 0 if the boot scripts appear to have finished.
837 */
838 {
839 int failed;
840 struct script_struct *script;
841 struct service_struct *service;
842
843 if ( ( script = find_script_bypid (pid, &starting_list) ) == NULL )
844 return pid;
845 remove_entry (&starting_list, script);
846 if ( WIFEXITED (status) && (WEXITSTATUS (status) == 0) )
847 {
848 struct script_struct *provider;
849
850 /* Notify needers and other providers */
851 for (service = script->first_service; service != NULL;
852 service = service->next)
853 {
854 signal_needers (service, SIG_PRESENT);
855 for (provider = service->attempting_providers; provider != NULL;
856 provider = provider->next_attempting_provider)
857 kill (provider->pid, SIG_PRESENT);
858 service->attempting_providers = NULL;
859 }
860 insert_entry (&available_list, script);
861 return force_progress ();
862 }
863 failed = ( WIFEXITED (status) && (WEXITSTATUS (status) == 2) ) ? 0 : 1;
864 for (service = script->first_service; service != NULL;
865 service = service->next)
866 service->failed = failed;
867 handle_nonworking (script);
868 return force_progress ();
869 } /* End Function process_pidstat */
870
871 static void process_command (const struct command_struct *command)
872 {
873 int ival;
874 struct script_struct *script;
875 struct service_struct *service;
876
877 switch (command->command)
878 {
879 case COMMAND_TEST:
880 kill (command->pid,
881 (find_script_byname (command->name, &available_list,
882 NULL) == NULL) ?
883 SIG_NOT_PRESENT : SIG_PRESENT);
884 break;
885 case COMMAND_NEED:
886 ival = run_command (command->name, command->name, command->pid);
887 if (ival == 0)
888 {
889 ++num_needers;
890 force_progress ();
891 }
892 else kill (command->pid, ival);
893 break;
894 case COMMAND_ROLLBACK:
895 if (command->name[0] == '\0') script = NULL;
896 else
897 {
898 if ( ( script = find_script_byname (command->name, &available_list,
899 NULL) ) == NULL )
900 {
901 kill (command->pid, SIG_NOT_PRESENT);
902 break;
903 }
904 }
905 while (script != available_list.first)
906 {
907 pid_t pid;
908 struct script_struct *victim = available_list.first;
909 char txt[256];
910
911 if ( ( pid = fork () ) == 0 ) /* Child */
912 {
913 for (ival = 1; ival < NSIG; ival++) signal (ival, SIG_DFL);
914 open ("/dev/console", O_RDONLY, 0);
915 open ("/dev/console", O_RDWR, 0);
916 dup2 (1, 2);
917 execlp (get_path (victim->first_service->name),
918 victim->first_service->name, "stop", NULL);
919 sprintf (txt, _("error stopping service: \"%s\""),
920 victim->first_service->name);
921 err (txt);
922 _exit (SIG_NOT_STOPPED);
923 }
924 else if (pid == -1) break; /* Error */
925 else /* Parent */
926 {
927 while (waitpid (pid, &ival, 0) != pid) /* Nothing */;
928 if ( WIFEXITED (ival) && (WEXITSTATUS (ival) == 0) )
929 {
930 sprintf (txt, "Stopped service: %s\n",
931 victim->first_service->name);
932 remove_entry (&available_list, victim);
933 free (victim);
934 err (txt);
935 }
936 else break;
937 }
938 }
939 kill (command->pid,
940 (script ==available_list.first) ? SIG_STOPPED : SIG_NOT_STOPPED);
941 break;
942 case COMMAND_DUMP_LIST:
943 if (fork () == 0) /* Do it in a child process so pid=1 doesn't block */
944 {
945 FILE *fp;
946
947 if ( ( fp = fopen (command->name, "w") ) == NULL ) _exit (1);
948 show_scripts (fp, available_list.first, "AVAILABLE");
949 show_scripts (fp, starting_list.first, "STARTING");
950 fputs ("UNAVAILABLE SERVICES:\n", fp);
951 for (service = unavailable_services; service != NULL;
952 service = service->next)
953 fprintf (fp, "%s (%s)\n", service->name,
954 service->failed ? "FAILED" : "not configured");
955 fclose (fp);
956 _exit (0);
957 }
958 break;
959 case COMMAND_PROVIDE:
960 /* Sanity check */
961 if ( ( script = find_script_bypid (command->ppid, &starting_list) )
962 == NULL )
963 {
964 kill (command->pid, SIG_NOT_CHILD);
965 break;
966 }
967 if (find_script_byname (command->name, &available_list, NULL) != NULL)
968 {
969 kill (command->pid, SIG_PRESENT);
970 break;
971 }
972 if (find_script_byname (command->name, &starting_list, &service)
973 != NULL)
974 { /* Someone else is trying to provide */
975 script->next_attempting_provider = service->attempting_providers;
976 service->attempting_providers = script;
977 break;
978 }
979 if ( ( service = find_service_in_list (command->name,
980 unavailable_services) )
981 == NULL )
982 { /* We're the first to try and provide: create it */
983 if ( ( service =
984 calloc (1, strlen (command->name) + sizeof *service) )
985 == NULL )
986 {
987 kill (command->pid, SIG_NOT_CHILD);
988 break;
989 }
990 strcpy (service->name, command->name);
991 }
992 else
993 { /* Orphaned service: unhook and grab it */
994 if (service->prev == NULL) unavailable_services = service->next;
995 else service->prev->next = service->next;
996 if (service->next != NULL) service->next->prev = service->prev;
997 service->next = NULL;
998 }
999 service->prev = script->last_service;
1000 script->last_service->next = service;
1001 script->last_service = service;
1002 kill (command->pid, SIG_NOT_PRESENT);
1003 break;
1004 case -1:
1005 default:
1006 break;
1007 }
1008 } /* End Function process_command */
1009
1010 static int run_command (const char *file, const char *name, pid_t pid)
1011 {
1012 struct script_struct *script;
1013 struct needer_struct *needer = NULL;
1014 struct service_struct *service;
1015
1016 if (find_script_byname (name, &available_list, NULL) != NULL)
1017 return SIG_PRESENT;
1018 if (pid != 0)
1019 {
1020 needer = calloc (1, sizeof *needer);
1021 if (needer == NULL) return SIG_FAILED;
1022 needer->pid = pid;
1023 }
1024 script = find_script_byname (name, &starting_list, &service);
1025 if (script == NULL)
1026 service = find_service_in_list (name, unavailable_services);
1027 if (service == NULL)
1028 {
1029 int i;
1030 char txt[1024];
1031
1032 if ( ( script = calloc (1, sizeof *script) ) == NULL )
1033 {
1034 if (needer != NULL) free (needer);
1035 return SIG_FAILED;
1036 }
1037 service = calloc (1, strlen (name) + sizeof *service);
1038 if (service == NULL)
1039 {
1040 free (script);
1041 return SIG_FAILED;
1042 }
1043 strcpy (service->name, name);
1044 switch ( script->pid = fork () )
1045 {
1046 case 0: /* Child */
1047 for (i = 1; i < NSIG; i++) signal (i, SIG_DFL);
1048 execlp (get_path (file), service->name, "start", NULL);
1049 sprintf (txt, "error running programme: \"%s\"\n", service->name);
1050 err ( _(txt) );
1051 _exit (SIG_FAILED);
1052 break;
1053 case -1: /* Error */
1054 service->next = unavailable_services;
1055 if (unavailable_services != NULL)
1056 unavailable_services->prev = service;
1057 unavailable_services = service;
1058 free (script);
1059 if (needer != NULL) free (needer);
1060 return SIG_FAILED;
1061 /*break;*/
1062 default: /* Parent */
1063 script->first_service = service;
1064 script->last_service = service;
1065 insert_entry (&starting_list, script);
1066 sched_yield ();
1067 break;
1068 }
1069 }
1070 if (needer == NULL) return 0;
1071 needer->next = service->needers;
1072 service->needers = needer;
1073 return 0;
1074 } /* End Function run_command */
1075
1076 static struct service_struct *find_service_in_list (const char *name,
1077 struct service_struct *sv)
1078 {
1079 for (; sv != NULL; sv = sv->next)
1080 if (strcmp (sv->name, name) == 0) return (sv);
1081 return NULL;
1082 } /* End Function find_service_in_list */
1083
1084 static struct script_struct *find_script_byname (const char *name,
1085 struct list_head *head,
1086 struct service_struct **service)
1087 {
1088 struct script_struct *script;
1089
1090 for (script = head->first; script != NULL; script = script->next)
1091 {
1092 struct service_struct *sv;
1093
1094 if ( ( sv = find_service_in_list (name, script->first_service) )
1095 != NULL )
1096 {
1097 if (service != NULL) *service = sv;
1098 return (script);
1099 }
1100 }
1101 if (service != NULL) *service = NULL;
1102 return NULL;
1103 } /* End Function find_script_byname */
1104
1105 static struct script_struct *find_script_bypid (pid_t pid,
1106 struct list_head *head)
1107 {
1108 struct script_struct *script;
1109
1110 for (script = head->first; script != NULL; script = script->next)
1111 if (script->pid == pid) return (script);
1112 return NULL;
1113 } /* End Function find_script_bypid */
1114
1115 static void insert_entry (struct list_head *head, struct script_struct *entry)
1116 {
1117 if (entry == NULL) return;
1118 entry->prev = NULL;
1119 entry->next = head->first;
1120 if (head->first != NULL) head->first->prev = entry;
1121 head->first = entry;
1122 if (head->last == NULL) head->last = entry;
1123 ++head->num_entries;
1124 } /* End Function insert_entry */
1125
1126 static void remove_entry (struct list_head *head, struct script_struct *entry)
1127 {
1128 if (entry->prev == NULL) head->first = entry->next;
1129 else entry->prev->next = entry->next;
1130 if (entry->next == NULL) head->last = entry->prev;
1131 else entry->next->prev = entry->prev;
1132 --head->num_entries;
1133 } /* End Function remove_entry */
1134
1135 static void signal_needers (struct service_struct *service, int sig)
1136 {
1137 struct needer_struct *needer, *next_needer;
1138
1139 for (needer = service->needers; needer != NULL; needer = next_needer)
1140 {
1141 kill (needer->pid, sig);
1142 next_needer = needer->next;
1143 free (needer);
1144 --num_needers;
1145 }
1146 service->needers = NULL;
1147 } /* End Function signal_needers */
1148
1149 static void handle_nonworking (struct script_struct *script)
1150 {
1151 struct service_struct *service, *next;
1152
1153 for (service = script->first_service; service != NULL; service = next)
1154 {
1155 struct script_struct *provider = service->attempting_providers;
1156
1157 next = service->next;
1158 if (provider == NULL)
1159 {
1160 service->prev = NULL;
1161 service->next = unavailable_services;
1162 if (unavailable_services != NULL)
1163 unavailable_services->prev = service;
1164 unavailable_services = service;
1165 continue;
1166 }
1167 service->attempting_providers = provider->next_attempting_provider;
1168 provider->last_service->next = service;
1169 service->prev = provider->last_service;
1170 provider->last_service = service;
1171 service->next = NULL;
1172 kill (provider->pid, SIG_NOT_PRESENT);
1173 }
1174 free (script);
1175 } /* End Function handle_nonworking */
1176
1177 static int force_progress (void)
1178 /* [RETURNS] 0 if boot scripts are still running, else -1.
1179 */
1180 {
1181 struct service_struct *service;
1182
1183 if (starting_list.num_entries > num_needers) return 0;
1184 /* No progress can be made: signal needers */
1185 for (service = unavailable_services; service != NULL;
1186 service = service->next)
1187 signal_needers (service,
1188 service->failed ? SIG_FAILED : SIG_NOT_PRESENT);
1189 return (starting_list.num_entries < 1) ? -1 : 0;
1190 } /* End Function force_progress */
1191
1192 static void show_scripts (FILE *fp, const struct script_struct *script,
1193 const char *type)
1194 {
1195 fprintf (fp, "%s SERVICES:\n", type);
1196 for (; script != NULL; script = script->next)
1197 {
1198 struct service_struct *service = script->first_service;
1199
1200 fputs (service->name, fp);
1201 for (service = service->next; service != NULL; service = service->next)
1202 fprintf (fp, " (%s)", service->name);
1203 putc ('\n', fp);
1204 }
1205 } /* End Function show_scripts */
1206
1207 static const char *get_path (const char *file)
1208 {
1209 char *p1, *p2;
1210 static char path[PATH_SIZE];
1211
1212 if (file[0] == '/') return file;
1213 if (init_path[0] == '\0') return file;
1214 for (p1 = init_path; *p1 != '\0'; p1 = p2)
1215 {
1216 if ( ( p2 = strchr (p1, ':') ) == NULL )
1217 p2 = p1 + strlen (p1);
1218 strncpy (path, p1, p2 - p1);
1219 path[p2 - p1] = '/';
1220 strcpy (path + (p2 - p1) + 1, file);
1221 if (*p2 == ':') ++p2;
1222 if (access (path, X_OK) == 0) return path;
1223 }
1224 return file;
1225 } /* End Function get_path */