/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: 2019-07-29 16:35:53 UTC
  • Revision ID: teddy@recompile.se-20190729163553-1i442i2cbx64c537
Make tests and man page examples match

Make the tests test_manual_page_example[1-5] match exactly what is
written in the manual page, and add comments to manual page as
reminders to keep tests and manual page examples in sync.

* mandos-ctl (Test_commands_from_options.test_manual_page_example_1):
  Remove "--verbose" option, since the manual does not have it as the
  first example, and change assertion to match.
* mandos-ctl.xml (EXAMPLE): Add comments to all examples documenting
  which test function they correspond to.  Also remove unnecessary
  quotes from option arguments in fourth example, and clarify language
  slightly in fifth example.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Plymouth - Read a password from Plymouth and output it
4
4
 * 
5
 
 * Copyright © 2010-2011 Teddy Hogeborn
6
 
 * Copyright © 2010-2011 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
 
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
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
26
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
52
53
#include <errno.h>              /* TEMP_FAILURE_RETRY */
53
54
#include <argz.h>               /* argz_count(), argz_extract() */
54
55
#include <stdarg.h>             /* va_list, va_start(), ... */
 
56
#include <argp.h>
55
57
 
56
58
sig_atomic_t interrupted_by_signal = 0;
57
 
const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid";
 
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
 
58
69
const char plymouth_path[] = "/bin/plymouth";
59
70
const char plymouthd_path[] = "/sbin/plymouthd";
60
71
const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
61
72
                                        "--mode=boot",
62
73
                                        "--attach-to-session",
63
 
                                        "--pid-file="
64
 
                                        "/dev/.initramfs/"
65
 
                                        "plymouth.pid",
66
74
                                        NULL };
 
75
bool debug = false;
67
76
 
