/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 plugbasedclient.c

Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Mandos plugin runner - Run Mandos plugins
4
4
 *
5
 
 * Copyright © 2008 Teddy Hogeborn & Björn Påhlsson
 
5
 * Copyright © 2007-2008 Teddy Hogeborn and Björn Påhlsson.
6
6
 * 
7
7
 * This program is free software: you can redistribute it and/or
8
8
 * modify it under the terms of the GNU General Public License as
18
18
 * along with this program.  If not, see
19
19
 * <http://www.gnu.org/licenses/>.
20
20
 * 
21
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
21
 * Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
 
22
 * <https://www.fukt.bsnet.se/~teddy/>.
22
23
 */
23
24
 
24
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
25
 
                                   asprintf() */
26
 
#include <stddef.h>             /* size_t, NULL */
27
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
28
 
                                   EXIT_SUCCESS, realloc() */
29
 
#include <stdbool.h>            /* bool, true, false */
30
 
#include <stdio.h>              /* perror, fileno(), fprintf(),
31
 
                                   stderr, STDOUT_FILENO */
32
 
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
33
 
                                   stat, waitpid(), WIFEXITED(),
34
 
                                   WEXITSTATUS(), wait(), pid_t,
35
 
                                   uid_t, gid_t, getuid(), getgid(),
36
 
                                   dirfd() */
37
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
38
 
                                   FD_SET(), FD_ISSET(), FD_CLR */
39
 
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
40
 
                                   WEXITSTATUS() */
41
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
42
 
#include <iso646.h>             /* and, or, not */
43
 
#include <dirent.h>             /* DIR, struct dirent, opendir(),
44
 
                                   readdir(), closedir(), dirfd() */
45
 
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
46
 
                                   fcntl(), setuid(), setgid(),
47
 
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
48
 
                                   access(), pipe(), fork(), close()
49
 
                                   dup2(), STDOUT_FILENO, _exit(),
50
 
                                   execv(), write(), read(),
51
 
                                   close() */
52
 
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
 
                                   FD_CLOEXEC */
54
 
#include <string.h>             /* strsep, strlen(), asprintf() */
55
 
#include <errno.h>              /* errno */
56
 
#include <argp.h>               /* struct argp_option, struct
57
 
                                   argp_state, struct argp,
58
 
                                   argp_parse(), ARGP_ERR_UNKNOWN,
59
 
                                   ARGP_KEY_END, ARGP_KEY_ARG,
60
 
                                   error_t */
61
 
#include <signal.h>             /* struct sigaction, sigemptyset(),
62
 
                                   sigaddset(), sigaction(),
63
 
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
64
 
                                   SIG_UNBLOCK, kill() */
65
 
#include <errno.h>              /* errno, EBADF */
66
 
 
67
 
#define BUFFER_SIZE 256
68
 
 
69
 
#define PDIR "/lib/mandos/plugins.d"
70
 
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
71
 
 
72
 
const char *argp_program_version = "plugin-runner " VERSION;
73
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
74
 
 
75
 
