/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-2011 Teddy Hogeborn
6
 
 * Copyright © 2010-2011 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;
109
137
bool become_a_daemon(void){
110
138
  int ret = setuid(geteuid());
111
139
  if(ret == -1){
112
 
    error(0, errno, "setuid");
 
140
    error_plus(0, errno, "setuid");
113
141
  }
114
142
    
115
143
  setsid();
116
144
  ret = chdir("/");
117
145
  if(ret == -1){
118
 
    error(0, errno, "chdir");
 
146
    error_plus(0, errno, "chdir");
119
147
    return false;
120
148
  }
121
149
  ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
122
150
  if(ret == -1){
123
 
    error(0, errno, "dup2");
 
151
    error_plus(0, errno, "dup2");
124
152
    return false;
125
153
  }
126
154
  return true;
127
155
}
128
156
 
 
157
__attribute__((nonnull (2, 3)))
129
158
bool exec_and_wait(pid_t *pid_return, const char *path,
130
 
                   const char **argv, bool interruptable,
 
159
                   const char * const *argv, bool interruptable,
131
160
                   bool daemonize){
132
161
  int status;
133
162
  int ret;
134
163
  pid_t pid;
135
164
  pid = fork();
136
165
  if(pid == -1){
137
 
    error(0, errno, "fork");
 
166
    error_plus(0, errno, "fork");
138
167
    return false;
139
168
  }
140
169
  if(pid == 0){
150
179
    int i = 0;
151
180
    for (; argv[i]!=NULL; i++){
152
181
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
153
 
      if (tmp == NULL){
154
 
        error(0, errno, "realloc");
 
182
      if(tmp == NULL){
 
183
        error_plus(0, errno, "realloc");
155
184
        free(new_argv);
156
185
        _exit(EX_OSERR);
157
186
      }
161
190
    new_argv[i] = NULL;
162
191
    
163
192
    execv(path, (char *const *)new_argv);
164
 
    error(0, errno, "execv");
 
193
    error_plus(0, errno, "execv");
165
194
    _exit(EXIT_FAILURE);
166
195
  }
167
196
  if(pid_return != NULL){
176
205
    return false;
177
206
  }
178
207
  if(ret == -1){
179
 
    error(0, errno, "waitpid");
 
208
    error_plus(0, errno, "waitpid");
180
209
    return false;
181
210
  }
182
211
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
185
214
  return false;
186
215
}
187
216
 
 
217
__attribute__((nonnull))
188
218
int is_plymouth(const struct dirent *proc_entry){
189
219
  int ret;
190
220
  {
191
 
    uintmax_t maxvalue;
 
221
    uintmax_t proc_id;
192
222
    char *tmp;
193
223
    errno = 0;
194
 
    maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
224
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
195
225
 
196
226
    if(errno != 0 or *tmp != '\0'
197
 
       or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
227
       or proc_id != (uintmax_t)((pid_t)proc_id)){
198
228
      return 0;
199
229
    }
200
230
  }
202
232
  char *exe_link;
203
233
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
204
234
  if(ret == -1){
205
 
    error(0, errno, "asprintf");
 
235
    error_plus(0, errno, "asprintf");
206
236
    return 0;
207
237
  }
208
238
  
211
241
  if(ret == -1){
212
242
    free(exe_link);
213
243
    if(errno != ENOENT){
214
 
      error(0, errno, "lstat");
 
244
      error_plus(0, errno, "lstat");
215
245
    }
216
246
    return 0;
217
247
  }
235
265
 
236
266
pid_t get_pid(void){
237
267
  int ret;
 
268
  uintmax_t proc_id = 0;
238
269
  FILE *pidfile = fopen(plymouth_pid, "r");
239
 
  uintmax_t maxvalue = 0;
 
270
  /* Try the new pid file location */
240
271
  if(pidfile != NULL){
241
 
    ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
 
272
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
242
273
    if(ret != 1){
243
 
      maxvalue = 0;
 
274
      proc_id = 0;
244
275
    }
245
276
    fclose(pidfile);
246
277
  }
247
 
  if(maxvalue == 0){
248
 
    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;
249
292
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
250
 
    if (ret == -1){
251
 
      error(0, errno, "scandir");
 
293
    if(ret == -1){
 
294
      error_plus(0, errno, "scandir");
252
295
    }
253
 
    if (ret > 0){
254
 
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
255
 
      if (ret < 0){
256
 
        error(0, errno, "sscanf");
 
296
    if(ret > 0){
 
297
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
 
298
      if(ret < 0){
 
299
        error_plus(0, errno, "sscanf");
257
300
      }
258
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);
259
305
  }
260
306
  pid_t pid;
261
 
  pid = (pid_t)maxvalue;
262
 
  if((uintmax_t)pid == maxvalue){
 
307
  pid = (pid_t)proc_id;
 
308
  if((uintmax_t)pid == proc_id){
263
309
    return pid;
264
310
  }
265
311
  
266
312
  return 0;
267
313
}
268
314
 
269
 
const char **getargv(pid_t pid){
 
315
const char * const * getargv(pid_t pid){
270
316
  int cl_fd;
271
317
  char *cmdline_filename;
272
318
  ssize_t sret;
275
321
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
276
322
                 (uintmax_t)pid);
277
323
  if(ret == -1){
278
 
    error(0, errno, "asprintf");
 
324
    error_plus(0, errno, "asprintf");
279
325
    return NULL;
280
326
  }
281
327
  
283
329
  cl_fd = open(cmdline_filename, O_RDONLY);
284
330
  free(cmdline_filename);
285
331
  if(cl_fd == -1){
286
 
    error(0, errno, "open");
 
332
    error_plus(0, errno, "open");
287
333
    return NULL;
288
334
  }
289
335
  
297
343
    if(cmdline_len + blocksize > cmdline_allocated){
298
344
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
299
345
      if(tmp == NULL){
300
 
        error(0, errno, "realloc");
 
346
        error_plus(0, errno, "realloc");
301
347
        free(cmdline);
302
348
        close(cl_fd);
303
349
        return NULL;
310
356
    sret = read(cl_fd, cmdline + cmdline_len,
311
357
                cmdline_allocated - cmdline_len);
312
358
    if(sret == -1){
313
 
      error(0, errno, "read");
 
359
      error_plus(0, errno, "read");
314
360
      free(cmdline);
315
361
      close(cl_fd);
316
362
      return NULL;
319
365
  } while(sret != 0);
320
366
  ret = close(cl_fd);
321
367
  if(ret == -1){
322
 
    error(0, errno, "close");
 
368
    error_plus(0, errno, "close");
323
369
    free(cmdline);
324
370
    return NULL;
325
371
  }
328
374
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
329
375
                       * sizeof(char *)); /* Get number of args */
330
376
  if(argv == NULL){
331
 
    error(0, errno, "argv = malloc()");
 
377
    error_plus(0, errno, "argv = malloc()");
332
378
    free(cmdline);
333
379
    return NULL;
334
380
  }
335
381
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
336
 
  return (const char **)argv;
 
382
  return (const char * const *)argv;
337
383
}
338
384
 
339
385
int main(__attribute__((unused))int argc,
361
407
        *sig != 0; sig++){
362
408
      ret = sigaddset(&new_action.sa_mask, *sig);
363
409
      if(ret == -1){
364
 
        error(EX_OSERR, errno, "sigaddset");
 
410
        error_plus(EX_OSERR, errno, "sigaddset");
365
411
      }
366
412
      ret = sigaction(*sig, NULL, &old_action);
367
413
      if(ret == -1){
368
 
        error(EX_OSERR, errno, "sigaction");
 
414
        error_plus(EX_OSERR, errno, "sigaction");
369
415
      }
370
416
      if(old_action.sa_handler != SIG_IGN){
371
417
        ret = sigaction(*sig, &new_action, NULL);
372
418
        if(ret == -1){
373
 
          error(EX_OSERR, errno, "sigaction");
 
419
          error_plus(EX_OSERR, errno, "sigaction");
374
420
        }
375
421
      }
376
422
    }
395
441
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
396
442
  free(prompt);
397
443
  if(ret == -1){
398
 
    error(EX_OSERR, errno, "asprintf");
 
444
    error_plus(EX_OSERR, errno, "asprintf");
399
445
  }
400
446
  
401
447
  /* plymouth ask-for-password --prompt="$prompt" */
414
460
  }
415
461
  kill_and_wait(plymouth_command_pid);
416
462
  
417
 
  const char **plymouthd_argv;
 
463
  const char * const *plymouthd_argv;
418
464
  pid_t pid = get_pid();
419
465
  if(pid == 0){
420
 
    error(0, 0, "plymouthd pid not found");
 
466
    error_plus(0, 0, "plymouthd pid not found");
421
467
    plymouthd_argv = plymouthd_default_argv;
422
468
  } else {
423
469
    plymouthd_argv = getargv(pid);