/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 at recompile
  • Date: 2020-04-05 21:30:59 UTC
  • Revision ID: teddy@recompile.se-20200405213059-fb2a61ckqynrmatk
Fix file descriptor leak in mandos-client

When the local network has Mandos servers announcing themselves using
real, globally reachable, IPv6 addresses (i.e. not link-local
addresses), but there is no router on the local network providing IPv6
RA (Router Advertisement) packets, the client cannot reach the server
by normal means, since the client only has a link-local IPv6 address,
and has no usable route to reach the server's global IPv6 address.
(This is not a common situation, and usually only happens when the
router itself reboots and runs a Mandos client, since it cannot then
give RA packets to itself.)  The client code has a solution for
this, which consists of adding a temporary local route to reach the
address of the server during communication, and removing this
temporary route afterwards.

This solution with a temporary route works, but has a file descriptor
leak; it leaks one file descriptor for each addition and for each
removal of a route.  If one server requiring an added route is present
on the network, but no servers gives a password, making the client
retry after the default ten seconds, and we furthermore assume a
default 1024 open files limit, the client runs out of file descriptors
after about 90 minutes, after which time the client process will be
useless and fail to retrieve any passwords, necessitating manual
password entry via the keyboard.

Fix this by eliminating the file descriptor leak in the client.

* plugins.d/mandos-client.c (add_delete_local_route): Do
  close(devnull) also in parent process, also if fork() fails, and on
  any failure in child process.

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