Fix typo in the Gentoo initscript.
[fbsplash.git] / gentoo / splash.c
blob522057166ec4d998c27e3cc9cf7cf3dd213b38ca
1 /*
2 * splash.c - Splash plugin for the Gentoo RC system.
4 * Copyright (c) 2007-2008, Michal Januszewski <spock@gentoo.org>
6 * Original splash plugin compatible with baselayout-1's splash-functions.sh
7 * written by Roy Marples <uberlord@gentoo.org>.
9 * This file is subject to the terms and conditions of the GNU General Public
10 * License v2. See the file COPYING in the main directory of this archive for
11 * more details.
14 #include <sys/stat.h>
15 #include <errno.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <sys/wait.h>
22 #include <sys/ioctl.h>
23 #include <linux/kd.h>
24 #include <linux/fb.h>
25 #include <einfo.h>
26 #include <rc.h>
27 #include <fbsplash.h>
29 /* Some queue.h implenetations don't have this macro */
30 #ifndef TAILQ_CONCAT
31 #define TAILQ_CONCAT(head1, head2, field) do { \
32 if (!TAILQ_EMPTY(head2)) { \
33 *(head1)->tqh_last = (head2)->tqh_first; \
34 (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
35 (head1)->tqh_last = (head2)->tqh_last; \
36 TAILQ_INIT((head2)); \
37 } \
38 } while (0)
39 #endif
41 #define SPLASH_CMD "export SPLASH_XRES='%d'; export SPLASH_YRES='%d';" \
42 "export SOFTLEVEL='%s'; export BOOTLEVEL='%s';" \
43 "export DEFAULTLEVEL='%s'; export svcdir=${RC_SVCDIR};" \
44 "export RUNLEVEL='%s'; . /sbin/splash-functions.sh; %s %s %s"
46 static char *bootlevel = NULL;
47 static char *defaultlevel = NULL;
48 static RC_STRINGLIST *svcs = NULL;
49 static RC_STRINGLIST *svcs_done = NULL;
50 static int svcs_cnt = 0;
51 static int svcs_done_cnt = 0;
52 static pid_t pid_daemon = 0;
53 static fbspl_cfg_t *config = NULL;
55 static int xres = 0;
56 static int yres = 0;
59 * Check whether a strlist contains a specific item.
61 static bool list_has(RC_STRINGLIST *list, const char *item)
63 RC_STRING *s;
65 if (list) {
66 TAILQ_FOREACH(s, list, entries)
67 if (strcmp(s->value, item) == 0)
68 return true;
70 return false;
74 * Count the number of items in a strlist.
76 static int strlist_count(RC_STRINGLIST *list)
78 RC_STRING *s;
79 int c = 0;
81 if (list)
82 TAILQ_FOREACH(s, list, entries)
83 c++;
85 return c;
89 * Create a strlist from a file pointer. Can be used
90 * to get a list of words printed by an app/script.
92 static void get_list_fp(RC_STRINGLIST *list, FILE *fp)
94 char buffer[512];
95 char *p;
96 char *token;
98 while (fgets(buffer, 512, fp)) {
99 p = buffer;
101 /* Remove the newline character */
102 if (p[strlen(p)-1] == '\n')
103 p[strlen(p)-1] = 0;
105 /* Strip leading spaces/tabs */
106 while ((*p == ' ') || (*p == '\t'))
107 p++;
109 /* Get entry - we do not want comments */
110 token = strsep(&p, "#");
111 if (!(token && (strlen(token) > 1)))
112 continue;
114 while ((p = strsep(&token, " ")) != NULL) {
115 if (strlen(p) > 1) {
116 rc_stringlist_add(list, p);
123 * Create a strlist from a file. Used for svcs_start/svcs_stop.
125 static void get_list(RC_STRINGLIST *list, const char *file)
127 FILE *fp;
129 if (!(fp = fopen(file, "r"))) {
130 ewarn("%s: `%s': %s", __func__, file, strerror(errno));
131 } else {
132 get_list_fp(list, fp);
133 fclose(fp);
137 static void fix_rc_variable(char *s)
139 do {
140 char *t = strstr(s, "\\$");
142 if (t) {
143 memmove(t, t+1, strlen(t));
144 } else {
145 break;
147 } while(1);
151 * Get splash settings from /etc/conf.d/splash
153 static int splash_config_gentoo(fbspl_cfg_t *cfg, fbspl_type_t type)
155 RC_STRINGLIST *confd;
156 char *t;
158 confd = rc_config_load("/etc/conf.d/splash");
160 t = rc_config_value(confd, "SPLASH_KDMODE");
161 if (t) {
162 if (!strcasecmp(t, "graphics")) {
163 cfg->kdmode = KD_GRAPHICS;
164 } else if (!strcasecmp(t, "text")) {
165 cfg->kdmode = KD_TEXT;
169 t = rc_config_value(confd, "SPLASH_PROFILE");
170 if (t) {
171 if (!strcasecmp(t, "on") || !strcasecmp(t, "yes"))
172 cfg->profile = true;
175 t = rc_config_value(confd, "SPLASH_TTY");
176 if (t) {
177 int i;
178 if (sscanf(t, "%d", &i) == 1 && i > 0) {
179 cfg->tty_s = i;
183 t = rc_config_value(confd, "SPLASH_THEME");
184 if (t)
185 fbsplash_acc_theme_set(t);
187 t = rc_config_value(confd, "SPLASH_MODE_REQ");
188 if (t) {
189 if (!strcasecmp(t, "verbose")) {
190 cfg->reqmode = FBSPL_MODE_VERBOSE;
191 } else if (!strcasecmp(t, "silent")) {
192 cfg->reqmode = FBSPL_MODE_VERBOSE | FBSPL_MODE_SILENT;
193 } else if (!strcasecmp(t, "silentonly")) {
194 cfg->reqmode = FBSPL_MODE_SILENT;
198 t = rc_config_value(confd, "SPLASH_VERBOSE_ON_ERRORS");
199 if (t && (!strcasecmp(t, "on") || !strcasecmp(t, "yes")))
200 cfg->vonerr = true;
202 switch(type) {
203 case fbspl_reboot:
204 t = rc_config_value(confd, "SPLASH_REBOOT_MESSAGE");
205 if (t) {
206 fix_rc_variable(t);
207 fbsplash_acc_message_set(t);
209 break;
211 case fbspl_shutdown:
212 t = rc_config_value(confd, "SPLASH_SHUTDOWN_MESSAGE");
213 if (t) {
214 fix_rc_variable(t);
215 fbsplash_acc_message_set(t);
217 break;
219 case fbspl_bootup:
220 default:
221 t = rc_config_value(confd, "SPLASH_BOOT_MESSAGE");
222 if (t) {
223 fix_rc_variable(t);
224 fbsplash_acc_message_set(t);
226 break;
229 t = rc_config_value(confd, "SPLASH_TEXTBOX");
230 if (t) {
231 if (!strcasecmp(t, "on") || !strcasecmp(t, "yes"))
232 cfg->textbox_visible = true;
235 t = rc_config_value(confd, "SPLASH_AUTOVERBOSE");
236 if (t) {
237 cfg->autoverbose = atoi(t);
240 t = rc_config_value(confd, "SPLASH_EFFECTS");
241 if (t) {
242 char *opt;
244 while ((opt = strsep(&t, ",")) != NULL) {
245 if (!strcmp(opt, "fadein")) {
246 cfg->effects |= FBSPL_EFF_FADEIN;
247 } else if (!strcmp(opt, "fadeout")) {
248 cfg->effects |= FBSPL_EFF_FADEOUT;
253 t = rc_config_value(confd, "SPLASH_XSERVICE");
254 if (t)
255 fbsplash_acc_xservice_set(t);
256 else
257 fbsplash_acc_xservice_set("xdm");
259 rc_stringlist_free(confd);
260 return 0;
263 static const char *splash_sysvinit_runlevel(const char *runlevel)
265 const char *runlev = runlevel ? runlevel : rc_runlevel_get();
267 if (!strcmp(runlev, RC_LEVEL_SHUTDOWN)) {
268 char *t = getenv("RC_REBOOT");
269 if (t && !strcmp(t, "YES")) {
270 return "6";
271 } else {
272 return "0";
274 } else if (!strcmp(runlev, RC_LEVEL_SYSINIT)) {
275 return "S";
276 } else if (!strcmp(runlev, RC_LEVEL_SINGLE)) {
277 return "1";
278 } else {
279 return "3";
285 * Call a function from /sbin/splash-functions.sh.
286 * This is rather slow, so use it only when really necessary.
288 static int splash_call(const char *cmd, const char *arg1, const char *arg2, const char *runlevel)
290 char *c;
291 int l;
292 char *soft = getenv("RC_RUNLEVEL");
294 if (!cmd || !soft)
295 return -1;
297 l = strlen(SPLASH_CMD) + strlen(soft) + strlen(cmd) + 10;
298 if (arg1)
299 l += strlen(arg1);
300 if (arg2)
301 l += strlen(arg2);
303 c = malloc(sizeof(char*) * l);
304 if (!c)
305 return -1;
307 snprintf(c, l, SPLASH_CMD, xres, yres,
308 arg1 ? (strcmp(arg1, RC_LEVEL_SYSINIT) == 0 ? bootlevel : soft) : soft,
309 bootlevel, defaultlevel, runlevel,
310 cmd, arg1 ? arg1 : "", arg2 ? arg2 : "");
311 l = system(c);
312 free(c);
313 return l;
317 * Run a theme hook script.
319 static int splash_theme_hook(const char *name, const char *type, const char *arg1)
321 char *buf;
322 int l = 256;
323 struct stat st;
325 if (arg1)
326 fbsplash_profile("%s %s %s\n", type, name, arg1);
327 else
328 fbsplash_profile("%s %s\n", type, name);
330 l += strlen(name);
331 l += strlen(config->theme);
333 buf = malloc(l * sizeof(char*));
334 snprintf(buf, l, "/etc/splash/%s/scripts/%s-%s", config->theme, name, type);
335 if (stat(buf, &st) != 0) {
336 free(buf);
337 return 0;
340 if (!strcmp(name, "rc_init") || !strcmp(name, "rc_exit")) {
341 const char *t = splash_sysvinit_runlevel(arg1);
342 l = splash_call(buf, arg1, t, t);
343 } else if (!strcmp(name, "svc_started") || !strcmp(name, "svc_stopped")) {
345 * Set the 2nd parameter to 0 so that we don't break themes using the
346 * legacy interface in which these events contained an error code.
348 l = splash_call(buf, arg1, "0", splash_sysvinit_runlevel(NULL));
349 } else {
350 l = splash_call(buf, arg1, NULL, splash_sysvinit_runlevel(NULL));
352 free(buf);
353 return l;
357 * Update service state.
359 static int splash_svc_state(const char *name, const char *state, bool paint)
361 if (paint)
362 splash_theme_hook(state, "pre", name);
364 if (!strcmp(state, "svc_started")) {
365 fbsplash_send("log Service '%s' started.\n", name);
366 } else if (!strcmp(state, "svc_start_failed")) {
367 fbsplash_send("log Service '%s' failed to start.\n", name);
368 } else if (!strcmp(state, "svc_stopped")) {
369 fbsplash_send("log Service '%s' stopped.\n", name);
370 } else if (!strcmp(state, "svc_stop_failed")) {
371 fbsplash_send("log Service '%s' failed to stop.\n", name);
374 fbsplash_send("update_svc %s %s\n", name, state);
376 if (paint) {
377 fbsplash_send("paint\n");
378 splash_theme_hook(state, "post", name);
381 return 0;
385 * Get the resolution that the silent splash will use.
387 static void splash_init_res()
389 struct fb_var_screeninfo var;
390 int fh;
392 if ((fh = open("/dev/fb0", O_RDONLY)) == -1)
393 if ((fh = open("/dev/fb/0", O_RDONLY)) == -1)
394 return;
396 if (ioctl(fh, FBIOGET_VSCREENINFO, &var))
397 return;
399 close(fh);
401 fbsplash_get_res(config->theme, (int*)&var.xres, (int*)&var.yres);
402 xres = var.xres;
403 yres = var.yres;
407 * Init splash config variables and check that the splash daemon
408 * is running.
410 static int splash_init(bool start)
412 RC_STRINGLIST *tmp;
414 config->verbosity = FBSPL_VERB_QUIET;
415 if (fbsplash_check_daemon(&pid_daemon)) {
416 config->verbosity = FBSPL_VERB_NORMAL;
417 return -1;
420 config->verbosity = FBSPL_VERB_NORMAL;
422 if (svcs) {
423 ewarn("%s: We already have a svcs list!", __func__);
424 rc_stringlist_free(svcs);
426 svcs = rc_stringlist_new();
428 /* Booting.. */
429 if (start) {
430 get_list(svcs, FBSPLASH_CACHEDIR"/svcs_start");
431 svcs_cnt = strlist_count(svcs);
433 svcs_done = rc_services_in_state(RC_SERVICE_STARTED);
435 tmp = rc_services_in_state(RC_SERVICE_INACTIVE);
436 if (svcs_done && tmp) {
437 TAILQ_CONCAT(svcs_done, tmp, entries);
438 free(tmp);
439 } else if (tmp)
440 svcs_done = tmp;
442 tmp = rc_services_in_state(RC_SERVICE_FAILED);
443 if (svcs_done && tmp) {
444 TAILQ_CONCAT(svcs_done, tmp, entries);
445 free(tmp);
446 } else if (tmp)
447 svcs_done = tmp;
449 tmp = rc_services_in_state(RC_SERVICE_SCHEDULED);
450 if (svcs_done && tmp) {
451 TAILQ_CONCAT(svcs_done, tmp, entries);
452 free(tmp);
453 } else if (tmp)
454 svcs_done = tmp;
456 svcs_done_cnt = strlist_count(svcs_done);
457 /* .. or rebooting? */
458 } else {
459 get_list(svcs, FBSPLASH_CACHEDIR"/svcs_stop");
460 svcs_cnt = strlist_count(svcs);
462 svcs_done = rc_services_in_state(RC_SERVICE_STARTED);
464 tmp = rc_services_in_state(RC_SERVICE_STARTING);
465 if (svcs_done && tmp) {
466 TAILQ_CONCAT(svcs_done, tmp, entries);
467 free(tmp);
468 } else if (tmp)
469 svcs_done = tmp;
471 tmp = rc_services_in_state(RC_SERVICE_INACTIVE);
472 if (svcs_done && tmp) {
473 TAILQ_CONCAT(svcs_done, tmp, entries);
474 free(tmp);
475 } else if (tmp)
476 svcs_done = tmp;
478 svcs_done_cnt = svcs_cnt - strlist_count(svcs_done);
481 splash_init_res();
483 return 0;
487 * Handle the start/stop of a single service.
489 static int splash_svc_handle(const char *name, const char *state, bool skip)
491 if (!skip) {
492 /* If we don't have any services, something must be broken.
493 * Bail out since there is nothing we can do about it. */
494 if (svcs_cnt == 0)
495 return -1;
497 /* Don't process services twice. */
498 if (list_has(svcs_done, name))
499 return 0;
501 if (!svcs_done)
502 svcs_done = rc_stringlist_new();
503 rc_stringlist_add(svcs_done, name);
504 svcs_done_cnt++;
507 /* Recalculate progress */
508 config->progress = svcs_done_cnt * FBSPL_PROGRESS_MAX / svcs_cnt;
510 splash_theme_hook(state, "pre", name);
511 splash_svc_state(name, state, 0);
512 fbsplash_send("progress %d\n", config->progress);
513 fbsplash_send("paint\n");
514 splash_theme_hook(state, "post", name);
516 return 0;
520 * Create a list of services that will be started during bootup.
522 int splash_svcs_start()
524 RC_DEPTREE *deptree;
525 FILE *fp;
526 RC_STRINGLIST *t, *deporder;
527 RC_STRING *s, *r;
528 int i, err = 0;
530 fp = fopen(FBSPLASH_CACHEDIR"/svcs_start", "w");
531 if (!fp) {
532 ewarn("%s: `%s': %s", __func__, FBSPLASH_CACHEDIR"/svcs_start", strerror(errno));
533 return -1;
536 if ((deptree = rc_deptree_load()) == NULL) {
537 eerror("%s: failed to load deptree", __func__);
538 err = -2;
539 goto out;
542 deporder = rc_deptree_order(deptree, bootlevel, RC_DEP_START);
544 /* Save what we've got so far to the svcs_start. */
545 if (deporder) {
546 i = 0;
547 TAILQ_FOREACH(s, deporder, entries) {
548 if (i > 0)
549 fprintf(fp, " ");
550 fprintf(fp, "%s", s->value);
551 i++;
555 t = deporder;
556 deporder = rc_deptree_order(deptree, defaultlevel, RC_DEP_START);
558 /* Print the new services and skip ones that have already been started
559 * in the 'boot' runlevel. */
560 if (deporder) {
561 TAILQ_FOREACH(s, deporder, entries) {
562 char duplicate = 0;
563 TAILQ_FOREACH(r, t, entries) {
564 if (!strcmp(s->value, r->value)) {
565 duplicate = 1;
566 break;
569 if (!duplicate) {
570 fprintf(fp, " %s", s->value);
575 rc_stringlist_free(deporder);
576 rc_stringlist_free(t);
577 rc_deptree_free(deptree);
579 out:
580 fclose(fp);
581 return 0;
585 * Create a list of services that will be stopped during reboot/shutdown.
587 int splash_svcs_stop(const char *runlevel)
589 RC_DEPTREE *deptree;
590 RC_STRINGLIST *deporder;
591 RC_STRING *s;
592 FILE *fp;
593 int i, err = 0;
595 fp = fopen(FBSPLASH_CACHEDIR"/svcs_stop", "w");
596 if (!fp) {
597 ewarn("%s: `%s': %s", __func__, FBSPLASH_CACHEDIR"/svcs_stop", strerror(errno));
598 return -1;
601 if ((deptree = rc_deptree_load()) == NULL) {
602 eerror("%s: failed to load deptree", __func__);
603 err = -2;
604 goto out;
607 deporder = rc_deptree_order(deptree, runlevel, RC_DEP_STOP);
609 if (deporder) {
610 i = 0;
611 TAILQ_FOREACH(s, deporder, entries) {
612 if (i > 0)
613 fprintf(fp, " ");
614 fprintf(fp, "%s", s->value);
615 i++;
619 rc_stringlist_free(deporder);
620 rc_deptree_free(deptree);
621 out:
622 fclose(fp);
623 return err;
627 * Start the splash daemon during boot/reboot.
629 static int splash_start(const char *runlevel)
631 bool start;
632 int err = 0;
633 char buf[2048];
634 RC_STRING *s;
636 /* Get a list of services that we'll have to handle. */
637 /* We're rebooting/shutting down. */
638 if (!strcmp(runlevel, RC_LEVEL_SHUTDOWN)) {
639 if ((err = fbsplash_cache_prep()))
640 return err;
641 splash_svcs_stop(runlevel);
642 start = false;
643 /* We're booting. */
644 } else {
645 if ((err = fbsplash_cache_prep()))
646 return err;
647 splash_svcs_start();
648 start = true;
650 splash_init_res();
651 splash_theme_hook("rc_init", "pre", runlevel);
653 /* Perform sanity checks (console=, CONSOLE= etc). */
654 if (fbsplash_check_sanity())
655 return -1;
657 /* Start the splash daemon */
658 snprintf(buf, 2048, "BOOT_MSG='%s' " FBSPLASH_DAEMON " --theme=\"%s\" --pidfile=" FBSPLASH_PIDFILE " --type=%s %s %s %s",
659 config->message, config->theme,
660 (config->type == fbspl_reboot) ? "reboot" : ((config->type == fbspl_shutdown) ? "shutdown" : "bootup"),
661 (config->kdmode == KD_GRAPHICS) ? "--kdgraphics" : "",
662 (config->textbox_visible) ? "--textbox" : "",
663 ((config->effects & (FBSPL_EFF_FADEOUT | FBSPL_EFF_FADEIN)) == (FBSPL_EFF_FADEOUT | FBSPL_EFF_FADEIN)) ? "--effects=fadeout,fadein" :
664 ((config->effects & FBSPL_EFF_FADEOUT) ? "--effects=fadeout" :
665 ((config->effects & FBSPL_EFF_FADEIN) ? "--effects=fadein" : "")));
667 err = system(buf);
668 if (err == -1 || WEXITSTATUS(err) != 0) {
669 eerror("Failed to start the splash daemon, error code %d", err);
670 return err;
673 err = splash_init(start);
674 if (err)
675 return err;
677 /* Set the initial state of all services. */
678 if (svcs)
679 TAILQ_FOREACH(s, svcs, entries)
680 splash_svc_state(s->value, start ? "svc_inactive_start" : "svc_inactive_stop", 0);
682 fbsplash_set_evdev();
683 fbsplash_send("set autoverbose %d\n", config->autoverbose);
684 fbsplash_send("set tty silent %d\n", config->tty_s);
685 fbsplash_send("set mode silent\n");
686 fbsplash_send("repaint\n");
687 return err;
691 * Stop the splash daemon.
693 static int splash_stop(const char *runlevel)
695 char *save[] = { "profile", "svcs_start", NULL };
696 char buf[128];
697 struct stat st;
698 int cnt = 0;
700 if (rc_service_state(config->xservice) & RC_SERVICE_STARTED) {
701 fbsplash_send("exit staysilent\n");
702 } else {
703 fbsplash_send("exit\n");
705 snprintf(buf, 128, "/proc/%d", pid_daemon);
707 /* Wait up to 1.0s for the splash daemon to exit. */
708 while (stat(buf, &st) == 0 && cnt < 100) {
709 usleep(10000);
710 cnt++;
713 /* Just to be sure we aren't stuck in a black ex-silent tty.. */
714 if (fbsplash_is_silent() && !(rc_service_state(config->xservice) & RC_SERVICE_STARTED))
715 fbsplash_set_verbose(0);
717 /* If we don't get a runlevel argument, then we're being executed
718 * because of a rc-abort event and we don't save any data. */
719 return fbsplash_cache_cleanup(save);
722 int rc_plugin_hook(RC_HOOK hook, const char *name)
724 int i = 0;
725 fbspl_type_t type = fbspl_bootup;
726 char *runlev;
727 bool skip = false;
728 int retval = 0;
730 runlev = rc_runlevel_get();
731 if (!strcmp(runlev, RC_LEVEL_SHUTDOWN)) {
732 if (!strcmp(splash_sysvinit_runlevel(NULL), "6")) {
733 type = fbspl_reboot;
734 } else {
735 type = fbspl_shutdown;
739 /* Get boot and default levels from env variables exported by RC.
740 * If unavailable, use the default ones. */
741 bootlevel = getenv("RC_BOOTLEVEL");
742 defaultlevel = getenv("RC_DEFAULTLEVEL");
744 /* We generally do nothing if we're in sysinit. Except if the
745 * autoconfig service is present, when we get a list of services
746 * that will be started by it and mark them as coldplugged. */
747 if (name && !strcmp(name, RC_LEVEL_SYSINIT)) {
748 if (hook == RC_HOOK_RUNLEVEL_START_OUT && rc_service_in_runlevel("autoconfig", defaultlevel)) {
749 FILE *fp;
750 RC_STRINGLIST *list;
751 RC_STRING *s;
753 fp = popen("if [ -e /etc/init.d/autoconfig ]; then . /etc/init.d/autoconfig ; list_services ; fi", "r");
754 if (!fp)
755 goto exit;
757 list = rc_stringlist_new();
758 get_list_fp(list, fp);
759 TAILQ_FOREACH(s, list, entries)
760 rc_service_mark(s->value, RC_SERVICE_HOTPLUGGED);
761 pclose(fp);
762 rc_stringlist_free(list);
764 goto exit;
767 /* Don't do anything if we're starting/stopping a service, but
768 * we aren't in the middle of a runlevel switch. */
769 if (!(rc_runlevel_starting() || rc_runlevel_stopping())) {
770 if (hook != RC_HOOK_RUNLEVEL_STOP_IN &&
771 hook != RC_HOOK_RUNLEVEL_STOP_OUT &&
772 hook != RC_HOOK_RUNLEVEL_START_IN &&
773 hook != RC_HOOK_RUNLEVEL_START_OUT)
774 goto exit;
775 } else {
776 int pid;
778 /* We're starting/stopping a runlevel. Check whether we're
779 * actually booting/rebooting. */
780 if (rc_runlevel_starting() && strcmp(runlev, bootlevel) &&
781 strcmp(runlev, RC_LEVEL_SYSINIT) && fbsplash_check_daemon(&pid))
782 goto exit;
784 if (rc_runlevel_stopping() && strcmp(runlev, bootlevel) &&
785 strcmp(runlev, RC_LEVEL_SHUTDOWN))
786 goto exit;
789 if (!config) {
790 config = fbsplash_lib_init(type);
791 splash_config_gentoo(config, type);
792 fbsplash_parse_kcmdline(false);
795 /* Extremely weird.. should never happen. */
796 if (!config) {
797 retval = -1;
798 goto exit;
801 /* Don't do anything if we're not running in silent mode. */
802 if (!(config->reqmode & FBSPL_MODE_SILENT))
803 goto exit;
805 switch (hook) {
806 case RC_HOOK_RUNLEVEL_STOP_IN:
807 /* Start the splash daemon on reboot. The theme hook is called
808 * from splash_start(). */
809 if (strcmp(name, RC_LEVEL_SHUTDOWN) == 0) {
810 if ((i = splash_start(name))) {
811 fbsplash_set_verbose(0);
812 retval= i;
813 goto exit;
814 } else {
815 if (rc_service_state("gpm") & RC_SERVICE_STARTED) {
816 fbsplash_send("set gpm\n");
817 fbsplash_send("repaint\n");
820 splash_theme_hook("rc_init", "post", name);
821 retval = i;
822 goto exit;
823 } else {
824 splash_theme_hook("rc_exit", "pre", name);
825 splash_theme_hook("rc_exit", "post", name);
826 fbsplash_lib_cleanup();
827 config = NULL;
829 break;
831 case RC_HOOK_RUNLEVEL_STOP_OUT:
832 /* Make sure the progress indicator reaches 100%, even if
833 * something went wrong along the way. */
834 if (strcmp(name, RC_LEVEL_SHUTDOWN) == 0) {
835 config->verbosity = FBSPL_VERB_QUIET;
836 i = fbsplash_check_daemon(&pid_daemon);
837 config->verbosity = FBSPL_VERB_NORMAL;
838 if (i) {
839 retval = -1;
840 goto exit;
843 fbsplash_send("progress %d\n", FBSPL_PROGRESS_MAX);
844 fbsplash_send("paint\n");
845 fbsplash_cache_cleanup(NULL);
847 break;
849 case RC_HOOK_RUNLEVEL_START_IN:
850 /* Start the splash daemon during boot right after we finish
851 * sysinit and are entering the boot runlevel. Due to historical
852 * reasons, we simulate a full sysinit cycle here for the theme
853 * scripts. */
854 if (strcmp(name, bootlevel) == 0) {
855 if ((i = splash_start(RC_LEVEL_SYSINIT)))
856 fbsplash_set_verbose(0);
857 splash_theme_hook("rc_init", "post", RC_LEVEL_SYSINIT);
858 splash_theme_hook("rc_exit", "pre", RC_LEVEL_SYSINIT);
859 splash_theme_hook("rc_exit", "post", RC_LEVEL_SYSINIT);
861 splash_theme_hook("rc_init", "pre", name);
862 splash_theme_hook("rc_init", "post", name);
863 break;
865 case RC_HOOK_RUNLEVEL_START_OUT:
866 /* Stop the splash daemon after boot-up is finished. */
867 if (strcmp(name, bootlevel)) {
868 config->verbosity = FBSPL_VERB_QUIET;
869 i = fbsplash_check_daemon(&pid_daemon);
870 config->verbosity = FBSPL_VERB_NORMAL;
871 if (i) {
872 retval = -1;
873 goto exit;
876 /* Make sure the progress indicator reaches 100%, even if
877 * something went wrong along the way. */
878 fbsplash_send("progress %d\n", FBSPL_PROGRESS_MAX);
879 fbsplash_send("paint\n");
880 splash_theme_hook("rc_exit", "pre", name);
881 i = splash_stop(name);
882 splash_theme_hook("rc_exit", "post", name);
883 fbsplash_lib_cleanup();
884 config = NULL;
886 break;
888 case RC_HOOK_SERVICE_START_NOW:
889 do_start:
890 /* If we've been inactive, do nothing since the service has
891 * already been handled before it went inactive. */
892 if (rc_service_state(name) & RC_SERVICE_WASINACTIVE)
893 goto exit;
895 /* If we're starting or stopping a service, we're being called by
896 * runscript and thus have to reload our config. */
897 if (splash_init(true)) {
898 retval = -1;
899 goto exit;
901 i = splash_svc_handle(name, "svc_start", skip);
902 break;
904 case RC_HOOK_SERVICE_START_OUT:
905 /* If a service gets scheduled, we want to increment the progress
906 * bar (as it is no longer blocking boot completion). However,
907 * the service may actually start during boot (some time after
908 * being scheduled), so we don't want to increment the progress
909 * bar twice. The following if clause satisfies this by catching
910 * the first case but not the second. */
911 if ((rc_service_state(name) & RC_SERVICE_SCHEDULED) &&
912 !(rc_service_state(name) & RC_SERVICE_STARTING)) {
913 skip = true;
914 goto do_start;
916 break;
918 case RC_HOOK_SERVICE_START_DONE:
919 config->verbosity = FBSPL_VERB_QUIET;
920 i = fbsplash_check_daemon(&pid_daemon);
921 config->verbosity = FBSPL_VERB_NORMAL;
922 if (i) {
923 retval = -1;
924 goto exit;
927 if (!(rc_service_state(name) & RC_SERVICE_FAILED) &&
928 !(rc_service_state(name) & RC_SERVICE_STOPPED)) {
929 bool gpm = false;
931 if (!strcmp(name, "gpm")) {
932 struct stat st;
933 int cnt = 0;
934 gpm = true;
935 /* Wait up to 0.25s for the GPM socket to appear. */
936 while (stat("/dev/gpmctl", &st) == 0 && cnt < 25) {
937 usleep(10000);
938 cnt++;
940 fbsplash_send("set gpm\n");
943 i = splash_svc_state(name, "svc_started", 1);
945 if (gpm) {
946 fbsplash_send("repaint\n");
948 } else {
949 i = splash_svc_state(name, "svc_start_failed", 1);
950 if (config->vonerr) {
951 fbsplash_set_verbose(0);
954 fbsplash_lib_cleanup();
955 config = NULL;
956 break;
958 case RC_HOOK_SERVICE_STOP_NOW:
959 if (splash_init(false)) {
960 retval = -1;
961 goto exit;
964 /* We need to stop localmount from unmounting our cache dir.
965 Luckily plugins can add to the unmount list. */
966 if (name && !strcmp(name, "localmount")) {
967 char *umounts = getenv("RC_NO_UMOUNTS");
969 if (umounts)
970 fprintf(rc_environ_fd, "RC_NO_UMOUNTS=%s:" FBSPLASH_CACHEDIR ":/etc/splash/%s", umounts, config->theme);
971 else
972 fprintf(rc_environ_fd, "RC_NO_UMOUNTS=" FBSPLASH_CACHEDIR ":/etc/splash/%s", config->theme);
974 i = splash_svc_handle(name, "svc_stop", false);
975 break;
977 case RC_HOOK_SERVICE_STOP_DONE:
978 config->verbosity = FBSPL_VERB_QUIET;
979 i = fbsplash_check_daemon(&pid_daemon);
980 config->verbosity = FBSPL_VERB_NORMAL;
981 if (i) {
982 retval = -1;
983 goto exit;
986 if (rc_service_state(name) & RC_SERVICE_STOPPED) {
987 i = splash_svc_state(name, "svc_stopped", 1);
988 } else {
989 i = splash_svc_state(name, "svc_stop_failed", 1);
990 if (config->vonerr) {
991 fbsplash_set_verbose(0);
994 fbsplash_lib_cleanup();
995 config = NULL;
996 break;
998 case RC_HOOK_ABORT:
999 i = splash_stop(name);
1000 fbsplash_lib_cleanup();
1001 config = NULL;
1002 break;
1004 default:
1005 break;
1008 exit:
1009 rc_stringlist_free(svcs);
1010 free (runlev);
1011 return i;