68
77
static void termination_handler(__attribute__((unused))int signum){
69
78
  if(interrupted_by_signal){
72
81
  interrupted_by_signal = 1;
73
82
}
74
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
 
75
92
/* Function to use when printing errors */
 
93
__attribute__((format (gnu_printf, 3, 4)))
76
94
void error_plus(int status, int errnum, const char *formatstring,
77
95
                ...){
78
96
  va_list ap;
81
99
  
82
100
  va_start(ap, formatstring);
83
101
  ret = vasprintf(&text, formatstring, ap);
84
 
  if (ret == -1){
 
102
  if(ret == -1){
85
103
    fprintf(stderr, "Mandos plugin %s: ",
86
104
            program_invocation_short_name);
87
105
    vfprintf(stderr, formatstring, ap);
151
169
  return true;
152
170
}
153
171
 
 
172
__attribute__((nonnull (2, 3)))
154
173
bool exec_and_wait(pid_t *pid_return, const char *path,
155
 
                   const char **argv, bool interruptable,
 
174
                   const char * const * const argv, bool interruptable,
156
175
                   bool daemonize){
157
176
  int status;
158
177
  int ret;
159
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
 
160
186
  pid = fork();
161
187
  if(pid == -1){
162
188
    error_plus(0, errno, "fork");
170
196
      }
171
197
    }
172
198
    
173
 
    char **new_argv = NULL;
 
199
    char **new_argv = malloc(sizeof(const char *));
 
200
    if(new_argv == NULL){
 
201
      error_plus(0, errno, "malloc");
 
202
      _exit(EX_OSERR);
 
203
    }
174
204
    char **tmp;
175
205
    int i = 0;
176
 
    for (; argv[i]!=NULL; i++){
177
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
178
 
      if (tmp == NULL){
 
206
    for (; argv[i] != NULL; i++){
 
207
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
 
208
      if(tmp == NULL){
179
209
        error_plus(0, errno, "realloc");
180
210
        free(new_argv);
181
211
        _exit(EX_OSERR);
198
228
          and ((not interrupted_by_signal)
199
229
               or (not interruptable)));
200
230
  if(interrupted_by_signal and interruptable){
 
231
    if(debug){
 
232
      fprintf_plus(stderr, "Interrupted by signal\n");
 
233
    }
201
234
    return false;
202
235
  }
203
236
  if(ret == -1){
204
237
    error_plus(0, errno, "waitpid");
205
238
    return false;
206
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
  }
207
249
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
208
250
    return true;
209
251
  }
210
252
  return false;
211
253
}
212
254
 
 
255
__attribute__((nonnull))
213
256
int is_plymouth(const struct dirent *proc_entry){
214
257
  int ret;
215
258
  {
216
 
    uintmax_t maxvalue;
 
259
    uintmax_t proc_id;
217
260
    char *tmp;
218
261
    errno = 0;
219
 
    maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
262
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
220
263
 
221
264
    if(errno != 0 or *tmp != '\0'
222
 
       or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
265
       or proc_id != (uintmax_t)((pid_t)proc_id)){
223
266
      return 0;
224
267
    }
225
268
  }
260
303
 
261
304
pid_t get_pid(void){
262
305
  int ret;
 
306
  uintmax_t proc_id = 0;
263
307
  FILE *pidfile = fopen(plymouth_pid, "r");
264
 
  uintmax_t maxvalue = 0;
 
308
  /* Try the new pid file location */
265
309
  if(pidfile != NULL){
266
 
    ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
 
310
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
267
311
    if(ret != 1){
268
 
      maxvalue = 0;
 
312
      proc_id = 0;
269
313
    }
270
314
    fclose(pidfile);
271
315
  }
272
 
  if(maxvalue == 0){
 
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){
273
340
    struct dirent **direntries = NULL;
274
341
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
275
 
    if (ret == -1){
 
342
    if(ret == -1){
276
343
      error_plus(0, errno, "scandir");
277
344
    }
278
 
    if (ret > 0){
279
 
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
280
 
      if (ret < 0){
281
 
        error_plus(0, errno, "sscanf");
 
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]);
282
354
      }
283
355
    }
284
356
    /* scandir might preallocate for this variable (man page unclear).
286
358
    free(direntries);
287
359
  }
288
360
  pid_t pid;
289
 
  pid = (pid_t)maxvalue;
290
 
  if((uintmax_t)pid == maxvalue){
 
361
  pid = (pid_t)proc_id;
 
362
  if((uintmax_t)pid == proc_id){
291
363
    return pid;
292
364
  }
293
365
  
294
366
  return 0;
295
367
}
296
368
 
297
 
const char **getargv(pid_t pid){
 
369
char **getargv(pid_t pid){
298
370
  int cl_fd;
299
371
  char *cmdline_filename;
300
372
  ssize_t sret;
361
433
    return NULL;
362
434
  }
363
435
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
364
 
  return (const char **)argv;
 
436
  return argv;
365
437
}
366
438
 
367
439
int main(__attribute__((unused))int argc,
368
440
         __attribute__((unused))char **argv){
369
 
  char *prompt;
 
441
  char *prompt = NULL;
370
442
  char *prompt_arg;
371
443
  pid_t plymouth_command_pid;
372
444
  int ret;
373
445
  bool bret;
374
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
  
375
495
  /* test -x /bin/plymouth */
376
496
  ret = access(plymouth_path, X_OK);
377
497
  if(ret == -1){
378
498
    /* Plymouth is probably not installed.  Don't print an error
379
499
       message, just exit. */
 
500
    if(debug){
 
501
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
 
502
                   plymouth_path);
 
503
    }
380
504
    exit(EX_UNAVAILABLE);
381
505
  }
382
506
  
416
540
    }
417
541
    /* Plymouth is probably not running.  Don't print an error
418
542
       message, just exit. */
 
543
    if(debug){
 
544
      fprintf_plus(stderr, "Plymouth not running\n");
 
545
    }
419
546
    exit(EX_UNAVAILABLE);
420
547
  }
421
548
  
422
 
  prompt = makeprompt();
423
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
424
 
  free(prompt);
 
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
  }
425
556
  if(ret == -1){
426
557
    error_plus(EX_OSERR, errno, "asprintf");
427
558
  }
428
559
  
429
560
  /* plymouth ask-for-password --prompt="$prompt" */
 
561
  if(debug){
 
562
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
 
563
  }
430
564
  bret = exec_and_wait(&plymouth_command_pid,
431
565
                       plymouth_path, (const char *[])
432
566
                       { plymouth_path, "ask-for-password",
442
576
  }
443
577
  kill_and_wait(plymouth_command_pid);
444
578
  
445
 
  const char **plymouthd_argv;
 
579
  char **plymouthd_argv = NULL;
446
580
  pid_t pid = get_pid();
447
581
  if(pid == 0){
448
582
    error_plus(0, 0, "plymouthd pid not found");
449
 
    plymouthd_argv = plymouthd_default_argv;
450
583
  } else {
451
584
    plymouthd_argv = getargv(pid);
452
585
  }
455
588
                       { plymouth_path, "quit", NULL },
456
589
                       false, false);
457
590
  if(not bret){
 
591
    if(plymouthd_argv != NULL){
 
592
      free(*plymouthd_argv);
 
593
      free(plymouthd_argv);
 
594
    }
458
595
    exit(EXIT_FAILURE);
459
596
  }
460
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
 
597
  bret = exec_and_wait(NULL, plymouthd_path,
 
598
                       (plymouthd_argv != NULL)
 
599
                       ? (const char * const *)plymouthd_argv
 
600
                       : plymouthd_default_argv,
461
601
                       false, true);
 
602
  if(plymouthd_argv != NULL){
 
603
    free(*plymouthd_argv);
 
604
    free(plymouthd_argv);
 
605
  }
462
606
  if(not bret){
463
607
    exit(EXIT_FAILURE);
464
608
  }