/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
 
      for(int i = ret-1; i >= 0; i--){
376
 
        if(proc_id == 0){
377
 
          ret = sscanf(direntries[i]->d_name, "%" SCNuMAX, &proc_id);
378
 
          if(ret < 0){
379
 
            error_plus(0, errno, "sscanf");
380
 
          }
381
 
        }
382
 
        free(direntries[i]);
383
 
      }
384
 
    }
385
 
    /* scandir might preallocate for this variable (man page unclear).
386
 
       even if ret == 0, therefore we need to free it. */
387
 
    free(direntries);
388
 
  }
389
 
  pid_t pid;
390
 
  pid = (pid_t)proc_id;
391
 
  if((uintmax_t)pid == proc_id){
392
 
    return pid;
393
 
  }
394
 
  
395
 
  return 0;
396
 
}
397
 
 
398
 
char **getargv(pid_t pid){
399
 
  int cl_fd;
400
 
  char *cmdline_filename;
401
 
  ssize_t sret;
402
 
  int ret;
403
 
  
404
 
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
405
 
                 (uintmax_t)pid);
406
 
  if(ret == -1){
407
 
    error_plus(0, errno, "asprintf");
408
 
    return NULL;
409
 
  }
410
 
  
411
 
  /* Open /proc/<pid>/cmdline  */
412
 
  cl_fd = open(cmdline_filename, O_RDONLY);
413
 
  free(cmdline_filename);
414
 
  if(cl_fd == -1){
415
 
    error_plus(0, errno, "open");
416
 
    return NULL;
417
 
  }
418
 
  
419
 
  size_t cmdline_allocated = 0;
420
 
  size_t cmdline_len = 0;
421
 
  char *cmdline = NULL;
422
 
  char *tmp;
423
 
  const size_t blocksize = 1024;
424
 
  do {
425
 
    /* Allocate more space? */
426
 
    if(cmdline_len + blocksize > cmdline_allocated){
427
 
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
428
 
      if(tmp == NULL){
429
 
        error_plus(0, errno, "realloc");
430
 
        free(cmdline);
431
 
        close(cl_fd);
432
 
        return NULL;
433
 
      }
434
 
      cmdline = tmp;
435
 
      cmdline_allocated += blocksize;
436
 
    }
437
 
    
438
 
    /* Read data */
439
 
    sret = read(cl_fd, cmdline + cmdline_len,
440
 
                cmdline_allocated - cmdline_len);
441
 
    if(sret == -1){
442
 
      error_plus(0, errno, "read");
443
 
      free(cmdline);
444
 
      close(cl_fd);
445
 
      return NULL;
446
 
    }
447
 
    cmdline_len += (size_t)sret;
448
 
  } while(sret != 0);
449
 
  ret = close(cl_fd);
450
 
  if(ret == -1){
451
 
    error_plus(0, errno, "close");
452
 
    free(cmdline);
453
 
    return NULL;
454
 
  }
455
 
  
456
 
  /* we got cmdline and cmdline_len, ignore rest... */
457
 
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
458
 
                       * sizeof(char *)); /* Get number of args */
459
 
  if(argv == NULL){
460
 
    error_plus(0, errno, "argv = malloc()");
461
 
    free(cmdline);
462
 
    return NULL;
463
 
  }
464
 
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
465
 
  return argv;
466
 
}
467
 
 
468
 
int main(__attribute__((unused))int argc,
469
 
         __attribute__((unused))char **argv){
470
 
  char *prompt = NULL;
471
 
  char *prompt_arg;
472
 
  pid_t plymouth_command_pid;
473
 
  int ret;
474
 
  bool bret;
475
 
 
476
 
  {
477
 
    struct argp_option options[] = {
478
 
      { .name = "prompt", .key = 128, .arg = "PROMPT",
479
 
        .doc = "The prompt to show" },
480
 
      { .name = "debug", .key = 129,
481
 
        .doc = "Debug mode" },
482
 
      { .name = NULL }
483
 
    };
484
 
    
485
 
    __attribute__((nonnull(3)))
486
 
    error_t parse_opt (int key, char *arg, __attribute__((unused))
487
 
                       struct argp_state *state){
488
 
      errno = 0;
489
 
      switch (key){
490
 
      case 128:                 /* --prompt */
491
 
        prompt = arg;
492
 
        if(debug){
493
 
          fprintf_plus(stderr, "Custom prompt \"%s\"\n", prompt);
494
 
        }
495
 
        break;
496
 
      case 129:                 /* --debug */
497
 
        debug = true;
498
 
        break;
499
 
      default:
500
 
        return ARGP_ERR_UNKNOWN;
501
 
      }
502
 
      return errno;
503
 
    }
504
 
    
505
 
    struct argp argp = { .options = options, .parser = parse_opt,
506
 
                         .args_doc = "",
507
 
                         .doc = "Mandos plymouth -- Read and"
508
 
                         " output a password" };
509
 
    ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, NULL);
510
 
    switch(ret){
511
 
    case 0:
512
 
      break;
513
 
    case ENOMEM:
514
 
    default:
515
 
      errno = ret;
516
 
      error_plus(0, errno, "argp_parse");
517
 
      return EX_OSERR;
518
 
    case EINVAL:
519
 
      error_plus(0, errno, "argp_parse");
520
 
      return EX_USAGE;
521
 
    }
522
 
  }
