/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: 2016-06-03 17:27:03 UTC
  • Revision ID: teddy@recompile.se-20160603172703-mc6tjor6rhq4xy74
mandos: Bug fix: Do multiprocessing cleanup correctly on exit

* mandos (main): Save module "multiprocessing" and open file "wnull"
                 as scope variables accessible by function cleanup(),
                 since the module and global variable may not be
                 accessible when the cleanup() function is run as
                 scheduled by atexit().

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-2022 Teddy Hogeborn
6
 
 * Copyright © 2010-2022 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-2016 Teddy Hogeborn
 
6
 * Copyright © 2010-2016 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
 */
25
24
 
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 */
 
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 */
38
30
#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() */
 
31
#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(), vasprintf(), fprintf(),
 
40
                                   vfprintf() */
 
41
#include <unistd.h>             /* close(), readlink(), read(),
 
42
                                   fork(), setsid(), chdir(), dup2(),
 
43
                                   STDERR_FILENO, execv(), access() */
 
44
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
 
45
                                   EXIT_SUCCESS, malloc(), _exit(),
 
46
                                   getenv() */
 
47
#include <dirent.h>             /* scandir(), alphasort() */
 
48
#include <inttypes.h>           /* intmax_t, strtoumax(), SCNuMAX */
 
49
#include <sys/stat.h>           /* struct stat, lstat() */
 
50
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
48
51
#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 */
 
52
#include <errno.h>              /* TEMP_FAILURE_RETRY */
74
53
#include <argz.h>               /* argz_count(), argz_extract() */
 
54
#include <stdarg.h>             /* va_list, va_start(), ... */
75
55
 
76
56
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
57
 
80
58
/* Used by Ubuntu 11.04 (Natty Narwahl) */
81
 
const char plymouth_old_old_pid[] = "/dev/.initramfs/plymouth.pid";
 
59
const char plymouth_old_pid[] = "/dev/.initramfs/plymouth.pid";
82
60
/* 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";
 
61
const char plymouth_pid[] = "/run/initramfs/plymouth.pid";
86
62
 
87
63
const char plymouth_path[] = "/bin/plymouth";
88
64
const char plymouthd_path[] = "/sbin/plymouthd";
90
66
                                        "--mode=boot",
91
67
                                        "--attach-to-session",
92
68
                                        NULL };
93
 
bool debug = false;
94
69
 
95
70
static void termination_handler(__attribute__((unused))int signum){
96
71
  if(interrupted_by_signal){
99
74
  interrupted_by_signal = 1;
100
75
}
101
76
 
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
77
/* Function to use when printing errors */
111
78
__attribute__((format (gnu_printf, 3, 4)))
112
79
void error_plus(int status, int errnum, const char *formatstring,
189
156
 
190
157
__attribute__((nonnull (2, 3)))
191
158
bool exec_and_wait(pid_t *pid_return, const char *path,
192
 
                   const char * const * const argv, bool interruptable,
 
159
                   const char * const *argv, bool interruptable,
193
160
                   bool daemonize){
194
161
  int status;
195
162
  int ret;
196
163
  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
164
  pid = fork();
205
165
  if(pid == -1){
206
166
    error_plus(0, errno, "fork");
221
181
    }
222
182
    char **tmp;
223
183
    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
 
184
    for (; argv[i]!=NULL; i++){
 
185
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
237
186
      if(tmp == NULL){
238
 
        error_plus(0, errno, "reallocarray");
 
187
        error_plus(0, errno, "realloc");
239
188
        free(new_argv);
240
189
        _exit(EX_OSERR);
241
190
      }
257
206
          and ((not interrupted_by_signal)
258
207
               or (not interruptable)));
259
208
  if(interrupted_by_signal and interruptable){
260
 
    if(debug){
261
 
      fprintf_plus(stderr, "Interrupted by signal\n");
262
 
    }
263
209
    return false;
264
210
  }
265
211
  if(ret == -1){
266
212
    error_plus(0, errno, "waitpid");
267
213
    return false;
268
214
  }
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
215
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
279
216
    return true;
280
217
  }
344
281
  }
345
282
  /* Try the old pid file location */
346
283
  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");
 
284
    pidfile = fopen(plymouth_pid, "r");
