/mandos/release

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

« back to all changes in this revision

Viewing changes to plugbasedclient.c

  • Committer: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8 -*- */
2
 
/*
3
 
 * Mandos plugin runner - Run Mandos plugins
4
 
 *
5
 
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
6
 
 * 
7
 
 * This program is free software: you can redistribute it and/or
8
 
 * modify it under the terms of the GNU General Public License as
9
 
 * published by the Free Software Foundation, either version 3 of the
10
 
 * License, or (at your option) any later version.
11
 
 * 
12
 
 * This program is distributed in the hope that it will be useful, but
13
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 
 * General Public License for more details.
16
 
 * 
17
 
 * You should have received a copy of the GNU General Public License
18
 
 * along with this program.  If not, see
19
 
 * <http://www.gnu.org/licenses/>.
20
 
 * 
21
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
22
 
 */
23
 
 
24
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
25
 
 
26
 
#include <stdio.h>              /* popen(), fileno(), fprintf(),
27
 
                                   stderr, STDOUT_FILENO */
28
 
#include <iso646.h>             /* and, or, not */
29
 
#include <sys/types.h>          /* DIR, opendir(), stat(),
30
 
                                   struct stat, waitpid(),
31
 
                                   WIFEXITED(), WEXITSTATUS(),
32
 
                                   wait() */
33
 
#include <sys/wait.h>           /* wait() */
34
 
#include <dirent.h>             /* DIR, struct dirent, opendir(),
35
 
                                   readdir(), closedir() */
36
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
37
 
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
38
 
                                   fcntl() */
39
 
#include <fcntl.h>              /* fcntl() */
40
 
#include <stddef.h>             /* NULL */
41
 
#include <stdlib.h>             /* EXIT_FAILURE */
42
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
43
 
                                   FD_SET(), FD_ISSET() */
44
 
#include <string.h>             /* strlen(), strcpy(), strcat() */
45
 
#include <stdbool.h>            /* true */
46
 
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
47
 
                                   WEXITSTATUS() */
48
 
#include <errno.h>              /* errno */
49
 
#include <argp.h>               /* struct argp_option,
50
 
                                   struct argp_state, struct argp,
51
 
                                   argp_parse() */
 
1
#include <stdio.h>      /* popen, fileno */
 
2
#include <iso646.h>     /* and, or, not */
 
3
#include <sys/types.h>  /* DIR, opendir, stat, struct stat, waitpid,
 
4
                           WIFEXITED, WEXITSTATUS, wait */
 
5
#include <sys/wait.h>   /* wait */
 
6
#include <dirent.h>     /* DIR, opendir */
 
7
#include <sys/stat.h>   /* stat, struct stat */
 
8
#include <unistd.h>     /* stat, struct stat, chdir */
 
9
#include <stdlib.h>     /* EXIT_FAILURE */
 
10
#include <sys/select.h> /* fd_set, select, FD_ZERO, FD_SET, FD_ISSET */
 
11
#include <string.h>     /* strlen, strcpy, strcat */
 
12
#include <stdbool.h>    /* true */
 
13
#include <sys/wait.h>   /* waitpid, WIFEXITED, WEXITSTATUS */
 
14
#include <errno.h>      /* errno */
52
15
 
53
16
struct process;
54
17
 
56
19
  pid_t pid;
57
20
  int fd;
58
21
  char *buffer;
59
 
  size_t buffer_size;
60
 
  size_t buffer_length;
61
 
  bool eof;
62
 
  bool completed;
63
 
  int status;
 
22
  int buffer_size;
 
23
  int buffer_length;
64
24
  struct process *next;
65
25
} process;
66
26
 
67
 
typedef struct plugin{
68
 
  char *name;                   /* can be NULL or any plugin name */
69
 
  char **argv;
70
 
  int argc;
71
 
  bool disabled;
72
 
  struct plugin *next;
73
 
} plugin;
74
 
 
75
 
plugin *getplugin(char *name, plugin **plugin_list){
76
 
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
77
 
    if ((p->name == name)
78
 
        or (p->name and name and (strcmp(p->name, name) == 0))){
79
 
      return p;
80
 
    }
81
 
  }