typedef struct plugin{
76
 
  char *name;                   /* can be NULL or any plugin name */
77
 
  char **argv;
78
 
  int argc;
79
 
  char **environ;
80
 
  int envc;
81
 
  bool disabled;
82
 
 
83
 
  /* Variables used for running processes*/
 
25
#define _FORTIFY_SOURCE 2
 
26
 
 
27
#include <stdio.h>      /* popen, fileno */
 
28
#include <iso646.h>     /* and, or, not */
 
29
#include <sys/types.h>  /* DIR, opendir, stat, struct stat, waitpid,
 
30
                           WIFEXITED, WEXITSTATUS, wait */
 
31
#include <sys/wait.h>   /* wait */
 
32
#include <dirent.h>     /* DIR, opendir */
 
33
#include <sys/stat.h>   /* stat, struct stat */
 
34
#include <unistd.h>     /* stat, struct stat, chdir */
 
35
#include <stdlib.h>     /* EXIT_FAILURE */
 
36
#include <sys/select.h> /* fd_set, select, FD_ZERO, FD_SET,
 
37
                           FD_ISSET */
 
38
#include <string.h>     /* strlen, strcpy, strcat */
 
39
#include <stdbool.h>    /* true */
 
40
#include <sys/wait.h>   /* waitpid, WIFEXITED, WEXITSTATUS */
 
41
#include <errno.h>      /* errno */
 
42
#include <argp.h>       /* argp */
 
43
 
 
44
struct process;
 
45
 
 
46
typedef struct process{
84
47
  pid_t pid;
85
48
  int fd;
86
49
  char *buffer;
87
50
  size_t buffer_size;
88
51
  size_t buffer_length;
89
 
  bool eof;
90
 
  volatile bool completed;
91
 
  volatile int status;
 
52
  struct process *next;
 
53
} process;
 
54
 
 
55
typedef struct plugin{
 
56
  char *name;           /* can be "global" and any plugin name */
 
57
  char **argv;
 
58
  int argc;
92
59
  struct plugin *next;
93
60
} plugin;
94
61
 
95
 
static plugin *plugin_list = NULL;
96
 
 
97
 
/* Gets an existing plugin based on name,
98
 
   or if none is found, creates a new one */
99
 
static plugin *getplugin(char *name){
100
 
  /* Check for exiting plugin with that name */
101
 
  for (plugin *p = plugin_list; p != NULL; p = p->next){
 
62
plugin *getplugin(char *name, plugin **plugin_list){
 
63
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
102
64
    if ((p->name == name)
103
65
        or (p->name and name and (strcmp(p->name, name) == 0))){
104
66
      return p;
107
69
  /* Create a new plugin */
108
70
  plugin *new_plugin = malloc(sizeof(plugin));
109
71
  if (new_plugin == NULL){
110
 
    return NULL;
111
 
  }
112
 
  char *copy_name = NULL;
113
 
  if(name != NULL){
114
 
    copy_name = strdup(name);
115
 
    if(copy_name == NULL){
116
 
      return NULL;
117
 
    }
118
 
  }
119
 
  
120
 
  *new_plugin = (plugin) { .name = copy_name,
121
 
                           .argc = 1,
122
 
                           .disabled = false,
123
 
                           .next = plugin_list };
124
 
  
 
72
    perror("malloc");
 
73
    exit(EXIT_FAILURE);
 
74
  }
 
75
  new_plugin->name = name;
125
76
  new_plugin->argv = malloc(sizeof(char *) * 2);
126
77
  if (new_plugin->argv == NULL){
127
 
    free(copy_name);
128
 
    free(new_plugin);
129
 
    return NULL;
 
78
    perror("malloc");
 
79
    exit(EXIT_FAILURE);
130
80
  }
131
 
  new_plugin->argv[0] = copy_name;
 
81
  new_plugin->argv[0] = name;
132
82
  new_plugin->argv[1] = NULL;
133
 
  
134
 
  new_plugin->environ = malloc(sizeof(char *));
135
 
  if(new_plugin->environ == NULL){
136
 
    free(copy_name);
137
 
    free(new_plugin->argv);
138
 
    free(new_plugin);
139
 
    return NULL;
140
 
  }
141
 
  new_plugin->environ[0] = NULL;
142
 
  
 
83
  new_plugin->argc = 1;
143
84
  /* Append the new plugin to the list */
144
 
  plugin_list = new_plugin;
 
85
  new_plugin->next = *plugin_list;
 
86
  *plugin_list = new_plugin;
145
87
  return new_plugin;
146
88
}
147
89
 
148
 
/* Helper function for add_argument and add_environment */
149
 
static bool add_to_char_array(const char *new, char ***array,
150
 
                              int *len){
151
 
  /* Resize the pointed-to array to hold one more pointer */
152
 
  *array = realloc(*array, sizeof(char *)
153
 
                   * (size_t) ((*len) + 2));
154
 
  /* Malloc check */
155
 
  if(*array == NULL){
156
 
    return false;
157
 
  }
158
 
  /* Make a copy of the new string */
159
 
  char *copy = strdup(new);
160
 
  if(copy == NULL){
161
 
    return false;
162
 
  }
163
 
  /* Insert the copy */
164
 
  (*array)[*len] = copy;
165
 
  (*len)++;
166
 
  /* Add a new terminating NULL pointer to the last element */
167
 
  (*array)[*len] = NULL;
168
 
  return true;
169
 
}
170
 
 
171
 
/* Add to a plugin's argument vector */
172
 
static bool add_argument(plugin *p, const char *arg){
173
 
  if(p == NULL){
174
 
    return false;
175
 
  }
176
 
  return add_to_char_array(arg, &(p->argv), &(p->argc));
177
 
}
178
 
 
179
 
/* Add to a plugin's environment */
180
 
static bool add_environment(plugin *p, const char *def, bool replace){
181
 
  if(p == NULL){
182
 
    return false;
183
 
  }
184
 
  /* namelen = length of name of environment variable */
185
 
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
186
 
  /* Search for this environment variable */
187
 
  for(char **e = p->environ; *e != NULL; e++){
188
 
    if(strncmp(*e, def, namelen + 1) == 0){
189
 
      /* It already exists */
190
 
      if(replace){
191
 
        char *new = realloc(*e, strlen(def) + 1);
192
 
        if(new == NULL){
193
 
          return false;
194
 
        }
195
 
        *e = new;
196
 
        strcpy(*e, def);
197
 
      }
198
 
      return true;
199
 
    }
200
 
  }
201
 
  return add_to_char_array(def, &(p->environ), &(p->envc));
202
 
}
203
 
 
204
 
/*
205
 
 * Based on the example in the GNU LibC manual chapter 13.13 "File
206
 
 * Descriptor Flags".
207
 
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
208
 
 */
209
 
static int set_cloexec_flag(int fd){
210
 
  int ret = fcntl(fd, F_GETFD, 0);
211
 
  /* If reading the flags failed, return error indication now. */
212
 
  if(ret < 0){
213
 
    return ret;
214
 
  }
215
 
  /* Store modified flag word in the descriptor. */
216
 
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
217
 
}
218
 
 
219
 
 
220
 
/* Mark processes as completed when they exit, and save their exit
221
 
   status. */
222
 
static void handle_sigchld(__attribute__((unused)) int sig){
223
 
  while(true){
224
 
    plugin *proc = plugin_list;
225
 
    int status;
226
 
    pid_t pid = waitpid(-1, &status, WNOHANG);
227
 
    if(pid == 0){
228
 
      /* Only still running child processes */
229
 
      break;
230
 
    }
231
 
    if(pid == -1){
232
 
      if (errno != ECHILD){
233
 
        perror("waitpid");
234
 
      }
235
 
      /* No child processes */
236
 
      break;
237
 
    }
238
 
    
239
 
    /* A child exited, find it in process_list */
240
 
    while(proc != NULL and proc->pid != pid){
241
 
      proc = proc->next;
242
 
    }
243
 
    if(proc == NULL){
244
 
      /* Process not found in process list */
245
 
      continue;
246
 
    }
247
 
    proc->status = status;
248
 
    proc->completed = true;
249
 
  }
250
 
}
251
 
 
252
 
/* Prints out a password to stdout */
253
 
static bool print_out_password(const char *buffer, size_t length){
254
 
  ssize_t ret;
255
 
  for(size_t written = 0; written < length; written += (size_t)ret){
256
 
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
257
 
                                   length - written));
258
 
    if(ret < 0){
259
 
      return false;
260
 
    }
261
 
  }
262
 
  return true;
263
 
}
264
 
 
265
 
/* Removes and free a plugin from the plugin list */
266
 
static void free_plugin(plugin *plugin_node){
267
 
  
268
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
269
 
    free(*arg);
270
 
  }
271
 
  free(plugin_node->argv);
272
 
  for(char **env = plugin_node->environ; *env != NULL; env++){
273
 
    free(*env);
274
 
  }
275
 
  free(plugin_node->environ);
276
 
  free(plugin_node->buffer);
277
 
 
278
 
  /* Removes the plugin from the singly-linked list */
279
 
  if(plugin_node == plugin_list){
280
 
    /* First one - simple */
281
 
    plugin_list = plugin_list->next;
282
 
  } else {
283
 
    /* Second one or later */
284
 
    for(plugin *p = plugin_list; p != NULL; p = p->next){
285
 
      if(p->next == plugin_node){
286
 
        p->next = plugin_node->next;
287
 
        break;
288
 
      }
289
 
    }
290
 
  }
291
 
  
292
 
  free(plugin_node);
293
 
}
294
 
 
295
 
