/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: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

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-2022 Teddy Hogeborn
6
 
 * Copyright © 2010-2022 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
 
 
26
 
#define _GNU_SOURCE             /* program_invocation_short_name,
27
 
                                   vasprintf(), asprintf(),
28
 
                                   TEMP_FAILURE_RETRY() */
29
 
#include <sys/types.h>          /* sig_atomic_t, pid_t, setuid(),
30
 
                                   geteuid(), setsid() */
31
 
#include <argp.h>               /* argp_program_version,
32
 
                                   argp_program_bug_address,
33
 
                                   struct argp_option,
34
 
                                   struct argp_state,
35
 
                                   ARGP_ERR_UNKNOWN, struct argp,
36
 
                                   argp_parse(), ARGP_IN_ORDER */
37
 
#include <stddef.h>             /* NULL, size_t */
38
 
#include <stdbool.h>            /* bool, false, true */
39
 
#include <stdio.h>              /* FILE, fprintf(), vfprintf(),
40
 
                                   vasprintf(), stderr, asprintf(),
41
 
                                   fopen(), fscanf(), fclose(),
42
 
                                   sscanf() */
43
 
#include <stdarg.h>             /* va_list, va_start(), vfprintf() */
44
 
#include <errno.h>              /* program_invocation_short_name,
45
 
                                   errno, ENOMEM, EINTR, ENOENT,
46
 
                                   error_t, EINVAL */
47
 
#include <string.h>             /* strerror(), strdup(), memcmp() */
48
 
#include <error.h>              /* error() */
49
 
#include <stdlib.h>             /* free(), getenv(), malloc(),
50
 
                                   reallocarray(), realloc(),
51
 
                                   EXIT_FAILURE, EXIT_SUCCESS */
52
 
#include <unistd.h>             /* TEMP_FAILURE_RETRY(), setuid(),
53
 
                                   geteuid(), setsid(), chdir(),
54
 
                                   dup2(), STDERR_FILENO,
55
 
                                   STDOUT_FILENO, fork(), _exit(),
56
 
                                   execv(), ssize_t, readlink(),
57
 
                                   close(), read(), access(), X_OK */
58
 
#include <signal.h>             /* kill(), SIGTERM, struct sigaction,
59
 
                                   sigemptyset(), SIGINT, SIGHUP,
60
 
                                   sigaddset(), SIG_IGN */
61
 
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
62
 
                                   WEXITSTATUS(), WIFSIGNALED(),
63
 
                                   WTERMSIG() */
64
 
#include <iso646.h>             /* not, and, or */
65
 
#include <sysexits.h>           /* EX_OSERR, EX_USAGE,
66
 
                                   EX_UNAVAILABLE */
67
 
#include <stdint.h>             /* SIZE_MAX */
68
 
#include <dirent.h>             /* struct dirent, scandir(),
69
 
                                   alphasort() */
70
 
#include <inttypes.h>           /* uintmax_t, strtoumax(), SCNuMAX,
71
 
                                   PRIuMAX */
72
 
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK() */
73
 
#include <fcntl.h>              /* open(), O_RDONLY */
74
 
#include <argz.h>               /* argz_count(), argz_extract() */
75
 
 
76
 
sig_atomic_t interrupted_by_signal = 0;
77
 
const char *argp_program_version = "plymouth " VERSION;
78
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
79
 
 
80
 
/* Used by Ubuntu 11.04 (Natty Narwahl) */
81
 
const char plymouth_old_old_pid[] = "/dev/.initramfs/plymouth.pid";
82
 
/* Used by Ubuntu 11.10 (Oneiric Ocelot) */
83
 
const char plymouth_old_pid[] = "/run/initramfs/plymouth.pid";
84
 
/* Used by Debian 9 (stretch) */
85
 
const char plymouth_pid[] = "/run/plymouth/pid";
86
 
 
87
 
const char plymouth_path[] = "/bin/plymouth";
88
 
const char plymouthd_path[] = "/sbin/plymouthd";
89
 
const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
90
 
                                        "--mode=boot",
91
 
                                        "--attach-to-session",
92
 
                                        NULL };
93
 
bool debug = false;
94
 
 
95
 