82
 
  /* Create a new plugin */
83
 
  plugin *new_plugin = malloc(sizeof(plugin));
84
 
  if (new_plugin == NULL){
85
 
    perror("malloc");
86
 
    exit(EXIT_FAILURE);
87
 
  }
88
 
  new_plugin->name = name;
89
 
  new_plugin->argv = malloc(sizeof(char *) * 2);
90
 
  if (new_plugin->argv == NULL){
91
 
    perror("malloc");
92
 
    exit(EXIT_FAILURE);
93
 
  }
94
 
  new_plugin->argv[0] = name;
95
 
  new_plugin->argv[1] = NULL;
96
 
  new_plugin->argc = 1;
97
 
  new_plugin->disabled = false;
98
 
  new_plugin->next = *plugin_list;
99
 
  /* Append the new plugin to the list */
100
 
  *plugin_list = new_plugin;
101
 
  return new_plugin;
102
 
}
103
 
 
104
 
void addargument(plugin *p, char *arg){
105
 
  p->argv[p->argc] = arg;
106
 
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
107
 
  if (p->argv == NULL){
108
 
    perror("malloc");
109
 
    exit(EXIT_FAILURE);
110
 
  }
111
 
  p->argc++;
112
 
  p->argv[p->argc] = NULL;
113
 
}
114
 
 
115
 
/*
116
 
 * Based on the example in the GNU LibC manual chapter 13.13 "File
117
 
 * Descriptor Flags".
118
 
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
119
 
 */
120
 
int set_cloexec_flag(int fd)
121
 
{
122
 
  int ret = fcntl(fd, F_GETFD, 0);
123
 
  /* If reading the flags failed, return error indication now. */
124
 
  if(ret < 0){
125
 
    return ret;
126
 
  }
127
 
  /* Store modified flag word in the descriptor. */
128
 
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
129
 
}
130
 
 
131
27
#define BUFFER_SIZE 256
132
28
 
133
 
const char *argp_program_version = "plugbasedclient 0.9";
134
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
135
 
 
136
 
process *process_list = NULL;
137
 
 
138
 
/* Mark a process as completed when it exits, and save its exit
139
 
   status. */
140
 
