/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugins.d/plymouth.c

  • Committer: Teddy Hogeborn
  • Date: 2019-07-29 16:35:53 UTC
  • Revision ID: teddy@recompile.se-20190729163553-1i442i2cbx64c537
Make tests and man page examples match

Make the tests test_manual_page_example[1-5] match exactly what is
written in the manual page, and add comments to manual page as
reminders to keep tests and manual page examples in sync.

* mandos-ctl (Test_commands_from_options.test_manual_page_example_1):
  Remove "--verbose" option, since the manual does not have it as the
  first example, and change assertion to match.
* mandos-ctl.xml (EXAMPLE): Add comments to all examples documenting
  which test function they correspond to.  Also remove unnecessary
  quotes from option arguments in fourth example, and clarify language
  slightly in fifth example.

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-2019 Teddy Hogeborn
 
6
 * Copyright © 2010-2019 Björn Påhlsson
 
7
 * 
 
8
 * This file is part of Mandos.
 
9
 * 
 
10
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 * under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 * 
 
15
 * Mandos is distributed in the hope that it will be useful, but
 
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
18
 * General Public License for more details.
 
19
 * 
 
20
 * You should have received a copy of the GNU General Public License
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
22
 * 
 
23
 * Contact the authors at <mandos@recompile.se>.
 
24
 */
 
25
 
1
26
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
2
27
#include <signal.h>             /* sig_atomic_t, struct sigaction,
3
28
                                   sigemptyset(), sigaddset(), SIGINT,
12
37
#include <stddef.h>             /* NULL */
13
38
#include <string.h>             /* strchr(), memcmp() */
14
39
#include <stdio.h>              /* asprintf(), perror(), fopen(),
15
 
                                   fscanf() */
 
40
                                   fscanf(), vasprintf(), fprintf(),
 
41
                                   vfprintf() */
16
42
#include <unistd.h>             /* close(), readlink(), read(),
17
43
                                   fork(), setsid(), chdir(), dup2(),
18
44
                                   STDERR_FILENO, execv(), access() */
26
52
#include <error.h>              /* error() */
27
53
#include <errno.h>              /* TEMP_FAILURE_RETRY */
28
54
#include <argz.h>               /* argz_count(), argz_extract() */
 
55
#include <stdarg.h>             /* va_list, va_start(), ... */
 
56
#include <argp.h>
29
57
 
30
58
sig_atomic_t interrupted_by_signal = 0;
31
 
const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid";
 
59
const char *argp_program_version = "plymouth " VERSION;
 
60
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
61
 
 
62
/* Used by Ubuntu 11.04 (Natty Narwahl) */
 
63
const char plymouth_old_old_pid[] = "/dev/.initramfs/plymouth.pid";
 
64
/* Used by Ubuntu 11.10 (Oneiric Ocelot) */
 
65
const char plymouth_old_pid[] = "/run/initramfs/plymouth.pid";
 
66
/* Used by Debian 9 (stretch) */
 
67
const char plymouth_pid[] = "/run/plymouth/pid";
 
68
 
32
69
const char plymouth_path[] = "/bin/plymouth";
33
70
const char plymouthd_path[] = "/sbin/plymouthd";
34
71
const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
35
72
                                        "--mode=boot",
36
73
                                        "--attach-to-session",
37
 
                                        "--pid-file="
38
 
                                        "/dev/.initramfs/"
39
 
                                        "plymouth.pid",
40
74
                                        NULL };
 
75
bool debug = false;
41
76
 
