/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 plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2008-12-10 01:26:02 UTC
  • mfrom: (237.1.2 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20081210012602-vhz3h75xkj24t340
First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8; mode: c; mode: orgtbl -*- */
 
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
3
 * Mandos plugin runner - Run Mandos plugins
4
4
 *
5
 
 * Copyright © 2008,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
 
5
 * Copyright © 2008 Teddy Hogeborn
 
6
 * Copyright © 2008 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
23
23
 */
24
24
 
25
25
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
 
                                   asprintf(), O_CLOEXEC */
 
26
                                   asprintf() */
27
27
#include <stddef.h>             /* size_t, NULL */
28
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
 
                                   realloc() */
 
28
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
 
29
                                   EXIT_SUCCESS, realloc() */
30
30
#include <stdbool.h>            /* bool, true, false */
31
31
#include <stdio.h>              /* perror, fileno(), fprintf(),
32
32
                                   stderr, STDOUT_FILENO */
33
 
#include <sys/types.h>          /* DIR, fdopendir(), stat(), struct
 
33
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
34
34
                                   stat, waitpid(), WIFEXITED(),
35
35
                                   WEXITSTATUS(), wait(), pid_t,
36
36
                                   uid_t, gid_t, getuid(), getgid(),
38
38
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
39
39
                                   FD_SET(), FD_ISSET(), FD_CLR */
40
40
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
41
 
                                   WEXITSTATUS(), WTERMSIG(),
42
 
                                   WCOREDUMP() */
 
41
                                   WEXITSTATUS() */
43
42
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
44
43
#include <iso646.h>             /* and, or, not */
45
 
#include <dirent.h>             /* DIR, struct dirent, fdopendir(),
 
44
#include <dirent.h>             /* DIR, struct dirent, opendir(),
46
45
                                   readdir(), closedir(), dirfd() */
47
46
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
48
47
                                   fcntl(), setuid(), setgid(),
53
52
                                   close() */
54
53
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
54
                                   FD_CLOEXEC */
56
 
#include <string.h>             /* strsep, strlen(), asprintf(),
57
 
                                   strsignal(), strcmp(), strncmp() */
 
55
#include <string.h>             /* strsep, strlen(), asprintf() */
58
56
#include <errno.h>              /* errno */
59
57
#include <argp.h>               /* struct argp_option, struct
60
58
                                   argp_state, struct argp,
64
62
#include <signal.h>             /* struct sigaction, sigemptyset(),
65
63
                                   sigaddset(), sigaction(),
66
64
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
67
 
                                   SIG_UNBLOCK, kill(), sig_atomic_t
68
 
                                */
 
65
                                   SIG_UNBLOCK, kill() */
69
66
#include <errno.h>              /* errno, EBADF */
70
 
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
71
 
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_IOERR,
72
 
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
73
67
 
74
68
#define BUFFER_SIZE 256
75
69
 
86
80
  char **environ;
87
81
  int envc;
88
82
  bool disabled;
89
 
  
 
83
 
90
84
  /* Variables used for running processes*/
91
85
  pid_t pid;
92
86
  int fd;
94
88
  size_t buffer_size;
95
89
  size_t buffer_length;
96
90
  bool eof;
97
 
  volatile sig_atomic_t completed;
98
 
  int status;
 
91
  volatile bool completed;
 
92
  volatile int status;
99
93
  struct plugin *next;
100
94
} plugin;
101
95
 
104
98
/* Gets an existing plugin based on name,
105
99
   or if none is found, creates a new one */