void handle_sigchld(__attribute__((unused)) int sig){
141
 
  process *proc = process_list;
142
 
  int status;
143
 
  pid_t pid = wait(&status);
144
 
  while(proc != NULL and proc->pid != pid){
145
 
    proc = proc->next;    
146
 
  }
147
 
  if(proc == NULL){
148
 
    /* Process not found in process list */
149
 
    return;
150
 
  }
151
 
  proc->status = status;
152
 
  proc->completed = true;
153
 
}
154
 
 
155
29
int main(int argc, char *argv[]){
156
 
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
157
 
  size_t d_name_len;
158
 
  DIR *dir = NULL;
 
30
  char plugindir[] = "plugins.d";
 
31
  size_t d_name_len, plugindir_len = sizeof(plugindir)-1;
 
32
  DIR *dir;
159
33
  struct dirent *dirst;
160
34
  struct stat st;
161
 
  fd_set rfds_all;
 
35
  fd_set rfds_orig;
162
36
  int ret, maxfd = 0;
163
 
  bool debug = false;
164
 
  int exitstatus = EXIT_SUCCESS;
165
 
  
166
 
  /* Establish a signal handler */
167
 
  struct sigaction old_sigchld_action,
168
 
    sigchld_action = { .sa_handler = handle_sigchld,
169
 
                       .sa_flags = SA_NOCLDSTOP };
170
 
  sigemptyset(&sigchld_action.sa_mask);
171
 
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
172
 
  if(ret < 0){
173
 
    perror("sigaddset");
174
 
    exit(EXIT_FAILURE);
175
 
  }
176
 
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
177
 
  if(ret < 0){
178
 
    perror("sigaction");
179
 
    exit(EXIT_FAILURE);
180
 
  }
181
 
  
182
 
  /* The options we understand. */
183
 
  struct argp_option options[] = {
184
 
    { .name = "global-options", .key = 'g',
185
 
      .arg = "OPTION[,OPTION[,...]]",
186
 
      .doc = "Options passed to all plugins" },
187
 
    { .name = "options-for", .key = 'o',
188
 
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
189
 
      .doc = "Options passed only to specified plugin" },
190
 
    { .name = "disable", .key = 'd',
191
 
      .arg = "PLUGIN",
192
 
      .doc = "Disable a specific plugin", .group = 1 },
193
 
    { .name = "plugin-dir", .key = 128,
194
 
      .arg = "DIRECTORY",
195
 
      .doc = "Specify a different plugin directory", .group = 2 },
196
 
    { .name = "debug", .key = 129,
197
 
      .doc = "Debug mode", .group = 3 },
198
 
    { .name = NULL }
199
 
  };
200
 
  
201
 
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
202
 
    /* Get the INPUT argument from `argp_parse', which we know is a
203
 
       pointer to our plugin list pointer. */
204
 
    plugin **plugins = state->input;
205
 
    switch (key) {
206
 
    case 'g':
207
 
      if (arg != NULL){
208
 
        char *p = strtok(arg, ",");
209
 
        do{
210
 
          addargument(getplugin(NULL, plugins), p);
211
 
          p = strtok(NULL, ",");
212
 
        } while (p);
213
 
      }
214
 
      break;
215
 
    case 'o':
216
 
      if (arg != NULL){
217
 
        char *name = strtok(arg, ":");
218
 
        char *p = strtok(NULL, ":");
219
 
        if(p){
220
 
          p = strtok(p, ",");
221
 
          do{
222
 
            addargument(getplugin(name, plugins), p);
223
 
            p = strtok(NULL, ",");
224
 
          } while (p);
225
 
        }
226
 
      }
227
 
      break;
228
 
    case 'd':
229
 
      if (arg != NULL){
230
 
        getplugin(arg, plugins)->disabled = true;
231
 
      }
232
 
      break;
233
 
    case 128:
234
 
      plugindir = arg;
235
 
      break;
236
 
    case 129:
237
 
      debug = true;
238
 
      break;
239
 
    case ARGP_KEY_ARG:
240
 
      argp_usage (state);
241
 
      break;
242
 
    case ARGP_KEY_END:
243
 
      break;
244
 
    default:
245
 
      return ARGP_ERR_UNKNOWN;
246
 
    }
247
 
    return 0;
248
 
  }
249
 
  
250
 
  plugin *plugin_list = NULL;
251
 
  
252
 
  struct argp argp = { .options = options, .parser = parse_opt,
253
 
                       .args_doc = "",
254
 
                       .doc = "Mandos plugin runner -- Run plugins" };
255
 
  
256
 
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
257
 
  
258
 
  if(debug){
259
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
260
 
      fprintf(stderr, "Plugin: %s has %d arguments\n",
261
 
              p->name ? p->name : "Global", p->argc - 1);
262
 
      for(char **a = p->argv; *a != NULL; a++){
263
 
        fprintf(stderr, "\tArg: %s\n", *a);
264
 
      }
265
 
    }
266
 
  }
 
37
  process *process_list = NULL;
267
38
  
268
39
  dir = opendir(plugindir);
 
40
 
269
41
  if(dir == NULL){
270
 
    perror("Could not open plugin dir");
271
 
    exitstatus = EXIT_FAILURE;
272
 
    goto end;
273
 
  }
274
 
  
275
 
  /* Set the FD_CLOEXEC flag on the directory, if possible */
276
 
  {
277
 
    int dir_fd = dirfd(dir);
278
 
    if(dir_fd >= 0){
279
 
      ret = set_cloexec_flag(dir_fd);
280
 
      if(ret < 0){
281
 
        perror("set_cloexec_flag");
282
 
        exitstatus = EXIT_FAILURE;
283
 
        goto end;
284
 
      }
285
 
    }
286
 
  }
287
 
  
288
 
  FD_ZERO(&rfds_all);
 
