/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
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
 
 * Usplash - Read a password from usplash and output it
4
 
 * 
5
 
 * Copyright © 2010 Teddy Hogeborn
6
 
 * Copyright © 2010 Björn Påhlsson
7
 
 * 
8
 
 * This program is free software: you can redistribute it and/or
9
 
 * modify it under the terms of the GNU General Public License as
10
 
 * published by the Free Software Foundation, either version 3 of the
11
 
 * License, or (at your option) any later version.
12
 
 * 
13
 
 * This program is distributed in the hope that it will be useful, but
 
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
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
24
25
 
25
 
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
26
 
#include <signal.h>             /* sig_atomic_t, struct sigaction,
27
 
                                   sigemptyset(), sigaddset(), SIGINT,
28
 
                                   SIGHUP, SIGTERM, sigaction(),
29
 
                                   kill(), SIG_IGN */
 
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 */
30
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() */
31
73
#include <fcntl.h>              /* open(), O_RDONLY */
32
 
#include <iso646.h>             /* and, or, not*/
33
 
#include <sys/types.h>          /* size_t, ssize_t, pid_t, struct
34
 
                                   dirent, waitpid() */
35
 
#include <sys/wait.h>           /* waitpid() */
36
 
#include <stddef.h>             /* NULL */
37
 
#include <string.h>             /* strchr(), memcmp() */
38
 
#include <stdio.h>              /* asprintf(), perror(), fopen(),
39
 
                                   fscanf() */
40
 
#include <unistd.h>             /* close(), readlink(), read(),
41
 
                                   fork(), setsid(), chdir(), dup2(),
42
 
                                   STDERR_FILENO, execv(), access() */
43
 
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
44
 
                                   EXIT_SUCCESS, malloc(), _exit(),
45
 
                                   getenv() */
46
 
#include <dirent.h>             /* scandir(), alphasort() */
47
 
#include <inttypes.h>           /* intmax_t, strtoumax(), SCNuMAX */
48
 
#include <sys/stat.h>           /* struct stat, lstat() */
49
 
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
50
 
#include <error.h>              /* error() */
51
 
#include <errno.h>              /* TEMP_FAILURE_RETRY */
52
74
#include <argz.h>               /* argz_count(), argz_extract() */
53
75
 
54
76
sig_atomic_t interrupted_by_signal = 0;
55
 
const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid";
 
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
 
56
87
const char plymouth_path[] = "/bin/plymouth";
57
88
const char plymouthd_path[] = "/sbin/plymouthd";
58
89
const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
59
90
                                        "--mode=boot",
60
91
                                        "--attach-to-session",
61
 
                                        "--pid-file="
62
 
                                        "/dev/.initramfs/"
63
 
                                        "plymouth.pid",
64
92
                                        NULL };
 
93
bool debug = false;
65
94
 
