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