42
    fprintf(stderr, "Can not open directory\n");
 
43
    return EXIT_FAILURE;
 
44
  }
 
45
  
 
46
  FD_ZERO(&rfds_orig);
289
47
  
290
48
  while(true){
291
49
    dirst = readdir(dir);
297
55
    
298
56
    d_name_len = strlen(dirst->d_name);
299
57
    
300
 
    // Ignore dotfiles, backup files and other junk
301
 
    {
302
 
      bool bad_name = false;
303
 
      
304
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
305
 
      
306
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
307
 
                                           ".dpkg-old",
308
 
                                           ".dpkg-divert", NULL };
309
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
310
 
        size_t pre_len = strlen(*pre);
311
 
        if((d_name_len >= pre_len)
312
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
313
 
          if(debug){
314
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
315
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
316
 
          }
317
 
          bad_name = true;
318
 
          break;
319
 
        }
320
 
      }
321
 
      
322
 
      if(bad_name){
323
 
        continue;
324
 
      }
325
 
      
326
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
327
 
        size_t suf_len = strlen(*suf);
328
 
        if((d_name_len >= suf_len)
329
 
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
330
 
                == 0)){
331
 
          if(debug){
332
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
333
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
334
 
          }
335
 
          bad_name = true;
336
 
          break;
337
 
        }
338
 
      }
339
 
      
340
 
      if(bad_name){
341
 
        continue;
342
 
      }
 
58
    // Ignore dotfiles and backup files
 
59
    if (dirst->d_name[0] == '.'
 
60
        or dirst->d_name[d_name_len - 1] == '~'){
 
61
      continue;
343
62
    }
344
63
    
345
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
346
 
    if (filename == NULL){
347
 
      perror("malloc");
348
 
      exitstatus = EXIT_FAILURE;
349
 
      goto end;
350
 
    }
351
 
    strcpy(filename, plugindir); /* Spurious warning */
352
 
    strcat(filename, "/");      /* Spurious warning */
353
 
    strcat(filename, dirst->d_name); /* Spurious warning */
 
64
    char *filename = malloc(d_name_len + plugindir_len + 1);
 
65
    strcpy(filename, plugindir);
 
66
    strcat(filename, "/");
 
67
    strcat(filename, dirst->d_name);    
354
68
    
355
69
    stat(filename, &st);
356
 
    
357
 
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
358
 
      if(debug){
359
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
360
 
                " with bad type or mode\n", filename);
361
 
      }
362
 
      free(filename);
363
 
      continue;
364
 
    }
365
 
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
366
 
      if(debug){
367
 
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
368
 
                dirst->d_name);
369
 
      }
370
 
      free(filename);
371
 
      continue;
372
 
    }
373
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
374
 
    {
375
 
      /* Add global arguments to argument list for this plugin */
376
 
      plugin *g = getplugin(NULL, &plugin_list);
377
 
      for(char **a = g->argv + 1; *a != NULL; a++){
378
 
        addargument(p, *a);
379
 
      }
380
 
    }
381
 
    int pipefd[2]; 
382
 
    ret = pipe(pipefd);
383
 
    if (ret == -1){
384
 
      perror("pipe");
385
 
      exitstatus = EXIT_FAILURE;
386
 
      goto end;
387
 
    }
388
 
    ret = set_cloexec_flag(pipefd[0]);
389
 
    if(ret < 0){
390
 
      perror("set_cloexec_flag");
391
 
      exitstatus = EXIT_FAILURE;
392
 
      goto end;
393
 
    }
394
 
    ret = set_cloexec_flag(pipefd[1]);
395
 
    if(ret < 0){
396
 
      perror("set_cloexec_flag");
397
 
      exitstatus = EXIT_FAILURE;
398
 
      goto end;
399
 
    }
400
 
    /* Block SIGCHLD until process is safely in process list */
401
 
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
402
 
    if(ret < 0){
403
 
      perror("sigprocmask");
404
 
      exitstatus = EXIT_FAILURE;
405
 
      goto end;
406
 
    }
