/mandos/release

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

« back to all changes in this revision

Viewing changes to plugbasedclient.c

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

Show diffs side-by-side

added added

removed removed

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