/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-16 03:29:08 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080816032908-ihw7c05r2mnyk389
Add feature to specify custom environment variables for plugins.

* plugin-runner.c (plugin): New members "environ" and "envc" to
                            contain possible custom environment.
  (getplugin): Return NULL on failure instead of doing exit(); all
               callers changed.
  (add_to_char_array): New helper function for "add_argument" and
                       "add_environment".
  (addargument): Renamed to "add_argument".  Return bool.  Call
                 "add_to_char_array" to actually do things.
  (add_environment): New; analogous to "add_argument".
  (addcustomargument): Renamed to "add_to_argv" to avoid confusion
                       with "add_argument".
  (main): New options "--global-envs" and "--envs-for" to specify
          custom environment for plugins.  Print environment for
          plugins in debug mode.  Use asprintf instead of strcpy and
          strcat.  Use execve() for plugins with custom environments.
          Free environment for plugin when freeing plugin list.

Show diffs side-by-side

added added

removed removed

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