407
 
    // Starting a new process to be watched
408
 
    pid_t pid = fork();
409
 
    if(pid == 0){
410
 
      /* this is the child process */
411
 
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
412
 
      if(ret < 0){
413
 
        perror("sigaction");
414
 
        _exit(EXIT_FAILURE);
415
 
      }
416
 
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
417
 
      if(ret < 0){
418
 
        perror("sigprocmask");
419
 
        _exit(EXIT_FAILURE);
420
 
      }
421
 
      dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
422
 
      
423
 
      if(dirfd(dir) < 0){
424
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
425
 
           above and must now close it manually here. */
 
70
 
 
71
    if (S_ISREG(st.st_mode) and (access(filename, X_OK) == 0)){
 
72
      // Starting a new process to be watched
 
73
      process *new_process = malloc(sizeof(process));
 
74
      int pipefd[2];
 
75
      pipe(pipefd);
 
76
      new_process->pid = fork();
 
77
      if(new_process->pid == 0){
 
78
        /* this is the child process */
426
79
        closedir(dir);
427
 
      }
428
 
      if(execv(filename, p->argv) < 0){
429
 
        perror("execv");
430
 
        _exit(EXIT_FAILURE);
431
 
      }
432
 
      /* no return */
433
 
    }
434
 
    /* parent process */
435
 
    free(filename);
436
 
    close(pipefd[1]);           /* close unused write end of pipe */
437
 
    process *new_process = malloc(sizeof(process));
438
 
    if (new_process == NULL){
439
 
      perror("malloc");
440
 
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
441
 
      if(ret < 0){
442
 
        perror("sigprocmask");
443
 
      }
444
 
      exitstatus = EXIT_FAILURE;
445
 
      goto end;
446
 
    }
447
 
    
448
 
    *new_process = (struct process){ .pid = pid,
449
 
                                     .fd = pipefd[0],
450
 
                                     .next = process_list };
451
 
    // List handling
452
 
    process_list = new_process;
453
 
    /* Unblock SIGCHLD so signal handler can be run if this process
454
 
       has already completed */
455
 
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
456
 
    if(ret < 0){
457
 
      perror("sigprocmask");
458
 
      exitstatus = EXIT_FAILURE;
459
 
      goto end;
460
 
    }
461
 
    
462
 
    FD_SET(new_process->fd, &rfds_all);
463
 
    
464
 
    if (maxfd < new_process->fd){
465
 
      maxfd = new_process->fd;
466
 
    }
467
 
    
468
 
  }
469
 
  
470
 
  /* Free the plugin list */
471
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
472
 
    next = plugin_list->next;
473
 
    free(plugin_list->argv);
474
 
    free(plugin_list);
 
80
        close(pipefd[0]);       /* close unused read end of pipe */
 
81
        dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
 
82
        /* create a new modified argument list */
 
83
        char **new_argv = malloc(sizeof(char *) * argc + 1);
 
84
        new_argv[0] = filename;
 
85
        for(int i = 1; i < argc; i++){
 
86
          new_argv[i] = argv[i];
 
87
        }
 
88
        new_argv[argc] = NULL;
 
89
        if(execv(filename, new_argv) < 0){
 
90
          perror(argv[0]);
 
91
          close(pipefd[1]);
 
92
          exit(EXIT_FAILURE);
 
93
        }
 
94
        /* no return */
 
95
      }
 
96
      close(pipefd[1]);         /* close unused write end of pipe */
 
97
      new_process->fd = pipefd[0];
 
98
      new_process->buffer = malloc(BUFFER_SIZE);
 
99
      if (new_process->buffer == NULL){
 
100
        perror(argv[0]);
 
101
        goto end;
 
102
      }
 
103
      new_process->buffer_size = BUFFER_SIZE;
 
104
      new_process->buffer_length = 0;
 
105
      FD_SET(new_process->fd, &rfds_orig);
 
106
      
 
107
      if (maxfd < new_process->fd){
 
108
        maxfd = new_process->fd;
 
109
      }
 
110
      
 
111
      //List handling
 