static void free_plugin_list(void){
296
 
  while(plugin_list != NULL){
297
 
    free_plugin(plugin_list);
298
 
  }
299
 
}
 
90
void addarguments(plugin *p, char *arg){
 
91
  p->argv[p->argc] = arg;
 
92
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
 
93
  if (p->argv == NULL){
 
94
    perror("malloc");
 
95
    exit(EXIT_FAILURE);
 
96
  }
 
97
  p->argc++;
 
98
  p->argv[p->argc] = NULL;
 
99
}
 
100
        
 
101
#define BUFFER_SIZE 256
 
102
 
 
103
const char *argp_program_version =
 
104
  "plugbasedclient 0.9";
 
105
const char *argp_program_bug_address =
 
106
  "<mandos@fukt.bsnet.se>";
 
107
static char doc[] =
 
108
  "Mandos plugin runner -- Run Mandos plugins";
 
109
/* A description of the arguments we accept. */
 
110
static char args_doc[] = "";
300
111
 
301
112
int main(int argc, char *argv[]){
302
 
  char *plugindir = NULL;
303
 
  char *argfile = NULL;
304
 
  FILE *conffp;
305
 
  size_t d_name_len;
306
 
  DIR *dir = NULL;
 
113
  char plugindir[] = "plugins.d";
 
114
  size_t d_name_len, plugindir_len = sizeof(plugindir)-1;
 
115
  DIR *dir;
307
116
  struct dirent *dirst;
308
117
  struct stat st;
309
 
  fd_set rfds_all;
 
118
  fd_set rfds_orig;
310
119
  int ret, maxfd = 0;
311
 
  uid_t uid = 65534;
312
 
  gid_t gid = 65534;
313
 
  bool debug = false;
314
 
  int exitstatus = EXIT_SUCCESS;
315
 
  struct sigaction old_sigchld_action;
316
 
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
317
 
                                      .sa_flags = SA_NOCLDSTOP };
318
 
  char **custom_argv = NULL;
319
 
  int custom_argc = 0;
320
 
  
321
 
  /* Establish a signal handler */
322
 
  sigemptyset(&sigchld_action.sa_mask);
323
 
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
324
 
  if(ret == -1){
325
 
    perror("sigaddset");
326
 
    exitstatus = EXIT_FAILURE;
327
 
    goto fallback;
328
 
  }
329
 
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
330
 
  if(ret == -1){
331
 
    perror("sigaction");
332
 
    exitstatus = EXIT_FAILURE;
333
 
    goto fallback;
334
 
  }
 
120
  process *process_list = NULL;
335
121
  
336
122
  /* The options we understand. */
337
123
  struct argp_option options[] = {
338
124
    { .name = "global-options", .key = 'g',
339
 
      .arg = "OPTION[,OPTION[,...]]",
340
 
      .doc = "Options passed to all plugins" },
341
 
    { .name = "global-env", .key = 'G',
342
 
      .arg = "VAR=value",
343
 
      .doc = "Environment variable passed to all plugins" },
 
125
      .arg = "option[,option[,...]]", .flags = 0,
 
126
      .doc = "Options effecting all plugins" },
344
127
    { .name = "options-for", .key = 'o',
345
 
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
346
 
      .doc = "Options passed only to specified plugin" },
347
 
    { .name = "env-for", .key = 'E',
348
 
      .arg = "PLUGIN:ENV=value",
349
 
      .doc = "Environment variable passed to specified plugin" },
350
 
    { .name = "disable", .key = 'd',
351
 
      .arg = "PLUGIN",
352
 
      .doc = "Disable a specific plugin", .group = 1 },
353
 
    { .name = "enable", .key = 'e',
354
 
      .arg = "PLUGIN",
355
 
      .doc = "Enable a specific plugin", .group = 1 },
356
 
    { .name = "plugin-dir", .key = 128,
357
 
      .arg = "DIRECTORY",
358
 
      .doc = "Specify a different plugin directory", .group = 2 },
359
 
    { .name = "config-file", .key = 129,
360
 
      .arg = "FILE",
361
 
      .doc = "Specify a different configuration file", .group = 2 },
362
 
    { .name = "userid", .key = 130,
363
 
      .arg = "ID", .flags = 0,
364
 
      .doc = "User ID the plugins will run as", .group = 3 },
365
 
    { .name = "groupid", .key = 131,
366
 
      .arg = "ID", .flags = 0,
367
 
      .doc = "Group ID the plugins will run as", .group = 3 },
368
 
    { .name = "debug", .key = 132,
369
 
      .doc = "Debug mode", .group = 4 },
 
128
      .arg = "plugin:option[,option[,...]]", .flags = 0,
 
129
      .doc = "Options effecting only specified plugins" },
370
130
    { .name = NULL }
371
131
  };
372
132
  
