/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« 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: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • 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:
1
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
 
 * Usplash - Read a password from usplash and output it
 
3
 * Plymouth - Read a password from Plymouth and output it
4
4
 * 
5
 
 * Copyright © 2010 Teddy Hogeborn
6
 
 * Copyright © 2010 Björn Påhlsson
 
5
 * Copyright © 2010-2014 Teddy Hogeborn
 
6
 * Copyright © 2010-2014 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
19
19
 * along with this program.  If not, see
20
20
 * <http://www.gnu.org/licenses/>.
21
21
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
22
 * Contact the authors at <mandos@recompile.se>.
23
23
 */
24
24
 
25
25
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
36
36
#include <stddef.h>             /* NULL */
37
37
#include <string.h>             /* strchr(), memcmp() */
38
38
#include <stdio.h>              /* asprintf(), perror(), fopen(),
39
 
                                   fscanf() */
 
39
                                   fscanf(), vasprintf(), fprintf(),
 
40
                                   vfprintf() */
40
41
#include <unistd.h>             /* close(), readlink(), read(),
41
42
                                   fork(), setsid(), chdir(), dup2(),
42
43
                                   STDERR_FILENO, execv(), access() */
50
51
#include <error.h>              /* error() */
51
52
#include <errno.h>              /* TEMP_FAILURE_RETRY */
52
53
#include <argz.h>               /* argz_count(), argz_extract() */
 
54
#include <stdarg.h>             /* va_list, va_start(), ... */
53
55
 
54
56
sig_atomic_t interrupted_by_signal = 0;
55
 
const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid";
 
57
 
 
58
/* Used by Ubuntu 11.04 (Natty Narwahl) */
 
59
const char plymouth_old_pid[] = "/dev/.initramfs/plymouth.pid";
 
60
/* Used by Ubuntu 11.10 (Oneiric Ocelot) */
 
61
const char plymouth_pid[] = "/run/initramfs/plymouth.pid";
 
62
 
56
63
const char plymouth_path[] = "/bin/plymouth";
57
64
const char plymouthd_path[] = "/sbin/plymouthd";
58
65
const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
59
66
                                        "--mode=boot",
60
67
                                        "--attach-to-session",
61
 
                                        "--pid-file="
62
 
                                        "/dev/.initramfs/"
63
 
                                        "plymouth.pid",
64
68
                                        NULL };
65
69
 
66
70
static void termination_handler(__attribute__((unused))int signum){
70
74
  interrupted_by_signal = 1;
71
75
}
72
76
 
 
77
/* Function to use when printing errors */
 
78
__attribute__((format (gnu_printf, 3, 4)))
 
79
void error_plus(int status, int errnum, const char *formatstring,
 
80
                ...){
 
81
  va_list ap;
 
82
  char *text;
 
83
  int ret;
 
84
  
 
85
  va_start(ap, formatstring);
 
86
  ret = vasprintf(&text, formatstring, ap);
 
87
  if(ret == -1){
 
88
    fprintf(stderr, "Mandos plugin %s: ",
 
89
            program_invocation_short_name);
 
90
    vfprintf(stderr, formatstring, ap);
 
91
    fprintf(stderr, ": ");
 
92
    fprintf(stderr, "%s\n", strerror(errnum));
 
93
    error(status, errno, "vasprintf while printing error");
 
94
    return;
 
95
  }
 
96
  fprintf(stderr, "Mandos plugin ");
 
97
  error(status, errnum, "%s", text);
 
98
  free(text);
 
99
}
 
100
 
