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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2885 - (show annotations) (download) (as text)
Tue Sep 18 12:20:55 2007 UTC (10 years, 1 month ago) by uberlord
File MIME type: text/x-csrc
File size: 35910 byte(s)
API change! rc_config_env is renamed to rc_make_env and takes no argument.
1 /*
2 rc.c
3 rc - manager for init scripts which control the startup, shutdown
4 and the running of daemons on a Gentoo system.
5
6 Also a multicall binary for various commands that can be used in shell
7 scripts to query service state, mark service state and provide the
8 Gentoo einfo family of informational functions.
9
10 Copyright 2007 Gentoo Foundation
11 Released under the GPLv2
12 */
13
14 #define APPLET "rc"
15
16 #define SYSLOG_NAMES
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/utsname.h>
21 #include <sys/wait.h>
22 #include <errno.h>
23 #include <ctype.h>
24 #include <getopt.h>
25 #include <libgen.h>
26 #include <limits.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <termios.h>
34 #include <unistd.h>
35
36 #include "builtins.h"
37 #include "einfo.h"
38 #include "rc.h"
39 #include "rc-misc.h"
40 #include "rc-plugin.h"
41 #include "strlist.h"
42
43 #define INITSH RC_LIBDIR "/sh/init.sh"
44 #define INITEARLYSH RC_LIBDIR "/sh/init-early.sh"
45 #define HALTSH RC_INITDIR "/halt.sh"
46
47 #define SHUTDOWN "/sbin/shutdown"
48 #define SULOGIN "/sbin/sulogin"
49
50 #define INTERACTIVE RC_SVCDIR "/interactive"
51
52 #define DEVBOOT "/dev/.rcboot"
53
54 /* Cleanup anything in main */
55 #define CHAR_FREE(_item) if (_item) { \
56 free (_item); \
57 _item = NULL; \
58 }
59
60 extern char **environ;
61
62 static char *RUNLEVEL = NULL;
63 static char *PREVLEVEL = NULL;
64
65 static char *applet = NULL;
66 static char **env = NULL;
67 static char **newenv = NULL;
68 static char **coldplugged_services = NULL;
69 static char **stop_services = NULL;
70 static char **start_services = NULL;
71 static rc_depinfo_t *deptree = NULL;
72 static char **types = NULL;
73 static char *tmp = NULL;
74
75 struct termios *termios_orig = NULL;
76
77 typedef struct pidlist
78 {
79 pid_t pid;
80 struct pidlist *next;
81 } pidlist_t;
82 static pidlist_t *service_pids = NULL;
83
84 static void cleanup (void)
85 {
86 if (applet && strcmp (applet, "rc") == 0) {
87 pidlist_t *pl = service_pids;
88
89 rc_plugin_unload ();
90
91 if (! rc_in_plugin && termios_orig) {
92 tcsetattr (fileno (stdin), TCSANOW, termios_orig);
93 free (termios_orig);
94 }
95
96 while (pl) {
97 pidlist_t *p = pl->next;
98 free (pl);
99 pl = p;
100 }
101
102 rc_strlist_free (env);
103 rc_strlist_free (newenv);
104 rc_strlist_free (coldplugged_services);
105 rc_strlist_free (stop_services);
106 rc_strlist_free (start_services);
107 rc_free_deptree (deptree);
108 rc_strlist_free (types);
109
110 /* Clean runlevel start, stop markers */
111 if (! rc_in_plugin) {
112 if (rc_is_dir (RC_STARTING))
113 rc_rm_dir (RC_STARTING, true);
114 if (rc_is_dir (RC_STOPPING))
115 rc_rm_dir (RC_STOPPING, true);
116 }
117 }
118
119 free (applet);
120 }
121
122 static int syslog_decode (char *name, CODE *codetab)
123 {
124 CODE *c;
125
126 if (isdigit (*name))
127 return (atoi (name));
128
129 for (c = codetab; c->c_name; c++)
130 if (! strcasecmp (name, c->c_name))
131 return (c->c_val);
132
133 return (-1);
134 }
135
136 static int do_e (int argc, char **argv)
137 {
138 int retval = EXIT_SUCCESS;
139 int i;
140 int l = 0;
141 char *message = NULL;
142 char *p;
143 char *fmt = NULL;
144 int level = 0;
145
146 if (strcmp (applet, "eval_ecolors") == 0) {
147 printf ("GOOD='%s'\nWARN='%s'\nBAD='%s'\nHILITE='%s'\nBRACKET='%s'\nNORMAL='%s'\n",
148 ecolor (ecolor_good),
149 ecolor (ecolor_warn),
150 ecolor (ecolor_bad),
151 ecolor (ecolor_hilite),
152 ecolor (ecolor_bracket),
153 ecolor (ecolor_normal));
154 exit (EXIT_SUCCESS);
155 }
156
157 if (argc > 0) {
158
159 if (strcmp (applet, "eend") == 0 ||
160 strcmp (applet, "ewend") == 0 ||
161 strcmp (applet, "veend") == 0 ||
162 strcmp (applet, "vweend") == 0)
163 {
164 errno = 0;
165 retval = strtol (argv[0], NULL, 0);
166 if (errno != 0)
167 retval = EXIT_FAILURE;
168 else {
169 argc--;
170 argv++;
171 }
172 } else if (strcmp (applet, "esyslog") == 0 ||
173 strcmp (applet, "elog") == 0) {
174 char *dot = strchr (argv[0], '.');
175 if ((level = syslog_decode (dot + 1, prioritynames)) == -1)
176 eerrorx ("%s: invalid log level `%s'", applet, argv[0]);
177
178 if (argc < 3)
179 eerrorx ("%s: not enough arguments", applet);
180
181 unsetenv ("RC_ELOG");
182 setenv ("RC_ELOG", argv[1], 1);
183
184 argc -= 2;
185 argv += 2;
186 }
187 }
188
189 if (argc > 0) {
190 for (i = 0; i < argc; i++)
191 l += strlen (argv[i]) + 1;
192
193 message = rc_xmalloc (l);
194 p = message;
195
196 for (i = 0; i < argc; i++) {
197 if (i > 0)
198 *p++ = ' ';
199 memcpy (p, argv[i], strlen (argv[i]));
200 p += strlen (argv[i]);
201 }
202 *p = 0;
203 }
204
205 if (message)
206 fmt = rc_xstrdup ("%s");
207
208 if (strcmp (applet, "einfo") == 0)
209 einfo (fmt, message);
210 else if (strcmp (applet, "einfon") == 0)
211 einfon (fmt, message);
212 else if (strcmp (applet, "ewarn") == 0)
213 ewarn (fmt, message);
214 else if (strcmp (applet, "ewarnn") == 0)
215 ewarnn (fmt, message);
216 else if (strcmp (applet, "eerror") == 0) {
217 eerror (fmt, message);
218 retval = 1;
219 } else if (strcmp (applet, "eerrorn") == 0) {
220 eerrorn (fmt, message);
221 retval = 1;
222 } else if (strcmp (applet, "ebegin") == 0)
223 ebegin (fmt, message);
224 else if (strcmp (applet, "eend") == 0)
225 eend (retval, fmt, message);
226 else if (strcmp (applet, "ewend") == 0)
227 ewend (retval, fmt, message);
228 else if (strcmp (applet, "esyslog") == 0)
229 elog (level, fmt, message);
230 else if (strcmp (applet, "veinfo") == 0)
231 einfov (fmt, message);
232 else if (strcmp (applet, "veinfon") == 0)
233 einfovn (fmt, message);
234 else if (strcmp (applet, "vewarn") == 0)
235 ewarnv (fmt, message);
236 else if (strcmp (applet, "vewarnn") == 0)
237 ewarnvn (fmt, message);
238 else if (strcmp (applet, "vebegin") == 0)
239 ebeginv (fmt, message);
240 else if (strcmp (applet, "veend") == 0)
241 eendv (retval, fmt, message);
242 else if (strcmp (applet, "vewend") == 0)
243 ewendv (retval, fmt, message);
244 else if (strcmp (applet, "eindent") == 0)
245 eindent ();
246 else if (strcmp (applet, "eoutdent") == 0)
247 eoutdent ();
248 else if (strcmp (applet, "veindent") == 0)
249 eindentv ();
250 else if (strcmp (applet, "veoutdent") == 0)
251 eoutdentv ();
252 else {
253 eerror ("%s: unknown applet", applet);
254 retval = EXIT_FAILURE;
255 }
256
257 if (fmt)
258 free (fmt);
259 if (message)
260 free (message);
261 return (retval);
262 }
263
264 static int do_service (int argc, char **argv)
265 {
266 bool ok = false;
267
268 if (argc < 1 || ! argv[0] || strlen (argv[0]) == 0)
269 eerrorx ("%s: no service specified", applet);
270
271 if (strcmp (applet, "service_started") == 0)
272 ok = rc_service_state (argv[0], rc_service_started);
273 else if (strcmp (applet, "service_stopped") == 0)
274 ok = rc_service_state (argv[0], rc_service_stopped);
275 else if (strcmp (applet, "service_inactive") == 0)
276 ok = rc_service_state (argv[0], rc_service_inactive);
277 else if (strcmp (applet, "service_starting") == 0)
278 ok = rc_service_state (argv[0], rc_service_starting);
279 else if (strcmp (applet, "service_stopping") == 0)
280 ok = rc_service_state (argv[0], rc_service_stopping);
281 else if (strcmp (applet, "service_coldplugged") == 0)
282 ok = rc_service_state (argv[0], rc_service_coldplugged);
283 else if (strcmp (applet, "service_wasinactive") == 0)
284 ok = rc_service_state (argv[0], rc_service_wasinactive);
285 else if (strcmp (applet, "service_started_daemon") == 0) {
286 int idx = 0;
287 if (argc > 2)
288 sscanf (argv[2], "%d", &idx);
289 exit (rc_service_started_daemon (argv[0], argv[1], idx)
290 ? 0 : 1);
291 } else
292 eerrorx ("%s: unknown applet", applet);
293
294 return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
295 }
296
297 static int do_mark_service (int argc, char **argv)
298 {
299 bool ok = false;
300 char *svcname = getenv ("SVCNAME");
301
302 if (argc < 1 || ! argv[0] || strlen (argv[0]) == 0)
303 eerrorx ("%s: no service specified", applet);
304
305 if (strcmp (applet, "mark_service_started") == 0)
306 ok = rc_mark_service (argv[0], rc_service_started);
307 else if (strcmp (applet, "mark_service_stopped") == 0)
308 ok = rc_mark_service (argv[0], rc_service_stopped);
309 else if (strcmp (applet, "mark_service_inactive") == 0)
310 ok = rc_mark_service (argv[0], rc_service_inactive);
311 else if (strcmp (applet, "mark_service_starting") == 0)
312 ok = rc_mark_service (argv[0], rc_service_starting);
313 else if (strcmp (applet, "mark_service_stopping") == 0)
314 ok = rc_mark_service (argv[0], rc_service_stopping);
315 else if (strcmp (applet, "mark_service_coldplugged") == 0)
316 ok = rc_mark_service (argv[0], rc_service_coldplugged);
317 else
318 eerrorx ("%s: unknown applet", applet);
319
320 /* If we're marking ourselves then we need to inform our parent runscript
321 process so they do not mark us based on our exit code */
322 if (ok && svcname && strcmp (svcname, argv[0]) == 0) {
323 char *runscript_pid = getenv ("RC_RUNSCRIPT_PID");
324 char *mtime;
325 pid_t pid = 0;
326 int l;
327
328 if (runscript_pid && sscanf (runscript_pid, "%d", &pid) == 1)
329 if (kill (pid, SIGHUP) != 0)
330 eerror ("%s: failed to signal parent %d: %s",
331 applet, pid, strerror (errno));
332
333 /* Remove the exclsive time test. This ensures that it's not
334 in control as well */
335 l = strlen (RC_SVCDIR "exclusive") +
336 strlen (svcname) +
337 strlen (runscript_pid) +
338 4;
339 mtime = rc_xmalloc (l);
340 snprintf (mtime, l, RC_SVCDIR "exclusive/%s.%s",
341 svcname, runscript_pid);
342 if (rc_exists (mtime) && unlink (mtime) != 0)
343 eerror ("%s: unlink: %s", applet, strerror (errno));
344 free (mtime);
345 }
346
347 return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
348 }
349
350 static int do_options (int argc, char **argv)
351 {
352 bool ok = false;
353 char *service = getenv ("SVCNAME");
354
355 if (! service)
356 eerrorx ("%s: no service specified", applet);
357
358 if (argc < 1 || ! argv[0] || strlen (argv[0]) == 0)
359 eerrorx ("%s: no option specified", applet);
360
361 if (strcmp (applet, "get_options") == 0) {
362 char buffer[1024];
363 memset (buffer, 0, 1024);
364 ok = rc_get_service_option (service, argv[0], buffer);
365 if (ok)
366 printf ("%s", buffer);
367 } else if (strcmp (applet, "save_options") == 0)
368 ok = rc_set_service_option (service, argv[0], argv[1]);
369 else
370 eerrorx ("%s: unknown applet", applet);
371
372 return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
373 }
374
375 #ifdef __linux__
376 static char *proc_getent (const char *ent)
377 {
378 FILE *fp;
379 char buffer[RC_LINEBUFFER];
380 char *p;
381 char *value = NULL;
382 int i;
383
384 if (! rc_exists ("/proc/cmdline"))
385 return (NULL);
386
387 if (! (fp = fopen ("/proc/cmdline", "r"))) {
388 eerror ("failed to open `/proc/cmdline': %s", strerror (errno));
389 return (NULL);
390 }
391
392 memset (buffer, 0, sizeof (buffer));
393 if (fgets (buffer, RC_LINEBUFFER, fp) &&
394 (p = strstr (buffer, ent)))
395 {
396 i = p - buffer;
397 if (i == '\0' || buffer[i - 1] == ' ') {
398 /* Trim the trailing carriage return if present */
399 i = strlen (buffer) - 1;
400 if (buffer[i] == '\n')
401 buffer[i] = 0;
402
403 p += strlen (ent);
404 if (*p == '=')
405 p++;
406 value = strdup (strsep (&p, " "));
407 }
408 } else
409 errno = ENOENT;
410 fclose (fp);
411
412 return (value);
413 }
414 #endif
415
416 static char read_key (bool block)
417 {
418 struct termios termios;
419 char c = 0;
420 int fd = fileno (stdin);
421
422 if (! isatty (fd))
423 return (false);
424
425 /* Now save our terminal settings. We need to restore them at exit as we
426 will be changing it for non-blocking reads for Interactive */
427 if (! termios_orig) {
428 termios_orig = rc_xmalloc (sizeof (struct termios));
429 tcgetattr (fd, termios_orig);
430 }
431
432 tcgetattr (fd, &termios);
433 termios.c_lflag &= ~(ICANON | ECHO);
434 if (block)
435 termios.c_cc[VMIN] = 1;
436 else {
437 termios.c_cc[VMIN] = 0;
438 termios.c_cc[VTIME] = 0;
439 }
440 tcsetattr (fd, TCSANOW, &termios);
441
442 read (fd, &c, 1);
443
444 tcsetattr (fd, TCSANOW, termios_orig);
445
446 return (c);
447 }
448
449 static bool want_interactive (void)
450 {
451 char c;
452
453 if (PREVLEVEL &&
454 strcmp (PREVLEVEL, "N") != 0 &&
455 strcmp (PREVLEVEL, "S") != 0 &&
456 strcmp (PREVLEVEL, "1") != 0)
457 return (false);
458
459 if (! rc_is_env ("RC_INTERACTIVE", "yes"))
460 return (false);
461
462 c = read_key (false);
463 return ((c == 'I' || c == 'i') ? true : false);
464 }
465
466 static void mark_interactive (void)
467 {
468 FILE *fp = fopen (INTERACTIVE, "w");
469 if (fp)
470 fclose (fp);
471 }
472
473 static void sulogin (bool cont)
474 {
475 #ifdef __linux__
476 char *e = getenv ("RC_SYS");
477
478 /* VPS systems cannot do an sulogin */
479 if (e && strcmp (e, "VPS") == 0) {
480 execl ("/sbin/halt", "/sbin/halt", "-f", (char *) NULL);
481 eerrorx ("%s: unable to exec `/sbin/halt': %s", applet, strerror (errno));
482 }
483 #endif
484
485 newenv = rc_filter_env ();
486
487 if (cont) {
488 int status = 0;
489 #ifdef __linux__
490 char *tty = ttyname (fileno (stdout));
491 #endif
492
493 pid_t pid = vfork ();
494
495 if (pid == -1)
496 eerrorx ("%s: vfork: %s", applet, strerror (errno));
497 if (pid == 0) {
498 #ifdef __linux__
499 if (tty)
500 execle (SULOGIN, SULOGIN, tty, (char *) NULL, newenv);
501 else
502 execle (SULOGIN, SULOGIN, (char *) NULL, newenv);
503
504 eerror ("%s: unable to exec `%s': %s", applet, SULOGIN,
505 strerror (errno));
506 #else
507 execle ("/bin/sh", "/bin/sh", (char *) NULL, newenv);
508 eerror ("%s: unable to exec `/bin/sh': %s", applet,
509 strerror (errno));
510 #endif
511 _exit (EXIT_FAILURE);
512 }
513 waitpid (pid, &status, 0);
514 } else {
515 #ifdef __linux__
516 execle ("/sbin/sulogin", "/sbin/sulogin", (char *) NULL, newenv);
517 eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno));
518 #else
519 exit (EXIT_SUCCESS);
520 #endif
521 }
522 }
523
524 static void single_user (void)
525 {
526 #ifdef __linux__
527 execl ("/sbin/telinit", "/sbin/telinit", "S", (char *) NULL);
528 eerrorx ("%s: unable to exec `/sbin/telinit': %s",
529 applet, strerror (errno));
530 #else
531 if (kill (1, SIGTERM) != 0)
532 eerrorx ("%s: unable to send SIGTERM to init (pid 1): %s",
533 applet, strerror (errno));
534 exit (EXIT_SUCCESS);
535 #endif
536 }
537
538 static void set_ksoftlevel (const char *runlevel)
539 {
540 FILE *fp;
541
542 if (! runlevel ||
543 strcmp (runlevel, getenv ("RC_BOOTLEVEL")) == 0 ||
544 strcmp (runlevel, RC_LEVEL_SINGLE) == 0 ||
545 strcmp (runlevel, RC_LEVEL_SYSINIT) == 0)
546 {
547 if (rc_exists (RC_KSOFTLEVEL) &&
548 unlink (RC_KSOFTLEVEL) != 0)
549 eerror ("unlink `%s': %s", RC_KSOFTLEVEL, strerror (errno));
550 return;
551 }
552
553 if (! (fp = fopen (RC_KSOFTLEVEL, "w"))) {
554 eerror ("fopen `%s': %s", RC_KSOFTLEVEL, strerror (errno));
555 return;
556 }
557
558 fprintf (fp, "%s", runlevel);
559 fclose (fp);
560 }
561
562 static int get_ksoftlevel (char *buffer, int buffer_len)
563 {
564 FILE *fp;
565 int i = 0;
566
567 if (! rc_exists (RC_KSOFTLEVEL))
568 return (0);
569
570 if (! (fp = fopen (RC_KSOFTLEVEL, "r"))) {
571 eerror ("fopen `%s': %s", RC_KSOFTLEVEL, strerror (errno));
572 return (-1);
573 }
574
575 if (fgets (buffer, buffer_len, fp)) {
576 i = strlen (buffer) - 1;
577 if (buffer[i] == '\n')
578 buffer[i] = 0;
579 }
580
581 fclose (fp);
582 return (i);
583 }
584
585 static void wait_for_services ()
586 {
587 int status = 0;
588 while (wait (&status) != -1);
589 }
590
591 static void add_pid (pid_t pid)
592 {
593 pidlist_t *sp = service_pids;
594 if (sp) {
595 while (sp->next)
596 sp = sp->next;
597 sp->next = rc_xmalloc (sizeof (pidlist_t));
598 sp = sp->next;
599 } else
600 sp = service_pids = rc_xmalloc (sizeof (pidlist_t));
601 memset (sp, 0, sizeof (pidlist_t));
602 sp->pid = pid;
603 }
604
605 static void remove_pid (pid_t pid)
606 {
607 pidlist_t *last = NULL;
608 pidlist_t *pl;
609
610 for (pl = service_pids; pl; pl = pl->next) {
611 if (pl->pid == pid) {
612 if (last)
613 last->next = pl->next;
614 else
615 service_pids = pl->next;
616 free (pl);
617 break;
618 }
619 last = pl;
620 }
621 }
622
623 static void handle_signal (int sig)
624 {
625 int serrno = errno;
626 char signame[10] = { '\0' };
627 pidlist_t *pl;
628 pid_t pid;
629 int status = 0;
630
631 switch (sig) {
632 case SIGCHLD:
633 do {
634 pid = waitpid (-1, &status, WNOHANG);
635 if (pid < 0) {
636 if (errno != ECHILD)
637 eerror ("waitpid: %s", strerror (errno));
638 return;
639 }
640 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
641
642 /* Remove that pid from our list */
643 if (pid > 0)
644 remove_pid (pid);
645 break;
646
647 case SIGINT:
648 if (! signame[0])
649 snprintf (signame, sizeof (signame), "SIGINT");
650 case SIGTERM:
651 if (! signame[0])
652 snprintf (signame, sizeof (signame), "SIGTERM");
653 case SIGQUIT:
654 if (! signame[0])
655 snprintf (signame, sizeof (signame), "SIGQUIT");
656 eerrorx ("%s: caught %s, aborting", applet, signame);
657 case SIGUSR1:
658 eerror ("rc: Aborting!");
659 /* Kill any running services we have started */
660
661 signal (SIGCHLD, SIG_IGN);
662 for (pl = service_pids; pl; pl = pl->next)
663 kill (pl->pid, SIGTERM);
664
665 /* Notify plugins we are aborting */
666 rc_plugin_run (rc_hook_abort, NULL);
667
668 /* Only drop into single user mode if we're booting */
669 if ((PREVLEVEL &&
670 (strcmp (PREVLEVEL, "S") == 0 ||
671 strcmp (PREVLEVEL, "1") == 0)) ||
672 (RUNLEVEL &&
673 (strcmp (RUNLEVEL, "S") == 0 ||
674 strcmp (RUNLEVEL, "1") == 0)))
675 single_user ();
676
677 exit (EXIT_FAILURE);
678 break;
679
680 default:
681 eerror ("%s: caught unknown signal %d", applet, sig);
682 }
683
684 /* Restore errno */
685 errno = serrno;
686 }
687
688 static void run_script (const char *script) {
689 int status = 0;
690 pid_t pid = vfork ();
691
692 if (pid < 0)
693 eerrorx ("%s: vfork: %s", applet, strerror (errno));
694 else if (pid == 0) {
695 execl (script, script, (char *) NULL);
696 eerror ("%s: unable to exec `%s': %s",
697 script, applet, strerror (errno));
698 _exit (EXIT_FAILURE);
699 }
700
701 do {
702 pid_t wpid = waitpid (pid, &status, 0);
703 if (wpid < 1)
704 eerror ("waitpid: %s", strerror (errno));
705 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
706
707 if (! WIFEXITED (status) || ! WEXITSTATUS (status) == 0)
708 eerrorx ("%s: failed to exec `%s'", applet, script);
709 }
710
711 #include "_usage.h"
712 #define getoptstring getoptstring_COMMON
713 static struct option longopts[] = {
714 longopts_COMMON
715 { NULL, 0, NULL, 0}
716 };
717 #include "_usage.c"
718
719 int main (int argc, char **argv)
720 {
721 char *runlevel = NULL;
722 const char *bootlevel = NULL;
723 char *newlevel = NULL;
724 char *service = NULL;
725 char **deporder = NULL;
726 int i = 0;
727 int j = 0;
728 bool going_down = false;
729 bool interactive = false;
730 int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
731 char ksoftbuffer [PATH_MAX];
732 char pidstr[6];
733 int opt;
734
735 atexit (cleanup);
736 if (argv[0])
737 applet = rc_xstrdup (basename (argv[0]));
738
739 if (! applet)
740 eerrorx ("arguments required");
741
742 /* These used to be programs in their own right, so we shouldn't
743 * touch argc or argv for them */
744 if (strcmp (applet, "env-update") == 0)
745 exit (env_update (argc, argv));
746 else if (strcmp (applet, "fstabinfo") == 0)
747 exit (fstabinfo (argc, argv));
748 else if (strcmp (applet, "mountinfo") == 0)
749 exit (mountinfo (argc, argv));
750 else if (strcmp (applet, "rc-depend") == 0)
751 exit (rc_depend (argc, argv));
752 else if (strcmp (applet, "rc-status") == 0)
753 exit (rc_status (argc, argv));
754 else if (strcmp (applet, "rc-update") == 0 ||
755 strcmp (applet, "update-rc") == 0)
756 exit (rc_update (argc, argv));
757 else if (strcmp (applet, "runscript") == 0)
758 exit (runscript (argc, argv));
759 else if (strcmp (applet, "start-stop-daemon") == 0)
760 exit (start_stop_daemon (argc, argv));
761
762 argc--;
763 argv++;
764
765 /* Handle multicall stuff */
766 if (applet[0] == 'e' || (applet[0] == 'v' && applet[1] == 'e'))
767 exit (do_e (argc, argv));
768
769 if (strncmp (applet, "service_", strlen ("service_")) == 0)
770 exit (do_service (argc, argv));
771
772 if (strcmp (applet, "get_options") == 0 ||
773 strcmp (applet, "save_options") == 0)
774 exit (do_options (argc, argv));
775
776 if (strncmp (applet, "mark_service_", strlen ("mark_service_")) == 0)
777 exit (do_mark_service (argc, argv));
778
779 if (strcmp (applet, "is_runlevel_start") == 0)
780 exit (rc_runlevel_starting () ? 0 : 1);
781 else if (strcmp (applet, "is_runlevel_stop") == 0)
782 exit (rc_runlevel_stopping () ? 0 : 1);
783
784 if (strcmp (applet, "rc-abort") == 0) {
785 char *p = getenv ("RC_PID");
786 pid_t pid = 0;
787
788 if (p && sscanf (p, "%d", &pid) == 1) {
789 if (kill (pid, SIGUSR1) != 0)
790 eerrorx ("rc-abort: failed to signal parent %d: %s",
791 pid, strerror (errno));
792 exit (EXIT_SUCCESS);
793 }
794 exit (EXIT_FAILURE);
795 }
796
797 if (strcmp (applet, "rc" ) != 0)
798 eerrorx ("%s: unknown applet", applet);
799
800 /* Change dir to / to ensure all scripts don't use stuff in pwd */
801 chdir ("/");
802
803 /* RUNLEVEL is set by sysvinit as is a magic number
804 RC_SOFTLEVEL is set by us and is the name for this magic number
805 even though all our userland documentation refers to runlevel */
806 RUNLEVEL = getenv ("RUNLEVEL");
807 PREVLEVEL = getenv ("PREVLEVEL");
808
809 /* Setup a signal handler */
810 signal (SIGINT, handle_signal);
811 signal (SIGQUIT, handle_signal);
812 signal (SIGTERM, handle_signal);
813 signal (SIGUSR1, handle_signal);
814
815 /* Ensure our environment is pure
816 Also, add our configuration to it */
817 env = rc_filter_env ();
818 env = rc_strlist_join (env, rc_make_env ());
819
820 if (env) {
821 char *p;
822
823 #ifdef __linux__
824 /* clearenv isn't portable, but there's no harm in using it
825 if we have it */
826 clearenv ();
827 #else
828 char *var;
829 /* No clearenv present here then.
830 We could manipulate environ directly ourselves, but it seems that
831 some kernels bitch about this according to the environ man pages
832 so we walk though environ and call unsetenv for each value. */
833 while (environ[0]) {
834 tmp = rc_xstrdup (environ[0]);
835 p = tmp;
836 var = strsep (&p, "=");
837 unsetenv (var);
838 free (tmp);
839 }
840 tmp = NULL;
841 #endif
842
843 STRLIST_FOREACH (env, p, i)
844 if (strcmp (p, "RC_SOFTLEVEL") != 0 && strcmp (p, "SOFTLEVEL") != 0)
845 putenv (p);
846
847 /* We don't free our list as that would be null in environ */
848 }
849
850 argc++;
851 argv--;
852 while ((opt = getopt_long (argc, argv, getoptstring,
853 longopts, (int *) 0)) != -1)
854 {
855 switch (opt) {
856 case_RC_COMMON_GETOPT
857 }
858 }
859
860 newlevel = argv[optind++];
861
862 /* OK, so we really are the main RC process
863 Only root should be able to run us */
864 if (geteuid () != 0)
865 eerrorx ("%s: root access required", applet);
866
867 /* Enable logging */
868 setenv ("RC_ELOG", "rc", 1);
869
870 /* Export our PID */
871 snprintf (pidstr, sizeof (pidstr), "%d", getpid ());
872 setenv ("RC_PID", pidstr, 1);
873
874 interactive = rc_exists (INTERACTIVE);
875 rc_plugin_load ();
876
877 /* Load current softlevel */
878 bootlevel = getenv ("RC_BOOTLEVEL");
879 runlevel = rc_get_runlevel ();
880
881 /* Check we're in the runlevel requested, ie from
882 rc single
883 rc shutdown
884 rc reboot
885 */
886 if (newlevel) {
887 if (strcmp (newlevel, RC_LEVEL_SYSINIT) == 0 &&
888 RUNLEVEL &&
889 (strcmp (RUNLEVEL, "S") == 0 ||
890 strcmp (RUNLEVEL, "1") == 0))
891 {
892 /* OK, we're either in runlevel 1 or single user mode */
893 struct utsname uts;
894 #ifdef __linux__
895 char *cmd;
896 #endif
897
898 /* exec init-early.sh if it exists
899 * This should just setup the console to use the correct
900 * font. Maybe it should setup the keyboard too? */
901 if (rc_exists (INITEARLYSH))
902 run_script (INITEARLYSH);
903
904 uname (&uts);
905
906 printf ("\n");
907 printf (" %sGentoo/%s; %shttp://www.gentoo.org/%s"
908 "\n Copyright 1999-2007 Gentoo Foundation; "
909 "Distributed under the GPLv2\n\n",
910 ecolor (ecolor_good), uts.sysname, ecolor (ecolor_bracket),
911 ecolor (ecolor_normal));
912
913 if (rc_is_env ("RC_INTERACTIVE", "yes"))
914 printf ("Press %sI%s to enter interactive boot mode\n\n",
915 ecolor (ecolor_good), ecolor (ecolor_normal));
916
917 setenv ("RC_SOFTLEVEL", newlevel, 1);
918 rc_plugin_run (rc_hook_runlevel_start_in, newlevel);
919 run_script (INITSH);
920
921 #ifdef __linux__
922 /* If we requested a softlevel, save it now */
923 set_ksoftlevel (NULL);
924 if ((cmd = proc_getent ("softlevel"))) {
925 set_ksoftlevel (cmd);
926 free (cmd);
927 }
928
929 #endif
930 rc_plugin_run (rc_hook_runlevel_start_out, newlevel);
931
932 if (want_interactive ())
933 mark_interactive ();
934
935 exit (EXIT_SUCCESS);
936 } else if (strcmp (newlevel, RC_LEVEL_SINGLE) == 0) {
937 if (! RUNLEVEL ||
938 (strcmp (RUNLEVEL, "S") != 0 &&
939 strcmp (RUNLEVEL, "1") != 0))
940 {
941 /* Remember the current runlevel for when we come back */
942 set_ksoftlevel (runlevel);
943 single_user ();
944 }
945 } else if (strcmp (newlevel, RC_LEVEL_REBOOT) == 0) {
946 if (! RUNLEVEL ||
947 strcmp (RUNLEVEL, "6") != 0)
948 {
949 execl (SHUTDOWN, SHUTDOWN, "-r", "now", (char *) NULL);
950 eerrorx ("%s: unable to exec `" SHUTDOWN "': %s",
951 applet, strerror (errno));
952 }
953 } else if (strcmp (newlevel, RC_LEVEL_SHUTDOWN) == 0) {
954 if (! RUNLEVEL ||
955 strcmp (RUNLEVEL, "0") != 0)
956 {
957 execl (SHUTDOWN, SHUTDOWN,
958 #ifdef __linux__
959 "-h",
960 #else
961 "-p",
962 #endif
963 "now", (char *) NULL);
964 eerrorx ("%s: unable to exec `" SHUTDOWN "': %s",
965 applet, strerror (errno));
966 }
967 }
968 }
969
970 /* Now we start handling our children */
971 signal (SIGCHLD, handle_signal);
972
973 /* We should only use ksoftlevel if we were in single user mode
974 If not, we need to erase ksoftlevel now. */
975 if (PREVLEVEL &&
976 (strcmp (PREVLEVEL, "1") == 0 ||
977 strcmp (PREVLEVEL, "S") == 0 ||
978 strcmp (PREVLEVEL, "N") == 0))
979 {
980 /* Try not to join boot and ksoftlevels together */
981 if (! newlevel ||
982 strcmp (newlevel, getenv ("RC_BOOTLEVEL")) != 0)
983 if (get_ksoftlevel (ksoftbuffer, sizeof (ksoftbuffer)))
984 newlevel = ksoftbuffer;
985 } else if (! RUNLEVEL ||
986 (strcmp (RUNLEVEL, "1") != 0 &&
987 strcmp (RUNLEVEL, "S") != 0 &&
988 strcmp (RUNLEVEL, "N") != 0))
989 {
990 set_ksoftlevel (NULL);
991 }
992
993 if (newlevel &&
994 (strcmp (newlevel, RC_LEVEL_REBOOT) == 0 ||
995 strcmp (newlevel, RC_LEVEL_SHUTDOWN) == 0 ||
996 strcmp (newlevel, RC_LEVEL_SINGLE) == 0))
997 {
998 going_down = true;
999 rc_set_runlevel (newlevel);
1000 setenv ("RC_SOFTLEVEL", newlevel, 1);
1001 rc_plugin_run (rc_hook_runlevel_stop_in, newlevel);
1002 } else {
1003 rc_plugin_run (rc_hook_runlevel_stop_in, runlevel);
1004 }
1005
1006 /* Check if runlevel is valid if we're changing */
1007 if (newlevel && strcmp (runlevel, newlevel) != 0 && ! going_down) {
1008 tmp = rc_strcatpaths (RC_RUNLEVELDIR, newlevel, (char *) NULL);
1009 if (! rc_is_dir (tmp))
1010 eerrorx ("%s: is not a valid runlevel", newlevel);
1011 CHAR_FREE (tmp);
1012 }
1013
1014 /* Load our deptree now */
1015 if ((deptree = rc_load_deptree ()) == NULL)
1016 eerrorx ("failed to load deptree");
1017
1018 /* Clean the failed services state dir now */
1019 if (rc_is_dir (RC_SVCDIR "/failed"))
1020 rc_rm_dir (RC_SVCDIR "/failed", false);
1021
1022 mkdir (RC_STOPPING, 0755);
1023
1024 #ifdef __linux__
1025 /* udev likes to start services before we're ready when it does
1026 its coldplugging thing. runscript knows when we're not ready so it
1027 stores a list of coldplugged services in DEVBOOT for us to pick up
1028 here when we are ready for them */
1029 if (rc_is_dir (DEVBOOT)) {
1030 start_services = rc_ls_dir (DEVBOOT, RC_LS_INITD);
1031 rc_rm_dir (DEVBOOT, true);
1032
1033 STRLIST_FOREACH (start_services, service, i)
1034 if (rc_allow_plug (service))
1035 rc_mark_service (service, rc_service_coldplugged);
1036 /* We need to dump this list now.
1037 This may seem redunant, but only Linux needs this and saves on
1038 code bloat. */
1039 rc_strlist_free (start_services);
1040 start_services = NULL;
1041 }
1042 #else
1043 /* BSD's on the other hand populate /dev automagically and use devd.
1044 The only downside of this approach and ours is that we have to hard code
1045 the device node to the init script to simulate the coldplug into
1046 runlevel for our dependency tree to work. */
1047 if (newlevel && strcmp (newlevel, bootlevel) == 0 &&
1048 (strcmp (runlevel, RC_LEVEL_SINGLE) == 0 ||
1049 strcmp (runlevel, RC_LEVEL_SYSINIT) == 0) &&
1050 rc_is_env ("RC_COLDPLUG", "yes"))
1051 {
1052 #if defined(__DragonFly__) || defined(__FreeBSD__)
1053 /* The net interfaces are easy - they're all in net /dev/net :) */
1054 start_services = rc_ls_dir ("/dev/net", 0);
1055 STRLIST_FOREACH (start_services, service, i) {
1056 j = (strlen ("net.") + strlen (service) + 1);
1057 tmp = rc_xmalloc (sizeof (char *) * j);
1058 snprintf (tmp, j, "net.%s", service);
1059 if (rc_service_exists (tmp) && rc_allow_plug (tmp))
1060 rc_mark_service (tmp, rc_service_coldplugged);
1061 CHAR_FREE (tmp);
1062 }
1063 rc_strlist_free (start_services);
1064 #endif
1065
1066 /* The mice are a little more tricky.
1067 If we coldplug anything else, we'll probably do it here. */
1068 start_services = rc_ls_dir ("/dev", 0);
1069 STRLIST_FOREACH (start_services, service, i) {
1070 if (strncmp (service, "psm", 3) == 0 ||
1071 strncmp (service, "ums", 3) == 0)
1072 {
1073 char *p = service + 3;
1074 if (p && isdigit (*p)) {
1075 j = (strlen ("moused.") + strlen (service) + 1);
1076 tmp = rc_xmalloc (sizeof (char *) * j);
1077 snprintf (tmp, j, "moused.%s", service);
1078 if (rc_service_exists (tmp) && rc_allow_plug (tmp))
1079 rc_mark_service (tmp, rc_service_coldplugged);
1080 CHAR_FREE (tmp);
1081 }
1082 }
1083 }
1084 rc_strlist_free (start_services);
1085 start_services = NULL;
1086 }
1087 #endif
1088
1089 /* Build a list of all services to stop and then work out the
1090 correct order for stopping them */
1091 stop_services = rc_ls_dir (RC_SVCDIR_STARTING, RC_LS_INITD);
1092 stop_services = rc_strlist_join (stop_services,
1093 rc_ls_dir (RC_SVCDIR_INACTIVE, RC_LS_INITD));
1094 stop_services = rc_strlist_join (stop_services,
1095 rc_ls_dir (RC_SVCDIR_STARTED, RC_LS_INITD));
1096
1097 types = NULL;
1098 rc_strlist_add (&types, "ineed");
1099 rc_strlist_add (&types, "iuse");
1100 rc_strlist_add (&types, "iafter");
1101 deporder = rc_get_depends (deptree, types, stop_services,
1102 runlevel, depoptions | RC_DEP_STOP);
1103 rc_strlist_free (stop_services);
1104 rc_strlist_free (types);
1105 types = NULL;
1106 stop_services = deporder;
1107 deporder = NULL;
1108 rc_strlist_reverse (stop_services);
1109
1110 /* Load our list of coldplugged services */
1111 coldplugged_services = rc_ls_dir (RC_SVCDIR_COLDPLUGGED, RC_LS_INITD);
1112
1113 /* Load our start services now.
1114 We have different rules dependent on runlevel. */
1115 if (newlevel && strcmp (newlevel, bootlevel) == 0) {
1116 if (coldplugged_services) {
1117 einfon ("Device initiated services:");
1118 STRLIST_FOREACH (coldplugged_services, service, i) {
1119 printf (" %s", service);
1120 rc_strlist_add (&start_services, service);
1121 }
1122 printf ("\n");
1123 }
1124 tmp = rc_strcatpaths (RC_RUNLEVELDIR, newlevel ? newlevel : runlevel,
1125 (char *) NULL);
1126 start_services = rc_strlist_join (start_services,
1127 rc_ls_dir (tmp, RC_LS_INITD));
1128 CHAR_FREE (tmp);
1129 } else {
1130 /* Store our list of coldplugged services */
1131 coldplugged_services = rc_strlist_join (coldplugged_services,
1132 rc_ls_dir (RC_SVCDIR_COLDPLUGGED, RC_LS_INITD));
1133 if (strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SINGLE) != 0 &&
1134 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 &&
1135 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_REBOOT) != 0)
1136 {
1137 /* We need to include the boot runlevel services if we're not in it */
1138 char **services = rc_services_in_runlevel (bootlevel);
1139
1140 start_services = rc_strlist_join (start_services, services);
1141 services = rc_services_in_runlevel (newlevel ? newlevel : runlevel);
1142 start_services = rc_strlist_join (start_services, services);
1143 services = NULL;
1144
1145 STRLIST_FOREACH (coldplugged_services, service, i)
1146 rc_strlist_add (&start_services, service);
1147
1148 }
1149 }
1150
1151 /* Save out softlevel now */
1152 if (going_down)
1153 rc_set_runlevel (newlevel);
1154
1155 types = NULL;
1156 rc_strlist_add (&types, "needsme");
1157 /* Now stop the services that shouldn't be running */
1158 STRLIST_FOREACH (stop_services, service, i) {
1159 bool found = false;
1160 char *conf = NULL;
1161 char **stopdeps = NULL;
1162 char *svc1 = NULL;
1163 char *svc2 = NULL;
1164 int k;
1165
1166 if (rc_service_state (service, rc_service_stopped))
1167 continue;
1168
1169 /* We always stop the service when in these runlevels */
1170 if (going_down) {
1171 pid_t pid = rc_stop_service (service);
1172 if (pid > 0 && ! rc_is_env ("RC_PARALLEL", "yes"))
1173 rc_waitpid (pid);
1174 continue;
1175 }
1176
1177 /* If we're in the start list then don't bother stopping us */
1178 STRLIST_FOREACH (start_services, svc1, j)
1179 if (strcmp (svc1, service) == 0) {
1180 found = true;
1181 break;
1182 }
1183
1184 /* Unless we would use a different config file */
1185 if (found) {
1186 int len;
1187 if (! newlevel)
1188 continue;
1189
1190 len = strlen (service) + strlen (runlevel) + 2;
1191 tmp = rc_xmalloc (sizeof (char *) * len);
1192 snprintf (tmp, len, "%s.%s", service, runlevel);
1193 conf = rc_strcatpaths (RC_CONFDIR, tmp, (char *) NULL);
1194 found = rc_exists (conf);
1195 CHAR_FREE (conf);
1196 CHAR_FREE (tmp);
1197 if (! found) {
1198 len = strlen (service) + strlen (newlevel) + 2;
1199 tmp = rc_xmalloc (sizeof (char *) * len);
1200 snprintf (tmp, len, "%s.%s", service, newlevel);
1201 conf = rc_strcatpaths (RC_CONFDIR, tmp, (char *) NULL);
1202 found = rc_exists (conf);
1203 CHAR_FREE (conf);
1204 CHAR_FREE (tmp);
1205 if (!found)
1206 continue;
1207 }
1208 } else {
1209 /* Allow coldplugged services not to be in the runlevels list */
1210 if (rc_service_state (service, rc_service_coldplugged))
1211 continue;
1212 }
1213
1214 /* We got this far! Or last check is to see if any any service that
1215 going to be started depends on us */
1216 rc_strlist_add (&stopdeps, service);
1217 deporder = rc_get_depends (deptree, types, stopdeps,
1218 runlevel, RC_DEP_STRICT);
1219 rc_strlist_free (stopdeps);
1220 stopdeps = NULL;
1221 found = false;
1222 STRLIST_FOREACH (deporder, svc1, j) {
1223 STRLIST_FOREACH (start_services, svc2, k)
1224 if (strcmp (svc1, svc2) == 0) {
1225 found = true;
1226 break;
1227 }
1228 if (found)
1229 break;
1230 }
1231 rc_strlist_free (deporder);
1232 deporder = NULL;
1233
1234 /* After all that we can finally stop the blighter! */
1235 if (! found) {
1236 pid_t pid = rc_stop_service (service);
1237 if (pid > 0 && ! rc_is_env ("RC_PARALLEL", "yes"))
1238 rc_waitpid (pid);
1239 }
1240 }
1241 rc_strlist_free (types);
1242 types = NULL;
1243
1244 /* Wait for our services to finish */
1245 wait_for_services ();
1246
1247 /* Notify the plugins we have finished */
1248 rc_plugin_run (rc_hook_runlevel_stop_out, runlevel);
1249
1250 rmdir (RC_STOPPING);
1251
1252 /* Store the new runlevel */
1253 if (newlevel) {
1254 rc_set_runlevel (newlevel);
1255 runlevel = newlevel;
1256 setenv ("RC_SOFTLEVEL", runlevel, 1);
1257 }
1258
1259 /* Run the halt script if needed */
1260 if (strcmp (runlevel, RC_LEVEL_SHUTDOWN) == 0 ||
1261 strcmp (runlevel, RC_LEVEL_REBOOT) == 0)
1262 {
1263 execl (HALTSH, HALTSH, runlevel, (char *) NULL);
1264 eerrorx ("%s: unable to exec `%s': %s",
1265 applet, HALTSH, strerror (errno));
1266 }
1267
1268 /* Single user is done now */
1269 if (strcmp (runlevel, RC_LEVEL_SINGLE) == 0) {
1270 if (rc_exists (INTERACTIVE))
1271 unlink (INTERACTIVE);
1272 sulogin (false);
1273 }
1274
1275 mkdir (RC_STARTING, 0755);
1276 rc_plugin_run (rc_hook_runlevel_start_in, runlevel);
1277
1278 /* Re-add our coldplugged services if they stopped */
1279 STRLIST_FOREACH (coldplugged_services, service, i)
1280 rc_mark_service (service, rc_service_coldplugged);
1281
1282 /* Order the services to start */
1283 rc_strlist_add (&types, "ineed");
1284 rc_strlist_add (&types, "iuse");
1285 rc_strlist_add (&types, "iafter");
1286 deporder = rc_get_depends (deptree, types, start_services,
1287 runlevel, depoptions | RC_DEP_START);
1288 rc_strlist_free (types);
1289 types = NULL;
1290 rc_strlist_free (start_services);
1291 start_services = deporder;
1292 deporder = NULL;
1293
1294 #ifdef __linux__
1295 /* mark any services skipped as started */
1296 if (PREVLEVEL && strcmp (PREVLEVEL, "N") == 0) {
1297 if ((service = proc_getent ("noinitd"))) {
1298 char *p = service;
1299 char *token;
1300
1301 while ((token = strsep (&p, ",")))
1302 rc_mark_service (token, rc_service_started);
1303 free (service);
1304 }
1305 }
1306 #endif
1307
1308
1309 STRLIST_FOREACH (start_services, service, i) {
1310 if (rc_service_state (service, rc_service_stopped)) {
1311 pid_t pid;
1312
1313 if (! interactive)
1314 interactive = want_interactive ();
1315
1316 if (interactive) {
1317 interactive_retry:
1318 printf ("\n");
1319 einfo ("About to start the service %s", service);
1320 eindent ();
1321 einfo ("1) Start the service\t\t2) Skip the service");
1322 einfo ("3) Continue boot process\t\t4) Exit to shell");
1323 eoutdent ();
1324 interactive_option:
1325 switch (read_key (true)) {
1326 case '1': break;
1327 case '2': continue;
1328 case '3': interactive = false; break;
1329 case '4': sulogin (true); goto interactive_retry;
1330 default: goto interactive_option;
1331 }
1332 }
1333
1334 /* Remember the pid if we're running in parallel */
1335 if ((pid = rc_start_service (service)))
1336 add_pid (pid);
1337
1338 if (! rc_is_env ("RC_PARALLEL", "yes")) {
1339 rc_waitpid (pid);
1340 remove_pid (pid);
1341 }
1342 }
1343 }
1344
1345 /* Wait for our services to finish */
1346 wait_for_services ();
1347
1348 rc_plugin_run (rc_hook_runlevel_start_out, runlevel);
1349
1350 #ifdef __linux__
1351 /* mark any services skipped as stopped */
1352 if (PREVLEVEL && strcmp (PREVLEVEL, "N") == 0) {
1353 if ((service = proc_getent ("noinitd"))) {
1354 char *p = service;
1355 char *token;
1356
1357 while ((token = strsep (&p, ",")))
1358 rc_mark_service (token, rc_service_stopped);
1359 free (service);
1360 }
1361 }
1362 #endif
1363
1364 /* Store our interactive status for boot */
1365 if (interactive && strcmp (runlevel, bootlevel) == 0)
1366 mark_interactive ();
1367 else {
1368 if (rc_exists (INTERACTIVE))
1369 unlink (INTERACTIVE);
1370 }
1371
1372 return (EXIT_SUCCESS);
1373 }
1374

  ViewVC Help
Powered by ViewVC 1.1.20