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