373
 
  error_t parse_opt (int key, char *arg, __attribute__((unused))
374
 
                     struct argp_state *state) {
375
 
    switch (key) {
376
 
    case 'g':                   /* --global-options */
377
 
      if (arg != NULL){
378
 
        char *p;
379
 
        while((p = strsep(&arg, ",")) != NULL){
380
 
          if(p[0] == '\0'){
381
 
            continue;
382
 
          }
383
 
          if(not add_argument(getplugin(NULL), p)){
384
 
            perror("add_argument");
385
 
            return ARGP_ERR_UNKNOWN;
386
 
          }
387
 
        }
388
 
      }
389
 
      break;
390
 
    case 'G':                   /* --global-env */
391
 
      if(arg == NULL){
392
 
        break;
393
 
      }
394
 
      if(not add_environment(getplugin(NULL), arg, true)){
395
 
        perror("add_environment");
396
 
      }
397
 
      break;
398
 
    case 'o':                   /* --options-for */
399
 
      if (arg != NULL){
400
 
        char *p_name = strsep(&arg, ":");
401
 
        if(p_name[0] == '\0' or arg == NULL){
402
 
          break;
403
 
        }
404
 
        char *opt = strsep(&arg, ":");
405
 
        if(opt[0] == '\0' or opt == NULL){
406
 
          break;
407
 
        }
408
 
        char *p;
409
 
        while((p = strsep(&opt, ",")) != NULL){
410
 
          if(p[0] == '\0'){
411
 
            continue;
412
 
          }
413
 
          if(not add_argument(getplugin(p_name), p)){
414
 
            perror("add_argument");
415
 
            return ARGP_ERR_UNKNOWN;
416
 
          }
417
 
        }
418
 
      }
419
 
      break;
420
 
    case 'E':                   /* --env-for */
421
 
      if(arg == NULL){
422
 
        break;
423
 
      }
424
 
      {
425
 
        char *envdef = strchr(arg, ':');
426
 
        if(envdef == NULL){
427
 
          break;
428
 
        }
429
 
        *envdef = '\0';
430
 
        if(not add_environment(getplugin(arg), envdef+1, true)){
431
 
          perror("add_environment");
432
 
        }
433
 
      }
434
 
      break;
435
 
    case 'd':                   /* --disable */
436
 
      if (arg != NULL){
437
 
        plugin *p = getplugin(arg);
438
 
        if(p == NULL){
439
 
          return ARGP_ERR_UNKNOWN;
440
 
        }
441
 
        p->disabled = true;
442
 
      }
443
 
      break;
444
 
    case 'e':                   /* --enable */
445
 
      if (arg != NULL){
446
 
        plugin *p = getplugin(arg);
447
 
        if(p == NULL){
448
 
          return ARGP_ERR_UNKNOWN;
449
 
        }
450
 
        p->disabled = false;
451
 
      }
452
 
      break;
453
 
    case 128:                   /* --plugin-dir */
454
 
      free(plugindir);
455
 
      plugindir = strdup(arg);
456
 
      if(plugindir == NULL){
457
 
        perror("strdup");
458
 
      }      
459
 
      break;
460
 
    case 129:                   /* --config-file */
461
 
      /* This is already done by parse_opt_config_file() */
462
 
      break;
463
 
    case 130:                   /* --userid */
464
 
      uid = (uid_t)strtol(arg, NULL, 10);
465
 
      break;
466
 
    case 131:                   /* --groupid */
467
 
      gid = (gid_t)strtol(arg, NULL, 10);
468
 
      break;
469
 
    case 132:                   /* --debug */
470
 
      debug = true;
471
 
      break;
472
 
    case ARGP_KEY_ARG:
473
 
      /* Cryptsetup always passes an argument, which is an empty
474
 
         string if "none" was specified in /etc/crypttab.  So if
475
 
         argument was empty, we ignore it silently. */
476
 
      if(arg[0] != '\0'){
477
 
        fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
478
 
      }
479
 
      break;
480
 
    case ARGP_KEY_END:
481
 
      break;
482
 
    default:
483
 
      return ARGP_ERR_UNKNOWN;
484
 
    }
485
 
    return 0;
486
 
  }
487
 
  
488
 
  /* This option parser is the same as parse_opt() above, except it
489
 
     ignores everything but the --config-file option. */
490
 
  error_t parse_opt_config_file (int key, char *arg,
491
 
                                 __attribute__((unused))
492
 
                                 struct argp_state *state) {
493
 
    switch (key) {
494
 
    case 'g':                   /* --global-options */
495
 
    case 'G':                   /* --global-env */
496
 
    case 'o':                   /* --options-for */
497
 
    case 'E':                   /* --env-for */
498
 
    case 'd':                   /* --disable */
499
 
    case 'e':                   /* --enable */
500
 
    case 128:                   /* --plugin-dir */
501
 
      break;
502
 
    case 129:                   /* --config-file */
503
 
      free(argfile);
504
 
      argfile = strdup(arg);
505
 
      if(argfile == NULL){
506
 
        perror("strdup");
507
 
      }
508
 
      break;      
509
 
    case 130:                   /* --userid */
510
 
    case 131:                   /* --groupid */
511
 
    case 132:                   /* --debug */
512
 
    case ARGP_KEY_ARG:
513
 
    case ARGP_KEY_END:
514
 
      break;
515
 
    default:
516
 
      return ARGP_ERR_UNKNOWN;
517
 
    }
518
 
    return 0;
519
 
  }
520
 
  
521
 
  struct argp argp = { .options = options,
522
 
                       .parser = parse_opt_config_file,
523
 
                       .args_doc = "",
524
 
                       .doc = "Mandos plugin runner -- Run plugins" };
525
 
  
526
 
  /* Parse using the parse_opt_config_file in order to get the custom
527
 
     config file location, if any. */
528
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
529
 
  if (ret == ARGP_ERR_UNKNOWN){
530
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
531
 
    exitstatus = EXIT_FAILURE;
532
 
    goto fallback;
533
 
  }
534
 
  
535
 
  /* Reset to the normal argument parser */
536
 
  argp.parser = parse_opt;
537
 
  
538
 
  /* Open the configfile if available */
539
 
  if (argfile == NULL){
540
 
    conffp = fopen(AFILE, "r");
541
 
  } else {
542
 
    conffp = fopen(argfile, "r");
543
 
  }  
544
 
  if(conffp != NULL){
545
 
    char *org_line = NULL;
546
 
    char *p, *arg, *new_arg, *line;
547
 
    size_t size = 0;
548
 
    ssize_t sret;
549
 
    const char whitespace_delims[] = " \r\t\f\v\n";
550
 
    const char comment_delim[] = "#";
551
 
 
552
 
    custom_argc = 1;
553
 
    custom_argv = malloc(sizeof(char*) * 2);
554
 
    if(custom_argv == NULL){
555
 
      perror("malloc");
556
 
      exitstatus = EXIT_FAILURE;
557
 
      goto fallback;
558
 
    }
559
 
    custom_argv[0] = argv[0];
560
 
    custom_argv[1] = NULL;
561
 
 
562
 
    /* for each line in the config file, strip whitespace and ignore
563
 
       commented text */
564
 
    while(true){
565
 
      sret = getline(&org_line, &size, conffp);
566
 
      if(sret == -1){
567
 
        break;
568
 
      }
569
 
 
570
 
      line = org_line;
571
 
      arg = strsep(&line, comment_delim);
572
 
      while((p = strsep(&arg, whitespace_delims)) != NULL){
573
 
        if(p[0] == '\0'){
574
 
          continue;
575
 
        }
576
 
        new_arg = strdup(p);
577
 
        if(new_arg == NULL){
578
 
          perror("strdup");
579
 
          exitstatus = EXIT_FAILURE;
580
 
          free(org_line);
581
 
          goto fallback;
582
 
        }
583
 
        
584
 
        custom_argc += 1;
585
 
        custom_argv = realloc(custom_argv, sizeof(char *)
586
 
                              * ((unsigned int) custom_argc + 1));
587
 
        if(custom_argv == NULL){
588
 
          perror("realloc");
589
 
          exitstatus = EXIT_FAILURE;
590
 
          free(org_line);
591
 
          goto fallback;
592
 
        }
593
 
        custom_argv[custom_argc-1] = new_arg;
594
 
        custom_argv[custom_argc] = NULL;        
595
 
      }
596
 
    }
597
 
    free(org_line);
598
 
  } else {
599
 
    /* Check for harmful errors and go to fallback. Other errors might
600
 
       not affect opening plugins */
601
 
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
602
 
      perror("fopen");
603
 
      exitstatus = EXIT_FAILURE;
604
 
      goto fallback;
605
 
    }