112
      new_process->next = process_list;
 
113
      process_list = new_process;
 
114
    }
475
115
  }
476
116
  
477
117
  closedir(dir);
478
 
  dir = NULL;
479
118
  
480
 
  if (process_list == NULL){
481
 
    fprintf(stderr, "No plugin processes started, exiting\n");
482
 
    exitstatus = EXIT_FAILURE;
483
 
    goto end;
484
 
  }
485
 
  while(process_list){
486
 
    fd_set rfds = rfds_all;
487
 
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
488
 
    if (select_ret == -1){
489
 
      perror("select");
490
 
      exitstatus = EXIT_FAILURE;
491
 
      goto end;
492
 
    }
493
 
    /* OK, now either a process completed, or something can be read
494
 
       from one of them */
495
 
    for(process *proc = process_list; proc ; proc = proc->next){
496
 
      /* Is this process completely done? */
497
 
      if(proc->eof and proc->completed){
498
 
        /* Only accept the plugin output if it exited cleanly */
499
 
        if(not WIFEXITED(proc->status)
500
 
           or WEXITSTATUS(proc->status) != 0){
501
 
          /* Bad exit by plugin */
502
 
          if(debug){
503
 
            if(WIFEXITED(proc->status)){
504
 
              fprintf(stderr, "Plugin %d exited with status %d\n",
505
 
                      proc->pid, WEXITSTATUS(proc->status));
506
 
            } else if(WIFSIGNALED(proc->status)) {
507
 
              fprintf(stderr, "Plugin %d killed by signal %d\n",
508
 
                      proc->pid, WTERMSIG(proc->status));
509
 
            } else if(WCOREDUMP(proc->status)){
510
 
              fprintf(stderr, "Plugin %d dumped core\n", proc->pid);
 
119
  if (process_list != NULL){
 
120
    while(true){
 
121
      fd_set rfds = rfds_orig;
 
122
      int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
 
123
      if (select_ret == -1){
 
124
        perror(argv[0]);
 
125
        goto end;
 
126
      }else{    
 
127
        for(process *process_itr = process_list; process_itr != NULL;
 
128
            process_itr = process_itr->next){
 
129
          if(FD_ISSET(process_itr->fd, &rfds)){
 
130
            if(process_itr->buffer_length + BUFFER_SIZE
 
131
               > process_itr->buffer_size){
 
132
                process_itr->buffer = realloc(process_itr->buffer,
 
133
                                              process_itr->buffer_size
 
134
                                              + BUFFER_SIZE);
 
135
                if (process_itr->buffer == NULL){
 
136
                  perror(argv[0]);
 
137
                  goto end;
 
138
                }
 
139
                process_itr->buffer_size += BUFFER_SIZE;
511
140
            }
512
 
          }
513
 
          /* Remove the plugin */
514
 
          FD_CLR(proc->fd, &rfds_all);
515
 
          /* Block signal while modifying process_list */
516
 
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
517
 
          if(ret < 0){
518
 
            perror("sigprocmask");
519
 
            exitstatus = EXIT_FAILURE;
520
 
            goto end;
521
 
          }
522
 
          /* Delete this process entry from the list */
523
 
          if(process_list == proc){
524
 
            /* First one - simple */
525
 
            process_list = proc->next;
526
 
          } else {
527
 
            /* Second one or later */
528
 
            for(process *p = process_list; p != NULL; p = p->next){
529
 
              if(p->next == proc){
530
 
                p->next = proc->next;
531
 
                break;
 
141
            ret = read(process_itr->fd, process_itr->buffer
 
142
                       + process_itr->buffer_length, BUFFER_SIZE);
 
143
            process_itr->buffer_length+=ret;
 
144
            if(ret == 0){
 
145
              /* got EOF */
 
146
              /* wait for process exit */
 
147
              int status;
 
148
              waitpid(process_itr->pid, &status, 0);
 
149
              if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
 
150
                write(STDOUT_FILENO, process_itr->buffer,
 
151
                      process_itr->buffer_length);
 
152
                goto end;
 
153
              } else {
 
154
                FD_CLR(process_itr->fd, &rfds_orig);
532
155
              }
533
156
            }
534
157
          }
535
 
          /* We are done modifying process list, so unblock signal */
536
 
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
537
 
                             NULL);
538
 
          if(ret < 0){
539
 
            perror("sigprocmask");
540
 
          }
541
 
          free(proc->buffer);
542
 
          free(proc);
543
 
          /* We deleted this process from the list, so we can't go
544
 
             proc->next.  Therefore, start over from the beginning of
545
 
             the process list */
546
 
          break;
547
 
        }
548
 
        /* This process exited nicely, so print its buffer */
549
 
        for(size_t written = 0; written < proc->buffer_length;
550
 
            written += (size_t)ret){
551
 
          ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
552
 
                                         proc->buffer + written,
553
 
                                         proc->buffer_length
554
 
                                         - written));
555
 
          if(ret < 0){
556
 
            perror("write");
557
 
            exitstatus = EXIT_FAILURE;
558
 
            goto end;
559
 
          }
560
 
        }
561
 
        goto end;
562
 
      }
563
 
      /* This process has not completed.  Does it have any output? */
564
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
565
 
        /* This process had nothing to say at this time */
566
 
        continue;
567
 
      }
568
 
      /* Before reading, make the process' data buffer large enough */
569
 
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
570
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
571
 
                               + (size_t) BUFFER_SIZE);
572
 
        if (proc->buffer == NULL){
573
 
          perror("malloc");
574
 
          exitstatus = EXIT_FAILURE;
575
 
          goto end;
576
 
        }
577
 
        proc->buffer_size += BUFFER_SIZE;
578
 
      }
579
 
      /* Read from the process */
580
 
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
581
 
                 BUFFER_SIZE);
582
 
      if(ret < 0){
583
 
        /* Read error from this process; ignore the error */
584
 
        continue;
585
 
      }
586
 
      if(ret == 0){
587
 
        /* got EOF */
588
 
        proc->eof = true;
589
 
      } else {
590
 
        proc->buffer_length += (size_t) ret;
 
158
        }
591
159
      }
592
160
    }