static void termination_handler(__attribute__((unused))int signum){
96
 
  if(interrupted_by_signal){
97
 
    return;
98
 
  }
99
 
  interrupted_by_signal = 1;
100
 
}
101
 
 
102
 
__attribute__((format (gnu_printf, 2, 3), nonnull))
103
 
int fprintf_plus(FILE *stream, const char *format, ...){
104
 
  va_list ap;
105
 
  va_start (ap, format);
106
 
  fprintf(stream, "Mandos plugin %s: ", program_invocation_short_name);
107
 
  return vfprintf(stream, format, ap);
108
 
}
109
 
 
110
 
/* Function to use when printing errors */
111
 
__attribute__((format (gnu_printf, 3, 4)))
112
 
void error_plus(int status, int errnum, const char *formatstring,
113
 
                ...){
114
 
  va_list ap;
115
 
  char *text;
116
 
  int ret;
117
 
  
118
 
  va_start(ap, formatstring);
119
 
  ret = vasprintf(&text, formatstring, ap);
120
 
  if(ret == -1){
121
 
    fprintf(stderr, "Mandos plugin %s: ",
122
 
            program_invocation_short_name);
123
 
    vfprintf(stderr, formatstring, ap);
124
 
    fprintf(stderr, ": ");
125
 
    fprintf(stderr, "%s\n", strerror(errnum));
126
 
    error(status, errno, "vasprintf while printing error");
127
 
    return;
128
 
  }
129
 
  fprintf(stderr, "Mandos plugin ");
130
 
  error(status, errnum, "%s", text);
131
 
  free(text);
132
 
}
133
 
 
134
 
/* Create prompt string */
135
 
char *makeprompt(void){
136
 
  int ret = 0;
137
 
  char *prompt;
138
 
  const char *const cryptsource = getenv("cryptsource");
139
 
  const char *const crypttarget = getenv("crypttarget");
140
 
  const char prompt_start[] = "Unlocking the disk";
141
 
  const char prompt_end[] = "Enter passphrase";
142
 
  
143
 
  if(cryptsource == NULL){
144
 
    if(crypttarget == NULL){
145
 
      ret = asprintf(&prompt, "%s\n%s", prompt_start, prompt_end);
146
 
    } else {
147
 
      ret = asprintf(&prompt, "%s (%s)\n%s", prompt_start,
148
 
                     crypttarget, prompt_end);
149
 
    }
150
 
  } else {
151
 
    if(crypttarget == NULL){
152
 
      ret = asprintf(&prompt, "%s %s\n%s", prompt_start, cryptsource,
153
 
                     prompt_end);
154
 
    } else {
155
 
      ret = asprintf(&prompt, "%s %s (%s)\n%s", prompt_start,
156
 
                     cryptsource, crypttarget, prompt_end);
157
 
    }
158
 
  }
159
 
  if(ret == -1){
160
 
    return NULL;
161
 
  }
162
 
  return prompt;
163
 
}
164
 
 
165
 
void kill_and_wait(pid_t pid){
166
 
  TEMP_FAILURE_RETRY(kill(pid, SIGTERM));
167
 
  TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
168
 
}
169
 
 
170
 
bool become_a_daemon(void){
171
 
  int ret = setuid(geteuid());
172
 
  if(ret == -1){
173
 
    error_plus(0, errno, "setuid");
174
 
  }
175
 
    
176
 
  setsid();
177
 
  ret = chdir("/");
178
 
  if(ret == -1){
179
 
    error_plus(0, errno, "chdir");
180
 
    return false;
181
 
  }
182
 
  ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
183
 
  if(ret == -1){
184
 
    error_plus(0, errno, "dup2");
185
 
    return false;
186
 
  }
187
 
  return true;
188
 
}
189
 
 
190
 
__attribute__((nonnull (2, 3)))
191
 