606
 
  }
607
 
  /* If there was any arguments from configuration file,
608
 
     pass them to parser as command arguments */
609
 
  if(custom_argv != NULL){
610
 
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
611
 
                      0, NULL);
612
 
    if (ret == ARGP_ERR_UNKNOWN){
613
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
614
 
      exitstatus = EXIT_FAILURE;
615
 
      goto fallback;
616
 
    }
617
 
  }
618
 
  
619
 
  /* Parse actual command line arguments, to let them override the
620
 
     config file */
621
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
622
 
  if (ret == ARGP_ERR_UNKNOWN){
623
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
624
 
    exitstatus = EXIT_FAILURE;
625
 
    goto fallback;
626
 
  }
627
 
  
628
 
  if(debug){
629
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
630
 
      fprintf(stderr, "Plugin: %s has %d arguments\n",
631
 
              p->name ? p->name : "Global", p->argc - 1);
632
 
      for(char **a = p->argv; *a != NULL; a++){
633
 
        fprintf(stderr, "\tArg: %s\n", *a);
634
 
      }
635
 
      fprintf(stderr, "...and %u environment variables\n", p->envc);
636
 
      for(char **a = p->environ; *a != NULL; a++){
637
 
        fprintf(stderr, "\t%s\n", *a);
638
 
      }
639
 
    }
640
 
  }
641
 
  
642
 
  /* Strip permissions down to nobody */
643
 
  ret = setuid(uid);
644
 
  if (ret == -1){
645
 
    perror("setuid");
646
 
  }  
647
 
  setgid(gid);
648
 
  if (ret == -1){
649
 
    perror("setgid");
650
 
  }
651
 
  
652
 
  if (plugindir == NULL){
653
 
    dir = opendir(PDIR);
654
 
  } else {
655
 
    dir = opendir(plugindir);
656
 
  }
 
133
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
 
134
       /* Get the INPUT argument from `argp_parse', which we
 
135
          know is a pointer to our arguments structure. */
 
136
    plugin **plugins = state->input;
 
137
    switch (key) {
 
138
    case 'g':
 
139
      if (arg != NULL){
 
140
        char *p = strtok(arg, ",");
 
141
        do{
 
142
          addarguments(getplugin(NULL, plugins), p);
 
143
          p = strtok(NULL, ",");
 
144
        } while (p);
 
145
      }
 
146
      break;
 
147
    case 'o':
 
148
      if (arg != NULL){
 
149
        char *name = strtok(arg, ":");
 
150
        char *p = strtok(NULL, ":");
 
151
        p = strtok(p, ",");
 
152
        do{
 
153
          addarguments(getplugin(name, plugins), p);
 
154
          p = strtok(NULL, ",");
 
155
        } while (p);
 
156
      }
 
157
      break;
 
158
    case ARGP_KEY_ARG:
 
159
      argp_usage (state);
 
160
      break;
 
161
    case ARGP_KEY_END:
 
162
      break;
 
163
    default:
 
164
      return ARGP_ERR_UNKNOWN;
 
165
    }
 
166
    return 0;
 
167
  }
 
168
 
 
169
  plugin *plugin_list = NULL;
 
170
  
 
171
  struct argp argp = { .options = options, .parser = parse_opt,
 
172
                       .args_doc = args_doc, .doc = doc };
 
173
 
 
174
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
 
175
 
 
176
/*   for(plugin *p = plugin_list; p != NULL; p=p->next){ */
 
177
/*     fprintf(stderr, "Plugin: %s has %d arguments\n", p->name ? p->name : "Global", p->argc); */
 
178
/*     for(char **a = p->argv + 1; *a != NULL; a++){ */
 
179
/*       fprintf(stderr, "\tArg: %s\n", *a); */
 
180
/*     } */
 
181
/*   } */
 
182
  
 
183
/*   return 0; */
 
184
  
 
185
  dir = opendir(plugindir);
657
186
  
658
187
  if(dir == NULL){
659
 
    perror("Could not open plugin dir");
660
 
    exitstatus = EXIT_FAILURE;
661
 
    goto fallback;
662
 
  }
663
 
  
664
 
  /* Set the FD_CLOEXEC flag on the directory, if possible */
665
 
  {
666
 
    int dir_fd = dirfd(dir);
667
 
    if(dir_fd >= 0){
668
 
      ret = set_cloexec_flag(dir_fd);
669
 
      if(ret < 0){
670
 
        perror("set_cloexec_flag");
671
 
        exitstatus = EXIT_FAILURE;
672
 
        goto fallback;
673
 
      }
674
 
    }
675
 
  }
676
 
  
677
 
  FD_ZERO(&rfds_all);
678
 
  
679
 
  /* Read and execute any executable in the plugin directory*/
 
188
    fprintf(stderr, "Can not open directory\n");
 
189
    return EXIT_FAILURE;
 
190
  }
 
191
  
 
192
  FD_ZERO(&rfds_orig);
 
193
  