42
77
static void termination_handler(__attribute__((unused))int signum){
43
78
  if(interrupted_by_signal){
46
81
  interrupted_by_signal = 1;
47
82
}
48
83
 
 
84
__attribute__((format (gnu_printf, 2, 3), nonnull))
 
85
int fprintf_plus(FILE *stream, const char *format, ...){
 
86
  va_list ap;
 
87
  va_start (ap, format);
 
88
  fprintf(stream, "Mandos plugin %s: ", program_invocation_short_name);
 
89
  return vfprintf(stream, format, ap);
 
90
}
 
91
 
 
92
/* Function to use when printing errors */
 
93
__attribute__((format (gnu_printf, 3, 4)))
 
94
void error_plus(int status, int errnum, const char *formatstring,
 
95
                ...){
 
96
  va_list ap;
 
97
  char *text;
 
98
  int ret;
 
99
  
 
100
  va_start(ap, formatstring);
 
101
  ret = vasprintf(&text, formatstring, ap);
 
102
  if(ret == -1){
 
103
    fprintf(stderr, "Mandos plugin %s: ",
 
104
            program_invocation_short_name);
 
105
    vfprintf(stderr, formatstring, ap);
 
106
    fprintf(stderr, ": ");
 
107
    fprintf(stderr, "%s\n", strerror(errnum));
 
108
    error(status, errno, "vasprintf while printing error");
 
109
    return;
 
110
  }
 
111
  fprintf(stderr, "Mandos plugin ");
 
112
  error(status, errnum, "%s", text);
 
113
  free(text);
 
114
}
 
115
 
49
116
/* Create prompt string */
50
117
char *makeprompt(void){
51
118
  int ret = 0;
52
119
  char *prompt;
53
120
  const char *const cryptsource = getenv("cryptsource");
54
121
  const char *const crypttarget = getenv("crypttarget");
55
 
  const char prompt_start[] = "Enter passphrase to unlock the disk";
 
122
  const char prompt_start[] = "Unlocking the disk";
 
123
  const char prompt_end[] = "Enter passphrase";
56
124
  
57
125
  if(cryptsource == NULL){
58
126
    if(crypttarget == NULL){
59
 
      ret = asprintf(&prompt, "%s: ", prompt_start);
 
127
      ret = asprintf(&prompt, "%s\n%s", prompt_start, prompt_end);
60
128
    } else {
61
 
      ret = asprintf(&prompt, "%s (%s): ", prompt_start,
62
 
                     crypttarget);
 
129
      ret = asprintf(&prompt, "%s (%s)\n%s", prompt_start,
 
130
                     crypttarget, prompt_end);
63
131
    }
64
132
  } else {
65
133
    if(crypttarget == NULL){
66
 
      ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
 
134
      ret = asprintf(&prompt, "%s %s\n%s", prompt_start, cryptsource,
 
135
                     prompt_end);
67
136
    } else {
68
 
      ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
69
 
                     cryptsource, crypttarget);
 
137
      ret = asprintf(&prompt, "%s %s (%s)\n%s", prompt_start,
 
138
                     cryptsource, crypttarget, prompt_end);
70
139
    }
71
140
  }
72
141
  if(ret == -1){
83
152
bool become_a_daemon(void){
84
153
  int ret = setuid(geteuid());
85
154
  if(ret == -1){
86
 
    error(0, errno, "setuid");
 
155
    error_plus(0, errno, "setuid");
87
156
  }
88
157
    
89
158
  setsid();
90
159
  ret = chdir("/");
91
160
  if(ret == -1){
92
 
    error(0, errno, "chdir");
 
161
    error_plus(0, errno, "chdir");
93
162
    return false;
94
163
  }
95
164
  ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
96
165
  if(ret == -1){
97
 
    error(0, errno, "dup2");
 
166
    error_plus(0, errno, "dup2");
98
167
    return false;
99
168
  }
100
169
  return true;
101
170
}
102
171
 
 
172
__attribute__((nonnull (2, 3)))
103
173
bool exec_and_wait(pid_t *pid_return, const char *path,
104
 
                   const char **argv, bool interruptable,
 
174
                   const char * const * const argv, bool interruptable,
105
175
                   bool daemonize){
106
176
  int status;
107
177
  int ret;
108
178
  pid_t pid;
 
179
  if(debug){
 
180
    for(const char * const *arg = argv; *arg != NULL; arg++){
 
181
      fprintf_plus(stderr, "exec_and_wait arg: %s\n", *arg);
 
182
    }
 
183
    fprintf_plus(stderr, "exec_and_wait end of args\n");
 
184
  }
 
185
 
109
186
  pid = fork();
110
187
  if(pid == -1){
111
 
    error(0, errno, "fork");
 
188
    error_plus(0, errno, "fork");
112
189
    return false;
113
190
  }
114
191
  if(pid == 0){
118
195
        _exit(EX_OSERR);
119
196
      }
120
197
    }
121
 
 
122
 
    char **new_argv = NULL;
123
 
    char *tmp;
 
198
    
 
199
    char **new_argv = malloc(sizeof(const char *));
 
200
    if(new_argv == NULL){
 
201
      error_plus(0, errno, "malloc");
 
202
      _exit(EX_OSERR);
 
203
    }
 
204
    char **tmp;
124
205
    int i = 0;
125
 
    for (; argv[i]!=(char *)NULL; i++){
126
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
127
 
      if (tmp == NULL){
128
 
        error(0, errno, "realloc");
 
206
    for (; argv[i] != NULL; i++){
 
207
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
 
208
      if(tmp == NULL){
 
209
        error_plus(0, errno, "realloc");
129
210
        free(new_argv);
130
211
        _exit(EX_OSERR);
131
212
      }
132
 
      new_argv = (char **)tmp;
 
213
      new_argv = tmp;
133
214
      new_argv[i] = strdup(argv[i]);
134
215
    }
135
 
    new_argv[i] = (char *) NULL;
 
216
    new_argv[i] = NULL;
136
217
    
137
218
    execv(path, (char *const *)new_argv);
138
 
    error(0, errno, "execv");
 
219
    error_plus(0, errno, "execv");
139
220
    _exit(EXIT_FAILURE);
140
221
  }
141
222
  if(pid_return != NULL){
147
228
          and ((not interrupted_by_signal)
148
229
               or (not interruptable)));
149
230
  if(interrupted_by_signal and interruptable){
 
231
    if(debug){
 
232
      fprintf_plus(stderr, "Interrupted by signal\n");
 
233
    }
150
234
    return false;
151
235
  }
152
236
  if(ret == -1){
153
 
    error(0, errno, "waitpid");
 
237
    error_plus(0, errno, "waitpid");
154
238
    return false;
155
239
  }
 
240
  if(debug){
 
241
    if(WIFEXITED(status)){
 
242
      fprintf_plus(stderr, "exec_and_wait exited: %d\n",
 
243
                   WEXITSTATUS(status));
 
244
    } else if(WIFSIGNALED(status)) {
 
245
      fprintf_plus(stderr, "exec_and_wait signaled: %d\n",
 
246
                   WTERMSIG(status));
 
247
    }
 
248
  }
156
249
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
157
250
    return true;
158
251
  }
159
252
  return false;
160
253
}
161
254
 
 
255
__attribute__((nonnull))
162
256
int is_plymouth(const struct dirent *proc_entry){
163
257
  int ret;
164
258
  {
165
 
    uintmax_t maxvalue;
 
259
    uintmax_t proc_id;
166
260
    char *tmp;
167
261
    errno = 0;
168
 
    maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
262
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
169
263
 
170
264
    if(errno != 0 or *tmp != '\0'
171
 
       or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
265
       or proc_id != (uintmax_t)((pid_t)proc_id)){
172
266
      return 0;
173
267
    }
174
268
  }
175
 
  char exe_target[sizeof(plymouth_path)];
 
269
  char exe_target[sizeof(plymouthd_path)];
176
270
  char *exe_link;
177
271
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
178
272
  if(ret == -1){
179
 
    error(0, errno, "asprintf");
 
273
    error_plus(0, errno, "asprintf");
180
274
    return 0;
181
275
  }
182
276
  
185
279
  if(ret == -1){
186
280
    free(exe_link);
187
281
    if(errno != ENOENT){
188
 
      error(0, errno, "lstat");
 
282
      error_plus(0, errno, "lstat");
189
283
    }
190
284
    return 0;
191
285
  }
199
293
  
200
294
  ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
201
295
  free(exe_link);
202
 
  if((sret != (ssize_t)sizeof(plymouth_path)-1) or
203
 
      (memcmp(plymouth_path, exe_target,
204
 
              sizeof(plymouth_path)-1) != 0)){
 
296
  if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
 
297
      (memcmp(plymouthd_path, exe_target,
 
298
              sizeof(plymouthd_path)-1) != 0)){
205
299
    return 0;
206
300
  }
207
301
  return 1;
209
303
 
210
304
pid_t get_pid(void){
211
305
  int ret;
 
306
  uintmax_t proc_id = 0;
212
307
  FILE *pidfile = fopen(plymouth_pid, "r");
213
 
  uintmax_t maxvalue = 0;
 
308
  /* Try the new pid file location */
214
309
  if(pidfile != NULL){
215
 
    ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
 
310
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
216
311
    if(ret != 1){
217
 
      maxvalue = 0;
 
312
      proc_id = 0;
218
313
    }
219
314
    fclose(pidfile);
220
315
  }
221
 
  if(maxvalue == 0){
222
 
    struct dirent **direntries;
 
316
  /* Try the old pid file location */
 
317
  if(proc_id == 0){
 
318
    pidfile = fopen(plymouth_old_pid, "r");
 
319
    if(pidfile != NULL){
 
320
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
 
321
      if(ret != 1){
 
322
        proc_id = 0;
 
323
      }
 
324
      fclose(pidfile);
 
325
    }
 
326
  }
 
327
  /* Try the old old pid file location */
 
328
  if(proc_id == 0){
 
329
    pidfile = fopen(plymouth_old_old_pid, "r");
 
330
    if(pidfile != NULL){
 
331
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
 
332
      if(ret != 1){
 
333
        proc_id = 0;
 
334
      }
 
335
      fclose(pidfile);
 
336
    }
 
337
  }
 
338
  /* Look for a plymouth process */
 
339
  if(proc_id == 0){
 
340
    struct dirent **direntries = NULL;
223
341
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
224
 
    sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
 
342
    if(ret == -1){
 
343
      error_plus(0, errno, "scandir");
 
344
    }
 
345
    if(ret > 0){
 
346
      for(int i = ret-1; i >= 0; i--){
 
347
        if(proc_id == 0){
 
348
          ret = sscanf(direntries[i]->d_name, "%" SCNuMAX, &proc_id);
 
349
          if(ret < 0){
 
350
            error_plus(0, errno, "sscanf");
 
351
          }
 
352
        }
 
353
        free(direntries[i]);
 
354
      }
 
355
    }
 
356
    /* scandir might preallocate for this variable (man page unclear).
 
357
       even if ret == 0, therefore we need to free it. */
 
358
    free(direntries);
225
359
  }
226
360
  pid_t pid;
227
 
  pid = (pid_t)maxvalue;
228
 
  if((uintmax_t)pid == maxvalue){
 
361
  pid = (pid_t)proc_id;
 
362
  if((uintmax_t)pid == proc_id){
229
363
    return pid;
230
364
  }
231
365
  
232
366
  return 0;
233
367
}
234
368
 
235
 
const char **getargv(pid_t pid){
 
369
char **getargv(pid_t pid){
236
370
  int cl_fd;
237
371
  char *cmdline_filename;
238
372
  ssize_t sret;
241
375
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
242
376
                 (uintmax_t)pid);
243
377
  if(ret == -1){
244
 
    error(0, errno, "asprintf");
 
378
    error_plus(0, errno, "asprintf");
245
379
    return NULL;
246
380
  }
247
381
  
249
383
  cl_fd = open(cmdline_filename, O_RDONLY);
250
384
  free(cmdline_filename);
251
385
  if(cl_fd == -1){
252
 
    error(0, errno, "open");
 
386
    error_plus(0, errno, "open");
253
387
    return NULL;
254
388
  }
255
389
  
263
397
    if(cmdline_len + blocksize > cmdline_allocated){
264
398
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
265
399
      if(tmp == NULL){
266
 
        error(0, errno, "realloc");
 
400
        error_plus(0, errno, "realloc");
267
401
        free(cmdline);
268
402
        close(cl_fd);
269
403
        return NULL;
276
410
    sret = read(cl_fd, cmdline + cmdline_len,
277
411
                cmdline_allocated - cmdline_len);
278
412
    if(sret == -1){
279
 
      error(0, errno, "read");
 
413
      error_plus(0, errno, "read");
280
414
      free(cmdline);
281
415
      close(cl_fd);
282
416
      return NULL;
285
419
  } while(sret != 0);
286
420
  ret = close(cl_fd);
287
421
  if(ret == -1){
288
 
    error(0, errno, "close");
 
422
    error_plus(0, errno, "close");
289
423
    free(cmdline);
290
424
    return NULL;
291
425
  }
294
428
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
295
429
                       * sizeof(char *)); /* Get number of args */
296
430
  if(argv == NULL){
297
 
    error(0, errno, "argv = malloc()");
 
431
    error_plus(0, errno, "argv = malloc()");
298
432
    free(cmdline);
299
433
    return NULL;
300
434
  }
301
435
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
302
 
  return (const char **)argv;
 
436
  return argv;
303
437
}
304
438
 
305
439
int main(__attribute__((unused))int argc,
306
440
         __attribute__((unused))char **argv){
307
 
  char *prompt;
 
441
  char *prompt = NULL;
308
442
  char *prompt_arg;
309
443
  pid_t plymouth_command_pid;
310
444
  int ret;
311
445
  bool bret;
312
446
 
 
447
  {
 
448
    struct argp_option options[] = {
 
449
      { .name = "prompt", .key = 128, .arg = "PROMPT",
 
450
        .doc = "The prompt to show" },
 
451
      { .name = "debug", .key = 129,
 
452
        .doc = "Debug mode" },
 
453
      { .name = NULL }
 
454
    };
 
455
    
 
456
    __attribute__((nonnull(3)))
 
457
    error_t parse_opt (int key, char *arg, __attribute__((unused))
 
458
                       struct argp_state *state){
 
459
      errno = 0;
 
460
      switch (key){
 
461
      case 128:                 /* --prompt */
 
462
        prompt = arg;
 
463
        if(debug){
 
464
          fprintf_plus(stderr, "Custom prompt \"%s\"\n", prompt);
 
465
        }
 
466
        break;
 
467
      case 129:                 /* --debug */
 
468
        debug = true;
 
469
        break;
 
470
      default:
 
471
        return ARGP_ERR_UNKNOWN;
 
472
      }
 
473
      return errno;
 
474
    }
 
475
    
 
476
    struct argp argp = { .options = options, .parser = parse_opt,
 
477
                         .args_doc = "",
 
478
                         .doc = "Mandos plymouth -- Read and"
 
479
                         " output a password" };
 
480
    ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, NULL);
 
481
    switch(ret){
 
482
    case 0:
 
483
      break;
 
484
    case ENOMEM:
 
485
    default:
 
486
      errno = ret;
 
487
      error_plus(0, errno, "argp_parse");
 
488
      return EX_OSERR;
 
489
    case EINVAL:
 
490
      error_plus(0, errno, "argp_parse");
 
491
      return EX_USAGE;
 
492
    }
 
493
  }
 
494
  
313
495
  /* test -x /bin/plymouth */
314
496
  ret = access(plymouth_path, X_OK);
315
497
  if(ret == -1){
316
498
    /* Plymouth is probably not installed.  Don't print an error
317
499
       message, just exit. */
 
500
    if(debug){
 
501
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
 
502
                   plymouth_path);
 
503
    }
318
504
    exit(EX_UNAVAILABLE);
319
505
  }
320
506
  
327
513
        *sig != 0; sig++){
328
514
      ret = sigaddset(&new_action.sa_mask, *sig);
329
515
      if(ret == -1){
330
 
        error(EX_OSERR, errno, "sigaddset");
 
516
        error_plus(EX_OSERR, errno, "sigaddset");
331
517
      }
332
518
      ret = sigaction(*sig, NULL, &old_action);
333
519
      if(ret == -1){
334
 
        error(EX_OSERR, errno, "sigaction");
 
520
        error_plus(EX_OSERR, errno, "sigaction");
335
521
      }
336
522
      if(old_action.sa_handler != SIG_IGN){
337
523
        ret = sigaction(*sig, &new_action, NULL);
338
524
        if(ret == -1){
339
 
          error(EX_OSERR, errno, "sigaction");
 
525
          error_plus(EX_OSERR, errno, "sigaction");
340
526
        }
341
527
      }
342
528
    }
354
540
    }
355
541
    /* Plymouth is probably not running.  Don't print an error
356
542
       message, just exit. */
 
543
    if(debug){
 
544
      fprintf_plus(stderr, "Plymouth not running\n");
 
545
    }
357
546
    exit(EX_UNAVAILABLE);
358
547
  }
359
548
  
360
 
  prompt = makeprompt();
361
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
362
 
  free(prompt);
 
549
  if(prompt != NULL){
 
550
    ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
 
551
  } else {
 
552
    char *made_prompt = makeprompt();
 
553
    ret = asprintf(&prompt_arg, "--prompt=%s", made_prompt);
 
554
    free(made_prompt);
 
555
  }
363
556
  if(ret == -1){
364
 
    error(EX_OSERR, errno, "asprintf");
 
557
    error_plus(EX_OSERR, errno, "asprintf");
365
558
  }
366
559
  
367
560
  /* plymouth ask-for-password --prompt="$prompt" */
 
561
  if(debug){
 
562
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
 
563
  }
368
564
  bret = exec_and_wait(&plymouth_command_pid,
369
565
                       plymouth_path, (const char *[])
370
566
                       { plymouth_path, "ask-for-password",
380
576
  }
381
577
  kill_and_wait(plymouth_command_pid);
382
578
  
383
 
  const char **plymouthd_argv;
 
579
  char **plymouthd_argv = NULL;
384
580
  pid_t pid = get_pid();
385
581
  if(pid == 0){
386
 
    error(0, 0, "plymouthd pid not found");
387
 
    plymouthd_argv = plymouthd_default_argv;
 
582
    error_plus(0, 0, "plymouthd pid not found");
388
583
  } else {
389
584
    plymouthd_argv = getargv(pid);
390
585
  }
393
588
                       { plymouth_path, "quit", NULL },
394
589
                       false, false);
395
590
  if(not bret){
 
591
    if(plymouthd_argv != NULL){
 
592
      free(*plymouthd_argv);
 
593
      free(plymouthd_argv);
 
594
    }
396
595
    exit(EXIT_FAILURE);
397
596
  }
398
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
 
597
  bret = exec_and_wait(NULL, plymouthd_path,
 
598
                       (plymouthd_argv != NULL)
 
599
                       ? (const char * const *)plymouthd_argv
 
600
                       : plymouthd_default_argv,
399
601
                       false, true);
 
602
  if(plymouthd_argv != NULL){
 
603
    free(*plymouthd_argv);
 
604
    free(plymouthd_argv);
 
605
  }
400
606
  if(not bret){
401
607
    exit(EXIT_FAILURE);
402
608
  }