593
161
  }
594
 
  if(process_list == NULL){
595
 
    fprintf(stderr, "All plugin processes failed, exiting\n");
596
 
    exitstatus = EXIT_FAILURE;
597
 
  }
598
162
  
599
163
 end:
600
 
  /* Restore old signal handler */
601
 
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
602
 
  
603
 
  /* Free the plugin list */
604
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
605
 
    next = plugin_list->next;
606
 
    free(plugin_list->argv);
607
 
    free(plugin_list);
608
 
  }
609
 
  
610
 
  if(dir != NULL){
611
 
    closedir(dir);
612
 
  }
613
 
  
614
 
  /* Free the process list and kill the processes */
615
 
  for(process *next; process_list != NULL; process_list = next){
616
 
    next = process_list->next;
617
 
    close(process_list->fd);
618
 
    kill(process_list->pid, SIGTERM);
619
 
    free(process_list->buffer);
620
 
    free(process_list);
621
 
  }
622
 
  
623
 
  /* Wait for any remaining child processes to terminate */
624
 
  do{
625
 
    ret = wait(NULL);
626
 
  } while(ret >= 0);
627
 
  if(errno != ECHILD){
628
 
    perror("wait");
629
 
  }
630
 
  
631
 
  return exitstatus;
 
164
  for(process *process_itr = process_list; process_itr != NULL;
 
165
      process_itr = process_itr->next){
 
166
    close(process_itr->fd);
 
167
    kill(process_itr->pid, SIGTERM);
 
168
    free(process_itr->buffer);
 
169
  }
 
170
  
 
171
  while(true){
 
172
    int status;
 
173
    ret = wait(&status);
 
174
    if (ret == -1){
 
175
      if(errno != ECHILD){
 
176
        perror("wait");
 
177
      }
 
178
      break;
 
179
    }
 
180
  }  
 
181
  return EXIT_SUCCESS;
632
182
}