680
194
  while(true){
681
195
    dirst = readdir(dir);
682
196
    
683
 
    /* All directory entries have been processed */
 
197
    // All directory entries have been processed
684
198
    if(dirst == NULL){
685
 
      if (errno == EBADF){
686
 
        perror("readdir");
687
 
        exitstatus = EXIT_FAILURE;
688
 
        goto fallback;
689
 
      }
690
199
      break;
691
200
    }
692
201
    
693
202
    d_name_len = strlen(dirst->d_name);
694
203
    
695
 
    /* Ignore dotfiles, backup files and other junk */
696
 
    {
697
 
      bool bad_name = false;
698
 
      
699
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
700
 
      
701
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
702
 
                                           ".dpkg-old",
703
 
                                           ".dpkg-bak",
704
 
                                           ".dpkg-divert", NULL };
705
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
706
 
        size_t pre_len = strlen(*pre);
707
 
        if((d_name_len >= pre_len)
708
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
709
 
          if(debug){
710
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
711
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
712
 
          }
713
 
          bad_name = true;
714
 
          break;
715
 
        }
716
 
      }
717
 
      if(bad_name){
718
 
        continue;
719
 
      }
720
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
721
 
        size_t suf_len = strlen(*suf);
722
 
        if((d_name_len >= suf_len)
723
 
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
724
 
                == 0)){
725
 
          if(debug){
726
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
727
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
728
 
          }
729
 
          bad_name = true;
730
 
          break;
731
 
        }
732
 
      }
733
 
      
734
 
      if(bad_name){
735
 
        continue;
736
 
      }
737
 
    }
738
 
 
739
 
    char *filename;
740
 
    if(plugindir == NULL){
741
 
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
742
 
    } else {
743
 
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
744
 
    }
745
 
    if(ret < 0){
746
 
      perror("asprintf");
747
 
      continue;
748
 
    }
749
 
    
750
 
    ret = stat(filename, &st);
751
 
    if (ret == -1){
752
 
      perror("stat");
753
 
      free(filename);
754
 
      continue;
755
 
    }
756
 
 
757
 
    /* Ignore non-executable files */
758
 
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
759
 
      if(debug){
760
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
761
 
                " with bad type or mode\n", filename);
762
 
      }
763
 
      free(filename);
764
 
      continue;
765
 
    }
766
 
    
767
 
    plugin *p = getplugin(dirst->d_name);
768
 
    if(p == NULL){
769
 
      perror("getplugin");
770
 
      free(filename);
771
 
      continue;
772
 
    }
773
 
    if(p->disabled){
774
 
      if(debug){
775
 
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
776
 
                dirst->d_name);
777
 
      }
778
 
      free(filename);
779
 
      continue;
780
 
    }
781
 
    {
782
 
      /* Add global arguments to argument list for this plugin */
783
 
      plugin *g = getplugin(NULL);
784
 
      if(g != NULL){
 
204
    // Ignore dotfiles and backup files
 
205
    if (dirst->d_name[0] == '.'
 
206
        or dirst->d_name[d_name_len - 1] == '~'){
 
207
      continue;
 
208
    }
 
209
 
 
210
    char *filename = malloc(d_name_len + plugindir_len + 2);
 
211
    strcpy(filename, plugindir);
 
212
    strcat(filename, "/");
 
213
    strcat(filename, dirst->d_name);    
 
214
 
 
215
    stat(filename, &st);
 
216
 
 
217
    if (S_ISREG(st.st_mode) and (access(filename, X_OK) == 0)){
 
218
      // Starting a new process to be watched
 
219
      process *new_process = malloc(sizeof(process));
 
220
      int pipefd[2];
 
221
      ret = pipe(pipefd);
 
222
      if (ret == -1){
 
223
        perror(argv[0]);
 
224
        goto end;
 
225
      }
 
226
      new_process->pid = fork();
 
227
      if(new_process->pid == 0){
 
228
        /* this is the child process */
 
229
        closedir(dir);
 
230
        close(pipefd[0]);       /* close unused read end of pipe */
 
231
        dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
 
232
        char *basename;
 
233
        basename = strrchr(filename, '/');
 
234
        if (basename == NULL){
 
235
          basename = filename;
 
236
        } else {
 
237
          basename++;
 
238
        }
 
239
        plugin *p = getplugin(basename, &plugin_list);
 
240
 
 
241
        plugin *g = getplugin(NULL, &plugin_list);
785
242
        for(char **a = g->argv + 1; *a != NULL; a++){
786
 
          if(not add_argument(p, *a)){
787
 
            perror("add_argument");
788
 
          }
789
 
        }
790
 
        /* Add global environment variables */
791
 
        for(char **e = g->environ; *e != NULL; e++){
792
 
          if(not add_environment(p, *e, false)){
793
 
            perror("add_environment");
794
 
          }
795
 
        }
796
 
      }
797
 
    }
798
 
    /* If this plugin has any environment variables, we will call
799
 
       using execve and need to duplicate the environment from this
800
 
       process, too. */
801
 
    if(p->environ[0] != NULL){
802
 
      for(char **e = environ; *e != NULL; e++){
803
 
        if(not add_environment(p, *e, false)){
804
 
          perror("add_environment");
805
 
        }
806
 
      }
807
 
    }
808
 
    
809
 
    int pipefd[2];
810
 
    ret = pipe(pipefd);
811
 
    if (ret == -1){
812
 
      perror("pipe");
813
 
      exitstatus = EXIT_FAILURE;
814
 
      goto fallback;
815
 
    }
816
 
    /* Ask OS to automatic close the pipe on exec */
817
 
    ret = set_cloexec_flag(pipefd[0]);
818
 
    if(ret < 0){
819
 
      perror("set_cloexec_flag");
820
 
      exitstatus = EXIT_FAILURE;
821
 
      goto fallback;
822
 
    }
823
 
    ret = set_cloexec_flag(pipefd[1]);
824
 
    if(ret < 0){
825
 
      perror("set_cloexec_flag");
826
 
      exitstatus = EXIT_FAILURE;
827
 
      goto fallback;
828
 
    }
829
 
    /* Block SIGCHLD until process is safely in process list */
830
 
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
831
 
    if(ret < 0){
832
 
      perror("sigprocmask");
833
 
      exitstatus = EXIT_FAILURE;
834
 
      goto fallback;
835
 
    }
836
 
    /* Starting a new process to be watched */
837
 
    pid_t pid = fork();
838
 
    if(pid == -1){
839
 
      perror("fork");
840
 
      exitstatus = EXIT_FAILURE;
841
 
      goto fallback;
842
 
    }
843
 
    if(pid == 0){
844
 
      /* this is the child process */
845
 
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
846
 
      if(ret < 0){
847
 
        perror("sigaction");
848
 
        _exit(EXIT_FAILURE);
849
 
      }
850
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
851
 
      if(ret < 0){
852
 
        perror("sigprocmask");
853
 
        _exit(EXIT_FAILURE);
854
 
      }
855
 
      
856
 
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
857
 
      if(ret == -1){
858
 
        perror("dup2");
859
 
        _exit(EXIT_FAILURE);
860
 
      }
861
 
      
862
 
      if(dirfd(dir) < 0){
863
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
864
 
           above and must now close it manually here. */
865
 
        closedir(dir);
866
 
      }
867
 
      if(p->environ[0] == NULL){
 
243
          addarguments(p, *a);
 
244
        }
868
245
        if(execv(filename, p->argv) < 0){
869
 
          perror("execv");
870
 
          _exit(EXIT_FAILURE);
871
 
        }
872
 
      } else {
873
 
        if(execve(filename, p->argv, p->environ) < 0){
874
 
          perror("execve");
875
 
          _exit(EXIT_FAILURE);
876
 
        }
877
 
      }
878
 
      /* no return */
879
 
    }
880
 
    /* Parent process */
881
 
    close(pipefd[1]);           /* Close unused write end of pipe */
882
 
    free(filename);
883
 
    plugin *new_plugin = getplugin(dirst->d_name);
884
 
    if (new_plugin == NULL){
885
 
      perror("getplugin");
886
 
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
887
 
      if(ret < 0){
888
 
        perror("sigprocmask");
889
 
      }
890
 
      exitstatus = EXIT_FAILURE;
891
 
      goto fallback;
892
 
    }
893
 
    
894
 
    new_plugin->pid = pid;
895
 
    new_plugin->fd = pipefd[0];
896
 
    
897
 
    /* Unblock SIGCHLD so signal handler can be run if this process
898
 
       has already completed */
899
 
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
900
 
    if(ret < 0){
901
 
      perror("sigprocmask");
902
 
      exitstatus = EXIT_FAILURE;
903
 
      goto fallback;
904
 
    }
905
 
    
906
 
    FD_SET(new_plugin->fd, &rfds_all);
907
 
    
908
 
    if (maxfd < new_plugin->fd){
909
 
      maxfd = new_plugin->fd;
 
246
          perror(argv[0]);
 
247
          close(pipefd[1]);
 
248
          exit(EXIT_FAILURE);
 
249
        }
 
250
        /* no return */
 
251
      }
 
