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