66
95
static void termination_handler(__attribute__((unused))int signum){
67
96
  if(interrupted_by_signal){
70
99
  interrupted_by_signal = 1;
71
100
}
72
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
 
73
134
/* Create prompt string */
74
135
char *makeprompt(void){
75
136
  int ret = 0;
76
137
  char *prompt;
77
138
  const char *const cryptsource = getenv("cryptsource");
78
139
  const char *const crypttarget = getenv("crypttarget");
79
 
  const char prompt_start[] = "Enter passphrase to unlock the disk";
 
140
  const char prompt_start[] = "Unlocking the disk";
 
141
  const char prompt_end[] = "Enter passphrase";
80
142
  
81
143
  if(cryptsource == NULL){
82
144
    if(crypttarget == NULL){
83
 
      ret = asprintf(&prompt, "%s: ", prompt_start);
 
145
      ret = asprintf(&prompt, "%s\n%s", prompt_start, prompt_end);
84
146
    } else {
85
 
      ret = asprintf(&prompt, "%s (%s): ", prompt_start,
86
 
                     crypttarget);
 
147
      ret = asprintf(&prompt, "%s (%s)\n%s", prompt_start,
 
148
                     crypttarget, prompt_end);
87
149
    }
88
150
  } else {
89
151
    if(crypttarget == NULL){
90
 
      ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
 
152
      ret = asprintf(&prompt, "%s %s\n%s", prompt_start, cryptsource,
 
153
                     prompt_end);
91
154
    } else {
92
 
      ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
93
 
                     cryptsource, crypttarget);
 
155
      ret = asprintf(&prompt, "%s %s (%s)\n%s", prompt_start,
 
156
                     cryptsource, crypttarget, prompt_end);
94
157
    }
95
158
  }
96
159
  if(ret == -1){
107
170
bool become_a_daemon(void){
108
171
  int ret = setuid(geteuid());
109
172
  if(ret == -1){
110
 
    error(0, errno, "setuid");
 
173
    error_plus(0, errno, "setuid");
111
174
  }
112
175
    
113
176
  setsid();
114
177
  ret = chdir("/");
115
178
  if(ret == -1){
116
 
    error(0, errno, "chdir");
 
179
    error_plus(0, errno, "chdir");
117
180
    return false;
118
181
  }
119
182
  ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
120
183
  if(ret == -1){
121
 
    error(0, errno, "dup2");
 
184
    error_plus(0, errno, "dup2");
122
185
    return false;
123
186
  }
124
187
  return true;
125
188
}
126
189
 
 
190
__attribute__((nonnull (2, 3)))
127
191
bool exec_and_wait(pid_t *pid_return, const char *path,
128
 
                   const char **argv, bool interruptable,
 
192
                   const char * const * const argv, bool interruptable,
129
193
                   bool daemonize){
130
194
  int status;
131
195
  int ret;
132
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
 
133
204
  pid = fork();
134
205
  if(pid == -1){
135
 
    error(0, errno, "fork");
 
206
    error_plus(0, errno, "fork");
136
207
    return false;
137
208
  }
138
209
  if(pid == 0){
142
213
        _exit(EX_OSERR);
143
214
      }
144
215
    }
145
 
 
146
 
    char **new_argv = NULL;
147
 
    char *tmp;
 
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;
148
223
    int i = 0;
149
 
    for (; argv[i]!=(char *)NULL; i++){
150
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
151
 
      if (tmp == NULL){
152
 
        error(0, errno, "realloc");
 
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");
153
239
        free(new_argv);
154
240
        _exit(EX_OSERR);
155
241
      }
156
 
      new_argv = (char **)tmp;
 
242
      new_argv = tmp;
157
243
      new_argv[i] = strdup(argv[i]);
158
244
    }
159
 
    new_argv[i] = (char *) NULL;
 
245
    new_argv[i] = NULL;
160
246
    
161
247
    execv(path, (char *const *)new_argv);
162
 
    error(0, errno, "execv");
 
248
    error_plus(0, errno, "execv");
163
249
    _exit(EXIT_FAILURE);
164
250
  }
165
251
  if(pid_return != NULL){
171
257
          and ((not interrupted_by_signal)
172
258
               or (not interruptable)));
173
259
  if(interrupted_by_signal and interruptable){
 
260
    if(debug){
 
261
      fprintf_plus(stderr, "Interrupted by signal\n");
 
262
    }
174
263
    return false;
175
264
  }
176
265
  if(ret == -1){
177
 
    error(0, errno, "waitpid");
 
266
    error_plus(0, errno, "waitpid");
178
267
    return false;
179
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
  }
180
278
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
181
279
    return true;
182
280
  }
183
281
  return false;
