1
#define _GNU_SOURCE /* asprintf(), TEMP_FAILURE_RETRY() */
2
#include <signal.h> /* sig_atomic_t, struct sigaction,
3
sigemptyset(), sigaddset(), SIGINT,
4
SIGHUP, SIGTERM, sigaction(),
6
#include <stdbool.h> /* bool, false, true */
7
#include <fcntl.h> /* open(), O_RDONLY */
8
#include <iso646.h> /* and, or, not*/
9
#include <sys/types.h> /* size_t, ssize_t, pid_t, struct
11
#include <sys/wait.h> /* waitpid() */
12
#include <stddef.h> /* NULL */
13
#include <string.h> /* strchr(), memcmp() */
14
#include <stdio.h> /* asprintf(), perror(), fopen(),
16
#include <unistd.h> /* close(), readlink(), read(),
17
fork(), setsid(), chdir(), dup2(),
18
STDERR_FILENO, execv(), access() */
19
#include <stdlib.h> /* free(), EXIT_FAILURE, realloc(),
20
EXIT_SUCCESS, malloc(), _exit(),
22
#include <dirent.h> /* scandir(), alphasort() */
23
#include <inttypes.h> /* intmax_t, strtoumax(), SCNuMAX */
24
#include <sys/stat.h> /* struct stat, lstat() */
25
#include <sysexits.h> /* EX_OSERR, EX_UNAVAILABLE */
26
#include <error.h> /* error() */
27
#include <errno.h> /* TEMP_FAILURE_RETRY */
28
#include <argz.h> /* argz_count(), argz_extract() */
30
sig_atomic_t interrupted_by_signal = 0;
31
const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid";
32
const char plymouth_path[] = "/bin/plymouth";
33
const char plymouthd_path[] = "/sbin/plymouthd";
34
const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
36
"--attach-to-session",
42
static void termination_handler(__attribute__((unused))int signum){
43
if(interrupted_by_signal){
46
interrupted_by_signal = 1;
49
/* Create prompt string */
50
char *makeprompt(void){
53
const char *const cryptsource = getenv("cryptsource");
54
const char *const crypttarget = getenv("crypttarget");
55
const char prompt_start[] = "Enter passphrase to unlock the disk";
57
if(cryptsource == NULL){
58
if(crypttarget == NULL){
59
ret = asprintf(&prompt, "%s: ", prompt_start);
61
ret = asprintf(&prompt, "%s (%s): ", prompt_start,
65
if(crypttarget == NULL){
66
ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
68
ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
69
cryptsource, crypttarget);
78
void kill_and_wait(pid_t pid){
79
TEMP_FAILURE_RETRY(kill(pid, SIGTERM));
80
TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
83
bool become_a_daemon(void){
84
int ret = setuid(geteuid());
86
error(0, errno, "setuid");
92
error(0, errno, "chdir");
95
ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
97
error(0, errno, "dup2");
103
bool exec_and_wait(pid_t *pid_return, const char *path,
104
const char **argv, bool interruptable,
111
error(0, errno, "fork");
117
if(not become_a_daemon()){
122
char **new_argv = NULL;
125
for (; argv[i]!=(char *)NULL; i++){
126
tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
128
error(0, errno, "realloc");
132
new_argv = (char **)tmp;
133
new_argv[i] = strdup(argv[i]);
135
new_argv[i] = (char *) NULL;
137
execv(path, (char *const *)new_argv);
138
error(0, errno, "execv");
141
if(pid_return != NULL){
145
ret = waitpid(pid, &status, 0);
146
} while(ret == -1 and errno == EINTR
147
and ((not interrupted_by_signal)
148
or (not interruptable)));
149
if(interrupted_by_signal and interruptable){
153
error(0, errno, "waitpid");
156
if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
162
int is_plymouth(const struct dirent *proc_entry){
168
maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
170
if(errno != 0 or *tmp != '\0'
171
or maxvalue != (uintmax_t)((pid_t)maxvalue)){
175
char exe_target[sizeof(plymouth_path)];
177
ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
179
error(0, errno, "asprintf");
183
struct stat exe_stat;
184
ret = lstat(exe_link, &exe_stat);
188
error(0, errno, "lstat");
193
if(not S_ISLNK(exe_stat.st_mode)
194
or exe_stat.st_uid != 0
195
or exe_stat.st_gid != 0){
200
ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
202
if((sret != (ssize_t)sizeof(plymouth_path)-1) or
203
(memcmp(plymouth_path, exe_target,
204
sizeof(plymouth_path)-1) != 0)){
212
FILE *pidfile = fopen(plymouth_pid, "r");
213
uintmax_t maxvalue = 0;
215
ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
222
struct dirent **direntries;
223
ret = scandir("/proc", &direntries, is_plymouth, alphasort);
224
sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
227
pid = (pid_t)maxvalue;
228
if((uintmax_t)pid == maxvalue){
235
const char **getargv(pid_t pid){
237
char *cmdline_filename;
241
ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
244
error(0, errno, "asprintf");
248
/* Open /proc/<pid>/cmdline */
249
cl_fd = open(cmdline_filename, O_RDONLY);
250
free(cmdline_filename);
252
error(0, errno, "open");
256
size_t cmdline_allocated = 0;
257
size_t cmdline_len = 0;
258
char *cmdline = NULL;
260
const size_t blocksize = 1024;
262
/* Allocate more space? */
263
if(cmdline_len + blocksize > cmdline_allocated){
264
tmp = realloc(cmdline, cmdline_allocated + blocksize);
266
error(0, errno, "realloc");
272
cmdline_allocated += blocksize;
276
sret = read(cl_fd, cmdline + cmdline_len,
277
cmdline_allocated - cmdline_len);
279
error(0, errno, "read");
284
cmdline_len += (size_t)sret;
288
error(0, errno, "close");
293
/* we got cmdline and cmdline_len, ignore rest... */
294
char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
295
* sizeof(char *)); /* Get number of args */
297
error(0, errno, "argv = malloc()");
301
argz_extract(cmdline, cmdline_len, argv); /* Create argv */
302
return (const char **)argv;
305
int main(__attribute__((unused))int argc,
306
__attribute__((unused))char **argv){
309
pid_t plymouth_command_pid;
313
/* test -x /bin/plymouth */
314
ret = access(plymouth_path, X_OK);
316
/* Plymouth is probably not installed. Don't print an error
317
message, just exit. */
318
exit(EX_UNAVAILABLE);
321
{ /* Add signal handlers */
322
struct sigaction old_action,
323
new_action = { .sa_handler = termination_handler,
325
sigemptyset(&new_action.sa_mask);
326
for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 };
328
ret = sigaddset(&new_action.sa_mask, *sig);
330
error(EX_OSERR, errno, "sigaddset");
332
ret = sigaction(*sig, NULL, &old_action);
334
error(EX_OSERR, errno, "sigaction");
336
if(old_action.sa_handler != SIG_IGN){
337
ret = sigaction(*sig, &new_action, NULL);
339
error(EX_OSERR, errno, "sigaction");
345
/* plymouth --ping */
346
bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
348
{ plymouth_path, "--ping", NULL },
351
if(interrupted_by_signal){
352
kill_and_wait(plymouth_command_pid);
355
/* Plymouth is probably not running. Don't print an error
356
message, just exit. */
357
exit(EX_UNAVAILABLE);
360
prompt = makeprompt();
361
ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
364
error(EX_OSERR, errno, "asprintf");
367
/* plymouth ask-for-password --prompt="$prompt" */
368
bret = exec_and_wait(&plymouth_command_pid,
369
plymouth_path, (const char *[])
370
{ plymouth_path, "ask-for-password",
377
if(not interrupted_by_signal){
378
/* exec_and_wait failed for some other reason */
381
kill_and_wait(plymouth_command_pid);
383
const char **plymouthd_argv;
384
pid_t pid = get_pid();
386
error(0, 0, "plymouthd pid not found");
387
plymouthd_argv = plymouthd_default_argv;
389
plymouthd_argv = getargv(pid);
392
bret = exec_and_wait(NULL, plymouth_path, (const char *[])
393
{ plymouth_path, "quit", NULL },
398
bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
403
exec_and_wait(NULL, plymouth_path, (const char *[])
404
{ plymouth_path, "show-splash", NULL },