/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugbasedclient.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-02 10:48:24 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080802104824-fx0miwp9o4g9r31e
* plugbasedclient.c (struct process): New fields "eof", "completed",
                                      and "status".
  (handle_sigchld): New function.
  (main): Initialize "dir" to NULL to only closedir() it if necessary.
          Move "process_list" to be a global variable to be accessible
          by "handle_sigchld".  Make "handle_sigchld" handle SIGCHLD.
          Remove redundant check for NULL "dir".  Free "filename" when
          no longer used.  Block SIGCHLD around fork()/exec().
          Restore normal signals in child.  Only loop while running
          processes exist.  Print process buffer when the process is
          done and it has emitted EOF, not when it only emits EOF.
          Remove processes from list which exit non-cleanly.  In
          cleaning up, closedir() if necessary.  Bug fix: set next
          pointer correctly when freeing process list.

* plugins.d/passprompt.c (main): Do not ignore SIGQUIT.

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