184
282
}
185
283
 
 
284
__attribute__((nonnull))
186
285
int is_plymouth(const struct dirent *proc_entry){
187
286
  int ret;
188
287
  {
189
 
    uintmax_t maxvalue;
 
288
    uintmax_t proc_id;
190
289
    char *tmp;
191
290
    errno = 0;
192
 
    maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
291
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
193
292
 
194
293
    if(errno != 0 or *tmp != '\0'
195
 
       or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
294
       or proc_id != (uintmax_t)((pid_t)proc_id)){
196
295
      return 0;
197
296
    }
198
297
  }
200
299
  char *exe_link;
201
300
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
202
301
  if(ret == -1){
203
 
    error(0, errno, "asprintf");
 
302
    error_plus(0, errno, "asprintf");
204
303
    return 0;
205
304
  }
206
305
  
209
308
  if(ret == -1){
210
309
    free(exe_link);
211
310
    if(errno != ENOENT){
212
 
      error(0, errno, "lstat");
 
311
      error_plus(0, errno, "lstat");
213
312
    }
214
313
    return 0;
215
314
  }
233
332
 
234
333
pid_t get_pid(void){
235
334
  int ret;
 
335
  uintmax_t proc_id = 0;
236
336
  FILE *pidfile = fopen(plymouth_pid, "r");
237
 
  uintmax_t maxvalue = 0;
 
337
  /* Try the new pid file location */
238
338
  if(pidfile != NULL){
239
 
    ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
 
339
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
240
340
    if(ret != 1){
241
 
      maxvalue = 0;
 
341
      proc_id = 0;
242
342
    }
243
343
    fclose(pidfile);
244
344
  }
245
 
  if(maxvalue == 0){
246
 
    struct dirent **direntries;
 
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;
247
370
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
248
 
    if (ret == -1){
249
 
      error(0, errno, "scandir");
 
371
    if(ret == -1){
 
372
      error_plus(0, errno, "scandir");
250
373
    }
251
 
    if (ret > 0){
252
 
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
253
 
      if (ret < 0){
254
 
        error(0, errno, "sscanf");
 
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]);
255
383
      }
256
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);
257
388
  }
258
389
  pid_t pid;
259
 
  pid = (pid_t)maxvalue;
260
 
  if((uintmax_t)pid == maxvalue){
 
390
  pid = (pid_t)proc_id;
 
391
  if((uintmax_t)pid == proc_id){
261
392
    return pid;
262
393
  }
263
394
  
264
395
  return 0;
265
396
}
266
397
 
267
 
const char **getargv(pid_t pid){
 
398
char **getargv(pid_t pid){
268
399
  int cl_fd;
269
400
  char *cmdline_filename;
270
401
  ssize_t sret;
273
404
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
274
405
                 (uintmax_t)pid);
275
406
  if(ret == -1){
276
 
    error(0, errno, "asprintf");
 
407
    error_plus(0, errno, "asprintf");
277
408
    return NULL;
278
409
  }
279
410
  
281
412
  cl_fd = open(cmdline_filename, O_RDONLY);
282
413
  free(cmdline_filename);
283
414
  if(cl_fd == -1){
284
 
    error(0, errno, "open");
 
415
    error_plus(0, errno, "open");
285
416
    return NULL;
286
417
  }
287
418
  
295
426
    if(cmdline_len + blocksize > cmdline_allocated){
296
427
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
297
428
      if(tmp == NULL){
298
 
        error(0, errno, "realloc");
 
429
        error_plus(0, errno, "realloc");
299
430
        free(cmdline);
300
431
        close(cl_fd);
301
432
        return NULL;
308
439
    sret = read(cl_fd, cmdline + cmdline_len,
309
440
                cmdline_allocated - cmdline_len);
310
441
    if(sret == -1){
311
 
      error(0, errno, "read");
 
442
      error_plus(0, errno, "read");
312
443
      free(cmdline);
313
444
      close(cl_fd);
314
445
      return NULL;
317
448
  } while(sret != 0);
318
449
  ret = close(cl_fd);
319
450
  if(ret == -1){
320
 
    error(0, errno, "close");
 
451
    error_plus(0, errno, "close");
321
452
    free(cmdline);
322
453
    return NULL;
323
454
  }
326
457
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
327
458
                       * sizeof(char *)); /* Get number of args */
328
459
  if(argv == NULL){
329
 
    error(0, errno, "argv = malloc()");
 
460
    error_plus(0, errno, "argv = malloc()");
330
461
    free(cmdline);
331
462
    return NULL;
332
463
  }
333
464
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
334
 
  return (const char **)argv;
 
465
  return argv;
335
466
}
336
467
 
337
468
int main(__attribute__((unused))int argc,
338
469
         __attribute__((unused))char **argv){
339
 
  char *prompt;
 
470
  char *prompt = NULL;
340
471
  char *prompt_arg;
341
472
  pid_t plymouth_command_pid;
342
473
  int ret;
343
474
  bool bret;
344
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
  
345
524
  /* test -x /bin/plymouth */
346
525
  ret = access(plymouth_path, X_OK);
347
526
  if(ret == -1){
348
527
    /* Plymouth is probably not installed.  Don't print an error
349
528
       message, just exit. */
 
529
    if(debug){
 
530
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
 
531
                   plymouth_path);
 
532
    }
350
533
    exit(EX_UNAVAILABLE);
351
534
  }
352
535
  
359
542
        *sig != 0; sig++){
360
543
      ret = sigaddset(&new_action.sa_mask, *sig);
361
544
      if(ret == -1){
362
 
        error(EX_OSERR, errno, "sigaddset");
 
545
        error_plus(EX_OSERR, errno, "sigaddset");
363
546
      }
364
547
      ret = sigaction(*sig, NULL, &old_action);
365
548
      if(ret == -1){
366
 
        error(EX_OSERR, errno, "sigaction");
 
549
        error_plus(EX_OSERR, errno, "sigaction");
367
550
      }
368
551
      if(old_action.sa_handler != SIG_IGN){
369
552
        ret = sigaction(*sig, &new_action, NULL);
370
553
        if(ret == -1){
371
 
          error(EX_OSERR, errno, "sigaction");
 
554
          error_plus(EX_OSERR, errno, "sigaction");
372
555
        }
373
556
      }
374
557
    }
386
569
    }
