/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

Show diffs side-by-side

added added

removed removed

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