73
101
/* Create prompt string */
74
102
char *makeprompt(void){
75
103
  int ret = 0;
76
104
  char *prompt;
77
105
  const char *const cryptsource = getenv("cryptsource");
78
106
  const char *const crypttarget = getenv("crypttarget");
79
 
  const char prompt_start[] = "Enter passphrase to unlock the disk";
 
107
  const char prompt_start[] = "Unlocking the disk";
 
108
  const char prompt_end[] = "Enter passphrase";
80
109
  
81
110
  if(cryptsource == NULL){
82
111
    if(crypttarget == NULL){
83
 
      ret = asprintf(&prompt, "%s: ", prompt_start);
 
112
      ret = asprintf(&prompt, "%s\n%s", prompt_start, prompt_end);
84
113
    } else {
85
 
      ret = asprintf(&prompt, "%s (%s): ", prompt_start,
86
 
                     crypttarget);
 
114
      ret = asprintf(&prompt, "%s (%s)\n%s", prompt_start,
 
115
                     crypttarget, prompt_end);
87
116
    }
88
117
  } else {
89
118
    if(crypttarget == NULL){
90
 
      ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
 
119
      ret = asprintf(&prompt, "%s %s\n%s", prompt_start, cryptsource,
 
120
                     prompt_end);
91
121
    } else {
92
 
      ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
93
 
                     cryptsource, crypttarget);
 
122
      ret = asprintf(&prompt, "%s %s (%s)\n%s", prompt_start,
 
123
                     cryptsource, crypttarget, prompt_end);
94
124
    }
95
125
  }
