/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: 2021-02-03 23:10:42 UTC
  • Revision ID: teddy@recompile.se-20210203231042-2z3egrvpo1zt7nej
mandos-ctl: Fix bad test for command.Remove and related minor issues

The test for command.Remove removes all clients from the spy server,
and then loops over all clients, looking for the corresponding Remove
command as recorded by the spy server.  But since since there aren't
any clients left after they were removed, no assertions are made, and
the test therefore does nothing.  Fix this.

In tests for command.Approve and command.Deny, add checks that clients
were not somehow removed by the command (in which case, likewise, no
assertions are made).

Add related checks to TestPropertySetterCmd.runTest; i.e. test that a
sequence is not empty before looping over it and making assertions.

* mandos-ctl (TestBaseCommands.test_Remove): Save a copy of the
  original "clients" dict, and loop over those instead.  Add assertion
  that all clients were indeed removed.  Also fix the code which looks
  for the Remove command, which now needs to actually work.
  (TestBaseCommands.test_Approve, TestBaseCommands.test_Deny): Add
  assertion that there are still clients before looping over them.
  (TestPropertySetterCmd.runTest): Add assertion that the list of
  values to get is not empty before looping over them.  Also add check
  that there are still clients before looping over clients.

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-2021 Teddy Hogeborn
 
6
 * Copyright © 2010-2021 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
}