bool exec_and_wait(pid_t *pid_return, const char *path,
192
 
                   const char * const * const argv, bool interruptable,
193
 
                   bool daemonize){
194
 
  int status;
195
 
  int ret;
196
 
  pid_t pid;
197
 
  if(debug){
198
 
    for(const char * const *arg = argv; *arg != NULL; arg++){
199
 
      fprintf_plus(stderr, "exec_and_wait arg: %s\n", *arg);
200
 
    }
201
 
    fprintf_plus(stderr, "exec_and_wait end of args\n");
202
 
  }
203
 
 
204
 
  pid = fork();
205
 
  if(pid == -1){
206
 
    error_plus(0, errno, "fork");
207
 
    return false;
208
 
  }
209
 
  if(pid == 0){
210
 
    /* Child */
211
 
    if(daemonize){
212
 
      if(not become_a_daemon()){
213
 
        _exit(EX_OSERR);
214
 
      }
215
 
    }
216
 
    
217
 
    char **new_argv = malloc(sizeof(const char *));
218
 
    if(new_argv == NULL){
219
 
      error_plus(0, errno, "malloc");
220
 
      _exit(EX_OSERR);
221
 
    }
222
 
    char **tmp;
223
 
    int i = 0;
224
 
    for (; argv[i] != NULL; i++){
225
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
226
 
      tmp = reallocarray(new_argv, ((size_t)i + 2),
227
 
                         sizeof(const char *));
228
 
#else
229
 
      if(((size_t)i + 2) > (SIZE_MAX / sizeof(const char *))){
230
 
        /* overflow */
231
 
        tmp = NULL;
232
 
        errno = ENOMEM;
233
 
      } else {
234
 
        tmp = realloc(new_argv, ((size_t)i + 2) * sizeof(const char *));
235
 
      }
236
 
#endif
237
 
      if(tmp == NULL){
238
 
        error_plus(0, errno, "reallocarray");
239
 
        free(new_argv);
240
 
        _exit(EX_OSERR);
241
 
      }
242
 
      new_argv = tmp;
243
 
      new_argv[i] = strdup(argv[i]);
244
 
    }
245
 
    new_argv[i] = NULL;
246
 
    
247
 
    execv(path, (char *const *)new_argv);
248
 
    error_plus(0, errno, "execv");
249
 
    _exit(EXIT_FAILURE);
250
 
  }
251
 
  if(pid_return != NULL){
252
 
    *pid_return = pid;
253
 
  }
254
 
  do {
255
 
    ret = waitpid(pid, &status, 0);
256
 
  } while(ret == -1 and errno == EINTR
257
 
          and ((not interrupted_by_signal)
258
 
               or (not interruptable)));
259
 
  if(interrupted_by_signal and interruptable){
260
 
    if(debug){
261
 
      fprintf_plus(stderr, "Interrupted by signal\n");
262
 
    }
263
 
    return false;
264
 
  }
265
 
  if(ret == -1){
266
 
    error_plus(0, errno, "waitpid");
267
 
    return false;
268
 
  }
269
 
  if(debug){
270
 
    if(WIFEXITED(status)){
271
 
      fprintf_plus(stderr, "exec_and_wait exited: %d\n",
272
 
                   WEXITSTATUS(status));
273
 
    } else if(WIFSIGNALED(status)) {
274
 
      fprintf_plus(stderr, "exec_and_wait signaled: %d\n",
275
 
                   WTERMSIG(status));
276
 
    }
277
 
  }
278
 
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
279
 
    return true;
280
 
  }
281
 
  return false;
282
 
}
283
 
 
284
 
__attribute__((nonnull))
285
 
int is_plymouth(const struct dirent *proc_entry){
286
 
  int ret;
287
 
  {
288
 
    uintmax_t proc_id;
289
 
    char *tmp;
290
 
    errno = 0;
291
 
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
292
 
 
293
 
    if(errno != 0 or *tmp != '\0'
294
 
       or proc_id != (uintmax_t)((pid_t)proc_id)){
295
 
      return 0;
296
 
    }
297
 
  }
298
 
  char exe_target[sizeof(plymouthd_path)];
299
 
  char *exe_link;
300
 
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
301
 
  if(ret == -1){
302
 
    error_plus(0, errno, "asprintf");
303
 
    return 0;
304
 
  }
305
 
  
306
 
  struct stat exe_stat;
307
 
  ret = lstat(exe_link, &exe_stat);
308
 
  if(ret == -1){
309
 
    free(exe_link);
310
 
    if(errno != ENOENT){
311
 
      error_plus(0, errno, "lstat");
312
 
    }
313
 
    return 0;
314
 
  }
315
 
  
316
 
  if(not S_ISLNK(exe_stat.st_mode)
317
 
     or exe_stat.st_uid != 0
318
 
     or exe_stat.st_gid != 0){
319
 
    free(exe_link);
320
 
    return 0;
321
 
  }
322
 
  
323
 
  ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
324
 
  free(exe_link);
325
 
  if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
326
 
      (memcmp(plymouthd_path, exe_target,
327
 
              sizeof(plymouthd_path)-1) != 0)){
328
 
    return 0;
329
 
  }