106
100
static plugin *getplugin(char *name){
107
 
  /* Check for existing plugin with that name */
108
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
109
 
    if((p->name == name)
110
 
       or (p->name and name and (strcmp(p->name, name) == 0))){
 
101
  /* Check for exiting plugin with that name */
 
102
  for (plugin *p = plugin_list; p != NULL; p = p->next){
 
103
    if ((p->name == name)
 
104
        or (p->name and name and (strcmp(p->name, name) == 0))){
111
105
      return p;
112
106
    }
113
107
  }
114
108
  /* Create a new plugin */
115
 
  plugin *new_plugin = NULL;
116
 
  do {
117
 
    new_plugin = malloc(sizeof(plugin));
118
 
  } while(new_plugin == NULL and errno == EINTR);
119
 
  if(new_plugin == NULL){
 
109
  plugin *new_plugin = malloc(sizeof(plugin));
 
110
  if (new_plugin == NULL){
120
111
    return NULL;
121
112
  }
122
113
  char *copy_name = NULL;
123
114
  if(name != NULL){
124
 
    do {
125
 
      copy_name = strdup(name);
126
 
    } while(copy_name == NULL and errno == EINTR);
 
115
    copy_name = strdup(name);
127
116
    if(copy_name == NULL){
128
 
      int e = errno;
129
 
      free(new_plugin);
130
 
      errno = e;
131
117
      return NULL;
132
118
    }
133
119
  }
134
120
  
135
 
  *new_plugin = (plugin){ .name = copy_name,
136
 
                          .argc = 1,
137
 
                          .disabled = false,
138
 
                          .next = plugin_list };
 
121
  *new_plugin = (plugin) { .name = copy_name,
 
122
                           .argc = 1,
 
123
                           .disabled = false,
 
124
                           .next = plugin_list };
139
125
  
140
 
  do {
141
 
    new_plugin->argv = malloc(sizeof(char *) * 2);
142
 
  } while(new_plugin->argv == NULL and errno == EINTR);
143
 
  if(new_plugin->argv == NULL){
144
 
    int e = errno;
 
126
  new_plugin->argv = malloc(sizeof(char *) * 2);
 
127
  if (new_plugin->argv == NULL){
145
128
    free(copy_name);
146
129
    free(new_plugin);
147
 
    errno = e;
148
130
    return NULL;
149
131
  }
150
132
  new_plugin->argv[0] = copy_name;
151
133
  new_plugin->argv[1] = NULL;
152
134
  
153
 
  do {
154
 
    new_plugin->environ = malloc(sizeof(char *));
155
 
  } while(new_plugin->environ == NULL and errno == EINTR);
 
135
  new_plugin->environ = malloc(sizeof(char *));
156
136
  if(new_plugin->environ == NULL){
157
 
    int e = errno;
158
137
    free(copy_name);
159
138
    free(new_plugin->argv);
160
139
    free(new_plugin);
161
 
    errno = e;
162
140
    return NULL;
163
141
  }
164
142
  new_plugin->environ[0] = NULL;
172
150
static bool add_to_char_array(const char *new, char ***array,
173
151
                              int *len){
174
152
  /* Resize the pointed-to array to hold one more pointer */
175
 
  do {
176
 
    *array = realloc(*array, sizeof(char *)
177
 
                     * (size_t) ((*len) + 2));
178
 
  } while(*array == NULL and errno == EINTR);
 
153
  *array = realloc(*array, sizeof(char *)
 
154
                   * (size_t) ((*len) + 2));
179
155
  /* Malloc check */
180
156
  if(*array == NULL){
181
157
    return false;
182
158
  }
183
159
  /* Make a copy of the new string */
184
 
  char *copy;
185
 
  do {
186
 
    copy = strdup(new);
187
 
  } while(copy == NULL and errno == EINTR);
 
160
  char *copy = strdup(new);
188
161
  if(copy == NULL){
189
162
    return false;
190
163
  }
216
189
    if(strncmp(*e, def, namelen + 1) == 0){
217
190
      /* It already exists */
218
191
      if(replace){
219
 
        char *new;
220
 
        do {
221
 
          new = realloc(*e, strlen(def) + 1);
222
 
        } while(new == NULL and errno == EINTR);
 
192
        char *new = realloc(*e, strlen(def) + 1);
223
193
        if(new == NULL){
224
194
          return false;
225
195
        }
235
205
/*
236
206
 * Based on the example in the GNU LibC manual chapter 13.13 "File
237
207
 * Descriptor Flags".
238
 
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
 
208
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
239
209
 */
240
210
static int set_cloexec_flag(int fd){
241
 
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
 
211
  int ret = fcntl(fd, F_GETFD, 0);
242
212
  /* If reading the flags failed, return error indication now. */
243
213
  if(ret < 0){
244
214
    return ret;
245
215
  }
246
216
  /* Store modified flag word in the descriptor. */
247
 
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
248
 
                                       ret | FD_CLOEXEC));
 
217
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
249
218
}
250
219
 
251
220
 
252
221
/* Mark processes as completed when they exit, and save their exit
253
222
   status. */
254
223
static void handle_sigchld(__attribute__((unused)) int sig){
255
 
  int old_errno = errno;
256
224
  while(true){
257
225
    plugin *proc = plugin_list;
258
226
    int status;
262
230
      break;
263
231
    }
264
232
    if(pid == -1){
265
 
      if(errno == ECHILD){
266
 
        /* No child processes */
267
 
        break;
 
233
      if (errno != ECHILD){
 
234
        perror("waitpid");
268
235
      }
269
 
      perror("waitpid");
 
236
      /* No child processes */
 
237
      break;
270
238
    }
271
239
    
272
240
    /* A child exited, find it in process_list */
278
246
      continue;
279
247
    }
280
248
    proc->status = status;
281
 
    proc->completed = 1;
 
249
    proc->completed = true;
282
250
  }
283
 
  errno = old_errno;
284
251
}
285
252
 
286
253
/* Prints out a password to stdout */
308
275
  }
309
276
  free(plugin_node->environ);
310
277
  free(plugin_node->buffer);
311
 
  
 
278
 
312
279
  /* Removes the plugin from the singly-linked list */
313
280
  if(plugin_node == plugin_list){
314
281
    /* First one - simple */
342
309
  struct stat st;
343
310
  fd_set rfds_all;
344
311
  int ret, maxfd = 0;
345
 
  ssize_t sret;
346
312
  uid_t uid = 65534;
347
313
  gid_t gid = 65534;
348
314
  bool debug = false;
358
324
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
359
325
  if(ret == -1){
360
326
    perror("sigaddset");
361
 
    exitstatus = EX_OSERR;
 
327
    exitstatus = EXIT_FAILURE;
362
328
    goto fallback;
363
329
  }
364
330
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
365
331
  if(ret == -1){
366
332
    perror("sigaction");
367
 
    exitstatus = EX_OSERR;
 
333
    exitstatus = EXIT_FAILURE;
368
334
    goto fallback;
369
335
  }
370
336
  
402
368
      .doc = "Group ID the plugins will run as", .group = 3 },
403
369
    { .name = "debug", .key = 132,
404
370
      .doc = "Debug mode", .group = 4 },
405
 
    /*
406
 
     * These reproduce what we would get without ARGP_NO_HELP
407
 
     */
408
 
    { .name = "help", .key = '?',
409
 
      .doc = "Give this help list", .group = -1 },
410
 
    { .name = "usage", .key = -3,
411
 
      .doc = "Give a short usage message", .group = -1 },
412
 
    { .name = "version", .key = 'V',
413
 
      .doc = "Print program version", .group = -1 },
414
371
    { .name = NULL }
415
372
  };
416
373
  
417
 
  error_t parse_opt(int key, char *arg, struct argp_state *state){
418
 
    errno = 0;
419
 
    switch(key){
420
 
      char *tmp;
421
 
      intmax_t tmpmax;
 
374
  error_t parse_opt (int key, char *arg, __attribute__((unused))
 
375
                     struct argp_state *state) {
 
376
    switch (key) {
422
377
    case 'g':                   /* --global-options */
423
 
      {
424
 
        char *plugin_option;
425
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
426
 
          if(not add_argument(getplugin(NULL), plugin_option)){
427
 
            break;
 
378
      if (arg != NULL){
 
379
        char *p;
 
380
        while((p = strsep(&arg, ",")) != NULL){
 
381
          if(p[0] == '\0'){
 
382
            continue;
 
383
          }
 
384
          if(not add_argument(getplugin(NULL), p)){
 
385
            perror("add_argument");
 
386
            return ARGP_ERR_UNKNOWN;
428
387
          }
429
388
        }
430
389
      }
431
390
      break;
432
391
    case 'G':                   /* --global-env */
433
 
      add_environment(getplugin(NULL), arg, true);
 
392
      if(arg == NULL){
 
393
        break;
 
394
      }
 
395
      if(not add_environment(getplugin(NULL), arg, true)){
 
396
        perror("add_environment");
 
397
      }
434
398
      break;
435
399
    case 'o':                   /* --options-for */
436
 
      {
437
 
        char *option_list = strchr(arg, ':');
438
 
        if(option_list == NULL){
439
 
          argp_error(state, "No colon in \"%s\"", arg);
440
 
          errno = EINVAL;
441
 
          break;
442
 
        }
443
 
        *option_list = '\0';
444
 
        option_list++;
445
 
        if(arg[0] == '\0'){
446
 
          argp_error(state, "Empty plugin name");
447
 
          errno = EINVAL;
448
 
          break;
449
 
        }
450
 
        char *option;
451
 
        while((option = strsep(&option_list, ",")) != NULL){
452
 
          if(not add_argument(getplugin(arg), option)){
453
 
            break;
 
400
      if (arg != NULL){
 
401
        char *p_name = strsep(&arg, ":");
 
402
        if(p_name[0] == '\0' or arg == NULL){
 
403
          break;
 
404
        }
 
405
        char *opt = strsep(&arg, ":");
 
406
        if(opt[0] == '\0' or opt == NULL){
 
407
          break;
 
408
        }
 
409
        char *p;
 
410
        while((p = strsep(&opt, ",")) != NULL){
 
411
          if(p[0] == '\0'){
 
412
            continue;
 
413
          }
 
414
          if(not add_argument(getplugin(p_name), p)){
 
415
            perror("add_argument");
 
416
            return ARGP_ERR_UNKNOWN;
454
417
          }
455
418
        }
456
419
      }
457
420
      break;
458
421
    case 'E':                   /* --env-for */
 
422
      if(arg == NULL){
 
423
        break;
 
424
      }
459
425
      {
460
426
        char *envdef = strchr(arg, ':');
461
427
        if(envdef == NULL){
462
 
          argp_error(state, "No colon in \"%s\"", arg);
463
 
          errno = EINVAL;
464
428
          break;
465
429
        }
466
430
        *envdef = '\0';
467
 
        envdef++;
468
 
        if(arg[0] == '\0'){
469
 
          argp_error(state, "Empty plugin name");
470
 
          errno = EINVAL;
471
 
          break;
 
431
        if(not add_environment(getplugin(arg), envdef+1, true)){
 
432
          perror("add_environment");
472
433
        }
473
 
        add_environment(getplugin(arg), envdef, true);
474
434
      }
475
435
      break;
476
436
    case 'd':                   /* --disable */
477
 
      {
 
437
      if (arg != NULL){
478
438
        plugin *p = getplugin(arg);
479
 
        if(p != NULL){
480
 
          p->disabled = true;
 
439
        if(p == NULL){
 
440
          return ARGP_ERR_UNKNOWN;
481
441
        }
 
442
        p->disabled = true;
482
443
      }
483
444
      break;
484
445
    case 'e':                   /* --enable */
485
 
      {
 
446
      if (arg != NULL){
486
447
        plugin *p = getplugin(arg);
487
 
        if(p != NULL){
488
 
          p->disabled = false;
 
448
        if(p == NULL){
 
449
          return ARGP_ERR_UNKNOWN;
489
450
        }
 
451
        p->disabled = false;
490
452
      }
491
453
      break;
492
454
    case 128:                   /* --plugin-dir */
493
455
      free(plugindir);
494
456
      plugindir = strdup(arg);
 
457
      if(plugindir == NULL){
 
458
        perror("strdup");
 
459
      }      
495
460
      break;
496
461
    case 129:                   /* --config-file */
497
462
      /* This is already done by parse_opt_config_file() */
498
463
      break;
499
464
    case 130:                   /* --userid */
500
 
      tmpmax = strtoimax(arg, &tmp, 10);
501
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
502
 
         or tmpmax != (uid_t)tmpmax){
503
 
        argp_error(state, "Bad user ID number: \"%s\", using %"
504
 
                   PRIdMAX, arg, (intmax_t)uid);
505
 
        break;
506
 
      }
507
 
      uid = (uid_t)tmpmax;
 
465
      uid = (uid_t)strtol(arg, NULL, 10);
508
466
      break;
509
467
    case 131:                   /* --groupid */
510
 
      tmpmax = strtoimax(arg, &tmp, 10);
511
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
512
 
         or tmpmax != (gid_t)tmpmax){
513
 
        argp_error(state, "Bad group ID number: \"%s\", using %"
514
 
                   PRIdMAX, arg, (intmax_t)gid);
515
 
        break;
516
 
      }
517
 
      gid = (gid_t)tmpmax;
 
468
      gid = (gid_t)strtol(arg, NULL, 10);
518
469
      break;
519
470
    case 132:                   /* --debug */
520
471
      debug = true;
521
472
      break;
522
 
      /*
523
 
       * These reproduce what we would get without ARGP_NO_HELP
524
 
       */
525
 
    case '?':                   /* --help */
526
 
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
527
 
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
528
 
    case -3:                    /* --usage */
529
 
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
530
 
      argp_state_help(state, state->out_stream,
531
 
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
532
 
    case 'V':                   /* --version */
533
 
      fprintf(state->out_stream, "%s\n", argp_program_version);
534
 
      exit(EXIT_SUCCESS);
535
 
      break;
536
 
/*
537
 
 * When adding more options before this line, remember to also add a
538
 
 * "case" to the "parse_opt_config_file" function below.
539
 
 */
540
473
    case ARGP_KEY_ARG:
541
474
      /* Cryptsetup always passes an argument, which is an empty
542
475
         string if "none" was specified in /etc/crypttab.  So if
543
476
         argument was empty, we ignore it silently. */
544
 
      if(arg[0] == '\0'){
545
 
        break;
 
477
      if(arg[0] != '\0'){
 
478
        fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
546
479
      }
 
480
      break;
 
481
    case ARGP_KEY_END:
 
482
      break;
547
483
    default:
548
484
      return ARGP_ERR_UNKNOWN;
549
485
    }
550
 
    return errno;               /* Set to 0 at start */
 
486
    return 0;
551
487
  }
552
488
  
553
489
  /* This option parser is the same as parse_opt() above, except it
554
490
     ignores everything but the --config-file option. */
555
 
  error_t parse_opt_config_file(int key, char *arg,
556
 
                                __attribute__((unused))
557
 
                                struct argp_state *state){
558
 
    errno = 0;
559
 
    switch(key){
 
491
  error_t parse_opt_config_file (int key, char *arg,
 
492
                                 __attribute__((unused))
 
493
                                 struct argp_state *state) {
 
494
    switch (key) {
560
495
    case 'g':                   /* --global-options */
561
496
    case 'G':                   /* --global-env */
562
497
    case 'o':                   /* --options-for */
568
503
    case 129:                   /* --config-file */
569
504
      free(argfile);
570
505
      argfile = strdup(arg);
571
 
      break;
 
506
      if(argfile == NULL){
 
507
        perror("strdup");
 
508
      }
 
509
      break;      
572
510
    case 130:                   /* --userid */
573
511
    case 131:                   /* --groupid */
574
512
    case 132:                   /* --debug */
575
 
    case '?':                   /* --help */
576
 
    case -3:                    /* --usage */
577
 
    case 'V':                   /* --version */
578
513
    case ARGP_KEY_ARG:
 
514
    case ARGP_KEY_END:
579
515
      break;
580
516
    default:
581
517
      return ARGP_ERR_UNKNOWN;
582
518
    }
583
 
    return errno;
 
519
    return 0;
584
520
  }
585
521
  
586
522
  struct argp argp = { .options = options,
588
524
                       .args_doc = "",
589
525
                       .doc = "Mandos plugin runner -- Run plugins" };
590
526
  
591
 
  /* Parse using parse_opt_config_file() in order to get the custom
 
527
  /* Parse using the parse_opt_config_file in order to get the custom
592
528
     config file location, if any. */
593
 
  ret = argp_parse(&argp, argc, argv,
594
 
                   ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
595
 
                   NULL, NULL);
596
 
  switch(ret){
597
 
  case 0:
598
 
    break;
599
 
  case ENOMEM:
600
 
  default:
601
 
    errno = ret;
602
 
    perror("argp_parse");
603
 
    exitstatus = EX_OSERR;
604
 
    goto fallback;
605
 
  case EINVAL:
606
 
    exitstatus = EX_USAGE;
 
529
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
530
  if (ret == ARGP_ERR_UNKNOWN){
 
531
    fprintf(stderr, "Unknown error while parsing arguments\n");
 
532
    exitstatus = EXIT_FAILURE;
607
533
    goto fallback;
608
534
  }
609
535
  
611
537
  argp.parser = parse_opt;
612
538
  
613
539
  /* Open the configfile if available */
614
 
  if(argfile == NULL){
 
540
  if (argfile == NULL){
615
541
    conffp = fopen(AFILE, "r");
616
542
  } else {
617
543
    conffp = fopen(argfile, "r");
618
 
  }
 
544
  }  
619
545
  if(conffp != NULL){
620
546
    char *org_line = NULL;
621
547
    char *p, *arg, *new_arg, *line;
622
548
    size_t size = 0;
 
549
    ssize_t sret;
623
550
    const char whitespace_delims[] = " \r\t\f\v\n";
624
551
    const char comment_delim[] = "#";
625
 
    
 
552
 
626
553
    custom_argc = 1;
627
554
    custom_argv = malloc(sizeof(char*) * 2);
628
555
    if(custom_argv == NULL){
629
556
      perror("malloc");
630
 
      exitstatus = EX_OSERR;
 
557
      exitstatus = EXIT_FAILURE;
631
558
      goto fallback;
632
559
    }
633
560
    custom_argv[0] = argv[0];
634
561
    custom_argv[1] = NULL;
635
 
    
 
562
 
636
563
    /* for each line in the config file, strip whitespace and ignore
637
564
       commented text */
638
565
    while(true){
640
567
      if(sret == -1){
641
568
        break;
642
569
      }
643
 
      
 
570
 
644
571
      line = org_line;
645
572
      arg = strsep(&line, comment_delim);
646
573
      while((p = strsep(&arg, whitespace_delims)) != NULL){
650
577
        new_arg = strdup(p);
651
578
        if(new_arg == NULL){
652
579
          perror("strdup");
653
 
          exitstatus = EX_OSERR;
 
580
          exitstatus = EXIT_FAILURE;
654
581
          free(org_line);
655
582
          goto fallback;
656
583
        }
660
587
                              * ((unsigned int) custom_argc + 1));
661
588
        if(custom_argv == NULL){
662
589
          perror("realloc");
663
 
          exitstatus = EX_OSERR;
 
590
          exitstatus = EXIT_FAILURE;
664
591
          free(org_line);
665
592
          goto fallback;
666
593
        }
667
594
        custom_argv[custom_argc-1] = new_arg;
668
 
        custom_argv[custom_argc] = NULL;
 
595
        custom_argv[custom_argc] = NULL;        
669
596
      }
670
597
    }
671
 
    do {
672
 
      ret = fclose(conffp);
673
 
    } while(ret == EOF and errno == EINTR);
674
 
    if(ret == EOF){
675
 
      perror("fclose");
676
 
      exitstatus = EX_IOERR;
677
 
      goto fallback;
678
 
    }
679
598
    free(org_line);
680
599
  } else {
681
600
    /* Check for harmful errors and go to fallback. Other errors might
682
601
       not affect opening plugins */
683
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
602
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
684
603
      perror("fopen");
685
 
      exitstatus = EX_OSERR;
 
604
      exitstatus = EXIT_FAILURE;
686
605
      goto fallback;
687
606
    }
688
607
  }
689
 
  /* If there were any arguments from the configuration file, pass
690
 
     them to parser as command line arguments */
 
608
  /* If there was any arguments from configuration file,
 
609
     pass them to parser as command arguments */
691
610
  if(custom_argv != NULL){
692
 
    ret = argp_parse(&argp, custom_argc, custom_argv,
693
 
                     ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
694
 
                     NULL, NULL);
695
 
    switch(ret){
696
 
    case 0:
697
 
      break;
698
 
    case ENOMEM:
699
 
    default:
700
 
      errno = ret;
701
 
      perror("argp_parse");
702
 
      exitstatus = EX_OSERR;
703
 
      goto fallback;
704
 
    case EINVAL:
705
 
      exitstatus = EX_CONFIG;
 
611
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
 
612
                      0, NULL);
 
613
    if (ret == ARGP_ERR_UNKNOWN){
 
614
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
615
      exitstatus = EXIT_FAILURE;
706
616
      goto fallback;
707
617
    }
708
618
  }
709
619
  
710
620
  /* Parse actual command line arguments, to let them override the
711
621
     config file */
712
 
  ret = argp_parse(&argp, argc, argv,
713
 
                   ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
714
 
                   NULL, NULL);
715
 
  switch(ret){
716
 
  case 0:
717
 
    break;
718
 
  case ENOMEM:
719
 
  default:
720
 
    errno = ret;
721
 
    perror("argp_parse");
722
 
    exitstatus = EX_OSERR;
723
 
    goto fallback;
724
 
  case EINVAL:
725
 
    exitstatus = EX_USAGE;
 
622
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
623
  if (ret == ARGP_ERR_UNKNOWN){
 
624
    fprintf(stderr, "Unknown error while parsing arguments\n");
 
625
    exitstatus = EXIT_FAILURE;
726
626
    goto fallback;
727
627
  }
728
628
  
733
633
      for(char **a = p->argv; *a != NULL; a++){
734
634
        fprintf(stderr, "\tArg: %s\n", *a);
735
635
      }
736
 
      fprintf(stderr, "...and %d environment variables\n", p->envc);
 
636
      fprintf(stderr, "...and %u environment variables\n", p->envc);
737
637
      for(char **a = p->environ; *a != NULL; a++){
738
638
        fprintf(stderr, "\t%s\n", *a);
739
639
      }
741
641
  }
742
642
  
743
643
  /* Strip permissions down to nobody */
 
644
  ret = setuid(uid);
 
645
  if (ret == -1){
 
646
    perror("setuid");
 
647
  }  
744
648
  setgid(gid);
745
 
  if(ret == -1){
 
649
  if (ret == -1){
746
650
    perror("setgid");
747
651
  }
748
 
  ret = setuid(uid);
749
 
  if(ret == -1){
750
 
    perror("setuid");
751
 
  }
752
 
  
753
 
  /* Open plugin directory with close_on_exec flag */
 
652
  
 
653
  if (plugindir == NULL){
 
654
    dir = opendir(PDIR);
 
655
  } else {
 
656
    dir = opendir(plugindir);
 
657
  }
 
658
  
 
659
  if(dir == NULL){
 
660
    perror("Could not open plugin dir");
 
661
    exitstatus = EXIT_FAILURE;
 
662
    goto fallback;
 
663
  }
 
664
  
 
665
  /* Set the FD_CLOEXEC flag on the directory, if possible */
754
666
  {
755
 
    int dir_fd = -1;
756
 
    if(plugindir == NULL){
757
 
      dir_fd = open(PDIR, O_RDONLY |
758
 
#ifdef O_CLOEXEC
759
 
                    O_CLOEXEC
760
 
#else  /* not O_CLOEXEC */
761
 
                    0
762
 
#endif  /* not O_CLOEXEC */
763
 
                    );
764
 
    } else {
765
 
      dir_fd = open(plugindir, O_RDONLY |
766
 
#ifdef O_CLOEXEC
767
 
                    O_CLOEXEC
768
 
#else  /* not O_CLOEXEC */
769
 
                    0
770
 
#endif  /* not O_CLOEXEC */
771
 
                    );
772
 
    }
773
 
    if(dir_fd == -1){
774
 
      perror("Could not open plugin dir");
775
 
      exitstatus = EX_UNAVAILABLE;
776
 
      goto fallback;
777
 
    }
778
 
    
779
 
#ifndef O_CLOEXEC
780
 
  /* Set the FD_CLOEXEC flag on the directory */
781
 
    ret = set_cloexec_flag(dir_fd);
782
 
    if(ret < 0){
783
 
      perror("set_cloexec_flag");
784
 
      TEMP_FAILURE_RETRY(close(dir_fd));
785
 
      exitstatus = EX_OSERR;
786
 
      goto fallback;
787
 
    }
788
 
#endif  /* O_CLOEXEC */
789
 
    
790
 
    dir = fdopendir(dir_fd);
791
 
    if(dir == NULL){
792
 
      perror("Could not open plugin dir");
793
 
      TEMP_FAILURE_RETRY(close(dir_fd));
794
 
      exitstatus = EX_OSERR;
795
 
      goto fallback;
 
667
    int dir_fd = dirfd(dir);
 
668
    if(dir_fd >= 0){
 
669
      ret = set_cloexec_flag(dir_fd);
 
670
      if(ret < 0){
 
671
        perror("set_cloexec_flag");
 
672
        exitstatus = EXIT_FAILURE;
 
673
        goto fallback;
 
674
      }
796
675
    }
797
676
  }
798
677
  
800
679
  
801
680
  /* Read and execute any executable in the plugin directory*/
802
681
  while(true){
803
 
    do {
804
 
      dirst = readdir(dir);
805
 
    } while(dirst == NULL and errno == EINTR);
 
682
    dirst = readdir(dir);
806
683
    
807
684
    /* All directory entries have been processed */
808
685
    if(dirst == NULL){
809
 
      if(errno == EBADF){
 
686
      if (errno == EBADF){
810
687
        perror("readdir");
811
 
        exitstatus = EX_IOERR;
 
688
        exitstatus = EXIT_FAILURE;
812
689
        goto fallback;
813
690
      }
814
691
      break;
844
721
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
845
722
        size_t suf_len = strlen(*suf);
846
723
        if((d_name_len >= suf_len)
847
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
 
724
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
848
725
                == 0)){
849
726
          if(debug){
850
727
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
859
736
        continue;
860
737
      }
861
738
    }
862
 
    
 
739
 
863
740
    char *filename;
864
741
    if(plugindir == NULL){
865
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
866
 
                                             dirst->d_name));
 
742
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
867
743
    } else {
868
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
869
 
                                             plugindir,
870
 
                                             dirst->d_name));
 
744
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
871
745
    }
872
746
    if(ret < 0){
873
747
      perror("asprintf");
874
748
      continue;
875
749
    }
876
750
    
877
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
878
 
    if(ret == -1){
 
751
    ret = stat(filename, &st);
 
752
    if (ret == -1){
879
753
      perror("stat");
880
754
      free(filename);
881
755
      continue;
882
756
    }
883
 
    
 
757
 
884
758
    /* Ignore non-executable files */
885
 
    if(not S_ISREG(st.st_mode)
886
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
759
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
887
760
      if(debug){
888
761
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
889
762
                " with bad type or mode\n", filename);
935
808
    }
936
809
    
937
810
    int pipefd[2];
938
 
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
939
 
    if(ret == -1){
 
811
    ret = pipe(pipefd);
 
812
    if (ret == -1){
940
813
      perror("pipe");
941
 
      exitstatus = EX_OSERR;
 
814
      exitstatus = EXIT_FAILURE;
942
815
      goto fallback;
943
816
    }
944
817
    /* Ask OS to automatic close the pipe on exec */
945
818
    ret = set_cloexec_flag(pipefd[0]);
946
819
    if(ret < 0){
947
820
      perror("set_cloexec_flag");
948
 
      exitstatus = EX_OSERR;
 
821
      exitstatus = EXIT_FAILURE;
949
822
      goto fallback;
950
823
    }
951
824
    ret = set_cloexec_flag(pipefd[1]);
952
825
    if(ret < 0){
953
826
      perror("set_cloexec_flag");
954
 
      exitstatus = EX_OSERR;
 
827
      exitstatus = EXIT_FAILURE;
955
828
      goto fallback;
956
829
    }
957
830
    /* Block SIGCHLD until process is safely in process list */
958
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
959
 
                                              &sigchld_action.sa_mask,
960
 
                                              NULL));
 
831
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
961
832
    if(ret < 0){
962
833
      perror("sigprocmask");
963
 
      exitstatus = EX_OSERR;
 
834
      exitstatus = EXIT_FAILURE;
964
835
      goto fallback;
965
836
    }
966
837
    /* Starting a new process to be watched */
967
 
    pid_t pid;
968
 
    do {
969
 
      pid = fork();
970
 
    } while(pid == -1 and errno == EINTR);
 
838
    pid_t pid = fork();
971
839
    if(pid == -1){
972
840
      perror("fork");
973
 
      exitstatus = EX_OSERR;
 
841
      exitstatus = EXIT_FAILURE;
974
842
      goto fallback;
975
843
    }
976
844
    if(pid == 0){
978
846
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
979
847
      if(ret < 0){
980
848
        perror("sigaction");
981
 
        _exit(EX_OSERR);
 
849
        _exit(EXIT_FAILURE);
982
850
      }
983
851
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
984
852
      if(ret < 0){
985
853
        perror("sigprocmask");
986
 
        _exit(EX_OSERR);
 
854
        _exit(EXIT_FAILURE);
987
855
      }
988
856
      
989
857
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
990
858
      if(ret == -1){
991
859
        perror("dup2");
992
 
        _exit(EX_OSERR);
 
860
        _exit(EXIT_FAILURE);
993
861
      }
994
862
      
995
863
      if(dirfd(dir) < 0){
1000
868
      if(p->environ[0] == NULL){
1001
869
        if(execv(filename, p->argv) < 0){
1002
870
          perror("execv");
1003
 
          _exit(EX_OSERR);
 
871
          _exit(EXIT_FAILURE);
1004
872
        }
1005
873
      } else {
1006
874
        if(execve(filename, p->argv, p->environ) < 0){
1007
875
          perror("execve");
1008
 
          _exit(EX_OSERR);
 
876
          _exit(EXIT_FAILURE);
1009
877
        }
1010
878
      }
1011
879
      /* no return */
1012
880
    }
1013
881
    /* Parent process */
1014
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1015
 
                                             pipe */
 
882
    close(pipefd[1]);           /* Close unused write end of pipe */
1016
883
    free(filename);
1017
884
    plugin *new_plugin = getplugin(dirst->d_name);
1018
 
    if(new_plugin == NULL){
 
885
    if (new_plugin == NULL){
1019
886
      perror("getplugin");
1020
 
      ret = (int)(TEMP_FAILURE_RETRY
1021
 
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1022
 
                               NULL)));
 
887
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1023
888
      if(ret < 0){
1024
889
        perror("sigprocmask");
1025
890
      }
1026
 
      exitstatus = EX_OSERR;
 
891
      exitstatus = EXIT_FAILURE;
1027
892
      goto fallback;
1028
893
    }
1029
894
    
1032
897
    
1033
898
    /* Unblock SIGCHLD so signal handler can be run if this process
1034
899
       has already completed */
1035
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1036
 
                                              &sigchld_action.sa_mask,
1037
 
                                              NULL));
 
900
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1038
901
    if(ret < 0){
1039
902
      perror("sigprocmask");
1040
 
      exitstatus = EX_OSERR;
 
903
      exitstatus = EXIT_FAILURE;
1041
904
      goto fallback;
1042
905
    }
1043
906
    
1044
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1045
 
                                          -Wconversion */
 
907
    FD_SET(new_plugin->fd, &rfds_all);
1046
908
    
1047
 
    if(maxfd < new_plugin->fd){
 
909
    if (maxfd < new_plugin->fd){
1048
910
      maxfd = new_plugin->fd;
1049
911
    }
1050
912
  }
1051
913
  
1052
 
  TEMP_FAILURE_RETRY(closedir(dir));
 
914
  closedir(dir);
1053
915
  dir = NULL;
1054
 
  free_plugin(getplugin(NULL));
1055
916
  
1056
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1057
918
    if(p->pid != 0){
1068
929
  while(plugin_list){
1069
930
    fd_set rfds = rfds_all;
1070
931
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1071
 
    if(select_ret == -1 and errno != EINTR){
 
932
    if (select_ret == -1){
1072
933
      perror("select");
1073
 
      exitstatus = EX_OSERR;
 
934
      exitstatus = EXIT_FAILURE;
1074
935
      goto fallback;
1075
936
    }
1076
937
    /* OK, now either a process completed, or something can be read
1077
938
       from one of them */
1078
939
    for(plugin *proc = plugin_list; proc != NULL;){
1079
940
      /* Is this process completely done? */
1080
 
      if(proc->completed and proc->eof){
 
941
      if(proc->eof and proc->completed){
1081
942
        /* Only accept the plugin output if it exited cleanly */
1082
943
        if(not WIFEXITED(proc->status)
1083
944
           or WEXITSTATUS(proc->status) != 0){
1084
945
          /* Bad exit by plugin */
1085
 
          
 
946
 
1086
947
          if(debug){
1087
948
            if(WIFEXITED(proc->status)){
1088
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
1089
 
                      " status %d\n", proc->name,
1090
 
                      (intmax_t) (proc->pid),
 
949
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
950
                      (unsigned int) (proc->pid),
1091
951
                      WEXITSTATUS(proc->status));
1092
 
            } else if(WIFSIGNALED(proc->status)){
1093
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
1094
 
                      " signal %d: %s\n", proc->name,
1095
 
                      (intmax_t) (proc->pid),
1096
 
                      WTERMSIG(proc->status),
1097
 
                      strsignal(WTERMSIG(proc->status)));
 
952
            } else if(WIFSIGNALED(proc->status)) {
 
953
              fprintf(stderr, "Plugin %u killed by signal %d\n",
 
954
                      (unsigned int) (proc->pid),
 
955
                      WTERMSIG(proc->status));
1098
956
            } else if(WCOREDUMP(proc->status)){
1099
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1100
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
 
957
              fprintf(stderr, "Plugin %d dumped core\n",
 
958
                      (unsigned int) (proc->pid));
1101
959
            }
1102
960
          }
1103
961
          
1104
962
          /* Remove the plugin */
1105
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1106
 
                                          -Wconversion */
1107
 
          
 
963
          FD_CLR(proc->fd, &rfds_all);
 
964
 
1108
965
          /* Block signal while modifying process_list */
1109
 
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1110
 
                                        (SIG_BLOCK,
1111
 
                                         &sigchld_action.sa_mask,
1112
 
                                         NULL));
 
966
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1113
967
          if(ret < 0){
1114
968
            perror("sigprocmask");
1115
 
            exitstatus = EX_OSERR;
 
969
            exitstatus = EXIT_FAILURE;
1116
970
            goto fallback;
1117
971
          }
1118
972
          
1121
975
          proc = next_plugin;
1122
976
          
1123
977
          /* We are done modifying process list, so unblock signal */
1124
 
          ret = (int)(TEMP_FAILURE_RETRY
1125
 
                      (sigprocmask(SIG_UNBLOCK,
1126
 
                                   &sigchld_action.sa_mask, NULL)));
 
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
979
                             NULL);
1127
980
          if(ret < 0){
1128
981
            perror("sigprocmask");
1129
 
            exitstatus = EX_OSERR;
 
982
            exitstatus = EXIT_FAILURE;
1130
983
            goto fallback;
1131
984
          }
1132
985
          
1143
996
                                       proc->buffer_length);
1144
997
        if(not bret){
1145
998
          perror("print_out_password");
1146
 
          exitstatus = EX_IOERR;
 
999
          exitstatus = EXIT_FAILURE;
1147
1000
        }
1148
1001
        goto fallback;
1149
1002
      }
1150
1003
      
1151
1004
      /* This process has not completed.  Does it have any output? */
1152
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1153
 
                                                         warning from
1154
 
                                                         -Wconversion */
 
1005
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1155
1006
        /* This process had nothing to say at this time */
1156
1007
        proc = proc->next;
1157
1008
        continue;
1160
1011
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1161
1012
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1162
1013
                               + (size_t) BUFFER_SIZE);
1163
 
        if(proc->buffer == NULL){
 
1014
        if (proc->buffer == NULL){
1164
1015
          perror("malloc");
1165
 
          exitstatus = EX_OSERR;
 
1016
          exitstatus = EXIT_FAILURE;
1166
1017
          goto fallback;
1167
1018
        }
1168
1019
        proc->buffer_size += BUFFER_SIZE;
1169
1020
      }
1170
1021
      /* Read from the process */
1171
 
      sret = TEMP_FAILURE_RETRY(read(proc->fd,
1172
 
                                     proc->buffer
1173
 
                                     + proc->buffer_length,
1174
 
                                     BUFFER_SIZE));
1175
 
      if(sret < 0){
 
1022
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
1023
                 BUFFER_SIZE);
 
1024
      if(ret < 0){
1176
1025
        /* Read error from this process; ignore the error */
1177
1026
        proc = proc->next;
1178
1027
        continue;
1179
1028
      }
1180
 
      if(sret == 0){
 
1029
      if(ret == 0){
1181
1030
        /* got EOF */
1182
1031
        proc->eof = true;
1183
1032
      } else {
1184
 
        proc->buffer_length += (size_t) sret;
 
1033
        proc->buffer_length += (size_t) ret;
1185
1034
      }
1186
1035
    }
1187
1036
  }
1188
 
  
1189
 
  
 
1037
 
 
1038
 
1190
1039
 fallback:
1191
1040
  
1192
 
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
1193
 
                             and exitstatus != EX_OK)){
 
1041
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1194
1042
    /* Fallback if all plugins failed, none are found or an error
1195
1043
       occured */
1196
1044
    bool bret;
1205
1053
    bret = print_out_password(passwordbuffer, len);
1206
1054
    if(not bret){
1207
1055
      perror("print_out_password");
1208
 
      exitstatus = EX_IOERR;
 
1056
      exitstatus = EXIT_FAILURE;
1209
1057
    }
1210
1058
  }
1211
1059
  
1213
1061
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1214
1062
  if(ret == -1){
1215
1063
    perror("sigaction");
1216
 
    exitstatus = EX_OSERR;
 
1064
    exitstatus = EXIT_FAILURE;
1217
1065
  }
1218
1066
  
1219
1067
  if(custom_argv != NULL){
1240
1088
  }
1241
1089
  
1242
1090
  /* Wait for any remaining child processes to terminate */
1243
 
  do {
 
1091
  do{
1244
1092
    ret = wait(NULL);
1245
1093
  } while(ret >= 0);
1246
1094
  if(errno != ECHILD){