/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: 2015-05-23 20:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 756.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

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-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
 
5
 * Copyright © 2010-2014 Teddy Hogeborn
 
6
 * Copyright © 2010-2014 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
16
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of
17
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
16
 * General Public License for more details.
19
17
 * 
20
18
 * You should have received a copy of the GNU General Public License
21
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
19
 * along with this program.  If not, see
 
20
 * <http://www.gnu.org/licenses/>.
22
21
 * 
23
22
 * Contact the authors at <mandos@recompile.se>.
24
23
 */
44
43
                                   STDERR_FILENO, execv(), access() */
45
44
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
46
45
                                   EXIT_SUCCESS, malloc(), _exit(),
47
 
                                   getenv(), reallocarray() */
 
46
                                   getenv() */
48
47
#include <dirent.h>             /* scandir(), alphasort() */
49
48
#include <inttypes.h>           /* intmax_t, strtoumax(), SCNuMAX */
50
49
#include <sys/stat.h>           /* struct stat, lstat() */
53
52
#include <errno.h>              /* TEMP_FAILURE_RETRY */
54
53
#include <argz.h>               /* argz_count(), argz_extract() */
55
54
#include <stdarg.h>             /* va_list, va_start(), ... */
56
 
#include <argp.h>
57
55
 
58
56
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
57
 
62
58
/* Used by Ubuntu 11.04 (Natty Narwahl) */
63
 
const char plymouth_old_old_pid[] = "/dev/.initramfs/plymouth.pid";
 
59
const char plymouth_old_pid[] = "/dev/.initramfs/plymouth.pid";
64
60
/* 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";
 
61
const char plymouth_pid[] = "/run/initramfs/plymouth.pid";
68
62
 
69
63
const char plymouth_path[] = "/bin/plymouth";
70
64
const char plymouthd_path[] = "/sbin/plymouthd";
72
66
                                        "--mode=boot",
73
67
                                        "--attach-to-session",
74
68
                                        NULL };
75
 
bool debug = false;
76
69
 
77
70
static void termination_handler(__attribute__((unused))int signum){
78
71
  if(interrupted_by_signal){
81
74
  interrupted_by_signal = 1;
82
75
}
83
76
 
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
77
/* Function to use when printing errors */
93
78
__attribute__((format (gnu_printf, 3, 4)))
94
79
void error_plus(int status, int errnum, const char *formatstring,
171
156
 
172
157
__attribute__((nonnull (2, 3)))
173
158
bool exec_and_wait(pid_t *pid_return, const char *path,
174
 
                   const char * const * const argv, bool interruptable,
 
159
                   const char * const *argv, bool interruptable,
175
160
                   bool daemonize){
176
161
  int status;
177
162
  int ret;
178
163
  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
164
  pid = fork();
187
165
  if(pid == -1){
188
166
    error_plus(0, errno, "fork");
196
174
      }
197
175
    }
198
176
    
199
 
    char **new_argv = malloc(sizeof(const char *));
200
 
    if(new_argv == NULL){
201
 
      error_plus(0, errno, "malloc");
202
 
      _exit(EX_OSERR);
203
 
    }
 
177
    char **new_argv = NULL;
204
178
    char **tmp;
205
179
    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
 
180
    for (; argv[i]!=NULL; i++){
 
181
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
219
182
      if(tmp == NULL){
220
 
        error_plus(0, errno, "reallocarray");
 
183
        error_plus(0, errno, "realloc");
221
184
        free(new_argv);
222
185
        _exit(EX_OSERR);
223
186
      }
239
202
          and ((not interrupted_by_signal)
240
203
               or (not interruptable)));
241
204
  if(interrupted_by_signal and interruptable){
242
 
    if(debug){
243
 
      fprintf_plus(stderr, "Interrupted by signal\n");
244
 
    }
245
205
    return false;
246
206
  }
247
207
  if(ret == -1){
248
208
    error_plus(0, errno, "waitpid");
249
209
    return false;
250
210
  }
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
211
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
261
212
    return true;
262
213
  }
