/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugins.d/plymouth.c

  • Committer: Teddy Hogeborn
  • Date: 2015-01-25 00:02:51 UTC
  • mto: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • Revision ID: teddy@recompile.se-20150125000251-j2bw50gfq9smqyxe
mandos.xml (SEE ALSO): Update links.

Update link to GnuPG home page, change reference from TLS 1.1 to TLS
1.2, and change to latest RFC for using OpenPGP keys with TLS (and use
its correct title).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  -*- coding: utf-8 -*- */
 
2
/*
 
3
 * Plymouth - Read a password from Plymouth and output it
 
4
 * 
 
5
 * Copyright © 2010-2014 Teddy Hogeborn
 
6
 * Copyright © 2010-2014 Björn Påhlsson
 
7
 * 
 
8
 * This program is free software: you can redistribute it and/or
 
9
 * modify it under the terms of the GNU General Public License as
 
10
 * published by the Free Software Foundation, either version 3 of the
 
11
 * License, or (at your option) any later version.
 
12
 * 
 
13
 * This program is distributed in the hope that it will be useful, but
 
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
16
 * General Public License for more details.
 
17
 * 
 
18
 * You should have received a copy of the GNU General Public License
 
19
 * along with this program.  If not, see
 
20
 * <http://www.gnu.org/licenses/>.
 
21
 * 
 
22
 * Contact the authors at <mandos@recompile.se>.
 
23
 */
 
24
 
1
25
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
2
26
#include <signal.h>             /* sig_atomic_t, struct sigaction,
3
27
                                   sigemptyset(), sigaddset(), SIGINT,
6
30
#include <stdbool.h>            /* bool, false, true */
7
31
#include <fcntl.h>              /* open(), O_RDONLY */
8
32
#include <iso646.h>             /* and, or, not*/
9
 
#include <sys/types.h>          /* size_t, ssize_t, pid_t, struct dirent,
10
 
                                   waitpid() */
 
33
#include <sys/types.h>          /* size_t, ssize_t, pid_t, struct
 
34
                                   dirent, waitpid() */
11
35
#include <sys/wait.h>           /* waitpid() */
12
36
#include <stddef.h>             /* NULL */
13
37
#include <string.h>             /* strchr(), memcmp() */
14
 
#include <stdio.h>              /* asprintf(), perror(), fopen(), fscanf() */
15
 
#include <unistd.h>             /* close(), readlink(), read(), fork()
16
 
                                   setsid(), chdir(), dup2()
 
38
#include <stdio.h>              /* asprintf(), perror(), fopen(),
 
39
                                   fscanf(), vasprintf(), fprintf(),
 
40
                                   vfprintf() */
 
41
#include <unistd.h>             /* close(), readlink(), read(),
 
42
                                   fork(), setsid(), chdir(), dup2(),
17
43
                                   STDERR_FILENO, execv(), access() */
18
44
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
19
45
                                   EXIT_SUCCESS, malloc(), _exit(),
21
47
#include <dirent.h>             /* scandir(), alphasort() */
22
48
#include <inttypes.h>           /* intmax_t, strtoumax(), SCNuMAX */
23
49
#include <sys/stat.h>           /* struct stat, lstat() */
24
 
#include <sysexits.h>           /* EX_OSERR */
 
50
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
25
51
#include <error.h>              /* error() */
26
52
#include <errno.h>              /* TEMP_FAILURE_RETRY */
27
 
#include <stdarg.h>
 
53
#include <argz.h>               /* argz_count(), argz_extract() */
 
54
#include <stdarg.h>             /* va_list, va_start(), ... */
28
55
 
29
56
sig_atomic_t interrupted_by_signal = 0;
30
 
const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid";
 
57
 
 
58
/* Used by Ubuntu 11.04 (Natty Narwahl) */
 
59
const char plymouth_old_pid[] = "/dev/.initramfs/plymouth.pid";
 
60
/* Used by Ubuntu 11.10 (Oneiric Ocelot) */
 
61
const char plymouth_pid[] = "/run/initramfs/plymouth.pid";
 
62
 
31
63
const char plymouth_path[] = "/bin/plymouth";
32
64
const char plymouthd_path[] = "/sbin/plymouthd";
33
 
const char *plymouthd_default_argv[] = {"/sbin/plymouthd", "--mode=boot",
 
65
const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
 
66
                                        "--mode=boot",
34
67
                                        "--attach-to-session",
35
 
                                        "--pid-file=/dev/.initramfs/plymouth.pid",
36
68
                                        NULL };
37
69
 