96
126
  if(ret == -1){
107
137
bool become_a_daemon(void){
108
138
  int ret = setuid(geteuid());
109
139
  if(ret == -1){
110
 
    error(0, errno, "setuid");
 
140
    error_plus(0, errno, "setuid");
111
141
  }
112
142
    
113
143
  setsid();
114
144
  ret = chdir("/");
115
145
  if(ret == -1){
116
 
    error(0, errno, "chdir");
 
146
    error_plus(0, errno, "chdir");
117
147
    return false;
118
148
  }
119
149
  ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
120
150
  if(ret == -1){
121
 
    error(0, errno, "dup2");
 
151
    error_plus(0, errno, "dup2");
122
152
    return false;
123
153
  }
124
154
  return true;
125
155
}
126
156
 
 
157
__attribute__((nonnull (2, 3)))
127
158
bool exec_and_wait(pid_t *pid_return, const char *path,
128
 
                   const char **argv, bool interruptable,
 
159
                   const char * const *argv, bool interruptable,
129
160
                   bool daemonize){
130
161
  int status;
131
162
  int ret;
132
163
  pid_t pid;
133
164
  pid = fork();
134
165
  if(pid == -1){
135
 
    error(0, errno, "fork");
 
166
    error_plus(0, errno, "fork");
136
167
    return false;
137
168
  }
138
169
  if(pid == 0){
142
173
        _exit(EX_OSERR);
143
174
      }
144
175
    }
145
 
 
 
176
    
146
177
    char **new_argv = NULL;
147
 
    char *tmp;
 
178
    char **tmp;
148
179
    int i = 0;
149
 
    for (; argv[i]!=(char *)NULL; i++){
 
180
    for (; argv[i]!=NULL; i++){
150
181
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
151
 
      if (tmp == NULL){
152
 
        error(0, errno, "realloc");
 
182
      if(tmp == NULL){
 
183
        error_plus(0, errno, "realloc");
153
184
        free(new_argv);
154
185
        _exit(EX_OSERR);
155
186
      }
156
 
      new_argv = (char **)tmp;
 
187
      new_argv = tmp;
157
188
      new_argv[i] = strdup(argv[i]);
158
189
    }
159
 
    new_argv[i] = (char *) NULL;
 
190
    new_argv[i] = NULL;
160
191
    
161
192
    execv(path, (char *const *)new_argv);
162
 
    error(0, errno, "execv");
 
193
    error_plus(0, errno, "execv");
163
194
    _exit(EXIT_FAILURE);
164
195
  }
165
196
  if(pid_return != NULL){
174
205
    return false;
175
206
  }
176
207
  if(ret == -1){
177
 
    error(0, errno, "waitpid");
 
208
    error_plus(0, errno, "waitpid");
178
209
    return false;
179
210
  }
180
211
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
183
214
  return false;
184
215
}
185
216
 
 
217
__attribute__((nonnull))
186
218
int is_plymouth(const struct dirent *proc_entry){
187
219
  int ret;
188
220
  {
189
 
    uintmax_t maxvalue;
 
221
    uintmax_t proc_id;
190
222
    char *tmp;
191
223
    errno = 0;
192
 
    maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
224
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
193
225
 
194
226
    if(errno != 0 or *tmp != '\0'
195
 
       or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
227
       or proc_id != (uintmax_t)((pid_t)proc_id)){
196
228
      return 0;
197
229
    }
198
230
  }
199
 
  char exe_target[sizeof(plymouth_path)];
 
231
  char exe_target[sizeof(plymouthd_path)];
200
232
  char *exe_link;
201
233
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
202
234
  if(ret == -1){
203
 
    error(0, errno, "asprintf");
 
235
    error_plus(0, errno, "asprintf");
204
236
    return 0;
205
237
  }
206
238
  
209
241
  if(ret == -1){
210
242
    free(exe_link);
211
243
    if(errno != ENOENT){
212
 
      error(0, errno, "lstat");
 
244
      error_plus(0, errno, "lstat");
213
245
    }
214
246
    return 0;
215
247
  }
223
255
  
224
256
  ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
225
257
  free(exe_link);
226
 
  if((sret != (ssize_t)sizeof(plymouth_path)-1) or
227
 
      (memcmp(plymouth_path, exe_target,
228
 
              sizeof(plymouth_path)-1) != 0)){
 
258
  if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
 
259
      (memcmp(plymouthd_path, exe_target,
 
260
              sizeof(plymouthd_path)-1) != 0)){
229
261
    return 0;
230
262
  }
231
263
  return 1;
233
265
 
234
266
pid_t get_pid(void){
235
267
  int ret;
 
268
  uintmax_t proc_id = 0;
236
269
  FILE *pidfile = fopen(plymouth_pid, "r");
237
 
  uintmax_t maxvalue = 0;
 
270
  /* Try the new pid file location */
238
271
  if(pidfile != NULL){
239
 
    ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
 
272
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
240
273
    if(ret != 1){
241
 
      maxvalue = 0;
 
274
      proc_id = 0;
242
275
    }
243
276
    fclose(pidfile);
244
277
  }
245
 
  if(maxvalue == 0){
246
 
    struct dirent **direntries;
 
278
  /* Try the old pid file location */
 
279
  if(proc_id == 0){
 
280
    pidfile = fopen(plymouth_pid, "r");
 
281
    if(pidfile != NULL){
 
282
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
 
283
      if(ret != 1){
 
284
        proc_id = 0;
 
285
      }
 
286
      fclose(pidfile);
 
287
    }
 
288
  }
 
289
  /* Look for a plymouth process */
 
290
  if(proc_id == 0){
 
291
    struct dirent **direntries = NULL;
247
292
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
248
 
    sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
 
293
    if(ret == -1){
 
294
      error_plus(0, errno, "scandir");
 
295
    }
 
296
    if(ret > 0){
 
297
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
 
298
      if(ret < 0){
 
299
        error_plus(0, errno, "sscanf");
 
300
      }
 
301
    }
 
302
    /* scandir might preallocate for this variable (man page unclear).
 
303
       even if ret == 0, therefore we need to free it. */
 
304
    free(direntries);
249
305
  }
250
306
  pid_t pid;
251
 
  pid = (pid_t)maxvalue;
252
 
  if((uintmax_t)pid == maxvalue){
 
307
  pid = (pid_t)proc_id;
 
308
  if((uintmax_t)pid == proc_id){
253
309
    return pid;
254
310
  }
255
311
  
256
312
  return 0;
257
313
}
258
314
 
259
 
const char **getargv(pid_t pid){
 
315
const char * const * getargv(pid_t pid){
260
316
  int cl_fd;
261
317
  char *cmdline_filename;
262
318
  ssize_t sret;
265
321
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
266
322
                 (uintmax_t)pid);
267
323
  if(ret == -1){
268
 
    error(0, errno, "asprintf");
 
324
    error_plus(0, errno, "asprintf");
269
325
    return NULL;
270
326
  }
271
327
  
273
329
  cl_fd = open(cmdline_filename, O_RDONLY);
274
330
  free(cmdline_filename);
275
331
  if(cl_fd == -1){
276
 
    error(0, errno, "open");
 
332
    error_plus(0, errno, "open");
277
333
    return NULL;
278
334
  }
279
335
  
287
343
    if(cmdline_len + blocksize > cmdline_allocated){
288
344
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
289
345
      if(tmp == NULL){
290
 
        error(0, errno, "realloc");
 
346
        error_plus(0, errno, "realloc");
291
347
        free(cmdline);
292
348
        close(cl_fd);
293
349
        return NULL;
300
356
    sret = read(cl_fd, cmdline + cmdline_len,
301
357
                cmdline_allocated - cmdline_len);
302
358
    if(sret == -1){
303
 
      error(0, errno, "read");
 
359
      error_plus(0, errno, "read");
304
360
      free(cmdline);
305
361
      close(cl_fd);
306
362
      return NULL;
309
365
  } while(sret != 0);
310
366
  ret = close(cl_fd);
311
367
  if(ret == -1){
312
 
    error(0, errno, "close");
 
368
    error_plus(0, errno, "close");
313
369
    free(cmdline);
314
370
    return NULL;
315
371
  }
318
374
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
319
375
                       * sizeof(char *)); /* Get number of args */
320
376
  if(argv == NULL){
321
 
    error(0, errno, "argv = malloc()");
 
377
    error_plus(0, errno, "argv = malloc()");
322
378
    free(cmdline);
323
379
    return NULL;
324
380
  }
325
381
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
326
 
  return (const char **)argv;
 
382
  return (const char * const *)argv;
327
383
}
328
384
 
329
385
int main(__attribute__((unused))int argc,
351
407
        *sig != 0; sig++){
352
408
      ret = sigaddset(&new_action.sa_mask, *sig);
353
409
      if(ret == -1){
354
 
        error(EX_OSERR, errno, "sigaddset");
 
410
        error_plus(EX_OSERR, errno, "sigaddset");
355
411
      }
356
412
      ret = sigaction(*sig, NULL, &old_action);
357
413
      if(ret == -1){
358
 
        error(EX_OSERR, errno, "sigaction");
 
414
        error_plus(EX_OSERR, errno, "sigaction");
359
415
      }
360
416
      if(old_action.sa_handler != SIG_IGN){
361
417
        ret = sigaction(*sig, &new_action, NULL);
362
418
        if(ret == -1){
363
 
          error(EX_OSERR, errno, "sigaction");
 
419
          error_plus(EX_OSERR, errno, "sigaction");
364
420
        }
365
421
      }
366
422
    }
385
441
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
386
442
  free(prompt);
387
443
  if(ret == -1){
388
 
    error(EX_OSERR, errno, "asprintf");
 
444
    error_plus(EX_OSERR, errno, "asprintf");
389
445
  }
390
446
  
391
447
  /* plymouth ask-for-password --prompt="$prompt" */
404
460
  }
405
461
  kill_and_wait(plymouth_command_pid);
406
462
  
407
 
  const char **plymouthd_argv;
 
463
  const char * const *plymouthd_argv;
408
464
  pid_t pid = get_pid();
409
465
  if(pid == 0){
410
 
    error(0, 0, "plymouthd pid not found");
 
466
    error_plus(0, 0, "plymouthd pid not found");
411
467
    plymouthd_argv = plymouthd_default_argv;
412
468
  } else {
413
469
    plymouthd_argv = getargv(pid);