252
      close(pipefd[1]);         /* close unused write end of pipe */
 
253
      new_process->fd = pipefd[0];
 
254
      new_process->buffer = malloc(BUFFER_SIZE);
 
255
      if (new_process->buffer == NULL){
 
256
        perror(argv[0]);
 
257
        goto end;
 
258
      }
 
259
      new_process->buffer_size = BUFFER_SIZE;
 
260
      new_process->buffer_length = 0;
 
261
      FD_SET(new_process->fd, &rfds_orig);
 
262
      
 
263
      if (maxfd < new_process->fd){
 
264
        maxfd = new_process->fd;
 
265
      }
 
266
      
 
267
      //List handling
 
268
      new_process->next = process_list;
 
269
      process_list = new_process;
910
270
    }
911
271
  }
912
272
  
913
273
  closedir(dir);
914
 
  dir = NULL;
915
 
  
916
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
917
 
    if(p->pid != 0){
 
274
  
 
275
  if (process_list != NULL){
 
276
    while(true){
 
277
      fd_set rfds = rfds_orig;
 
278
      int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
 
279
      if (select_ret == -1){
 
280
        perror(argv[0]);
 
281
        goto end;
 
282
      }else{    
 
283
        for(process *process_itr = process_list; process_itr != NULL;
 
284
            process_itr = process_itr->next){
 
285
          if(FD_ISSET(process_itr->fd, &rfds)){
 
286
            if(process_itr->buffer_length + BUFFER_SIZE
 
287
               > process_itr->buffer_size){
 
288
                process_itr->buffer = realloc(process_itr->buffer,
 
289
                                              process_itr->buffer_size
 
290
                                              + (size_t) BUFFER_SIZE);
 
291
                if (process_itr->buffer == NULL){
 
292
                  perror(argv[0]);
 
293
                  goto end;
 
294
                }
 
295
                process_itr->buffer_size += BUFFER_SIZE;
 
296
            }
 
297
            ret = read(process_itr->fd, process_itr->buffer
 
298
                       + process_itr->buffer_length, BUFFER_SIZE);
 
299
            if(ret < 0){
 
300
              /* Read error from this process; ignore it */
 
301
              continue;
 
302
            }
 
303
            process_itr->buffer_length += (size_t) ret;
 
304
            if(ret == 0){
 
305
              /* got EOF */
 
306
              /* wait for process exit */
 
307
              int status;
 
308
              waitpid(process_itr->pid, &status, 0);
 
309
              if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
 
310
                for(size_t written = 0;
 
311
                    written < process_itr->buffer_length;){
 
312
                  ret = write(STDOUT_FILENO,
 
313
                              process_itr->buffer + written,
 
314
                              process_itr->buffer_length - written);
 
315
                  if(ret < 0){
 
316
                    perror(argv[0]);
 
317
                    goto end;
 
318
                  }
 
319
                  written += (size_t)ret;
 
320
                }
 
321
                goto end;
 
322
              } else {
 
323
                FD_CLR(process_itr->fd, &rfds_orig);
 
324
              }
 
325
            }
 
326
          }
 
327
        }
 
328
      }
 
329
    }
 
330
  }
 
331
  
 
332
 end:
 
333
  for(process *process_itr = process_list; process_itr != NULL;
 
334
      process_itr = process_itr->next){
 
335
    close(process_itr->fd);
 
336
    kill(process_itr->pid, SIGTERM);
 
337
    free(process_itr->buffer);
 
338
  }
 
339
  
 
340
  while(true){
 
341
    int status;
 
342
    ret = wait(&status);
 
343
    if (ret == -1){
 
344
      if(errno != ECHILD){
 
345
        perror("wait");
 
346
      }
918
347
      break;
919
348
    }
920
 
    if(p->next == NULL){
921
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
922
 
              " directory?\n");
923
 
      free_plugin_list();
924
 
    }
925
 
  }
926
 
  
927
 
  /* Main loop while running plugins exist */
