/[baselayout]/trunk/src/rc.c
Gentoo

Diff of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 2640 Revision 2814
8 Gentoo einfo family of informational functions. 8 Gentoo einfo family of informational functions.
9 9
10 Copyright 2007 Gentoo Foundation 10 Copyright 2007 Gentoo Foundation
11 Released under the GPLv2 11 Released under the GPLv2
12 */ 12 */
13
14#define APPLET "rc"
13 15
14#include <sys/types.h> 16#include <sys/types.h>
15#include <sys/stat.h> 17#include <sys/stat.h>
16#include <sys/utsname.h> 18#include <sys/utsname.h>
17#include <sys/wait.h> 19#include <sys/wait.h>
18#include <errno.h> 20#include <errno.h>
19#include <ctype.h> 21#include <ctype.h>
22#include <getopt.h>
20#include <libgen.h> 23#include <libgen.h>
21#include <limits.h> 24#include <limits.h>
22#include <stdbool.h> 25#include <stdbool.h>
23#include <stdio.h> 26#include <stdio.h>
24#include <stdlib.h> 27#include <stdlib.h>
25#include <signal.h> 28#include <signal.h>
26#include <string.h> 29#include <string.h>
27#include <termios.h> 30#include <termios.h>
28#include <unistd.h> 31#include <unistd.h>
29 32
33#include "builtins.h"
30#include "einfo.h" 34#include "einfo.h"
31#include "rc.h" 35#include "rc.h"
32#include "rc-misc.h" 36#include "rc-misc.h"
33#include "rc-plugin.h" 37#include "rc-plugin.h"
34#include "strlist.h" 38#include "strlist.h"
35 39
36#define INITSH RC_LIBDIR "sh/init.sh" 40#define INITSH RC_LIBDIR "sh/init.sh"
37#define HALTSH RC_INITDIR "halt.sh" 41#define INITEARLYSH RC_LIBDIR "sh/init-early.sh"
42#define HALTSH RC_INITDIR "halt.sh"
43#define SULOGIN "/sbin/sulogin"
38 44
39#define RC_SVCDIR_STARTING RC_SVCDIR "starting/" 45#define RC_SVCDIR_STARTING RC_SVCDIR "starting/"
40#define RC_SVCDIR_INACTIVE RC_SVCDIR "inactive/" 46#define RC_SVCDIR_INACTIVE RC_SVCDIR "inactive/"
41#define RC_SVCDIR_STARTED RC_SVCDIR "started/" 47#define RC_SVCDIR_STARTED RC_SVCDIR "started/"
42#define RC_SVCDIR_COLDPLUGGED RC_SVCDIR "coldplugged/" 48#define RC_SVCDIR_COLDPLUGGED RC_SVCDIR "coldplugged/"
43 49
44#define INTERACTIVE RC_SVCDIR "interactive" 50#define INTERACTIVE RC_SVCDIR "interactive"
45 51
46#define DEVBOOT "/dev/.rcboot" 52#define DEVBOOT "/dev/.rcboot"
47 53
48/* Cleanup anything in main */ 54/* Cleanup anything in main */
49#define CHAR_FREE(_item) if (_item) { \ 55#define CHAR_FREE(_item) if (_item) { \
50 free (_item); \ 56 free (_item); \
51 _item = NULL; \ 57 _item = NULL; \
52} 58}
53 59
54extern char **environ; 60extern char **environ;
61
62static char *RUNLEVEL = NULL;
63static char *PREVLEVEL = NULL;
55 64
56static char *applet = NULL; 65static char *applet = NULL;
57static char **env = NULL; 66static char **env = NULL;
58static char **newenv = NULL; 67static char **newenv = NULL;
59static char **coldplugged_services = NULL; 68static char **coldplugged_services = NULL;
63static char **types = NULL; 72static char **types = NULL;
64static char *tmp = NULL; 73static char *tmp = NULL;
65 74
66struct termios *termios_orig = NULL; 75struct termios *termios_orig = NULL;
67 76
77typedef struct pidlist
78{
79 pid_t pid;
80 struct pidlist *next;
81} pidlist_t;
82static pidlist_t *service_pids = NULL;
83
68static void cleanup (void) 84static void cleanup (void)
69{ 85{
86 if (applet && strcmp (applet, "rc") == 0) {
87 pidlist_t *pl = service_pids;
88
70 rc_plugin_unload (); 89 rc_plugin_unload ();
71 90
72 if (termios_orig) { 91 if (! rc_in_plugin && termios_orig) {
73 tcsetattr (STDIN_FILENO, TCSANOW, termios_orig); 92 tcsetattr (fileno (stdin), TCSANOW, termios_orig);
74 free (termios_orig); 93 free (termios_orig);
75 } 94 }
76 95
96 while (pl) {
97 pidlist_t *p = pl->next;
98 free (pl);
99 pl = p;
100 }
101
77 rc_strlist_free (env); 102 rc_strlist_free (env);
78 rc_strlist_free (newenv); 103 rc_strlist_free (newenv);
79 rc_strlist_free (coldplugged_services); 104 rc_strlist_free (coldplugged_services);
80 rc_strlist_free (stop_services); 105 rc_strlist_free (stop_services);
81 rc_strlist_free (start_services); 106 rc_strlist_free (start_services);
82 rc_free_deptree (deptree); 107 rc_free_deptree (deptree);
83 rc_strlist_free (types); 108 rc_strlist_free (types);
84 109
85 /* Clean runlevel start, stop markers */ 110 /* Clean runlevel start, stop markers */
111 if (! rc_in_plugin) {
86 if (rc_is_dir (RC_SVCDIR "softscripts.new")) 112 if (rc_is_dir (RC_SVCDIR "softscripts.new"))
87 rc_rm_dir (RC_SVCDIR "softscripts.new", true); 113 rc_rm_dir (RC_SVCDIR "softscripts.new", true);
88 if (rc_is_dir (RC_SVCDIR "softscripts.old")) 114 if (rc_is_dir (RC_SVCDIR "softscripts.old"))
89 rc_rm_dir (RC_SVCDIR "softscripts.old", true); 115 rc_rm_dir (RC_SVCDIR "softscripts.old", true);
116 }
117 }
90 118
91 free (applet); 119 free (applet);
92} 120}
93 121
94static int do_e (int argc, char **argv) 122static int do_e (int argc, char **argv)
124 else { 152 else {
125 argc--; 153 argc--;
126 argv++; 154 argv++;
127 } 155 }
128 } 156 }
129 else
130 retval = EXIT_FAILURE;
131 } 157 }
132 158
133 if (argc > 0) { 159 if (argc > 0) {
134 for (i = 0; i < argc; i++) 160 for (i = 0; i < argc; i++)
135 l += strlen (argv[i]) + 1; 161 l += strlen (argv[i]) + 1;
189 eoutdent (); 215 eoutdent ();
190 else if (strcmp (applet, "veindent") == 0) 216 else if (strcmp (applet, "veindent") == 0)
191 eindentv (); 217 eindentv ();
192 else if (strcmp (applet, "veoutdent") == 0) 218 else if (strcmp (applet, "veoutdent") == 0)
193 eoutdentv (); 219 eoutdentv ();
194 else if (strcmp (applet, "eflush") == 0)
195 eflush ();
196 else { 220 else {
197 eerror ("%s: unknown applet", applet); 221 eerror ("%s: unknown applet", applet);
198 retval = EXIT_FAILURE; 222 retval = EXIT_FAILURE;
199 } 223 }
200 224
314 eerrorx ("%s: unknown applet", applet); 338 eerrorx ("%s: unknown applet", applet);
315 339
316 return (ok ? EXIT_SUCCESS : EXIT_FAILURE); 340 return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
317} 341}
318 342
343#ifdef __linux__
344static char *proc_getent (const char *ent)
345{
346 FILE *fp;
347 char buffer[RC_LINEBUFFER];
348 char *p;
349 char *value = NULL;
350 int i;
351
352 if (! (fp = fopen ("/proc/cmdline", "r"))) {
353 eerror ("failed to open `/proc/cmdline': %s", strerror (errno));
354 return (NULL);
355 }
356
357 memset (buffer, 0, sizeof (buffer));
358 if (fgets (buffer, RC_LINEBUFFER, fp) &&
359 (p = strstr (buffer, ent)))
360 {
361 i = p - buffer;
362 if (i == '\0' || buffer[i - 1] == ' ') {
363 /* Trim the trailing carriage return if present */
364 i = strlen (buffer) - 1;
365 if (buffer[i] == '\n')
366 buffer[i] = 0;
367
368 p += strlen (ent);
369 if (*p == '=')
370 p++;
371 value = strdup (strsep (&p, " "));
372 }
373 } else
374 errno = ENOENT;
375 fclose (fp);
376
377 return (value);
378}
379#endif
380
319static char read_key (bool block) 381static char read_key (bool block)
320{ 382{
321 struct termios termios; 383 struct termios termios;
322 char c = 0; 384 char c = 0;
385 int fd = fileno (stdin);
323 386
324 if (! isatty (STDIN_FILENO)) 387 if (! isatty (fd))
325 return (false); 388 return (false);
326 389
327 /* Now save our terminal settings. We need to restore them at exit as we 390 /* Now save our terminal settings. We need to restore them at exit as we
328 will be changing it for non-blocking reads for Interactive */ 391 will be changing it for non-blocking reads for Interactive */
329 if (! termios_orig) { 392 if (! termios_orig) {
330 termios_orig = rc_xmalloc (sizeof (struct termios)); 393 termios_orig = rc_xmalloc (sizeof (struct termios));
331 tcgetattr (STDIN_FILENO, termios_orig); 394 tcgetattr (fd, termios_orig);
332 } 395 }
333 396
334 tcgetattr (STDIN_FILENO, &termios); 397 tcgetattr (fd, &termios);
335 termios.c_lflag &= ~(ICANON | ECHO); 398 termios.c_lflag &= ~(ICANON | ECHO);
336 if (block) 399 if (block)
337 termios.c_cc[VMIN] = 1; 400 termios.c_cc[VMIN] = 1;
338 else { 401 else {
339 termios.c_cc[VMIN] = 0; 402 termios.c_cc[VMIN] = 0;
340 termios.c_cc[VTIME] = 0; 403 termios.c_cc[VTIME] = 0;
341 } 404 }
342 tcsetattr (STDIN_FILENO, TCSANOW, &termios); 405 tcsetattr (fd, TCSANOW, &termios);
343 406
344 read (STDIN_FILENO, &c, 1); 407 read (fd, &c, 1);
345 408
346 tcsetattr (STDIN_FILENO, TCSANOW, termios_orig); 409 tcsetattr (fd, TCSANOW, termios_orig);
347 410
348 return (c); 411 return (c);
349} 412}
350 413
351static bool want_interactive (void) 414static bool want_interactive (void)
352{ 415{
416 char c;
417
418 if (PREVLEVEL &&
419 strcmp (PREVLEVEL, "N") != 0 &&
420 strcmp (PREVLEVEL, "S") != 0 &&
421 strcmp (PREVLEVEL, "1") != 0)
422 return (false);
423
424 if (! rc_is_env ("RC_INTERACTIVE", "yes"))
425 return (false);
426
353 char c = read_key (false); 427 c = read_key (false);
354 return ((c == 'I' || c == 'i') ? true : false); 428 return ((c == 'I' || c == 'i') ? true : false);
355} 429}
356 430
357static void mark_interactive (void) 431static void mark_interactive (void)
358{ 432{
369 /* VPS systems cannot do an sulogin */ 443 /* VPS systems cannot do an sulogin */
370 if (e && strcmp (e, "VPS") == 0) { 444 if (e && strcmp (e, "VPS") == 0) {
371 execl ("/sbin/halt", "/sbin/halt", "-f", (char *) NULL); 445 execl ("/sbin/halt", "/sbin/halt", "-f", (char *) NULL);
372 eerrorx ("%s: unable to exec `/sbin/halt': %s", applet, strerror (errno)); 446 eerrorx ("%s: unable to exec `/sbin/halt': %s", applet, strerror (errno));
373 } 447 }
448#endif
449
450 newenv = rc_filter_env ();
374 451
375 if (cont) { 452 if (cont) {
376 int status = 0; 453 int status = 0;
454#ifdef __linux__
455 char *tty = ttyname (fileno (stdout));
456#endif
457
377 pid_t pid = vfork(); 458 pid_t pid = vfork ();
378 459
379 if (pid == -1) 460 if (pid == -1)
380 eerrorx ("%s: vfork: %s", applet, strerror (errno)); 461 eerrorx ("%s: vfork: %s", applet, strerror (errno));
381 if (pid == 0) { 462 if (pid == 0) {
382 newenv = rc_filter_env (); 463#ifdef __linux__
383 execl ("/sbin/sulogin", "/sbin/sulogin", 464 if (tty)
384 getenv ("CONSOLE"), (char *) NULL, newenv); 465 execle (SULOGIN, SULOGIN, tty, (char *) NULL, newenv);
466 else
467 execle (SULOGIN, SULOGIN, (char *) NULL, newenv);
468
385 eerror ("%s: unable to exec `/sbin/sulogin': %s", applet, 469 eerror ("%s: unable to exec `%s': %s", applet, SULOGIN,
386 strerror (errno)); 470 strerror (errno));
471#else
472 execle ("/bin/sh", "/bin/sh", (char *) NULL, newenv);
473 eerror ("%s: unable to exec `/bin/sh': %s", applet,
474 strerror (errno));
475#endif
387 _exit (EXIT_FAILURE); 476 _exit (EXIT_FAILURE);
388 } 477 }
389 waitpid (pid, &status, 0); 478 waitpid (pid, &status, 0);
390 } else { 479 } else {
391 newenv = rc_filter_env (); 480#ifdef __linux
392 execl ("/sbin/sulogin", "/sbin/sulogin", 481 execle ("/sbin/sulogin", "/sbin/sulogin", (char *) NULL, newenv);
393 getenv ("CONSOLE"), (char *) NULL, newenv);
394 eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno)); 482 eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno));
395 }
396#else 483#else
397 exit (cont ? EXIT_FAILURE : EXIT_SUCCESS); 484 exit (EXIT_SUCCESS);
398#endif 485#endif
486 }
399} 487}
400 488
401static void single_user (void) 489static void single_user (void)
402{ 490{
403#ifdef __linux__ 491#ifdef __linux__
415static void set_ksoftlevel (const char *runlevel) 503static void set_ksoftlevel (const char *runlevel)
416{ 504{
417 FILE *fp; 505 FILE *fp;
418 506
419 if (! runlevel || 507 if (! runlevel ||
420 strcmp (runlevel, RC_LEVEL_BOOT) == 0 || 508 strcmp (runlevel, getenv ("RC_BOOTLEVEL")) == 0 ||
421 strcmp (runlevel, RC_LEVEL_SINGLE) == 0 || 509 strcmp (runlevel, RC_LEVEL_SINGLE) == 0 ||
422 strcmp (runlevel, RC_LEVEL_SYSINIT) == 0) 510 strcmp (runlevel, RC_LEVEL_SYSINIT) == 0)
423 { 511 {
424 if (rc_exists (RC_SVCDIR "ksoftlevel") && 512 if (rc_exists (RC_SVCDIR "ksoftlevel") &&
425 unlink (RC_SVCDIR "ksoftlevel") != 0) 513 unlink (RC_SVCDIR "ksoftlevel") != 0)
434 522
435 fprintf (fp, "%s", runlevel); 523 fprintf (fp, "%s", runlevel);
436 fclose (fp); 524 fclose (fp);
437} 525}
438 526
527static int get_ksoftlevel (char *buffer, int buffer_len)
528{
529 FILE *fp;
530 int i = 0;
531
532 if (! rc_exists (RC_SVCDIR "ksoftlevel"))
533 return 0;
534
535 if (! (fp = fopen (RC_SVCDIR "ksoftlevel", "r"))) {
536 eerror ("fopen `%s': %s", RC_SVCDIR "ksoftlevel",
537 strerror (errno));
538 return -1;
539 }
540
541 if (fgets (buffer, buffer_len, fp)) {
542 i = strlen (buffer) - 1;
543 if (buffer[i] == '\n')
544 buffer[i] = 0;
545 }
546
547 fclose (fp);
548 return i;
549}
550
439static void wait_for_services () 551static void wait_for_services ()
440{ 552{
441 int status = 0; 553 int status = 0;
442 struct timeval tv;
443 while (wait (&status) != -1); 554 while (wait (&status) != -1);
555}
444 556
445 /* Wait for a little bit to flush our ebuffer */ 557static void add_pid (pid_t pid)
446 tv.tv_usec = 50000; 558{
447 tv.tv_sec = 0; 559 pidlist_t *sp = service_pids;
448 select (0, NULL, NULL, NULL, &tv); 560 if (sp) {
561 while (sp->next)
562 sp = sp->next;
563 sp->next = rc_xmalloc (sizeof (pidlist_t));
564 sp = sp->next;
565 } else
566 sp = service_pids = rc_xmalloc (sizeof (pidlist_t));
567 memset (sp, 0, sizeof (pidlist_t));
568 sp->pid = pid;
569}
570
571static void remove_pid (pid_t pid)
572{
573 pidlist_t *last = NULL;
574 pidlist_t *pl;
575
576 for (pl = service_pids; pl; pl = pl->next) {
577 if (pl->pid == pid) {
578 if (last)
579 last->next = pl->next;
580 else
581 service_pids = pl->next;
582 free (pl);
583 break;
584 }
585 last = pl;
586 }
449} 587}
450 588
451static void handle_signal (int sig) 589static void handle_signal (int sig)
452{ 590{
453 int serrno = errno; 591 int serrno = errno;
454 char signame[10] = { '\0' }; 592 char signame[10] = { '\0' };
455 char *run; 593 pidlist_t *pl;
456 char *prev; 594 pid_t pid;
595 int status = 0;
457 596
458 switch (sig) { 597 switch (sig) {
598 case SIGCHLD:
599 do {
600 pid = waitpid (-1, &status, WNOHANG);
601 if (pid < 0) {
602 if (errno != ECHILD)
603 eerror ("waitpid: %s", strerror (errno));
604 return;
605 }
606 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
607
608 /* Remove that pid from our list */
609 if (pid > 0)
610 remove_pid (pid);
611 break;
612
459 case SIGINT: 613 case SIGINT:
460 if (! signame[0]) 614 if (! signame[0])
461 snprintf (signame, sizeof (signame), "SIGINT"); 615 snprintf (signame, sizeof (signame), "SIGINT");
462 case SIGTERM: 616 case SIGTERM:
463 if (! signame[0]) 617 if (! signame[0])
467 snprintf (signame, sizeof (signame), "SIGQUIT"); 621 snprintf (signame, sizeof (signame), "SIGQUIT");
468 eerrorx ("%s: caught %s, aborting", applet, signame); 622 eerrorx ("%s: caught %s, aborting", applet, signame);
469 case SIGUSR1: 623 case SIGUSR1:
470 eerror ("rc: Aborting!"); 624 eerror ("rc: Aborting!");
471 /* Kill any running services we have started */ 625 /* Kill any running services we have started */
626
472 signal (SIGTERM, SIG_IGN); 627 signal (SIGCHLD, SIG_IGN);
473 killpg (getpgrp (), SIGTERM); 628 for (pl = service_pids; pl; pl = pl->next)
629 kill (pl->pid, SIGTERM);
474 630
475 /* Notify plugins we are aborting */ 631 /* Notify plugins we are aborting */
476 rc_plugin_run (rc_hook_abort, "rc"); 632 rc_plugin_run (rc_hook_abort, NULL);
477 633
478 run = getenv ("RUNLEVEL");
479 prev = getenv ("PREVLEVEL");
480 /* Only drop into single user mode if we're booting */ 634 /* Only drop into single user mode if we're booting */
481 if ((prev && strcmp (prev, "S") == 0) || 635 if ((PREVLEVEL &&
482 (run && strcmp (run, "S") == 0)) 636 (strcmp (PREVLEVEL, "S") == 0 ||
637 strcmp (PREVLEVEL, "1") == 0)) ||
638 (RUNLEVEL &&
639 (strcmp (RUNLEVEL, "S") == 0 ||
640 strcmp (RUNLEVEL, "1") == 0)))
483 single_user (); 641 single_user ();
484 642
485 exit (EXIT_FAILURE); 643 exit (EXIT_FAILURE);
486 break; 644 break;
487 645
491 649
492 /* Restore errno */ 650 /* Restore errno */
493 errno = serrno; 651 errno = serrno;
494} 652}
495 653
654static void run_script (const char *script) {
655 int status = 0;
656 pid_t pid = vfork ();
657
658 if (pid < 0)
659 eerrorx ("%s: vfork: %s", applet, strerror (errno));
660 else if (pid == 0) {
661 execl (script, script, (char *) NULL);
662 eerror ("%s: unable to exec `%s': %s",
663 script, applet, strerror (errno));
664 _exit (EXIT_FAILURE);
665 }
666
667 do {
668 pid_t wpid = waitpid (pid, &status, 0);
669 if (wpid < 1)
670 eerror ("waitpid: %s", strerror (errno));
671 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
672
673 if (! WIFEXITED (status) || ! WEXITSTATUS (status) == 0)
674 exit (EXIT_FAILURE);
675}
676
677#include "_usage.h"
678#define getoptstring getoptstring_COMMON
679static struct option longopts[] = {
680 longopts_COMMON
681 { NULL, 0, NULL, 0}
682};
683#include "_usage.c"
684
496int main (int argc, char **argv) 685int main (int argc, char **argv)
497{ 686{
498 char *RUNLEVEL = NULL;
499 char *PREVLEVEL = NULL;
500 char *runlevel = NULL; 687 char *runlevel = NULL;
688 const char *bootlevel = NULL;
501 char *newlevel = NULL; 689 char *newlevel = NULL;
502 char *service = NULL; 690 char *service = NULL;
503 char **deporder = NULL; 691 char **deporder = NULL;
504 int i = 0; 692 int i = 0;
505 int j = 0; 693 int j = 0;
506 bool going_down = false; 694 bool going_down = false;
507 bool interactive = false; 695 bool interactive = false;
508 int depoptions = RC_DEP_STRICT | RC_DEP_TRACE; 696 int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
509 char ksoftbuffer [PATH_MAX]; 697 char ksoftbuffer [PATH_MAX];
510 char pidstr[6]; 698 char pidstr[6];
699 int opt;
511 700
701 atexit (cleanup);
512 if (argv[0]) 702 if (argv[0])
513 applet = rc_xstrdup (basename (argv[0])); 703 applet = rc_xstrdup (basename (argv[0]));
514 704
515 if (! applet) 705 if (! applet)
516 eerrorx ("arguments required"); 706 eerrorx ("arguments required");
707
708 /* These used to be programs in their own right, so we shouldn't
709 * touch argc or argv for them */
710 if (strcmp (applet, "env-update") == 0)
711 exit (env_update (argc, argv));
712 else if (strcmp (applet, "fstabinfo") == 0)
713 exit (fstabinfo (argc, argv));
714 else if (strcmp (applet, "mountinfo") == 0)
715 exit (mountinfo (argc, argv));
716 else if (strcmp (applet, "rc-depend") == 0)
717 exit (rc_depend (argc, argv));
718 else if (strcmp (applet, "rc-status") == 0)
719 exit (rc_status (argc, argv));
720 else if (strcmp (applet, "rc-update") == 0 ||
721 strcmp (applet, "update-rc") == 0)
722 exit (rc_update (argc, argv));
723 else if (strcmp (applet, "runscript") == 0)
724 exit (runscript (argc, argv));
725 else if (strcmp (applet, "start-stop-daemon") == 0)
726 exit (start_stop_daemon (argc, argv));
517 727
518 argc--; 728 argc--;
519 argv++; 729 argv++;
520 730
521 /* Handle multicall stuff */ 731 /* Handle multicall stuff */
551 } 761 }
552 762
553 if (strcmp (applet, "rc" ) != 0) 763 if (strcmp (applet, "rc" ) != 0)
554 eerrorx ("%s: unknown applet", applet); 764 eerrorx ("%s: unknown applet", applet);
555 765
556 /* OK, so we really are the main RC process 766 /* Change dir to / to ensure all scripts don't use stuff in pwd */
557 Only root should be able to run us */ 767 chdir ("/");
558 if (geteuid () != 0)
559 eerrorx ("%s: root access required", applet);
560 768
561 atexit (cleanup); 769 /* RUNLEVEL is set by sysvinit as is a magic number
562 newlevel = argv[0]; 770 RC_SOFTLEVEL is set by us and is the name for this magic number
771 even though all our userland documentation refers to runlevel */
772 RUNLEVEL = getenv ("RUNLEVEL");
773 PREVLEVEL = getenv ("PREVLEVEL");
563 774
564 /* Setup a signal handler */ 775 /* Setup a signal handler */
565 signal (SIGINT, handle_signal); 776 signal (SIGINT, handle_signal);
566 signal (SIGQUIT, handle_signal); 777 signal (SIGQUIT, handle_signal);
567 signal (SIGTERM, handle_signal); 778 signal (SIGTERM, handle_signal);
600 putenv (p); 811 putenv (p);
601 812
602 /* We don't free our list as that would be null in environ */ 813 /* We don't free our list as that would be null in environ */
603 } 814 }
604 815
816 argc++;
817 argv--;
818 while ((opt = getopt_long (argc, argv, getoptstring,
819 longopts, (int *) 0)) != -1)
820 {
821 switch (opt) {
822 case_RC_COMMON_GETOPT
823 }
824 }
825
826 newlevel = argv[optind++];
827
828 /* OK, so we really are the main RC process
829 Only root should be able to run us */
830 if (geteuid () != 0)
831 eerrorx ("%s: root access required", applet);
832
605 /* Enable logging */ 833 /* Enable logging */
606 setenv ("RC_ELOG", "rc", 1); 834 setenv ("RC_ELOG", "rc", 1);
607 835
608 /* Export our PID */ 836 /* Export our PID */
609 snprintf (pidstr, sizeof (pidstr), "%d", getpid ()); 837 snprintf (pidstr, sizeof (pidstr), "%d", getpid ());
610 setenv ("RC_PID", pidstr, 1); 838 setenv ("RC_PID", pidstr, 1);
611 839
612 interactive = rc_exists (INTERACTIVE); 840 interactive = rc_exists (INTERACTIVE);
613 rc_plugin_load (); 841 rc_plugin_load ();
614 842
615 /* RUNLEVEL is set by sysvinit as is a magic number 843 /* Load current softlevel */
616 RC_SOFTLEVEL is set by us and is the name for this magic number 844 bootlevel = getenv ("RC_BOOTLEVEL");
617 even though all our userland documentation refers to runlevel */ 845 runlevel = rc_get_runlevel ();
618 RUNLEVEL = getenv ("RUNLEVEL");
619 PREVLEVEL = getenv ("PREVLEVEL");
620
621 if (RUNLEVEL && newlevel) {
622 if (strcmp (RUNLEVEL, "S") == 0 || strcmp (RUNLEVEL, "1") == 0) {
623 /* OK, we're either in runlevel 1 or single user mode */
624 if (strcmp (newlevel, RC_LEVEL_SYSINIT) == 0) {
625 struct utsname uts;
626 pid_t pid;
627 pid_t wpid;
628 int status = 0;
629#ifdef __linux__
630 FILE *fp;
631#endif
632
633 uname (&uts);
634
635 printf ("\n");
636 printf (" %sGentoo/%s; %shttp://www.gentoo.org/%s"
637 "\n Copyright 1999-2007 Gentoo Foundation; "
638 "Distributed under the GPLv2\n\n",
639 ecolor (ecolor_good), uts.sysname, ecolor (ecolor_bracket),
640 ecolor (ecolor_normal));
641
642 printf ("Press %sI%s to enter interactive boot mode\n\n",
643 ecolor (ecolor_good), ecolor (ecolor_normal));
644
645 setenv ("RC_SOFTLEVEL", newlevel, 1);
646 rc_plugin_run (rc_hook_runlevel_start_in, newlevel);
647
648 if ((pid = vfork ()) == -1)
649 eerrorx ("%s: vfork: %s", applet, strerror (errno));
650
651 if (pid == 0) {
652 execl (INITSH, INITSH, (char *) NULL);
653 eerror ("%s: unable to exec `" INITSH "': %s",
654 applet, strerror (errno));
655 _exit (EXIT_FAILURE);
656 }
657
658 do {
659 wpid = waitpid (pid, &status, 0);
660 if (wpid < 1)
661 eerror ("waitpid: %s", strerror (errno));
662 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
663
664 if (! WIFEXITED (status) || ! WEXITSTATUS (status) == 0)
665 exit (EXIT_FAILURE);
666
667 /* If we requested a softlevel, save it now */
668#ifdef __linux__
669 set_ksoftlevel (NULL);
670
671 if ((fp = fopen ("/proc/cmdline", "r"))) {
672 char buffer[RC_LINEBUFFER];
673 char *soft;
674
675 memset (buffer, 0, sizeof (buffer));
676 if (fgets (buffer, RC_LINEBUFFER, fp) &&
677 (soft = strstr (buffer, "softlevel=")))
678 {
679 i = soft - buffer;
680 if (i == 0 || buffer[i - 1] == ' ') {
681 char *level;
682
683 /* Trim the trailing carriage return if present */
684 i = strlen (buffer) - 1;
685 if (buffer[i] == '\n')
686 buffer[i] = 0;
687
688 soft += strlen ("softlevel=");
689 level = strsep (&soft, " ");
690 set_ksoftlevel (level);
691 }
692 }
693 fclose (fp);
694 }
695#endif
696 rc_plugin_run (rc_hook_runlevel_start_out, newlevel);
697
698 if (want_interactive ())
699 mark_interactive ();
700
701 exit (EXIT_SUCCESS);
702 }
703
704#ifdef __linux__
705 /* Parse the inittab file so we can work out the level to telinit */
706 if (strcmp (newlevel, RC_LEVEL_BOOT) != 0 &&
707 strcmp (newlevel, RC_LEVEL_SINGLE) != 0)
708 {
709 char **inittab = rc_get_list (NULL, "/etc/inittab");
710 char *line;
711 char *p;
712 char *token;
713 char lvl[2] = {0, 0};
714
715 STRLIST_FOREACH (inittab, line, i) {
716 p = line;
717 token = strsep (&p, ":");
718 if (! token || token[0] != 'l')
719 continue;
720
721 if ((token = strsep (&p, ":")) == NULL)
722 continue;
723
724 /* Snag the level */
725 lvl[0] = token[0];
726
727 /* The name is spaced after this */
728 if ((token = strsep (&p, " ")) == NULL)
729 continue;
730
731 if ((token = strsep (&p, " ")) == NULL)
732 continue;
733
734 if (strcmp (token, newlevel) == 0)
735 break;
736 }
737 rc_strlist_free (inittab);
738
739 /* We have a level, so telinit into it */
740 if (lvl[0] == 0) {
741 eerrorx ("%s: couldn't find a runlevel called `%s'",
742 applet, newlevel);
743 } else {
744 execl ("/sbin/telinit", "/sbin/telinit", lvl, (char *) NULL);
745 eerrorx ("%s: unable to exec `/sbin/telinit': %s",
746 applet, strerror (errno));
747 }
748 }
749#endif
750 }
751 }
752 846
753 /* Check we're in the runlevel requested, ie from 847 /* Check we're in the runlevel requested, ie from
754 rc single 848 rc single
755 rc shutdown 849 rc shutdown
756 rc reboot 850 rc reboot
757 */ 851 */
758 if (newlevel) { 852 if (newlevel) {
853 if (strcmp (newlevel, RC_LEVEL_SYSINIT) == 0 &&
854 RUNLEVEL &&
855 (strcmp (RUNLEVEL, "S") == 0 ||
856 strcmp (RUNLEVEL, "1") == 0))
857 {
858 /* OK, we're either in runlevel 1 or single user mode */
859 struct utsname uts;
860#ifdef __linux__
861 char *cmd;
862#endif
863
864 /* exec init-early.sh if it exists
865 * This should just setup the console to use the correct
866 * font. Maybe it should setup the keyboard too? */
867 if (rc_exists (INITEARLYSH))
868 run_script (INITEARLYSH);
869
870 uname (&uts);
871
872 printf ("\n");
873 printf (" %sGentoo/%s; %shttp://www.gentoo.org/%s"
874 "\n Copyright 1999-2007 Gentoo Foundation; "
875 "Distributed under the GPLv2\n\n",
876 ecolor (ecolor_good), uts.sysname, ecolor (ecolor_bracket),
877 ecolor (ecolor_normal));
878
879 if (rc_is_env ("RC_INTERACTIVE", "yes"))
880 printf ("Press %sI%s to enter interactive boot mode\n\n",
881 ecolor (ecolor_good), ecolor (ecolor_normal));
882
883 setenv ("RC_SOFTLEVEL", newlevel, 1);
884 rc_plugin_run (rc_hook_runlevel_start_in, newlevel);
885 run_script (INITSH);
886
887#ifdef __linux__
888 /* If we requested a softlevel, save it now */
889 set_ksoftlevel (NULL);
890 if ((cmd = proc_getent ("softlevel"))) {
891 set_ksoftlevel (cmd);
892 free (cmd);
893 }
894
895#endif
896 rc_plugin_run (rc_hook_runlevel_start_out, newlevel);
897
898 if (want_interactive ())
899 mark_interactive ();
900
901 exit (EXIT_SUCCESS);
759 if (strcmp (newlevel, RC_LEVEL_SINGLE) == 0) { 902 } else if (strcmp (newlevel, RC_LEVEL_SINGLE) == 0) {
760 if (! RUNLEVEL || 903 if (! RUNLEVEL ||
761 (strcmp (RUNLEVEL, "S") != 0 && 904 (strcmp (RUNLEVEL, "S") != 0 &&
762 strcmp (RUNLEVEL, "1") != 0)) 905 strcmp (RUNLEVEL, "1") != 0))
763 { 906 {
764 /* Remember the current runlevel for when we come back */ 907 /* Remember the current runlevel for when we come back */
788 applet, strerror (errno)); 931 applet, strerror (errno));
789 } 932 }
790 } 933 }
791 } 934 }
792 935
793 /* Export our current softlevel */ 936 /* Now we start handling our children */
794 runlevel = rc_get_runlevel (); 937 signal (SIGCHLD, handle_signal);
795 938
796 /* If we're in the default runlevel and ksoftlevel exists, we should use 939 /* We should only use ksoftlevel if we were in single user mode
797 that instead */ 940 If not, we need to erase ksoftlevel now. */
798 if (newlevel && 941 if (PREVLEVEL &&
799 rc_exists (RC_SVCDIR "ksoftlevel") && 942 (strcmp (PREVLEVEL, "1") == 0 ||
800 strcmp (newlevel, RC_LEVEL_DEFAULT) == 0) 943 strcmp (PREVLEVEL, "S") == 0 ||
944 strcmp (PREVLEVEL, "N") == 0))
801 { 945 {
802 /* We should only use ksoftlevel if we were in single user mode
803 If not, we need to erase ksoftlevel now. */
804 if (PREVLEVEL &&
805 (strcmp (PREVLEVEL, "1") == 0 ||
806 strcmp (PREVLEVEL, "S") == 0 ||
807 strcmp (PREVLEVEL, "N") == 0))
808 {
809 FILE *fp;
810
811 if (! (fp = fopen (RC_SVCDIR "ksoftlevel", "r")))
812 eerror ("fopen `%s': %s", RC_SVCDIR "ksoftlevel",
813 strerror (errno));
814 else {
815 if (fgets (ksoftbuffer, sizeof (ksoftbuffer), fp)) { 946 if (get_ksoftlevel (ksoftbuffer, sizeof (ksoftbuffer)))
816 i = strlen (ksoftbuffer) - 1;
817 if (ksoftbuffer[i] == '\n')
818 ksoftbuffer[i] = 0;
819 newlevel = ksoftbuffer; 947 newlevel = ksoftbuffer;
820 } 948 } else if (! RUNLEVEL ||
821 fclose (fp); 949 (strcmp (RUNLEVEL, "1") != 0 &&
822 } 950 strcmp (RUNLEVEL, "S") != 0 &&
823 } else 951 strcmp (RUNLEVEL, "N") != 0))
952 {
824 set_ksoftlevel (NULL); 953 set_ksoftlevel (NULL);
825 } 954 }
826 955
827 if (newlevel && 956 if (newlevel &&
828 (strcmp (newlevel, RC_LEVEL_REBOOT) == 0 || 957 (strcmp (newlevel, RC_LEVEL_REBOOT) == 0 ||
829 strcmp (newlevel, RC_LEVEL_SHUTDOWN) == 0 || 958 strcmp (newlevel, RC_LEVEL_SHUTDOWN) == 0 ||
876#else 1005#else
877 /* BSD's on the other hand populate /dev automagically and use devd. 1006 /* BSD's on the other hand populate /dev automagically and use devd.
878 The only downside of this approach and ours is that we have to hard code 1007 The only downside of this approach and ours is that we have to hard code
879 the device node to the init script to simulate the coldplug into 1008 the device node to the init script to simulate the coldplug into
880 runlevel for our dependency tree to work. */ 1009 runlevel for our dependency tree to work. */
881 if (newlevel && strcmp (newlevel, RC_LEVEL_BOOT) == 0 && 1010 if (newlevel && strcmp (newlevel, bootlevel) == 0 &&
882 (strcmp (runlevel, RC_LEVEL_SINGLE) == 0 || 1011 (strcmp (runlevel, RC_LEVEL_SINGLE) == 0 ||
883 strcmp (runlevel, RC_LEVEL_SYSINIT) == 0) && 1012 strcmp (runlevel, RC_LEVEL_SYSINIT) == 0) &&
884 rc_is_env ("RC_COLDPLUG", "yes")) 1013 rc_is_env ("RC_COLDPLUG", "yes"))
885 { 1014 {
1015#if defined(__DragonFly__) || defined(__FreeBSD__)
886 /* The net interfaces are easy - they're all in net /dev/net :) */ 1016 /* The net interfaces are easy - they're all in net /dev/net :) */
887 start_services = rc_ls_dir (NULL, "/dev/net", 0); 1017 start_services = rc_ls_dir (NULL, "/dev/net", 0);
888 STRLIST_FOREACH (start_services, service, i) { 1018 STRLIST_FOREACH (start_services, service, i) {
889 j = (strlen ("net.") + strlen (service) + 1); 1019 j = (strlen ("net.") + strlen (service) + 1);
890 tmp = rc_xmalloc (sizeof (char *) * j); 1020 tmp = rc_xmalloc (sizeof (char *) * j);
892 if (rc_service_exists (tmp) && rc_allow_plug (tmp)) 1022 if (rc_service_exists (tmp) && rc_allow_plug (tmp))
893 rc_mark_service (tmp, rc_service_coldplugged); 1023 rc_mark_service (tmp, rc_service_coldplugged);
894 CHAR_FREE (tmp); 1024 CHAR_FREE (tmp);
895 } 1025 }
896 rc_strlist_free (start_services); 1026 rc_strlist_free (start_services);
1027#endif
897 1028
898 /* The mice are a little more tricky. 1029 /* The mice are a little more tricky.
899 If we coldplug anything else, we'll probably do it here. */ 1030 If we coldplug anything else, we'll probably do it here. */
900 start_services = rc_ls_dir (NULL, "/dev", 0); 1031 start_services = rc_ls_dir (NULL, "/dev", 0);
901 STRLIST_FOREACH (start_services, service, i) { 1032 STRLIST_FOREACH (start_services, service, i) {
926 1057
927 types = rc_strlist_add (NULL, "ineed"); 1058 types = rc_strlist_add (NULL, "ineed");
928 types = rc_strlist_add (types, "iuse"); 1059 types = rc_strlist_add (types, "iuse");
929 types = rc_strlist_add (types, "iafter"); 1060 types = rc_strlist_add (types, "iafter");
930 deporder = rc_get_depends (deptree, types, stop_services, 1061 deporder = rc_get_depends (deptree, types, stop_services,
931 runlevel, depoptions); 1062 runlevel, depoptions | RC_DEP_STOP);
932 rc_strlist_free (stop_services); 1063 rc_strlist_free (stop_services);
933 rc_strlist_free (types); 1064 rc_strlist_free (types);
934 stop_services = deporder; 1065 stop_services = deporder;
935 deporder = NULL; 1066 deporder = NULL;
936 types = NULL; 1067 types = NULL;
940 coldplugged_services = rc_ls_dir (coldplugged_services, 1071 coldplugged_services = rc_ls_dir (coldplugged_services,
941 RC_SVCDIR_COLDPLUGGED, RC_LS_INITD); 1072 RC_SVCDIR_COLDPLUGGED, RC_LS_INITD);
942 1073
943 /* Load our start services now. 1074 /* Load our start services now.
944 We have different rules dependent on runlevel. */ 1075 We have different rules dependent on runlevel. */
945 if (newlevel && strcmp (newlevel, RC_LEVEL_BOOT) == 0) { 1076 if (newlevel && strcmp (newlevel, bootlevel) == 0) {
946 if (coldplugged_services) { 1077 if (coldplugged_services) {
947 einfon ("Device initiated services:"); 1078 einfon ("Device initiated services:");
948 STRLIST_FOREACH (coldplugged_services, service, i) { 1079 STRLIST_FOREACH (coldplugged_services, service, i) {
949 printf (" %s", service); 1080 printf (" %s", service);
950 start_services = rc_strlist_add (start_services, service); 1081 start_services = rc_strlist_add (start_services, service);
962 if (strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SINGLE) != 0 && 1093 if (strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SINGLE) != 0 &&
963 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 && 1094 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 &&
964 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_REBOOT) != 0) 1095 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_REBOOT) != 0)
965 { 1096 {
966 /* We need to include the boot runlevel services if we're not in it */ 1097 /* We need to include the boot runlevel services if we're not in it */
967 start_services = rc_ls_dir (start_services, RC_RUNLEVELDIR RC_LEVEL_BOOT, 1098 char **services = rc_services_in_runlevel (bootlevel);
968 RC_LS_INITD); 1099
1100 start_services = rc_strlist_join (start_services, services);
1101 services = rc_services_in_runlevel (newlevel ? newlevel : runlevel);
1102 start_services = rc_strlist_join (start_services, services);
1103 services = NULL;
1104
969 STRLIST_FOREACH (coldplugged_services, service, i) 1105 STRLIST_FOREACH (coldplugged_services, service, i)
970 start_services = rc_strlist_add (start_services, service); 1106 start_services = rc_strlist_add (start_services, service);
971 1107
972 tmp = rc_strcatpaths (RC_RUNLEVELDIR,
973 newlevel ? newlevel : runlevel, (char *) NULL);
974 start_services = rc_ls_dir (start_services, tmp, RC_LS_INITD);
975 CHAR_FREE (tmp);
976 } 1108 }
977 } 1109 }
978 1110
979 /* Save out softlevel now */ 1111 /* Save out softlevel now */
980 if (going_down) 1112 if (going_down)
994 if (rc_service_state (service, rc_service_stopped)) 1126 if (rc_service_state (service, rc_service_stopped))
995 continue; 1127 continue;
996 1128
997 /* We always stop the service when in these runlevels */ 1129 /* We always stop the service when in these runlevels */
998 if (going_down) { 1130 if (going_down) {
999 rc_stop_service (service); 1131 pid_t pid = rc_stop_service (service);
1132 if (pid > 0 && ! rc_is_env ("RC_PARALLEL", "yes"))
1133 rc_waitpid (pid);
1000 continue; 1134 continue;
1001 } 1135 }
1002 1136
1003 /* If we're in the start list then don't bother stopping us */ 1137 /* If we're in the start list then don't bother stopping us */
1004 STRLIST_FOREACH (start_services, svc1, j) 1138 STRLIST_FOREACH (start_services, svc1, j)
1056 } 1190 }
1057 rc_strlist_free (deporder); 1191 rc_strlist_free (deporder);
1058 deporder = NULL; 1192 deporder = NULL;
1059 1193
1060 /* After all that we can finally stop the blighter! */ 1194 /* After all that we can finally stop the blighter! */
1061 if (! found) 1195 if (! found) {
1062 rc_stop_service (service); 1196 pid_t pid = rc_stop_service (service);
1197 if (pid > 0 && ! rc_is_env ("RC_PARALLEL", "yes"))
1198 rc_waitpid (pid);
1199 }
1063 } 1200 }
1064 rc_strlist_free (types); 1201 rc_strlist_free (types);
1065 types = NULL; 1202 types = NULL;
1066 1203
1067 /* Wait for our services to finish */ 1204 /* Wait for our services to finish */
1068 if (rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
1069 wait_for_services (); 1205 wait_for_services ();
1070 1206
1071 /* Notify the plugins we have finished */ 1207 /* Notify the plugins we have finished */
1072 rc_plugin_run (rc_hook_runlevel_stop_out, runlevel); 1208 rc_plugin_run (rc_hook_runlevel_stop_out, runlevel);
1073 1209
1074 rmdir (RC_SVCDIR "/softscripts.new"); 1210 rmdir (RC_SVCDIR "/softscripts.new");
1106 /* Order the services to start */ 1242 /* Order the services to start */
1107 types = rc_strlist_add (NULL, "ineed"); 1243 types = rc_strlist_add (NULL, "ineed");
1108 types = rc_strlist_add (types, "iuse"); 1244 types = rc_strlist_add (types, "iuse");
1109 types = rc_strlist_add (types, "iafter"); 1245 types = rc_strlist_add (types, "iafter");
1110 deporder = rc_get_depends (deptree, types, start_services, 1246 deporder = rc_get_depends (deptree, types, start_services,
1111 runlevel, depoptions); 1247 runlevel, depoptions | RC_DEP_START);
1112 rc_strlist_free (types); 1248 rc_strlist_free (types);
1113 types = NULL; 1249 types = NULL;
1114 rc_strlist_free (start_services); 1250 rc_strlist_free (start_services);
1115 start_services = deporder; 1251 start_services = deporder;
1116 deporder = NULL; 1252 deporder = NULL;
1117 1253
1254#ifdef __linux__
1255 /* mark any services skipped as started */
1256 if (PREVLEVEL && strcmp (PREVLEVEL, "N") == 0) {
1257 if ((service = proc_getent ("noinitd"))) {
1258 char *p = service;
1259 char *token;
1260
1261 while ((token = strsep (&p, ",")))
1262 rc_mark_service (token, rc_service_started);
1263 free (service);
1264 }
1265 }
1266#endif
1267
1268
1118 STRLIST_FOREACH (start_services, service, i) { 1269 STRLIST_FOREACH (start_services, service, i) {
1119 if (rc_service_state (service, rc_service_stopped)) { 1270 if (rc_service_state (service, rc_service_stopped)) {
1271 pid_t pid;
1272
1120 if (! interactive) 1273 if (! interactive)
1121 interactive = want_interactive (); 1274 interactive = want_interactive ();
1122 1275
1123 if (interactive) { 1276 if (interactive) {
1124interactive_retry: 1277interactive_retry:
1135 case '3': interactive = false; break; 1288 case '3': interactive = false; break;
1136 case '4': sulogin (true); goto interactive_retry; 1289 case '4': sulogin (true); goto interactive_retry;
1137 default: goto interactive_option; 1290 default: goto interactive_option;
1138 } 1291 }
1139 } 1292 }
1293
1294 /* Remember the pid if we're running in parallel */
1140 rc_start_service (service); 1295 if ((pid = rc_start_service (service)))
1296 add_pid (pid);
1297
1298 if (! rc_is_env ("RC_PARALLEL", "yes")) {
1299 rc_waitpid (pid);
1300 remove_pid (pid);
1301 }
1141 } 1302 }
1142 } 1303 }
1143 1304
1144 /* Wait for our services to finish */ 1305 /* Wait for our services to finish */
1145 wait_for_services (); 1306 wait_for_services ();
1146 1307
1147 rc_plugin_run (rc_hook_runlevel_start_out, runlevel); 1308 rc_plugin_run (rc_hook_runlevel_start_out, runlevel);
1148 1309
1310#ifdef __linux__
1311 /* mark any services skipped as stopped */
1312 if (PREVLEVEL && strcmp (PREVLEVEL, "N") == 0) {
1313 if ((service = proc_getent ("noinitd"))) {
1314 char *p = service;
1315 char *token;
1316
1317 while ((token = strsep (&p, ",")))
1318 rc_mark_service (token, rc_service_stopped);
1319 free (service);
1320 }
1321 }
1322#endif
1323
1149 /* Store our interactive status for boot */ 1324 /* Store our interactive status for boot */
1150 if (interactive && strcmp (runlevel, RC_LEVEL_BOOT) == 0) 1325 if (interactive && strcmp (runlevel, bootlevel) == 0)
1151 mark_interactive (); 1326 mark_interactive ();
1152 else { 1327 else {
1153 if (rc_exists (INTERACTIVE)) 1328 if (rc_exists (INTERACTIVE))
1154 unlink (INTERACTIVE); 1329 unlink (INTERACTIVE);
1155 } 1330 }

Legend:
Removed from v.2640  
changed lines
  Added in v.2814

  ViewVC Help
Powered by ViewVC 1.1.20