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
20 #include <sys/ioctl.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 */
49 char *arg_pidfile
= NULL
;
57 list svcs
= { NULL
, NULL
};
59 /* A container for the original settings of the silent TTY. */
62 /* Specifies what to do when SIGALRM is raised. */
66 * Handle displaying of special effects and animations of the type 'once'
69 void *thf_anim(void *unused
)
76 item
*i
, *iprev
, *itmp
;
77 int delay
= 10000, rdelay
, oldstate
;
78 struct timespec ts
, tsc
;
83 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, &oldstate
);
84 pthread_mutex_lock(&mtx_paint
);
87 /* Find the shortest delay. */
88 for (i
= theme
->anims
.head
; i
!= NULL
; i
= i
->next
) {
90 co
= container_of(ca
);
93 (ca
->flags
& F_ANIM_METHOD_MASK
) == F_ANIM_PROPORTIONAL
||
94 ca
->status
== F_ANIM_STATUS_DONE
)
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
) {
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) {
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 */
149 for (i
= theme
->fxobjs
.head
; i
!= NULL
; i
= i
->next
) {
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
) {
163 list_del(&theme
->fxobjs
, iprev
, i
);
166 if (prevo
< co
->opacity
) {
170 list_del(&theme
->fxobjs
, iprev
, i
);
175 co
->wait_msecs
= co
->op_tstep
;
186 /* Don't paint anything if we aren't in silent mode. */
187 if (ctty
!= CTTY_SILENT
)
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.
197 /* Update the wait time for all relevant animation objects. */
198 for (i
= theme
->anims
.head
; i
!= NULL
; i
= i
->next
) {
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
)
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 */
226 * The following two functions are called with
229 void vt_silent_init(void)
233 fbsplashr_tty_silent_init(false);
234 ioctl(fd_tty
[config
.tty_s
], TIOCSCTTY
, 0);
236 vt
.mode
= VT_PROCESS
;
240 ioctl(fd_tty
[config
.tty_s
], VT_SETMODE
, &vt
);
245 void vt_silent_cleanup(void)
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();
262 * Handles a switch to the silent mode.
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
);
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
);
283 static void do_cleanup(void)
285 pthread_mutex_trylock(&mtx_tty
);
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
);
312 * This thread is reponsible for allowing switches between the
313 * silent and verbose ttys, and for cleanup tasks after reception
316 void* thf_sighandler(void *unusued
)
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
);
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
);
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
);
352 pthread_mutex_unlock(&mtx_paint
);
355 } else if (sig
== SIGINT
) {
356 /* internally generated terminate signal */
359 } else if (sig
== SIGTERM
) {
367 * Event device monitor thread.
369 void* thf_switch_evdev(void *unused
)
373 struct input_event ev
[8];
376 rb
= read(fd_evdev
, ev
, sizeof(struct input_event
)*8);
377 if (rb
< (int) sizeof(struct input_event
))
380 for (i
= 0; i
< (int) (rb
/ sizeof(struct input_event
)); i
++) {
381 if (ev
[i
].type
!= EV_KEY
|| ev
[i
].value
!= 0)
384 switch (ev
[i
].code
) {
386 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, &oldstate
);
387 pthread_mutex_lock(&mtx_paint
);
388 if (ctty
== CTTY_SILENT
) {
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
);
409 config
.textbox_visible
= !config
.textbox_visible
;
410 invalidate_textbox(theme
, config
.textbox_visible
);
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
)
432 flags
= fcntl(fd_tty
[config
.tty_s
], F_GETFL
, 0);
438 fcntl(fd_tty
[config
.tty_s
], F_SETFL
, flags
& (~O_NDELAY
));
439 read(fd_tty
[config
.tty_s
], &ret
, 1);
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
);
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
) {
478 fbsplashr_tty_silent_set(stty
);
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");
491 if (pthread_create(&th_switchmon
, NULL
, &thf_switch_ttymon
, NULL
)) {
492 iprint(MSG_ERROR
, "TTY monitor thread creation failed.\n");
502 int reload_theme(void)
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
);
517 static int dcr_filter(const struct dirent
*dre
)
521 if (sscanf(dre
->d_name
, "%d", &pid
) == 1)
527 static int daemon_check_running(const char *pname
)
529 struct dirent
**namelist
;
533 int n
, pid
, fpid
= 0, mpid
= getpid();
534 int l
= min(strlen(pname
), 15);
536 n
= scandir("/proc", &namelist
, dcr_filter
, alphasort
);
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
)) {
558 * Start the splash daemon.
563 FILE *fp_fifo
= NULL
;
565 struct vt_stat vtstat
;
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");
575 /* No one is being notified about anything by default. */
576 for (i
= 0; i
< 2; i
++) {
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");
590 fp_fifo
= fopen(FBSPLASH_FIFO
, "r+");
594 iprint(MSG_ERROR
, "Can't open the splash FIFO (" FBSPLASH_FIFO
") for reading: %s\n", strerror(errno
));
599 /* Go into background. */
603 FILE *fp
= fopen(arg_pidfile
, "w");
605 iprint(MSG_ERROR
, "Failed to open pidfile %s for writing.\n", arg_pidfile
);
607 fprintf(fp
, "%d\n", i
);
617 /* Make /dev/null stdin, and /dev/console stdout/stderr */
618 i
= open("/dev/null", O_RDWR
);
620 i
= open("/dev/console", O_RDWR
);
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
) {
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
);
670 static struct option options
[] = {
671 { "theme", required_argument
, NULL
, 0x100 },
672 { "progress",required_argument
, NULL
, 0x101 },
673 { "kdgraphics", no_argument
, NULL
, 0x102 },
675 { "mesg", required_argument
, NULL
, 0x103 },
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'},
690 "fbsplashd/splashutils-" PACKAGE_VERSION
"\n"
691 "Usage: fbsplashd [options]\n\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"
700 " --mesg=TEXT use TEXT as the main splash message\n"
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
)
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
) {
733 fbsplash_acc_theme_set(optarg
);
738 config
.progress
= atoi(optarg
);
742 config
.kdmode
= KD_GRAPHICS
;
746 fbsplash_acc_message_set(optarg
);
750 arg_pidfile
= strdup(optarg
);
754 config
.minstances
= true;
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
;
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
;
780 config
.type
= fbspl_bootup
;
784 config
.textbox_visible
= true;
787 /* Verbosity level adjustment. */
789 config
.verbosity
= FBSPL_VERB_QUIET
;
793 config
.verbosity
= FBSPL_VERB_HIGH
;
798 if (fbsplash_is_silent())
799 config
.effects
&= ~FBSPL_EFF_FADEIN
;
801 theme
= fbsplashr_theme_load();
803 iprint(MSG_ERROR
, "Failed to load theme '%s'.\n", config
.theme
);
807 invalidate_textbox(theme
, config
.textbox_visible
);
811 #ifndef UNIFIED_BUILD
812 int main(int argc
, char **argv
)
814 return fbsplashd_main(argc
, argv
);