326
277
  }
327
278
  /* Try the old pid file location */
328
279
  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");
 
280
    pidfile = fopen(plymouth_pid, "r");
341
281
    if(pidfile != NULL){
342
282
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
343
283
      if(ret != 1){
354
294
      error_plus(0, errno, "scandir");
355
295
    }
356
296
    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]);
 
297
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
 
298
      if(ret < 0){
 
299
        error_plus(0, errno, "sscanf");
365
300
      }
366
301
    }
367
302
    /* scandir might preallocate for this variable (man page unclear).
377
312
  return 0;
378
313
}
379
314
 
380
 
char **getargv(pid_t pid){
 
315
const char * const * getargv(pid_t pid){
381
316
  int cl_fd;
382
317
  char *cmdline_filename;
383
318
  ssize_t sret;
444
379
    return NULL;
445
380
  }
446
381
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
447
 
  return argv;
 
382
  return (const char * const *)argv;
448
383
}
449
384
 
450
385
int main(__attribute__((unused))int argc,
451
386
         __attribute__((unused))char **argv){
452
 
  char *prompt = NULL;
 
387
  char *prompt;
453
388
  char *prompt_arg;
454
389
  pid_t plymouth_command_pid;
455
390
  int ret;
456
391
  bool bret;
457
392
 
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
393
  /* test -x /bin/plymouth */
507
394
  ret = access(plymouth_path, X_OK);
508
395
  if(ret == -1){
509
396
    /* Plymouth is probably not installed.  Don't print an error
510
397
       message, just exit. */
511
 
    if(debug){
512
 
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
513
 
                   plymouth_path);
514
 
    }
515
398
    exit(EX_UNAVAILABLE);
516
399
  }
517
400
  
551
434
    }
552
435
    /* Plymouth is probably not running.  Don't print an error
553
436
       message, just exit. */
554
 
    if(debug){
555
 
      fprintf_plus(stderr, "Plymouth not running\n");
556
 
    }
557
437
    exit(EX_UNAVAILABLE);
558
438
  }
559
439
  
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
 
  }
 
440
  prompt = makeprompt();
 
441
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
 
442
  free(prompt);
567
443
  if(ret == -1){
568
444
    error_plus(EX_OSERR, errno, "asprintf");
569
445
  }
570
446
  
571
447
  /* plymouth ask-for-password --prompt="$prompt" */
572
 
  if(debug){
573
 
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
574
 
  }
575
448
  bret = exec_and_wait(&plymouth_command_pid,
576
449
                       plymouth_path, (const char *[])
577
450
                       { plymouth_path, "ask-for-password",
587
460
  }
588
461
  kill_and_wait(plymouth_command_pid);
589
462
  
590
 
  char **plymouthd_argv = NULL;
 
463
  const char * const *plymouthd_argv;
591
464
  pid_t pid = get_pid();
592
465
  if(pid == 0){
593
466
    error_plus(0, 0, "plymouthd pid not found");
 
467
    plymouthd_argv = plymouthd_default_argv;
594
468
  } else {
595
469
    plymouthd_argv = getargv(pid);
596
470
  }
599
473
                       { plymouth_path, "quit", NULL },
600
474
                       false, false);
601
475
  if(not bret){
602
 
    if(plymouthd_argv != NULL){
603
 
      free(*plymouthd_argv);
604
 
      free(plymouthd_argv);
605
 
    }
606
476
    exit(EXIT_FAILURE);
607
477
  }
608
 
  bret = exec_and_wait(NULL, plymouthd_path,
609
 
                       (plymouthd_argv != NULL)
610
 
                       ? (const char * const *)plymouthd_argv
611
 
                       : plymouthd_default_argv,
 
478
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
612
479
                       false, true);
613
 
  if(plymouthd_argv != NULL){
614
 
    free(*plymouthd_argv);
615
 
    free(plymouthd_argv);
616
 
  }
617
480
  if(not bret){
618
481
    exit(EXIT_FAILURE);
619
482
  }