core: use a monotonic clock for animation timing calculations
[fbsplash.git] / core / src / daemon.c
blobe3463fc9bb84cd188e2d129d26bf44b2809bb1bb
1 /*
2 * daemon.c - The splash daemon
4 * Copyright (C) 2005-2008 Michal Januszewski <spock@gentoo.org>
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License v2. See the file COPYING in the main directory of this archive for
8 * more details.
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <termios.h>
18 #include <signal.h>
19 #include <sys/stat.h>
20 #include <sys/ioctl.h>
21 #include <sys/wait.h>
22 #include <sys/mman.h>
23 #include <pthread.h>
24 #include <errno.h>
25 #include <dirent.h>
26 #include <time.h>
27 #include <getopt.h>
29 #include "common.h"
30 #include "daemon.h"
32 /* Threading structures */
33 pthread_mutex_t mtx_tty = PTHREAD_MUTEX_INITIALIZER;
34 pthread_mutex_t mtx_paint = PTHREAD_MUTEX_INITIALIZER;
35 pthread_mutex_t mtx_anim = PTHREAD_MUTEX_INITIALIZER;
36 pthread_cond_t cnd_anim;
37 pthread_condattr_t cnd_attr;
39 pthread_t th_switchmon, th_sighandler, th_anim;
41 int ctty = CTTY_VERBOSE;
43 /* File descriptors */
44 int fd_evdev = -1;
45 #ifdef CONFIG_GPM
46 int fd_gpm = -1;
47 #endif
49 char *arg_pidfile = NULL;
50 stheme_t *theme;
52 /* Misc settings */
53 char *notify[2];
54 char *evdev = NULL;
56 /* Service list */
57 list svcs = { NULL, NULL };
59 /* A container for the original settings of the silent TTY. */
60 struct termios tios;
62 /* Specifies what to do when SIGALRM is raised. */
63 int alarm_type;
66 * Handle displaying of special effects and animations of the type 'once'
67 * or 'loop'.
69 void *thf_anim(void *unused)
71 #if WANT_MNG
72 mng_anim *mng;
73 anim *ca;
74 #endif
75 obj *co;
76 item *i, *iprev, *itmp;
77 int delay = 10000, rdelay, oldstate;
78 struct timespec ts, tsc;
82 while(1) {
83 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
84 pthread_mutex_lock(&mtx_paint);
86 #if WANT_MNG
87 /* Find the shortest delay. */
88 for (i = theme->anims.head; i != NULL; i = i->next) {
89 ca = i->p;
90 co = container_of(ca);
92 if (!co->visible ||
93 (ca->flags & F_ANIM_METHOD_MASK) == F_ANIM_PROPORTIONAL ||
94 ca->status == F_ANIM_STATUS_DONE)
95 continue;
97 mng = mng_get_userdata(ca->mng);
99 /* If this is a new animation (activated by a service),
100 * display it immediately. */
101 if (!mng->displayed_first) {
102 anim_render_canvas(ca);
104 if (ctty == CTTY_SILENT)
105 fbsplashr_render_screen(theme, true, false, FBSPL_EFF_NONE);
108 if (mng->wait_msecs < delay && mng->wait_msecs > 0) {
109 delay = mng->wait_msecs;
112 #endif /* WANT_MNG */
114 for (i = theme->fxobjs.head; i != NULL; i = i->next) {
115 co = i->p;
116 if (co->wait_msecs < delay && co->wait_msecs > 0) {
117 delay = co->wait_msecs;
121 pthread_mutex_unlock(&mtx_paint);
122 pthread_setcancelstate(oldstate, NULL);
124 pthread_mutex_lock(&mtx_anim);
125 clock_gettime(CLOCK_MONOTONIC, &ts);
127 ts.tv_sec += (int)(delay / 1000);
128 ts.tv_nsec += (delay % 1000) * 1000000;
130 /* Check for overflow of the nanoseconds field */
131 if (ts.tv_nsec >= 1000000000) {
132 ts.tv_sec++;
133 ts.tv_nsec -= 1000000000;
136 pthread_cond_timedwait(&cnd_anim, &mtx_anim, &ts);
137 pthread_mutex_unlock(&mtx_anim);
139 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
140 pthread_mutex_lock(&mtx_paint);
142 /* Calculate the real delay. We might have been signalled by
143 * the splash daemon before 'delay' msecs passed. */
144 clock_gettime(CLOCK_MONOTONIC, &tsc);
145 rdelay = delay + (tsc.tv_sec - ts.tv_sec)*1000 + (tsc.tv_nsec - ts.tv_nsec)/1000000;
147 /* Handle special effects */
148 iprev = NULL;
149 for (i = theme->fxobjs.head; i != NULL; i = i->next) {
150 loop: itmp = NULL;
151 co = i->p;
153 if (co->wait_msecs > 0) {
154 co->wait_msecs -= rdelay;
155 if (co->wait_msecs <= 0) {
156 u8 prevo = co->opacity;
157 co->opacity += co->op_step;
159 if (co->op_step > 0) {
160 if (prevo > co->opacity) {
161 co->opacity = 0xff;
162 itmp = i->next;
163 list_del(&theme->fxobjs, iprev, i);
165 } else {
166 if (prevo < co->opacity) {
167 co->opacity = 0x0;
168 co->visible = false;
169 itmp = i->next;
170 list_del(&theme->fxobjs, iprev, i);
174 co->invalid = true;
175 co->wait_msecs = co->op_tstep;
177 if (itmp) {
178 i = itmp;
179 goto loop;
183 iprev = i;
186 /* Don't paint anything if we aren't in silent mode. */
187 if (ctty != CTTY_SILENT)
188 goto next;
191 * TODO: Currently, we don't update the anims if the silent
192 * splash screen is not visible. Investigate the performance
193 * impact of changing this.
196 #if WANT_MNG
197 /* Update the wait time for all relevant animation objects. */
198 for (i = theme->anims.head; i != NULL; i = i->next) {
199 ca = i->p;
200 co = container_of(ca);
202 if (!co->visible || (ca->flags & F_ANIM_METHOD_MASK) == F_ANIM_PROPORTIONAL ||
203 ca->status == F_ANIM_STATUS_DONE)
204 continue;
206 mng = mng_get_userdata(ca->mng);
207 if (mng->wait_msecs > 0) {
208 mng->wait_msecs -= rdelay;
209 if (mng->wait_msecs <= 0)
210 anim_render_canvas(ca);
213 #endif /* WANT_MNG */
215 fbsplashr_render_screen(theme, true, false, FBSPL_EFF_NONE);
217 next: pthread_mutex_unlock(&mtx_paint);
218 pthread_setcancelstate(oldstate, NULL);
220 /* Default delay is 10s */
221 delay = 10000;
226 * The following two functions are called with
227 * mtx_tty held.
229 void vt_silent_init(void)
231 struct vt_mode vt;
233 fbsplashr_tty_silent_init(false);
234 ioctl(fd_tty[config.tty_s], TIOCSCTTY, 0);
236 vt.mode = VT_PROCESS;
237 vt.waitv = 0;
238 vt.relsig = SIGUSR1;
239 vt.acqsig = SIGUSR2;
240 ioctl(fd_tty[config.tty_s], VT_SETMODE, &vt);
242 return;
245 void vt_silent_cleanup(void)
247 struct vt_mode vt;
249 vt.mode = VT_AUTO;
250 vt.waitv = 0;
252 tcsetattr(fd_tty[config.tty_s], TCSANOW, &tios);
253 ioctl(fd_tty[config.tty_s], VT_RELDISP, 1);
254 ioctl(fd_tty[config.tty_s], KDSETMODE, KD_TEXT);
255 ioctl(fd_tty[config.tty_s], VT_SETMODE, &vt);
257 fbsplashr_tty_silent_cleanup();
258 return;
262 * Handles a switch to the silent mode.
264 void switch_silent()
266 pthread_mutex_lock(&mtx_paint);
268 if (fbsplashr_tty_silent_update()) {
269 if (reload_theme()) {
270 iprint(MSG_ERROR, "Failed to (re-)load the '%s' theme.\n", config.theme);
271 exit(1);
275 /* Set KD_GRAPHICS if necessary. */
276 if (config.kdmode == KD_GRAPHICS)
277 ioctl(fd_tty[config.tty_s], KDSETMODE, KD_GRAPHICS);
279 pthread_mutex_unlock(&mtx_paint);
280 cmd_repaint(NULL);
283 static void do_cleanup(void)
285 pthread_mutex_trylock(&mtx_tty);
286 #ifdef CONFIG_GPM
287 if (fd_gpm >= 0) {
288 Gpm_Close();
290 #endif
291 vt_silent_cleanup();
295 * SIGALRM handler.
297 void handler_alarm(int unused)
299 if (alarm_type == ALRM_AUTOVERBOSE) {
300 pthread_mutex_lock(&mtx_paint);
301 if (ctty == CTTY_SILENT)
302 fbsplash_set_verbose(0);
303 pthread_mutex_unlock(&mtx_paint);
306 return;
310 * Signal handler.
312 * This thread is reponsible for allowing switches between the
313 * silent and verbose ttys, and for cleanup tasks after reception
314 * of SIGTERM.
316 void* thf_sighandler(void *unusued)
318 sigset_t sigset;
319 int sig;
321 /* We don't handle SIGALRM. */
322 sigemptyset(&sigset);
323 sigaddset(&sigset, SIGALRM);
324 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
326 sigemptyset(&sigset);
327 sigaddset(&sigset, SIGUSR1);
328 sigaddset(&sigset, SIGUSR2);
329 sigaddset(&sigset, SIGTERM);
330 sigaddset(&sigset, SIGINT);
332 while (1) {
333 sigwait(&sigset, &sig);
335 /* Switch from silent to verbose. */
336 if (sig == SIGUSR1) {
337 pthread_mutex_lock(&mtx_paint);
338 pthread_mutex_lock(&mtx_tty);
339 ioctl(fd_tty[config.tty_s], VT_RELDISP, 1);
340 pthread_mutex_unlock(&mtx_tty);
342 ctty = CTTY_VERBOSE;
343 pthread_mutex_unlock(&mtx_paint);
344 /* Switch back to silent. */
345 } else if (sig == SIGUSR2) {
346 pthread_mutex_lock(&mtx_paint);
347 pthread_mutex_lock(&mtx_tty);
348 ioctl(fd_tty[config.tty_s], VT_RELDISP, 2);
349 pthread_mutex_unlock(&mtx_tty);
351 ctty = CTTY_SILENT;
352 pthread_mutex_unlock(&mtx_paint);
354 switch_silent();
355 } else if (sig == SIGINT) {
356 /* internally generated terminate signal */
357 do_cleanup();
358 pthread_exit(NULL);
359 } else if (sig == SIGTERM) {
360 do_cleanup();
361 exit(0);
367 * Event device monitor thread.
369 void* thf_switch_evdev(void *unused)
371 int i, h, oldstate;
372 size_t rb;
373 struct input_event ev[8];
375 while (1) {
376 rb = read(fd_evdev, ev, sizeof(struct input_event)*8);
377 if (rb < (int) sizeof(struct input_event))
378 continue;
380 for (i = 0; i < (int) (rb / sizeof(struct input_event)); i++) {
381 if (ev[i].type != EV_KEY || ev[i].value != 0)
382 continue;
384 switch (ev[i].code) {
385 case KEY_F2:
386 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
387 pthread_mutex_lock(&mtx_paint);
388 if (ctty == CTTY_SILENT) {
389 h = config.tty_v;
390 } else {
391 h = config.tty_s;
393 pthread_mutex_unlock(&mtx_paint);
394 pthread_setcancelstate(oldstate, NULL);
396 /* Switch to the new tty. This ioctl has to be done on
397 * the silent tty. Sometimes init will mess with the
398 * settings of the verbose console which will prevent
399 * console switching from working properly.
401 * Don't worry about fd_tty[config.tty_s] not being protected by a
402 * mutex -- this thread is always killed before any changes
403 * are made to fd_tty[config.tty_s].
405 ioctl(fd_tty[config.tty_s], VT_ACTIVATE, h);
406 break;
408 case KEY_F3:
409 config.textbox_visible = !config.textbox_visible;
410 invalidate_textbox(theme, config.textbox_visible);
411 cmd_paint(NULL);
412 break;
417 pthread_exit(NULL);
421 * Silent TTY monitor thread.
423 * This thread listens for F2 keypresses on the silent TTY.
424 * We don't have to worry about fd_tty[config.tty_s] not being protected
425 * by the mtx_tty mutex, since this thread is killed before
426 * a new silent tty is opened.
428 void* thf_switch_ttymon(void *unused)
430 int flags, oldstate;
432 flags = fcntl(fd_tty[config.tty_s], F_GETFL, 0);
434 while(1) {
435 char ret = 0xff;
436 int t = 0;
438 fcntl(fd_tty[config.tty_s], F_SETFL, flags & (~O_NDELAY));
439 read(fd_tty[config.tty_s], &ret, 1);
441 if (ret == '\x1b') {
442 fcntl(fd_tty[config.tty_s], F_SETFL, flags | O_NDELAY);
444 /* FIXME: is <F2> always 1b5b5b42? */
445 if (read(fd_tty[config.tty_s], &t, 3) == 3) {
446 if ((endianess == little && t == 0x425b5b) ||
447 (endianess == big && (t & 0xffffff00) == 0x5b5b4200)) {
448 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
449 pthread_mutex_lock(&mtx_tty);
450 ioctl(fd_tty0, VT_ACTIVATE, config.tty_v);
451 pthread_mutex_unlock(&mtx_tty);
452 pthread_setcancelstate(oldstate, NULL);
453 } else if ((endianess == little && t == 0x435b5b) ||
454 (endianess == big && (t & 0xffffff00) == 0x5b5b4300)) {
455 config.textbox_visible = !config.textbox_visible;
456 invalidate_textbox(theme, config.textbox_visible);
457 cmd_paint(NULL);
463 pthread_exit(NULL);
467 * Start a keypress monitoring thread and reopen switch
468 * to a new silent TTY.
470 * When called with UPD_SILENT, mtx_tty should be held.
472 void switchmon_start(int update, int stty)
474 /* Has the silent TTY changed? */
475 if (update & UPD_SILENT) {
476 if (config.tty_s != stty) {
477 vt_silent_cleanup();
478 fbsplashr_tty_silent_set(stty);
480 vt_silent_init();
483 /* Do we have to start a monitor thread? */
484 if (update & UPD_MON) {
485 if (fd_evdev != -1) {
486 if (pthread_create(&th_switchmon, NULL, &thf_switch_evdev, NULL)) {
487 iprint(MSG_ERROR, "Evdev monitor thread creation failed.\n");
488 exit(3);
490 } else {
491 if (pthread_create(&th_switchmon, NULL, &thf_switch_ttymon, NULL)) {
492 iprint(MSG_ERROR, "TTY monitor thread creation failed.\n");
493 exit(3);
500 * Load a new theme.
502 int reload_theme(void)
504 item *i;
506 fbsplashr_theme_free(theme);
507 theme = fbsplashr_theme_load();
509 for (i = svcs.head ; i != NULL; i = i->next) {
510 svc_state *ss = (svc_state*)i->p;
511 invalidate_service(theme, ss->svc, ss->state);
514 return 0;
517 static int dcr_filter(const struct dirent *dre)
519 int pid;
521 if (sscanf(dre->d_name, "%d", &pid) == 1)
522 return 1;
523 else
524 return 0;
527 static int daemon_check_running(const char *pname)
529 struct dirent **namelist;
530 FILE *fp;
531 char name[128];
532 char buf[128];
533 int n, pid, fpid = 0, mpid = getpid();
534 int l = min(strlen(pname), 15);
536 n = scandir("/proc", &namelist, dcr_filter, alphasort);
537 if (n < 0)
538 perror("blah");
539 else {
540 while(n--) {
541 snprintf(name, 128, "/proc/%s/stat", namelist[n]->d_name);
542 if ((fp = fopen(name, "r")) != NULL) {
543 if ((fscanf(fp, "%d (%s)", &pid, buf) == 2) && mpid != pid && !strncmp(buf, pname, l)) {
544 fpid = pid;
546 fclose(fp);
549 free(namelist[n]);
551 free(namelist);
554 return fpid;
558 * Start the splash daemon.
560 void daemon_start()
562 int i = 0;
563 FILE *fp_fifo = NULL;
564 struct stat mystat;
565 struct vt_stat vtstat;
566 struct sigaction sa;
567 sigset_t sigset;
569 if (!config.minstances && (i = daemon_check_running("fbsplashd"))) {
570 iprint(MSG_ERROR, "It looks like there's another instance of the splash daemon running (pid %d).\n", i);
571 iprint(MSG_ERROR, "Stop it first or run this program with `--minstances'.\n");
572 exit(1);
575 /* No one is being notified about anything by default. */
576 for (i = 0; i < 2; i++) {
577 notify[i] = NULL;
580 /* Create the splash FIFO if it's not already in place. */
581 if (stat(FBSPLASH_FIFO, &mystat) == -1 || !S_ISFIFO(mystat.st_mode)) {
582 unlink(FBSPLASH_FIFO);
583 if (mkfifo(FBSPLASH_FIFO, 0700)) {
584 iprint(MSG_ERROR, "mkfifo("FBSPLASH_FIFO") failed.\n");
585 exit(3);
589 while (!fp_fifo) {
590 fp_fifo = fopen(FBSPLASH_FIFO, "r+");
591 if (!fp_fifo) {
592 if (errno == EINTR)
593 continue;
594 iprint(MSG_ERROR, "Can't open the splash FIFO (" FBSPLASH_FIFO ") for reading: %s\n", strerror(errno));
595 exit(4);
599 /* Go into background. */
600 i = fork();
601 if (i) {
602 if (arg_pidfile) {
603 FILE *fp = fopen(arg_pidfile, "w");
604 if (!fp) {
605 iprint(MSG_ERROR, "Failed to open pidfile %s for writing.\n", arg_pidfile);
606 } else {
607 fprintf(fp, "%d\n", i);
608 fclose(fp);
611 exit(0);
614 setsid();
615 chdir("/");
617 /* Make /dev/null stdin, and /dev/console stdout/stderr */
618 i = open("/dev/null", O_RDWR);
619 dup2(i, 0);
620 i = open("/dev/console", O_RDWR);
621 dup2(i, 1);
622 dup2(i, 2);
624 pthread_condattr_init(&cnd_attr);
625 pthread_condattr_setclock(&cnd_attr, CLOCK_MONOTONIC);
626 pthread_cond_init(&cnd_anim, &cnd_attr);
628 /* Make all our threads ignore these signals. SIGUSR1, SIGUSR2,
629 * SIGTERM and SIGINT will be handled in the sighandler thread.
630 * The use of a separate thread for handling signals is required
631 * in order to avoid potential deadlocks. */
632 sigemptyset(&sigset);
633 sigaddset(&sigset, SIGABRT);
634 sigaddset(&sigset, SIGUSR1);
635 sigaddset(&sigset, SIGUSR2);
636 sigaddset(&sigset, SIGTERM);
637 sigaddset(&sigset, SIGINT);
638 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
639 pthread_mutex_lock(&mtx_paint);
640 pthread_create(&th_sighandler, NULL, &thf_sighandler, NULL);
642 /* Setup a dummy handler for SIGALRM. Unlike the other signals,
643 * we don't care which thread executes this handler. */
644 sa.sa_handler = handler_alarm;
645 sa.sa_flags = SA_RESTART;
646 sigemptyset(&sa.sa_mask);
647 sigaction(SIGALRM, &sa, NULL);
649 /* Check which TTY is active */
650 if (ioctl(fd_tty0, VT_GETSTATE, &vtstat) != -1) {
651 if (vtstat.v_active == config.tty_s) {
652 ctty = CTTY_SILENT;
653 } else {
654 ctty = CTTY_VERBOSE;
657 pthread_mutex_unlock(&mtx_paint);
659 /* Start the animation thread */
660 pthread_create(&th_anim, NULL, &thf_anim, NULL);
662 pthread_mutex_lock(&mtx_tty);
663 switchmon_start(UPD_ALL, config.tty_s);
664 pthread_mutex_unlock(&mtx_tty);
666 daemon_comm(fp_fifo);
667 exit(0);
670 static struct option options[] = {
671 { "theme", required_argument, NULL, 0x100 },
672 { "progress",required_argument, NULL, 0x101 },
673 { "kdgraphics", no_argument, NULL, 0x102 },
674 #ifdef CONFIG_TTF
675 { "mesg", required_argument, NULL, 0x103 },
676 #endif
677 { "pidfile",required_argument, NULL, 0x104 },
678 { "minstances", no_argument, NULL, 0x105 },
679 { "effects", required_argument, NULL, 0x106 },
680 { "type", required_argument, NULL, 0x107 },
681 { "textbox", no_argument, NULL, 0x108 },
682 { "help", no_argument, NULL, 'h'},
683 { "verbose", no_argument, NULL, 'v'},
684 { "quiet", no_argument, NULL, 'q'},
687 static void usage()
689 printf(
690 "fbsplashd/splashutils-" PACKAGE_VERSION "\n"
691 "Usage: fbsplashd [options]\n\n"
692 "Options:\n"
693 " -h, --help show this help message\n"
694 " -t, --theme=THEME use theme THEME\n"
695 " -p, --progress=NUM set progress to NUM/65535 * 100%%\n"
696 " --kdgraphics use KD_GRAPHICS mode for the splash screen\n"
697 " -v, --verbose display verbose error messages\n"
698 " -q, --quiet don't display any messages\n"
699 #ifdef CONFIG_TTF
700 " --mesg=TEXT use TEXT as the main splash message\n"
701 #endif
702 " --pidfile=FILE save the PID of the daemon to FILE\n"
703 " --minstances allow multiple instances of the splash daemon\n"
704 " --textbox show the textbox by default\n"
705 " --effects=LIST a comma-separated list of effects to use;\n"
706 " supported effects: fadein, fadeout\n"
707 " --type=TYPE TYPE can be: bootup, reboot, shutdown, suspend, resume\n"
711 int fbsplashd_main(int argc, char **argv)
713 unsigned int c, i;
714 int err = 0;
715 int arg_vc = -1;
717 arg_vc = -1;
719 fbsplash_lib_init(fbspl_undef);
720 fbsplashr_init(false);
722 config.reqmode = FBSPL_MODE_SILENT;
724 while ((c = getopt_long(argc, argv, "t:p:hvq", options, NULL)) != EOF) {
726 switch (c) {
727 case 'h':
728 usage();
729 return 0;
731 case 0x100:
732 case 't':
733 fbsplash_acc_theme_set(optarg);
734 break;
736 case 'p':
737 case 0x101:
738 config.progress = atoi(optarg);
739 break;
741 case 0x102:
742 config.kdmode = KD_GRAPHICS;
743 break;
744 #ifdef CONFIG_TTF
745 case 0x103:
746 fbsplash_acc_message_set(optarg);
747 break;
748 #endif
749 case 0x104:
750 arg_pidfile = strdup(optarg);
751 break;
753 case 0x105:
754 config.minstances = true;
755 break;
757 case 0x106:
759 char *topt;
761 while ((topt = strsep(&optarg, ",")) != NULL) {
762 if (!strcmp(topt, "fadein"))
763 config.effects |= FBSPL_EFF_FADEIN;
764 else if (!strcmp(topt, "fadeout"))
765 config.effects |= FBSPL_EFF_FADEOUT;
767 break;
770 case 0x107:
771 if (!strcmp(optarg, "reboot"))
772 config.type = fbspl_reboot;
773 else if (!strcmp(optarg, "shutdown"))
774 config.type = fbspl_shutdown;
775 else if (!strcmp(optarg, "suspend"))
776 config.type = fbspl_suspend;
777 else if (!strcmp(optarg, "resume"))
778 config.type = fbspl_resume;
779 else
780 config.type = fbspl_bootup;
781 break;
783 case 0x108:
784 config.textbox_visible = true;
785 break;
787 /* Verbosity level adjustment. */
788 case 'q':
789 config.verbosity = FBSPL_VERB_QUIET;
790 break;
792 case 'v':
793 config.verbosity = FBSPL_VERB_HIGH;
794 break;
798 if (fbsplash_is_silent())
799 config.effects &= ~FBSPL_EFF_FADEIN;
801 theme = fbsplashr_theme_load();
802 if (!theme) {
803 iprint(MSG_ERROR, "Failed to load theme '%s'.\n", config.theme);
804 exit(1);
807 invalidate_textbox(theme, config.textbox_visible);
808 daemon_start();
811 #ifndef UNIFIED_BUILD
812 int main(int argc, char **argv)
814 return fbsplashd_main(argc, argv);
816 #endif