/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: 2008-12-10 01:26:02 UTC
  • mfrom: (237.1.2 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20081210012602-vhz3h75xkj24t340
First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

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() */
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
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
208
 
      if(tmp == NULL){
209
 
        error_plus(0, errno, "realloc");
210
 
        free(new_argv);
211
 
        _exit(EX_OSERR);
212
 
      }
213
 
      new_argv = tmp;
214
 
      new_argv[i] = strdup(argv[i]);
215
 
    }
216
 
    new_argv[i] = NULL;
217
 
    
218
 
    execv(path, (char *const *)new_argv);
219
 
    error_plus(0, errno, "execv");
220
 
    _exit(EXIT_FAILURE);
221
 
  }
222
 
  if(pid_return != NULL){
223
 
    *pid_return = pid;
224
 
  }
225
 
  do {
226
 
    ret = waitpid(pid, &status, 0);
227
 
  } while(ret == -1 and errno == EINTR
228
 
          and ((not interrupted_by_signal)
229
 
               or (not interruptable)));
230
 
  if(interrupted_by_signal and interruptable){
231
 
    if(debug){
232
 
      fprintf_plus(stderr, "Interrupted by signal\n");
233
 
    }
234
 
    return false;
235
 
  }
236
 
  if(ret == -1){
237
 
    error_plus(0, errno, "waitpid");
238
 
    return false;
239
 
  }
240
 
  if(debug){
241
 
    if(WIFEXITED(status)){
242
 
      fprintf_plus(stderr, "exec_and_wait exited: %d\n",
243
 
                   WEXITSTATUS(status));
244
 
    } else if(WIFSIGNALED(status)) {
245
 
      fprintf_plus(stderr, "exec_and_wait signaled: %d\n",
246
 
                   WTERMSIG(status));
247
 
    }
248
 
  }
249
 
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
250
 
    return true;
251
 
  }
252
 
  return false;
253
 
}
254
 
 
255
 
__attribute__((nonnull))
256
 
int is_plymouth(const struct dirent *proc_entry){
257
 
  int ret;
258
 
  {
259
 
    uintmax_t proc_id;
260
 
    char *tmp;
261
 
    errno = 0;
262
 
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
263
 
 
264
 
    if(errno != 0 or *tmp != '\0'
265
 
       or proc_id != (uintmax_t)((pid_t)proc_id)){
266
 
      return 0;
267
 
    }
268
 
  }
269
 
  char exe_target[sizeof(plymouthd_path)];
270
 
  char *exe_link;
271
 
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
272
 
  if(ret == -1){
273
 
    error_plus(0, errno, "asprintf");
274
 
    return 0;
275
 
  }
276
 
  
277
 
  struct stat exe_stat;
278
 
  ret = lstat(exe_link, &exe_stat);
279
 
  if(ret == -1){
280
 
    free(exe_link);
281
 
    if(errno != ENOENT){
282
 
      error_plus(0, errno, "lstat");
283
 
    }
284
 
    return 0;
285
 
  }
286
 
  
287
 
  if(not S_ISLNK(exe_stat.st_mode)
288
 
     or exe_stat.st_uid != 0
289
 
     or exe_stat.st_gid != 0){
290
 
    free(exe_link);
291
 
    return 0;
292
 
  }
293
 
  
294
 
  ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
295
 
  free(exe_link);
296
 
  if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
297
 
      (memcmp(plymouthd_path, exe_target,
298
 
              sizeof(plymouthd_path)-1) != 0)){
299
 
    return 0;
300
 
  }
301
 
  return 1;
302
 
}
303
 
 
304
 
pid_t get_pid(void){
305
 
  int ret;
306
 
  uintmax_t proc_id = 0;
307
 
  FILE *pidfile = fopen(plymouth_pid, "r");
308
 
  /* Try the new pid file location */
309
 
  if(pidfile != NULL){
310
 
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
311
 
    if(ret != 1){
312
 
      proc_id = 0;
313
 
    }
314
 
    fclose(pidfile);
315
 
  }
316
 
  /* Try the old pid file location */
317
 
  if(proc_id == 0){
318
 
    pidfile = fopen(plymouth_old_pid, "r");
319
 
    if(pidfile != NULL){
320
 
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
321
 
      if(ret != 1){
322
 
        proc_id = 0;
323
 
      }
324
 
      fclose(pidfile);
325
 
    }
326
 
  }
327
 
  /* Try the old old pid file location */
328
 
  if(proc_id == 0){
329
 
    pidfile = fopen(plymouth_old_old_pid, "r");
330
 
    if(pidfile != NULL){
331
 
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
332
 
      if(ret != 1){
333
 
        proc_id = 0;
334
 
      }
335
 
      fclose(pidfile);
336
 
    }
337
 
  }
338
 
  /* Look for a plymouth process */
339
 
  if(proc_id == 0){
340
 
    struct dirent **direntries = NULL;
341
 
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
342
 
    if(ret == -1){
343
 
      error_plus(0, errno, "scandir");
344
 
    }
345
 
    if(ret > 0){
346
 
      for(int i = ret-1; i >= 0; i--){
347
 
        if(proc_id == 0){
348
 
          ret = sscanf(direntries[i]->d_name, "%" SCNuMAX, &proc_id);
349
 
          if(ret < 0){
350
 
            error_plus(0, errno, "sscanf");
351
 
          }
352
 
        }
353
 
        free(direntries[i]);
354
 
      }
355
 
    }
356
 
    /* scandir might preallocate for this variable (man page unclear).
357
 
       even if ret == 0, therefore we need to free it. */
358
 
    free(direntries);
359
 
  }
360
 
  pid_t pid;
361
 
  pid = (pid_t)proc_id;
362
 
  if((uintmax_t)pid == proc_id){
363
 
    return pid;
364
 
  }
365
 
  
366
 
  return 0;
367
 
}
368
 
 
369
 