387
570
    /* Plymouth is probably not running.  Don't print an error
388
571
       message, just exit. */
 
572
    if(debug){
 
573
      fprintf_plus(stderr, "Plymouth not running\n");
 
574
    }
389
575
    exit(EX_UNAVAILABLE);
390
576
  }
391
577
  
392
 
  prompt = makeprompt();
393
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
394
 
  free(prompt);
 
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
  }
395
585
  if(ret == -1){
396
 
    error(EX_OSERR, errno, "asprintf");
 
586
    error_plus(EX_OSERR, errno, "asprintf");
397
587
  }
398
588
  
399
589
  /* plymouth ask-for-password --prompt="$prompt" */
 
590
  if(debug){
 
591
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
 
592
  }
400
593
  bret = exec_and_wait(&plymouth_command_pid,
401
594
                       plymouth_path, (const char *[])
402
595
                       { plymouth_path, "ask-for-password",
412
605
  }
413
606
  kill_and_wait(plymouth_command_pid);
414
607
  
415
 
  const char **plymouthd_argv;
 
608
  char **plymouthd_argv = NULL;
416
609
  pid_t pid = get_pid();
417
610
  if(pid == 0){
418
 
    error(0, 0, "plymouthd pid not found");
419
 
    plymouthd_argv = plymouthd_default_argv;
 
611
    error_plus(0, 0, "plymouthd pid not found");
420
612
  } else {
421
613
    plymouthd_argv = getargv(pid);
422
614
  }
425
617
                       { plymouth_path, "quit", NULL },
426
618
                       false, false);
427
619
  if(not bret){
 
620
    if(plymouthd_argv != NULL){
 
621
      free(*plymouthd_argv);
 
622
      free(plymouthd_argv);
 
623
    }
428
624
    exit(EXIT_FAILURE);
429
625
  }
430
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
 
626
  bret = exec_and_wait(NULL, plymouthd_path,
 
627
                       (plymouthd_argv != NULL)
 
628
                       ? (const char * const *)plymouthd_argv
 
629
                       : plymouthd_default_argv,
431
630
                       false, true);
 
631
  if(plymouthd_argv != NULL){
 
632
    free(*plymouthd_argv);
 
633
    free(plymouthd_argv);
 
634
  }
432
635
  if(not bret){
433
636
    exit(EXIT_FAILURE);
434
637
  }