359
285
    if(pidfile != NULL){
360
286
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
361
287
      if(ret != 1){
372
298
      error_plus(0, errno, "scandir");
373
299
    }
374
300
    if(ret > 0){
375
 
      const int num_entries = ret;
376
 
      for(int i = 0; i < num_entries; i++){
377
 
        if(proc_id == 0){
378
 
          ret = sscanf(direntries[i]->d_name, "%" SCNuMAX, &proc_id);
379
 
          if(ret < 0){
380
 
            error_plus(0, errno, "sscanf");
381
 
          }
382
 
        }
383
 
        free(direntries[i]);
 
301
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
 
302
      if(ret < 0){
 
303
        error_plus(0, errno, "sscanf");
384
304
      }
385
305
    }
386
306
    /* scandir might preallocate for this variable (man page unclear).
396
316
  return 0;
397
317
}
398
318
 
399
 
char **getargv(pid_t pid){
 
319
const char * const * getargv(pid_t pid){
400
320
  int cl_fd;
401
321
  char *cmdline_filename;
402
322
  ssize_t sret;
463
383
    return NULL;
464
384
  }
465
385
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
466
 
  return argv;
 
386
  return (const char * const *)argv;
467
387
}
468
388
 
469
389
int main(__attribute__((unused))int argc,
470
390
         __attribute__((unused))char **argv){
471
 
  char *prompt = NULL;
 
391
  char *prompt;
472
392
  char *prompt_arg;
473
393
  pid_t plymouth_command_pid;
474
394
  int ret;
475
395
  bool bret;
476
396
 
477
 
  {
478
 
    struct argp_option options[] = {
479
 
      { .name = "prompt", .key = 128, .arg = "PROMPT",
480
 
        .doc = "The prompt to show" },
481
 
      { .name = "debug", .key = 129,
482
 
        .doc = "Debug mode" },
483
 
      { .name = NULL }
484
 
    };
485
 
    
486
 
    __attribute__((nonnull(3)))
487
 
    error_t parse_opt (int key, char *arg, __attribute__((unused))
488
 
                       struct argp_state *state){
489
 
      errno = 0;
490
 
      switch (key){
491
 
      case 128:                 /* --prompt */
492
 
        prompt = arg;
493
 
        if(debug){
494
 
          fprintf_plus(stderr, "Custom prompt \"%s\"\n", prompt);
495
 
        }
496
 
        break;
497
 
      case 129:                 /* --debug */
498
 
        debug = true;
499
 
        break;
500
 
      default:
501
 
        return ARGP_ERR_UNKNOWN;
502
 
      }
503
 
      return errno;
504
 
    }
505
 
    
506
 
    struct argp argp = { .options = options, .parser = parse_opt,
507
 
                         .args_doc = "",
508
 
                         .doc = "Mandos plymouth -- Read and"
509
 
                         " output a password" };
510
 
    ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, NULL);
511
 
    switch(ret){
512
 
    case 0:
513
 
      break;
514
 
    case ENOMEM:
515
 
    default:
516
 
      errno = ret;
517
 
      error_plus(0, errno, "argp_parse");
518
 
      return EX_OSERR;
519
 
    case EINVAL:
520
 
      error_plus(0, errno, "argp_parse");
521
 
      return EX_USAGE;
522
 
    }
523
 
  }
524
 
  
525
397
  /* test -x /bin/plymouth */
526
398
  ret = access(plymouth_path, X_OK);
527
399
  if(ret == -1){
528
400
    /* Plymouth is probably not installed.  Don't print an error
529
401
       message, just exit. */
530
 
    if(debug){
531
 
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
532
 
                   plymouth_path);
533
 
    }
534
402
    exit(EX_UNAVAILABLE);
535
403
  }
536
404
  
570
438
    }
571
439
    /* Plymouth is probably not running.  Don't print an error
572
440
       message, just exit. */
573
 
    if(debug){
574
 
      fprintf_plus(stderr, "Plymouth not running\n");
575
 
    }
576
441
    exit(EX_UNAVAILABLE);
577
442
  }
578
443
  
579
 
  if(prompt != NULL){
580
 
    ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
581
 
  } else {
582
 
    char *made_prompt = makeprompt();
583
 
    ret = asprintf(&prompt_arg, "--prompt=%s", made_prompt);
584
 
    free(made_prompt);
585
 
  }
 
444
  prompt = makeprompt();
 
445
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
 
446
  free(prompt);
586
447
  if(ret == -1){
587
448
    error_plus(EX_OSERR, errno, "asprintf");
588
449
  }
589
450
  
590
451
  /* plymouth ask-for-password --prompt="$prompt" */
591
 
  if(debug){
592
 
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
593
 
  }
594
452
  bret = exec_and_wait(&plymouth_command_pid,
595
453
                       plymouth_path, (const char *[])
596
454
                       { plymouth_path, "ask-for-password",
606
464
  }
607
465
  kill_and_wait(plymouth_command_pid);
608
466
  
609
 
  char **plymouthd_argv = NULL;
 
467
  const char * const *plymouthd_argv;
610
468
  pid_t pid = get_pid();
611
469
  if(pid == 0){
612
470
    error_plus(0, 0, "plymouthd pid not found");
 
471
    plymouthd_argv = plymouthd_default_argv;
613
472
  } else {
614
473
    plymouthd_argv = getargv(pid);
615
474
  }
618
477
                       { plymouth_path, "quit", NULL },
619
478
                       false, false);
620
479
  if(not bret){
621
 
    if(plymouthd_argv != NULL){
622
 
      free(*plymouthd_argv);
623
 
      free(plymouthd_argv);
624
 
    }
625
480
    exit(EXIT_FAILURE);
626
481
  }
627
 
  bret = exec_and_wait(NULL, plymouthd_path,
628
 
                       (plymouthd_argv != NULL)
629
 
                       ? (const char * const *)plymouthd_argv
630
 
                       : plymouthd_default_argv,
 
482
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
631
483
                       false, true);
632
 
  if(plymouthd_argv != NULL){
633
 
    free(*plymouthd_argv);
634
 
    free(plymouthd_argv);
635
 
  }
636
484
  if(not bret){
637
485
    exit(EXIT_FAILURE);
638
486
  }