330
 
  return 1;
331
 
}
332
 
 
333
 
pid_t get_pid(void){
334
 
  int ret;
335
 
  uintmax_t proc_id = 0;
336
 
  FILE *pidfile = fopen(plymouth_pid, "r");
337
 
  /* Try the new pid file location */
338
 
  if(pidfile != NULL){
339
 
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
340
 
    if(ret != 1){
341
 
      proc_id = 0;
342
 
    }
343
 
    fclose(pidfile);
344
 
  }
345
 
  /* Try the old pid file location */
346
 
  if(proc_id == 0){
347
 
    pidfile = fopen(plymouth_old_pid, "r");
348
 
    if(pidfile != NULL){
349
 
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
350
 
      if(ret != 1){
351
 
        proc_id = 0;
352
 
      }
353
 
      fclose(pidfile);
354
 
    }
355
 
  }
356
 
  /* Try the old old pid file location */
357
 
  if(proc_id == 0){
358
 
    pidfile = fopen(plymouth_old_old_pid, "r");
359
 
    if(pidfile != NULL){
360
 
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
361
 
      if(ret != 1){
362
 
        proc_id = 0;
363
 
      }
364
 
      fclose(pidfile);
365
 
    }
366
 
  }
367
 
  /* Look for a plymouth process */
368
 
  if(proc_id == 0){
369
 
    struct dirent **direntries = NULL;
370
 
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
371
 
    if(ret == -1){
372
 
      error_plus(0, errno, "scandir");
373
 
    }
374
 
    if(ret > 0){
375
 
      const int num_entries = ret;
376
 
      for(int i = 0; i < num_entries; i++){
377
 
        if(proc_id == 0){
378
 
          ret = sscanf(direntries[i]->d_name, "%" SCNuMAX, &proc_id);
379
 
          if(ret < 0){
380
 
            error_plus(0, errno, "sscanf");
381
 
          }
382
 
        }
383
 
        free(direntries[i]);
384
 
      }
385
 
    }
386
 
    /* scandir might preallocate for this variable (man page unclear).
387
 
       even if ret == 0, therefore we need to free it. */
388
 
    free(direntries);
389
 
  }
390
 
  pid_t pid;
391
 
  pid = (pid_t)proc_id;
392
 
  if((uintmax_t)pid == proc_id){
393
 
    return pid;
394
 
  }
395
 
  
396
 
  return 0;
397
 
}
398
 
 
399
 
char **getargv(pid_t pid){
400
 
  int cl_fd;
401
 
  char *cmdline_filename;
402
 
  ssize_t sret;
403
 
  int ret;
404
 
  
405
 
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
406
 
                 (uintmax_t)pid);
407
 
  if(ret == -1){
408
 
    error_plus(0, errno, "asprintf");
409
 
    return NULL;
410
 
  }
411
 
  
412
 
  /* Open /proc/<pid>/cmdline  */
413
 
  cl_fd = open(cmdline_filename, O_RDONLY);
414
 
  free(cmdline_filename);
415
 
  if(cl_fd == -1){
416
 
    error_plus(0, errno, "open");
417
 
    return NULL;
418
 
  }
419
 
  
420
 
  size_t cmdline_allocated = 0;
421
 
  size_t cmdline_len = 0;
422
 
  char *cmdline = NULL;
423
 
  char *tmp;
424
 
  const size_t blocksize = 1024;