38
70
static void termination_handler(__attribute__((unused))int signum){
42
74
  interrupted_by_signal = 1;
43
75
}
44
76
 
 
77
/* Function to use when printing errors */
 
78
__attribute__((format (gnu_printf, 3, 4)))
 
79
void error_plus(int status, int errnum, const char *formatstring,
 
80
                ...){
 
81
  va_list ap;
 
82
  char *text;
 
83
  int ret;
 
84
  
 
85
  va_start(ap, formatstring);
 
86
  ret = vasprintf(&text, formatstring, ap);
 
87
  if(ret == -1){
 
88
    fprintf(stderr, "Mandos plugin %s: ",
 
89
            program_invocation_short_name);
 
90
    vfprintf(stderr, formatstring, ap);
 
91
    fprintf(stderr, ": ");
 
92
    fprintf(stderr, "%s\n", strerror(errnum));
 
93
    error(status, errno, "vasprintf while printing error");
 
94
    return;
 
95
  }
 
96
  fprintf(stderr, "Mandos plugin ");
 
97
  error(status, errnum, "%s", text);
 
98
  free(text);
 
99
}
 
100
 
45
101
/* Create prompt string */
46
102
char *makeprompt(void){
47
103
  int ret = 0;
48
104
  char *prompt;
49
105
  const char *const cryptsource = getenv("cryptsource");
50
106
  const char *const crypttarget = getenv("crypttarget");
51
 
  const char prompt_start[] = "Enter passphrase to unlock the disk";
 
107
  const char prompt_start[] = "Unlocking the disk";
 
108
  const char prompt_end[] = "Enter passphrase";
52
109
  
53
110
  if(cryptsource == NULL){
54
111
    if(crypttarget == NULL){
55
 
      ret = asprintf(&prompt, "%s: ", prompt_start);
 
112
      ret = asprintf(&prompt, "%s\n%s", prompt_start, prompt_end);
56
113
    } else {
57
 
      ret = asprintf(&prompt, "%s (%s): ", prompt_start,
58
 
                     crypttarget);
 
114
      ret = asprintf(&prompt, "%s (%s)\n%s", prompt_start,
 
115
                     crypttarget, prompt_end);
59
116
    }
60
117
  } else {
61
118
    if(crypttarget == NULL){
62
 
      ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
 
119
      ret = asprintf(&prompt, "%s %s\n%s", prompt_start, cryptsource,
 
120
                     prompt_end);
63
121
    } else {
64
 
      ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
65
 
                     cryptsource, crypttarget);
 
122
      ret = asprintf(&prompt, "%s %s (%s)\n%s", prompt_start,
 
123
                     cryptsource, crypttarget, prompt_end);
66
124
    }
67
125
  }