char **getargv(pid_t pid){
370
 
  int cl_fd;
371
 
  char *cmdline_filename;
372
 
  ssize_t sret;
373
 
  int ret;
374
 
  
375
 
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
376
 
                 (uintmax_t)pid);
377
 
  if(ret == -1){
378
 
    error_plus(0, errno, "asprintf");
379
 
    return NULL;
380
 
  }
381
 
  
382
 
  /* Open /proc/<pid>/cmdline  */
383
 
  cl_fd = open(cmdline_filename, O_RDONLY);
384
 
  free(cmdline_filename);
385
 
  if(cl_fd == -1){
386
 
    error_plus(0, errno, "open");
387
 
    return NULL;
388
 
  }
389
 
  
390
 
  size_t cmdline_allocated = 0;
391
 
  size_t cmdline_len = 0;
392
 
  char *cmdline = NULL;
393
 
  char *tmp;
394
 
  const size_t blocksize = 1024;
395
 
  do {
396
 
    /* Allocate more space? */
397
 
    if(cmdline_len + blocksize > cmdline_allocated){
398
 
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
399
 
      if(tmp == NULL){
400
 
        error_plus(0, errno, "realloc");
401
 
        free(cmdline);
402
 
        close(cl_fd);
403
 
        return NULL;
404
 
      }
405
 
      cmdline = tmp;
406
 
      cmdline_allocated += blocksize;
407
 
    }
408
 
    
409
 
    /* Read data */
410
 
    sret = read(cl_fd, cmdline + cmdline_len,
411
 
                cmdline_allocated - cmdline_len);
412
 
    if(sret == -1){
413
 
      error_plus(0, errno, "read");
414
 
      free(cmdline);
415
 
      close(cl_fd);
416
 
      return NULL;
417
 
    }
418
 
    cmdline_len += (size_t)sret;
419
 
  } while(sret != 0);
420
 
  ret = close(cl_fd);
421
 
  if(ret == -1){
422
 
    error_plus(0, errno, "close");
423
 
    free(cmdline);
424
 
    return NULL;
425
 
  }
426
 
  
427
 
  /* we got cmdline and cmdline_len, ignore rest... */
428
 
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
429
 
                       * sizeof(char *)); /* Get number of args */
430
 
  if(argv == NULL){
431
 
    error_plus(0, errno, "argv = malloc()");
432
 
    free(cmdline);
433
 
    return NULL;
434
 
  }
435
 
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
436
 
  return argv;
437
 
}
438
 
 
439
 
int main(__attribute__((unused))int argc,
440
 
         __attribute__((unused))char **argv){
441
 
  char *prompt = NULL;
442
 
  char *prompt_arg;
443
 
  pid_t plymouth_command_pid;
444
 
  int ret;
445
 
  bool bret;
446
 
 
447
 
  {
448
 
    struct argp_option options[] = {
449
 
      { .name = "prompt", .key = 128, .arg = "PROMPT",
450
 
        .doc = "The prompt to show" },
451
 
      { .name = "debug", .key = 129,
452
 
        .doc = "Debug mode" },
453
 
      { .name = NULL }
454
 
    };
455
 
    
456
 
    __attribute__((nonnull(3)))
457
 
    error_t parse_opt (int key, char *arg, __attribute__((unused))
458
 
                       struct argp_state *state){
459
 
      errno = 0;
460
 
      switch (key){
461
 
      case 128:                 /* --prompt */
462
 
        prompt = arg;
463
 
        if(debug){
464
 
          fprintf_plus(stderr, "Custom prompt \"%s\"\n", prompt);
465
 
        }
466
 
        break;
467
 
      case 129:                 /* --debug */
468
 
        debug = true;
469
 
        break;
470
 
      default:
471
 
        return ARGP_ERR_UNKNOWN;
472
 
      }
473
 
      return errno;
474
 
    }
475
 
    
476
 
    struct argp argp = { .options = options, .parser = parse_opt,
477
 
                         .args_doc = "",
478
 
                         .doc = "Mandos plymouth -- Read and"
479
 
                         " output a password" };
480
 
    ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, NULL);
481
 
    switch(ret){
482
 
    case 0:
483
 
      break;
484
 
    case ENOMEM:
485
 
    default:
486
 
      errno = ret;
487
 
      error_plus(0, errno, "argp_parse");
488
 
      return EX_OSERR;
489
 
    case EINVAL:
490
 
      error_plus(0, errno, "argp_parse");
491
 
      return EX_USAGE;
492
 
    }
493
 
  }