425
 
  do {
426
 
    /* Allocate more space? */
427
 
    if(cmdline_len + blocksize > cmdline_allocated){
428
 
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
429
 
      if(tmp == NULL){
430
 
        error_plus(0, errno, "realloc");
431
 
        free(cmdline);
432
 
        close(cl_fd);
433
 
        return NULL;
434
 
      }
435
 
      cmdline = tmp;
436
 
      cmdline_allocated += blocksize;
437
 
    }
438
 
    
439
 
    /* Read data */
440
 
    sret = read(cl_fd, cmdline + cmdline_len,
441
 
                cmdline_allocated - cmdline_len);
442
 
    if(sret == -1){
443
 
      error_plus(0, errno, "read");
444
 
      free(cmdline);
445
 
      close(cl_fd);
446
 
      return NULL;
447
 
    }
448
 
    cmdline_len += (size_t)sret;
449
 
  } while(sret != 0);
450
 
  ret = close(cl_fd);
451
 
  if(ret == -1){
452
 
    error_plus(0, errno, "close");
453
 
    free(cmdline);
454
 
    return NULL;
455
 
  }
456
 
  
457
 
  /* we got cmdline and cmdline_len, ignore rest... */
458
 
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
459
 
                       * sizeof(char *)); /* Get number of args */
460
 
  if(argv == NULL){
461
 
    error_plus(0, errno, "argv = malloc()");
462
 
    free(cmdline);
463
 
    return NULL;
464
 
  }
465
 
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
466
 
  return argv;
467
 
}
468
 
 
469
 
int main(__attribute__((unused))int argc,
470
 
         __attribute__((unused))char **argv){
471
 
  char *prompt = NULL;
472
 
  char *prompt_arg;
473
 
  pid_t plymouth_command_pid;
474
 
  int ret;
475
 
  bool bret;
476
 
 
477
 
  {
478
 
    struct argp_option options[] = {
479
 
      { .name = "prompt", .key = 128, .arg = "PROMPT",
480
 
        .doc = "The prompt to show" },
481
 
      { .name = "debug", .key = 129,
482
 
        .doc = "Debug mode" },
483
 
      { .name = NULL }
484
 
    };
485
 
    
486
 
    __attribute__((nonnull(3)))
487
 
    error_t parse_opt (int key, char *arg, __attribute__((unused))
488
 
                       struct argp_state *state){
489
 
      errno = 0;
490
 
      switch (key){
491
 
      case 128:                 /* --prompt */
492
 
        prompt = arg;
493
 
        if(debug){
494
 
          fprintf_plus(stderr, "Custom prompt \"%s\"\n", prompt);
495
 
        }
496
 
        break;
497
 
      case 129:                 /* --debug */
498
 
        debug = true;
499
 
        break;
500
 
      default:
501
 
        return ARGP_ERR_UNKNOWN;
502
 
      }
503
 
      return errno;
504
 
    }
505
 
    
506
 
    struct argp argp = { .options = options, .parser = parse_opt,
507
 
                         .args_doc = "",
508
 
                         .doc = "Mandos plymouth -- Read and"
509
 
                         " output a password" };
510
 
    ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, NULL);
511
 
    switch(ret){
512
 
    case 0:
513
 
      break;
514
 
    case ENOMEM:
515
 
    default:
516
 
      errno = ret;
517
 
      error_plus(0, errno, "argp_parse");
518
 
      return EX_OSERR;
519
 
    case EINVAL:
520
 
      error_plus(0, errno, "argp_parse");
521
 
      return EX_USAGE;
522
 
    }
523
 
  }
524
 
  
525
 
  /* test -x /bin/plymouth */
526
 
  ret = access(plymouth_path, X_OK);
527
 
  if(ret == -1){
528
 
    /* Plymouth is probably not installed.  Don't print an error
529
 
       message, just exit. */
530
 
    if(debug){
531
 
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
532
 
                   plymouth_path);
533
 
    }
534
 
    exit(EX_UNAVAILABLE);
535
 
  }
536
 
  
537
 
  { /* Add signal handlers */
538
 
    struct sigaction old_action,
539
 
      new_action = { .sa_handler = termination_handler,
540
 
                     .sa_flags = 0 };
541
 
    sigemptyset(&new_action.sa_mask);
542
 
    for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 };