928
 
  while(plugin_list){
929
 
    fd_set rfds = rfds_all;
930
 
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
931
 
    if (select_ret == -1){
932
 
      perror("select");
933
 
      exitstatus = EXIT_FAILURE;
934
 
      goto fallback;
935
 
    }
936
 
    /* OK, now either a process completed, or something can be read
937
 
       from one of them */
938
 
    for(plugin *proc = plugin_list; proc != NULL;){
939
 
      /* Is this process completely done? */
940
 
      if(proc->eof and proc->completed){
941
 
        /* Only accept the plugin output if it exited cleanly */
942
 
        if(not WIFEXITED(proc->status)
943
 
           or WEXITSTATUS(proc->status) != 0){
944
 
          /* Bad exit by plugin */
945
 
 
946
 
          if(debug){
947
 
            if(WIFEXITED(proc->status)){
948
 
              fprintf(stderr, "Plugin %u exited with status %d\n",
949
 
                      (unsigned int) (proc->pid),
950
 
                      WEXITSTATUS(proc->status));
951
 
            } else if(WIFSIGNALED(proc->status)) {
952
 
              fprintf(stderr, "Plugin %u killed by signal %d\n",
953
 
                      (unsigned int) (proc->pid),
954
 
                      WTERMSIG(proc->status));
955
 
            } else if(WCOREDUMP(proc->status)){
956
 
              fprintf(stderr, "Plugin %d dumped core\n",
957
 
                      (unsigned int) (proc->pid));
958
 
            }
959
 
          }
960
 
          
961
 
          /* Remove the plugin */
962
 
          FD_CLR(proc->fd, &rfds_all);
963
 
 
964
 
          /* Block signal while modifying process_list */
965
 
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
966
 
          if(ret < 0){
967
 
            perror("sigprocmask");
968
 
            exitstatus = EXIT_FAILURE;
969
 
            goto fallback;
970
 
          }
971
 
          
972
 
          plugin *next_plugin = proc->next;
973
 
          free_plugin(proc);
974
 
          proc = next_plugin;
975
 
          
976
 
          /* We are done modifying process list, so unblock signal */
977
 
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
978
 
                             NULL);
979
 
          if(ret < 0){
980
 
            perror("sigprocmask");
981
 
            exitstatus = EXIT_FAILURE;
982
 
            goto fallback;
983
 
          }
984
 
          
985
 
          if(plugin_list == NULL){
986
 
            break;
987
 
          }
988
 
          
989
 
          continue;
990
 
        }
991
 
        
992
 
        /* This process exited nicely, so print its buffer */
993
 
        
994
 
        bool bret = print_out_password(proc->buffer,
995
 
                                       proc->buffer_length);
996
 
        if(not bret){
997
 
          perror("print_out_password");
998
 
          exitstatus = EXIT_FAILURE;
999
 
        }
1000
 
        goto fallback;
1001
 
      }
1002
 
      
1003
 
      /* This process has not completed.  Does it have any output? */
1004
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1005
 
        /* This process had nothing to say at this time */
1006
 
        proc = proc->next;
1007
 
        continue;
1008
 
      }
1009
 
      /* Before reading, make the process' data buffer large enough */
1010
 
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1011
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1012
 
                               + (size_t) BUFFER_SIZE);
1013
 
        if (proc->buffer == NULL){
1014
 
          perror("malloc");
1015
 
          exitstatus = EXIT_FAILURE;
1016
 
          goto fallback;
1017
 
        }
1018
 
        proc->buffer_size += BUFFER_SIZE;
1019
 
      }
1020
 
      /* Read from the process */
1021
 
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
1022
 
                 BUFFER_SIZE);
1023
 
      if(ret < 0){
1024
 
        /* Read error from this process; ignore the error */
1025
 
        proc = proc->next;
1026
 
        continue;
1027
 
      }
1028
 
      if(ret == 0){
1029
 
        /* got EOF */
1030
 
        proc->eof = true;
1031
 
      } else {
1032
 
        proc->buffer_length += (size_t) ret;
1033
 
      }
1034
 
    }
1035
 
  }
1036
 
 
1037
 
 
1038
 
 fallback:
1039
 
  
1040
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1041
 
    /* Fallback if all plugins failed, none are found or an error
1042
 
       occured */
1043
 
    bool bret;
1044
 
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1045
 
    char *passwordbuffer = getpass("Password: ");
1046
 
    size_t len = strlen(passwordbuffer);
1047
 
    /* Strip trailing newline */
1048
 
    if(len > 0 and passwordbuffer[len-1] == '\n'){
1049
 
      passwordbuffer[len-1] = '\0'; /* not strictly necessary */
1050
 
      len--;
1051
 
    }
1052
 
    bret = print_out_password(passwordbuffer, len);
1053
 
    if(not bret){
1054
 
      perror("print_out_password");
1055
 
      exitstatus = EXIT_FAILURE;
1056
 
    }
1057
 
  }
1058
 
  
1059
 
  /* Restore old signal handler */
1060
 
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1061
 
  if(ret == -1){
1062
 
    perror("sigaction");
1063
 
    exitstatus = EXIT_FAILURE;
1064
 
  }
1065
 
  
1066
 
  if(custom_argv != NULL){
1067
 
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
1068
 
      free(*arg);
1069
 
    }
1070
 
    free(custom_argv);
1071
 
  }
1072
 
  
1073
 
  if(dir != NULL){
1074
 
    closedir(dir);
1075
 
  }
1076
 
  
1077
 
  /* Kill the processes */
1078
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1079
 
    if(p->pid != 0){
1080
 
      close(p->fd);
1081
 
      ret = kill(p->pid, SIGTERM);
1082
 
      if(ret == -1 and errno != ESRCH){
1083
 
        /* Set-uid proccesses might not get closed */
1084
 
        perror("kill");
1085
 
      }
1086
 
    }
1087
 
  }
1088
 
  
1089
 
  /* Wait for any remaining child processes to terminate */
1090
 
  do{
1091
 
    ret = wait(NULL);
1092
 
  } while(ret >= 0);
1093
 
  if(errno != ECHILD){
1094
 
    perror("wait");
1095
 
  }
1096
 
  
1097
 
  free_plugin_list();
1098
 
  
1099
 
  free(plugindir);
1100
 
  free(argfile);
1101
 
  
1102
 
  return exitstatus;
 
349
  }  
 
350
  return EXIT_SUCCESS;
1103
351
}