494
 
  
495
 
  /* test -x /bin/plymouth */
496
 
  ret = access(plymouth_path, X_OK);
497
 
  if(ret == -1){
498
 
    /* Plymouth is probably not installed.  Don't print an error
499
 
       message, just exit. */
500
 
    if(debug){
501
 
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
502
 
                   plymouth_path);
503
 
    }
504
 
    exit(EX_UNAVAILABLE);
505
 
  }
506
 
  
507
 
  { /* Add signal handlers */
508
 
    struct sigaction old_action,
509
 
      new_action = { .sa_handler = termination_handler,
510
 
                     .sa_flags = 0 };
511
 
    sigemptyset(&new_action.sa_mask);
512
 
    for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 };
513
 
        *sig != 0; sig++){
514
 
      ret = sigaddset(&new_action.sa_mask, *sig);
515
 
      if(ret == -1){
516
 
        error_plus(EX_OSERR, errno, "sigaddset");
517
 
      }
518
 
      ret = sigaction(*sig, NULL, &old_action);
519
 
      if(ret == -1){
520
 
        error_plus(EX_OSERR, errno, "sigaction");
521
 
      }
522
 
      if(old_action.sa_handler != SIG_IGN){
523
 
        ret = sigaction(*sig, &new_action, NULL);
524
 
        if(ret == -1){
525
 
          error_plus(EX_OSERR, errno, "sigaction");
526
 
        }
527
 
      }
528
 
    }
529
 
  }
530
 
  
531
 
  /* plymouth --ping */
532
 
  bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
533
 
                       (const char *[])
534
 
                       { plymouth_path, "--ping", NULL },
535
 
                       true, false);
536
 
  if(not bret){
537
 
    if(interrupted_by_signal){
538
 
      kill_and_wait(plymouth_command_pid);
539
 
      exit(EXIT_FAILURE);
540
 
    }
541
 
    /* Plymouth is probably not running.  Don't print an error
542
 
       message, just exit. */
543
 
    if(debug){
544
 
      fprintf_plus(stderr, "Plymouth not running\n");
545
 
    }
546
 
    exit(EX_UNAVAILABLE);
547
 
  }
548
 
  
549
 
  if(prompt != NULL){
550
 
    ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
551
 
  } else {
552
 
    char *made_prompt = makeprompt();
553
 
    ret = asprintf(&prompt_arg, "--prompt=%s", made_prompt);
554
 
    free(made_prompt);
555
 
  }
556
 
  if(ret == -1){
557
 
    error_plus(EX_OSERR, errno, "asprintf");
558
 
  }
559
 
  
560
 
  /* plymouth ask-for-password --prompt="$prompt" */
561
 
  if(debug){
562
 
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
563
 
  }
564
 
  bret = exec_and_wait(&plymouth_command_pid,
565
 
                       plymouth_path, (const char *[])
566
 
                       { plymouth_path, "ask-for-password",
567
 
                           prompt_arg, NULL },
568
 
                       true, false);
569
 
  free(prompt_arg);
570
 
  if(bret){
571
 
    exit(EXIT_SUCCESS);
572
 
  }
573
 
  if(not interrupted_by_signal){
574
 
    /* exec_and_wait failed for some other reason */
575
 
    exit(EXIT_FAILURE);
576
 
  }
577
 
  kill_and_wait(plymouth_command_pid);
578
 
  
579
 
  char **plymouthd_argv = NULL;
580
 
  pid_t pid = get_pid();
581
 
  if(pid == 0){
582
 
    error_plus(0, 0, "plymouthd pid not found");
583
 
  } else {
584
 
    plymouthd_argv = getargv(pid);
585
 
  }
586
 
  
587
 
  bret = exec_and_wait(NULL, plymouth_path, (const char *[])
588
 
                       { plymouth_path, "quit", NULL },
589
 
                       false, false);
590
 
  if(not bret){
591
 
    if(plymouthd_argv != NULL){
592
 
      free(*plymouthd_argv);
593
 
      free(plymouthd_argv);
594
 
    }
595
 
    exit(EXIT_FAILURE);
596
 
  }
597
 
  bret = exec_and_wait(NULL, plymouthd_path,
598
 
                       (plymouthd_argv != NULL)
599
 
                       ? (const char * const *)plymouthd_argv
600
 
                       : plymouthd_default_argv,
601
 
                       false, true);
602
 
  if(plymouthd_argv != NULL){
603
 
    free(*plymouthd_argv);
604
 
    free(plymouthd_argv);
605
 
  }
606
 
  if(not bret){
607
 
    exit(EXIT_FAILURE);
608
 
  }
609
 
  exec_and_wait(NULL, plymouth_path, (const char *[])
610
 
                { plymouth_path, "show-splash", NULL },
611
 
                false, false);
612
 
  exit(EXIT_FAILURE);
613
 
}