543
 
        *sig != 0; sig++){
544
 
      ret = sigaddset(&new_action.sa_mask, *sig);
545
 
      if(ret == -1){
546
 
        error_plus(EX_OSERR, errno, "sigaddset");
547
 
      }
548
 
      ret = sigaction(*sig, NULL, &old_action);
549
 
      if(ret == -1){
550
 
        error_plus(EX_OSERR, errno, "sigaction");
551
 
      }
552
 
      if(old_action.sa_handler != SIG_IGN){
553
 
        ret = sigaction(*sig, &new_action, NULL);
554
 
        if(ret == -1){
555
 
          error_plus(EX_OSERR, errno, "sigaction");
556
 
        }
557
 
      }
558
 
    }
559
 
  }
560
 
  
561
 
  /* plymouth --ping */
562
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
563
 
                       (const char *[])
564
 
                       { plymouth_path, "--ping", NULL },
565
 
                       true, false);
566
 
  if(not bret){
567
 
    if(interrupted_by_signal){
568
 
      kill_and_wait(plymouth_command_pid);
569
 
      exit(EXIT_FAILURE);
570
 
    }
571
 
    /* Plymouth is probably not running.  Don't print an error
572
 
       message, just exit. */
573
 
    if(debug){
574
 
      fprintf_plus(stderr, "Plymouth not running\n");
575
 
    }
576
 
    exit(EX_UNAVAILABLE);
577
 
  }
578
 
  
579
 
  if(prompt != NULL){
580
 
    ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
581
 
  } else {
582
 
    char *made_prompt = makeprompt();
583
 
    ret = asprintf(&prompt_arg, "--prompt=%s", made_prompt);
584
 
    free(made_prompt);
585
 
  }
586
 
  if(ret == -1){
587
 
    error_plus(EX_OSERR, errno, "asprintf");
588
 
  }
589
 
  
590
 
  /* plymouth ask-for-password --prompt="$prompt" */
591
 
  if(debug){
592
 
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
593
 
  }
594
 
  bret = exec_and_wait(&plymouth_command_pid,
595
 
                       plymouth_path, (const char *[])
596
 
                       { plymouth_path, "ask-for-password",
597
 
                           prompt_arg, NULL },
598
 
                       true, false);
599
 
  free(prompt_arg);
600
 
  if(bret){
601
 
    exit(EXIT_SUCCESS);
602
 
  }
603
 
  if(not interrupted_by_signal){
604
 
    /* exec_and_wait failed for some other reason */
605
 
    exit(EXIT_FAILURE);
606
 
  }
607
 
  kill_and_wait(plymouth_command_pid);
608
 
  
609
 
  char **plymouthd_argv = NULL;
610
 
  pid_t pid = get_pid();
611
 
  if(pid == 0){
612
 
    error_plus(0, 0, "plymouthd pid not found");
613
 
  } else {
614
 
    plymouthd_argv = getargv(pid);
615
 
  }
616
 
  
617
 
  bret = exec_and_wait(NULL, plymouth_path, (const char *[])
618
 
                       { plymouth_path, "quit", NULL },
619
 
                       false, false);
620
 
  if(not bret){
621
 
    if(plymouthd_argv != NULL){
622
 
      free(*plymouthd_argv);
623
 
      free(plymouthd_argv);
624
 
    }
625
 
    exit(EXIT_FAILURE);
626
 
  }
627
 
  bret = exec_and_wait(NULL, plymouthd_path,
628
 
                       (plymouthd_argv != NULL)
629
 
                       ? (const char * const *)plymouthd_argv
630
 
                       : plymouthd_default_argv,
631
 
                       false, true);
632
 
  if(plymouthd_argv != NULL){
633
 
    free(*plymouthd_argv);
634
 
    free(plymouthd_argv);
635
 
  }
636
 
  if(not bret){
637
 
    exit(EXIT_FAILURE);
638
 
  }
639
 
  exec_and_wait(NULL, plymouth_path, (const char *[])
640
 
                { plymouth_path, "show-splash", NULL },
641
 
                false, false);
642
 
  exit(EXIT_FAILURE);
643
 
}