/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 = "mandos-client 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 %u exited with status %d\n",
610
 
                      (unsigned int) (proc->pid),
611
 
                      WEXITSTATUS(proc->status));
612
 
            } else if(WIFSIGNALED(proc->status)) {
613
 
              fprintf(stderr, "Plugin %u killed by signal %d\n",
614
 
                      (unsigned int) (proc->pid),
615
 
                      WTERMSIG(proc->status));
616
 
            } else if(WCOREDUMP(proc->status)){
617
 
              fprintf(stderr, "Plugin %d dumped core\n",
618
 
                      (unsigned int) (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;
619
140
            }
620
 
          }
621
 
          /* Remove the plugin */
622
 
          FD_CLR(proc->fd, &rfds_all);
623
 
          /* Block signal while modifying process_list */
624
 
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
625
 
          if(ret < 0){
626
 
            perror("sigprocmask");
627
 
            exitstatus = EXIT_FAILURE;
628
 
            goto end;
629
 
          }
630
 
          /* Delete this process entry from the list */
631
 
          if(process_list == proc){
632
 
            /* First one - simple */
633
 
            process_list = proc->next;
634
 
          } else {
635
 
            /* Second one or later */
636
 
            for(process *p = process_list; p != NULL; p = p->next){
637
 
              if(p->next == proc){
638
 
                p->next = proc->next;
639
 
                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);
640
155
              }
641
156
            }
642
157
          }
643
 
          /* We are done modifying process list, so unblock signal */
644
 
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
645
 
                             NULL);
646
 
          if(ret < 0){
647
 
            perror("sigprocmask");
648
 
          }
649
 
          free(proc->buffer);
650
 
          free(proc);
651
 
          /* We deleted this process from the list, so we can't go
652
 
             proc->next.  Therefore, start over from the beginning of
653
 
             the process list */
654
 
          break;
655
 
        }
656
 
        /* This process exited nicely, so print its buffer */
657
 
        for(size_t written = 0; written < proc->buffer_length;
658
 
            written += (size_t)ret){
659
 
          ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
660
 
                                         proc->buffer + written,
661
 
                                         proc->buffer_length
662
 
                                         - written));
663
 
          if(ret < 0){
664
 
            perror("write");
665
 
            exitstatus = EXIT_FAILURE;
666
 
            goto end;
667
 
          }
668
 
        }
669
 
        goto end;
670
 
      }
671
 
      /* This process has not completed.  Does it have any output? */
672
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
673
 
        /* This process had nothing to say at this time */
674
 
        continue;
675
 
      }
676
 
      /* Before reading, make the process' data buffer large enough */
677
 
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
678
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
679
 
                               + (size_t) BUFFER_SIZE);
680
 
        if (proc->buffer == NULL){
681
 
          perror("malloc");
682
 
          exitstatus = EXIT_FAILURE;
683
 
          goto end;
684
 
        }
685
 
        proc->buffer_size += BUFFER_SIZE;
686
 
      }
687
 
      /* Read from the process */
688
 
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
689
 
                 BUFFER_SIZE);
690
 
      if(ret < 0){
691
 
        /* Read error from this process; ignore the error */
692
 
        continue;
693
 
      }
694
 
      if(ret == 0){
695
 
        /* got EOF */
696
 
        proc->eof = true;
697
 
      } else {
698
 
        proc->buffer_length += (size_t) ret;
 
158
        }
699
159
      }
700
160
    }
701
161
  }
702
 
  if(process_list == NULL){
703
 
    fprintf(stderr, "All plugin processes failed, exiting\n");
704
 
    exitstatus = EXIT_FAILURE;
705
 
  }
706
162
  
707
163
 end:
708
 
  /* Restore old signal handler */
709
 
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
710
 
  
711
 
  free(plus_argv);
712
 
  
713
 
  /* Free the plugin list */
714
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
715
 
    next = plugin_list->next;
716
 
    free(plugin_list->argv);
717
 
    free(plugin_list);
718
 
  }
719
 
  
720
 
  if(dir != NULL){
721
 
    closedir(dir);
722
 
  }
723
 
  
724
 
  /* Free the process list and kill the processes */
725
 
  for(process *next; process_list != NULL; process_list = next){
726
 
    next = process_list->next;
727
 
    close(process_list->fd);
728
 
    ret = kill(process_list->pid, SIGTERM);
729
 
    if(ret == -1 and errno != ESRCH){
730
 
      /* set-uid proccesses migth not get closed */
731
 
      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;
732
179
    }
733
 
    free(process_list->buffer);
734
 
    free(process_list);
735
 
  }
736
 
  
737
 
  /* Wait for any remaining child processes to terminate */
738
 
  do{
739
 
    ret = wait(NULL);
740
 
  } while(ret >= 0);
741
 
  if(errno != ECHILD){
742
 
    perror("wait");
743
 
  }
744
 
  
745
 
  return exitstatus;
 
180
  }  
 
181
  return EXIT_SUCCESS;
746
182
}