523
 
  
524
 
  /* test -x /bin/plymouth */
525
 
  ret = access(plymouth_path, X_OK);
526
 
  if(ret == -1){
527
 
    /* Plymouth is probably not installed.  Don't print an error
528
 
       message, just exit. */
529
 
    if(debug){
530
 
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
531
 
                   plymouth_path);
532
 
    }
533
 
    exit(EX_UNAVAILABLE);
534
 
  }
535
 
  
536
 
  { /* Add signal handlers */
537
 
    struct sigaction old_action,
538
 
      new_action = { .sa_handler = termination_handler,
539
 
                     .sa_flags = 0 };
540
 
    sigemptyset(&new_action.sa_mask);
541
 
    for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 };
542
 
        *sig != 0; sig++){
543
 
      ret = sigaddset(&new_action.sa_mask, *sig);
544
 
      if(ret == -1){
545
 
        error_plus(EX_OSERR, errno, "sigaddset");
546
 
      }
547
 
      ret = sigaction(*sig, NULL, &old_action);
548
 
      if(ret == -1){
549
 
        error_plus(EX_OSERR, errno, "sigaction");
550
 
      }
551
 
      if(old_action.sa_handler != SIG_IGN){
552
 
        ret = sigaction(*sig, &new_action, NULL);
553
 
        if(ret == -1){
554
 
          error_plus(EX_OSERR, errno, "sigaction");
555
 
        }
556
 
      }
557
 
    }
558
 
  }
559
 
  
560
 
  /* plymouth --ping */
561
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
562
 
                       (const char *[])
563
 
                       { plymouth_path, "--ping", NULL },
564
 
                       true, false);
565
 
  if(not bret){
566
 
    if(interrupted_by_signal){
567
 
      kill_and_wait(plymouth_command_pid);
568
 
      exit(EXIT_FAILURE);
569
 
    }
570
 
    /* Plymouth is probably not running.  Don't print an error
571
 
       message, just exit. */
572
 
    if(debug){
573
 
      fprintf_plus(stderr, "Plymouth not running\n");
574
 
    }
575
 
    exit(EX_UNAVAILABLE);
576
 
  }
577
 
  
578
 
  if(prompt != NULL){
579
 
    ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
580
 
  } else {
581
 
    char *made_prompt = makeprompt();
582
 
    ret = asprintf(&prompt_arg, "--prompt=%s", made_prompt);
583
 
    free(made_prompt);
584
 
  }
585
 
  if(ret == -1){
586
 
    error_plus(EX_OSERR, errno, "asprintf");
587
 
  }
588
 
  
589
 
  /* plymouth ask-for-password --prompt="$prompt" */
590
 
  if(debug){
591
 
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
592
 
  }
593
 
  bret = exec_and_wait(&plymouth_command_pid,
594
 
                       plymouth_path, (const char *[])
595
 
                       { plymouth_path, "ask-for-password",
596
 
                           prompt_arg, NULL },
597
 
                       true, false);
598
 
  free(prompt_arg);
599
 
  if(bret){
600
 
    exit(EXIT_SUCCESS);
601
 
  }
602
 
  if(not interrupted_by_signal){
603
 
    /* exec_and_wait failed for some other reason */
604
 
    exit(EXIT_FAILURE);
605
 
  }
606
 
  kill_and_wait(plymouth_command_pid);
607
 
  
608
 
  char **plymouthd_argv = NULL;
609
 
  pid_t pid = get_pid();
610
 
  if(pid == 0){
611
 
    error_plus(0, 0, "plymouthd pid not found");
612
 
  } else {
613
 
    plymouthd_argv = getargv(pid);
614
 
  }
615
 
  
616
 
  bret = exec_and_wait(NULL, plymouth_path, (const char *[])
617
 
                       { plymouth_path, "quit", NULL },
618
 
                       false, false);
619
 
  if(not bret){
620
 
    if(plymouthd_argv != NULL){
621
 
      free(*plymouthd_argv);
622
 
      free(plymouthd_argv);
623
 
    }
624
 
    exit(EXIT_FAILURE);
625
 
  }
626
 
  bret = exec_and_wait(NULL, plymouthd_path,
627
 
                       (plymouthd_argv != NULL)
628
 
                       ? (const char * const *)plymouthd_argv
629
 
                       : plymouthd_default_argv,
630
 
                       false, true);
631
 
  if(plymouthd_argv != NULL){
632
 
    free(*plymouthd_argv);
633
 
    free(plymouthd_argv);
634
 
  }
635
 
  if(not bret){
636
 
    exit(EXIT_FAILURE);
637
 
  }
638
 
  exec_and_wait(NULL, plymouth_path, (const char *[])
639
 
                { plymouth_path, "show-splash", NULL },
640
 
                false, false);
641
 
  exit(EXIT_FAILURE);
642
 
}