68
126
  if(ret == -1){
79
137
bool become_a_daemon(void){
80
138
  int ret = setuid(geteuid());
81
139
  if(ret == -1){
82
 
    error(0, errno, "setuid");
 
140
    error_plus(0, errno, "setuid");
83
141
  }
84
142
    
85
143
  setsid();
86
144
  ret = chdir("/");
87
145
  if(ret == -1){
88
 
    error(0, errno, "chdir");
 
146
    error_plus(0, errno, "chdir");
89
147
    return false;
90
148
  }
91
149
  ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
92
150
  if(ret == -1){
93
 
    error(0, errno, "dup2");
 
151
    error_plus(0, errno, "dup2");
94
152
    return false;
95
153
  }
96
154
  return true;
97
155
}
98
156
 
 
157
__attribute__((nonnull (2, 3)))
99
158
bool exec_and_wait(pid_t *pid_return, const char *path,
100
 
                   const char **argv, bool interruptable,
 
159
                   const char * const *argv, bool interruptable,
101
160
                   bool daemonize){
102
161
  int status;
103
162
  int ret;
104
163
  pid_t pid;
105
164
  pid = fork();
106
165
  if(pid == -1){
107
 
    error(0, errno, "fork");
 
166
    error_plus(0, errno, "fork");
108
167
    return false;
109
168
  }
110
169
  if(pid == 0){
114
173
        _exit(EX_OSERR);
115
174
      }
116
175
    }
117
 
 
 
176
    
118
177
    char **new_argv = NULL;
119
 
    char *tmp;
 
178
    char **tmp;
120
179
    int i = 0;
121
 
    for (; argv[i]!=(char *)NULL; i++){
 
180
    for (; argv[i]!=NULL; i++){
122
181
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
123
 
      if (tmp == NULL){
124
 
        error(0, errno, "realloc");
 
182
      if(tmp == NULL){
 
183
        error_plus(0, errno, "realloc");
125
184
        free(new_argv);
126
 
        _exit(EXIT_FAILURE);
 
185
        _exit(EX_OSERR);
127
186
      }
128
 
      new_argv = (char **)tmp;
 
187
      new_argv = tmp;
129
188
      new_argv[i] = strdup(argv[i]);
130
189
    }
131
 
    new_argv[i] = (char *) NULL;
 
190
    new_argv[i] = NULL;
132
191
    
133
192
    execv(path, (char *const *)new_argv);
134
 
    error(0, errno, "execv");
 
193
    error_plus(0, errno, "execv");
135
194
    _exit(EXIT_FAILURE);
136
195
  }
137
196
  if(pid_return != NULL){
146
205
    return false;
147
206
  }
148
207
  if(ret == -1){
149
 
    error(0, errno, "waitpid");
 
208
    error_plus(0, errno, "waitpid");
150
209
    return false;
151
210
  }
152
 
  if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
 
211
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
153
212
    return true;
154
213
  }
155
214
  return false;
156
215
}
157
216
 
 
217
__attribute__((nonnull))
158
218
int is_plymouth(const struct dirent *proc_entry){
159
219
  int ret;
160
220
  {
161
 
    uintmax_t maxvalue;
 
221
    uintmax_t proc_id;
162
222
    char *tmp;
163
223
    errno = 0;
164
 
    maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
224
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
165
225
 
166
 
    if(errno != 0 or *tmp != '\0' or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
226
    if(errno != 0 or *tmp != '\0'
 
227
       or proc_id != (uintmax_t)((pid_t)proc_id)){
167
228
      return 0;
168
229
    }
169
230
  }
170
 
  char exe_target[sizeof(plymouth_path)];
 
231
  char exe_target[sizeof(plymouthd_path)];
171
232
  char *exe_link;
172
233
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
173
234
  if(ret == -1){
174
 
    error(0, errno, "asprintf");
 
235
    error_plus(0, errno, "asprintf");
175
236
    return 0;
176
237
  }
177
 
 
 
238
  
178
239
  struct stat exe_stat;
179
240
  ret = lstat(exe_link, &exe_stat);
180
241
  if(ret == -1){
181
242
    free(exe_link);
182
243
    if(errno != ENOENT){
183
 
      error(0, errno, "lstat");
 
244
      error_plus(0, errno, "lstat");
184
245
    }
185
246
    return 0;
186
247
  }
191
252
    free(exe_link);
192
253
    return 0;
193
254
  }
194
 
 
 
255
  
195
256
  ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
196
257
  free(exe_link);
197
 
  if((sret != (ssize_t)sizeof(plymouth_path)-1) or
198
 
      (memcmp(plymouth_path, exe_target,
199
 
              sizeof(plymouth_path)-1) != 0)){
 
258
  if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
 
259
      (memcmp(plymouthd_path, exe_target,
 
260
              sizeof(plymouthd_path)-1) != 0)){
200
261
    return 0;
201
262
  }
202
263
  return 1;
204
265
 
205
266
pid_t get_pid(void){
206
267
  int ret;
 
268
  uintmax_t proc_id = 0;
207
269
  FILE *pidfile = fopen(plymouth_pid, "r");
208
 
  uintmax_t maxvalue = 0;
 
270
  /* Try the new pid file location */
209
271
  if(pidfile != NULL){
210
 
    ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
 
272
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
211
273
    if(ret != 1){
212
 
      maxvalue = 0;
 
274
      proc_id = 0;
213
275
    }
214
276
    fclose(pidfile);
215
277
  }
216
 
  if(maxvalue == 0){
217
 
    struct dirent **direntries;
 
278
  /* Try the old pid file location */
 
279
  if(proc_id == 0){
 
280
    pidfile = fopen(plymouth_pid, "r");
 
281
    if(pidfile != NULL){
 
282
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
 
283
      if(ret != 1){
 
284
        proc_id = 0;
 
285
      }
 
286
      fclose(pidfile);
 
287
    }
 
288
  }
 
289
  /* Look for a plymouth process */
 
290
  if(proc_id == 0){
 
291
    struct dirent **direntries = NULL;
218
292
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
219
 
    sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
 
293
    if(ret == -1){
 
294
      error_plus(0, errno, "scandir");
 
295
    }
 
296
    if(ret > 0){
 
297
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
 
298
      if(ret < 0){
 
299
        error_plus(0, errno, "sscanf");
 
300
      }
 
301
    }
 
302
    /* scandir might preallocate for this variable (man page unclear).
 
303
       even if ret == 0, therefore we need to free it. */
 
304
    free(direntries);
220
305
  }
221
306
  pid_t pid;
222
 
  pid = (pid_t)maxvalue;
223
 
  if((uintmax_t)pid == maxvalue){
 
307
  pid = (pid_t)proc_id;
 
308
  if((uintmax_t)pid == proc_id){
224
309
    return pid;
225
310
  }
226
311
  
227
312
  return 0;
228
313
}
229
314
 
230
 
const char **getargv(pid_t pid){
 
315
const char * const * getargv(pid_t pid){
231
316
  int cl_fd;
232
317
  char *cmdline_filename;
233
318
  ssize_t sret;
236
321
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
237
322
                 (uintmax_t)pid);
238
323
  if(ret == -1){
239
 
    error(0, errno, "asprintf");
 
324
    error_plus(0, errno, "asprintf");
240
325
    return NULL;
241
326
  }
242
327
  
244
329
  cl_fd = open(cmdline_filename, O_RDONLY);
245
330
  free(cmdline_filename);
246
331
  if(cl_fd == -1){
247
 
    error(0, errno, "open");
 
332
    error_plus(0, errno, "open");
248
333
    return NULL;
249
334
  }
250
335
  
258
343
    if(cmdline_len + blocksize > cmdline_allocated){
259
344
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
260
345
      if(tmp == NULL){
261
 
        error(0, errno, "realloc");
 
346
        error_plus(0, errno, "realloc");
262
347
        free(cmdline);
263
348
        close(cl_fd);
264
349
        return NULL;
271
356
    sret = read(cl_fd, cmdline + cmdline_len,
272
357
                cmdline_allocated - cmdline_len);
273
358
    if(sret == -1){
274
 
      error(0, errno, "read");
 
359
      error_plus(0, errno, "read");
275
360
      free(cmdline);
276
361
      close(cl_fd);
277
362
      return NULL;
280
365
  } while(sret != 0);
281
366
  ret = close(cl_fd);
282
367
  if(ret == -1){
283
 
    error(0, errno, "close");
 
368
    error_plus(0, errno, "close");
284
369
    free(cmdline);
285
370
    return NULL;
286
371
  }
287
372
  
288
373
  /* we got cmdline and cmdline_len, ignore rest... */
289
 
  const char **argv = NULL;
290
 
  size_t argv_size = 0;
291
 
  for(char *arg = cmdline; arg-cmdline < (ssize_t)cmdline_len;
292
 
      arg = strchr(arg, '\0')+1){
293
 
    tmp = realloc(argv, ((++argv_size)+1)*sizeof(char *));
294
 
    if(tmp == NULL){
295
 
      error(0, errno, "realloc");
296
 
      free(argv);
297
 
      return NULL;
298
 
    }
299
 
    argv = (const char **)tmp;
300
 
    argv[argv_size-1] = arg;
 
374
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
 
375
                       * sizeof(char *)); /* Get number of args */
 
376
  if(argv == NULL){
 
377
    error_plus(0, errno, "argv = malloc()");
 
378
    free(cmdline);
 
379
    return NULL;
301
380
  }
302
 
  argv[argv_size] = NULL;
303
 
  return argv;
 
381
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
 
382
  return (const char * const *)argv;
304
383
}
305
384
 
306
385
int main(__attribute__((unused))int argc,
314
393
  /* test -x /bin/plymouth */
315
394
  ret = access(plymouth_path, X_OK);
316
395
  if(ret == -1){
317
 
    exit(EXIT_FAILURE);
 
396
    /* Plymouth is probably not installed.  Don't print an error
 
397
       message, just exit. */
 
398
    exit(EX_UNAVAILABLE);
318
399
  }
319
 
 
 
400
  
320
401
  { /* Add signal handlers */
321
402
    struct sigaction old_action,
322
403
      new_action = { .sa_handler = termination_handler,
323
404
                     .sa_flags = 0 };
324
405
    sigemptyset(&new_action.sa_mask);
325
 
    for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 }; *sig != 0; sig++){
 
406
    for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 };
 
407
        *sig != 0; sig++){
326
408
      ret = sigaddset(&new_action.sa_mask, *sig);
327
409
      if(ret == -1){
328
 
        error(0, errno, "sigaddset");
329
 
        exit(EX_OSERR);
 
410
        error_plus(EX_OSERR, errno, "sigaddset");
330
411
      }
331
412
      ret = sigaction(*sig, NULL, &old_action);
332
413
      if(ret == -1){
333
 
        error(0, errno, "sigaction");
334
 
        exit(EX_OSERR);
 
414
        error_plus(EX_OSERR, errno, "sigaction");
335
415
      }
336
416
      if(old_action.sa_handler != SIG_IGN){
337
417
        ret = sigaction(*sig, &new_action, NULL);
338
418
        if(ret == -1){
339
 
          error(0, errno, "sigaction");
340
 
          exit(EX_OSERR);
 
419
          error_plus(EX_OSERR, errno, "sigaction");
341
420
        }
342
421
      }
343
422
    }
344
423
  }
345
 
    
 
424
  
346
425
  /* plymouth --ping */
347
426
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
348
 
                       (const char *[]){ (const char *)plymouth_path, (const char *)"--ping", (const char *)NULL},
 
427
                       (const char *[])
 
428
                       { plymouth_path, "--ping", NULL },
349
429
                       true, false);
350
430
  if(not bret){
351
431
    if(interrupted_by_signal){
352
432
      kill_and_wait(plymouth_command_pid);
 
433
      exit(EXIT_FAILURE);
353
434
    }
354
 
    exit(EXIT_FAILURE);
 
435
    /* Plymouth is probably not running.  Don't print an error
 
436
       message, just exit. */
 
437
    exit(EX_UNAVAILABLE);
355
438
  }
356
439
  
357
440
  prompt = makeprompt();
358
441
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
359
442
  free(prompt);
360
443
  if(ret == -1){
361
 
    error(0, errno, "asprintf");
362
 
    exit(EXIT_FAILURE);
 
444
    error_plus(EX_OSERR, errno, "asprintf");
363
445
  }
364
446
  
365
447
  /* plymouth ask-for-password --prompt="$prompt" */
366
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
367
 
                       (const char *[]){plymouth_path, "ask-for-password", prompt_arg, NULL},
 
448
  bret = exec_and_wait(&plymouth_command_pid,
 
449
                       plymouth_path, (const char *[])
 
450
                       { plymouth_path, "ask-for-password",
 
451
                           prompt_arg, NULL },
368
452
                       true, false);
369
453
  free(prompt_arg);
370
 
  if(not bret){
371
 
    if(interrupted_by_signal){
372
 
      kill_and_wait(plymouth_command_pid);
373
 
    } else {
374
 
      exit(EXIT_FAILURE);
375
 
    }
376
 
  }
377
 
  
378
454
  if(bret){
379
455
    exit(EXIT_SUCCESS);
380
456
  }
 
457
  if(not interrupted_by_signal){
 
458
    /* exec_and_wait failed for some other reason */
 
459
    exit(EXIT_FAILURE);
 
460
  }
 
461
  kill_and_wait(plymouth_command_pid);
381
462
  
382
 
  const char **plymouthd_argv = NULL;
 
463
  const char * const *plymouthd_argv;
383
464
  pid_t pid = get_pid();
384
465
  if(pid == 0){
385
 
    error(0, 0, "plymouthd pid not found");
 
466
    error_plus(0, 0, "plymouthd pid not found");
 
467
    plymouthd_argv = plymouthd_default_argv;
386
468
  } else {
387
469
    plymouthd_argv = getargv(pid);
388
470
  }
389
 
  if(plymouthd_argv == NULL){
390
 
    plymouthd_argv = plymouthd_default_argv;
391
 
  }
392
471
  
393
 
  bret = exec_and_wait(NULL, plymouth_path,
394
 
                       (const char *[]){plymouth_path, "quit", NULL}, false, false);
395
 
  if(not bret){
396
 
    exit(EXIT_FAILURE);
397
 
  }
398
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv, false, true);
399
 
  if(not bret){
400
 
    exit(EXIT_FAILURE);
401
 
  }
402
 
  exec_and_wait(NULL, plymouth_path,
403
 
                (const char *[]){ plymouth_path, "show-splash", NULL }, false, false);
 
472
  bret = exec_and_wait(NULL, plymouth_path, (const char *[])
 
473
                       { plymouth_path, "quit", NULL },
 
474
                       false, false);
 
475
  if(not bret){
 
476
    exit(EXIT_FAILURE);
 
477
  }
 
478
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
 
479
                       false, true);
 
480
  if(not bret){
 
481
    exit(EXIT_FAILURE);
 
482
  }
 
483
  exec_and_wait(NULL, plymouth_path, (const char *[])
 
484
                { plymouth_path, "show-splash", NULL },
 
485
                false, false);
404
486
  exit(EXIT_FAILURE);
405
487
}