/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2008-12-10 01:26:02 UTC
  • mfrom: (237.1.2 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20081210012602-vhz3h75xkj24t340
First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

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,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
 
5
 * Copyright © 2008 Teddy Hogeborn
 
6
 * Copyright © 2008 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
38
38
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
39
39
                                   FD_SET(), FD_ISSET(), FD_CLR */
40
40
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
41
 
                                   WEXITSTATUS(), WTERMSIG(),
42
 
                                   WCOREDUMP() */
 
41
                                   WEXITSTATUS() */
43
42
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
44
43
#include <iso646.h>             /* and, or, not */
45
44
#include <dirent.h>             /* DIR, struct dirent, opendir(),
53
52
                                   close() */
54
53
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
54
                                   FD_CLOEXEC */
56
 
#include <string.h>             /* strsep, strlen(), asprintf(),
57
 
                                   strsignal(), strcmp(), strncmp() */
 
55
#include <string.h>             /* strsep, strlen(), asprintf() */
58
56
#include <errno.h>              /* errno */
59
57
#include <argp.h>               /* struct argp_option, struct
60
58
                                   argp_state, struct argp,
64
62
#include <signal.h>             /* struct sigaction, sigemptyset(),
65
63
                                   sigaddset(), sigaction(),
66
64
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
67
 
                                   SIG_UNBLOCK, kill(), sig_atomic_t
68
 
                                */
 
65
                                   SIG_UNBLOCK, kill() */
69
66
#include <errno.h>              /* errno, EBADF */
70
 
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
71
67
 
72
68
#define BUFFER_SIZE 256
73
69
 
84
80
  char **environ;
85
81
  int envc;
86
82
  bool disabled;
87
 
  
 
83
 
88
84
  /* Variables used for running processes*/
89
85
  pid_t pid;
90
86
  int fd;
92
88
  size_t buffer_size;
93
89
  size_t buffer_length;
94
90
  bool eof;
95
 
  volatile sig_atomic_t completed;
96
 
  int status;
 
91
  volatile bool completed;
 
92
  volatile int status;
97
93
  struct plugin *next;
98
94
} plugin;
99
95
 
103
99
   or if none is found, creates a new one */
104
100
static plugin *getplugin(char *name){
105
101
  /* Check for exiting plugin with that name */
106
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
107
 
    if((p->name == name)
108
 
       or (p->name and name and (strcmp(p->name, name) == 0))){
 
102
  for (plugin *p = plugin_list; p != NULL; p = p->next){
 
103
    if ((p->name == name)
 
104
        or (p->name and name and (strcmp(p->name, name) == 0))){
109
105
      return p;
110
106
    }
111
107
  }
112
108
  /* Create a new plugin */
113
 
  plugin *new_plugin = NULL;
114
 
  do {
115
 
    new_plugin = malloc(sizeof(plugin));
116
 
  } while(new_plugin == NULL and errno == EINTR);
117
 
  if(new_plugin == NULL){
 
109
  plugin *new_plugin = malloc(sizeof(plugin));
 
110
  if (new_plugin == NULL){
118
111
    return NULL;
119
112
  }
120
113
  char *copy_name = NULL;
121
114
  if(name != NULL){
122
 
    do {
123
 
      copy_name = strdup(name);
124
 
    } while(copy_name == NULL and errno == EINTR);
 
115
    copy_name = strdup(name);
125
116
    if(copy_name == NULL){
126
 
      free(new_plugin);
127
117
      return NULL;
128
118
    }
129
119
  }
130
120
  
131
 
  *new_plugin = (plugin){ .name = copy_name,
132
 
                          .argc = 1,
133
 
                          .disabled = false,
134
 
                          .next = plugin_list };
 
121
  *new_plugin = (plugin) { .name = copy_name,
 
122
                           .argc = 1,
 
123
                           .disabled = false,
 
124
                           .next = plugin_list };
135
125
  
136
 
  do {
137
 
    new_plugin->argv = malloc(sizeof(char *) * 2);
138
 
  } while(new_plugin->argv == NULL and errno == EINTR);
139
 
  if(new_plugin->argv == NULL){
 
126
  new_plugin->argv = malloc(sizeof(char *) * 2);
 
127
  if (new_plugin->argv == NULL){
140
128
    free(copy_name);
141
129
    free(new_plugin);
142
130
    return NULL;
144
132
  new_plugin->argv[0] = copy_name;
145
133
  new_plugin->argv[1] = NULL;
146
134
  
147
 
  do {
148
 
    new_plugin->environ = malloc(sizeof(char *));
149
 
  } while(new_plugin->environ == NULL and errno == EINTR);
 
135
  new_plugin->environ = malloc(sizeof(char *));
150
136
  if(new_plugin->environ == NULL){
151
137
    free(copy_name);
152
138
    free(new_plugin->argv);
164
150
static bool add_to_char_array(const char *new, char ***array,
165
151
                              int *len){
166
152
  /* Resize the pointed-to array to hold one more pointer */
167
 
  do {
168
 
    *array = realloc(*array, sizeof(char *)
169
 
                     * (size_t) ((*len) + 2));
170
 
  } while(*array == NULL and errno == EINTR);
 
153
  *array = realloc(*array, sizeof(char *)
 
154
                   * (size_t) ((*len) + 2));
171
155
  /* Malloc check */
172
156
  if(*array == NULL){
173
157
    return false;
174
158
  }
175
159
  /* Make a copy of the new string */
176
 
  char *copy;
177
 
  do {
178
 
    copy = strdup(new);
179
 
  } while(copy == NULL and errno == EINTR);
 
160
  char *copy = strdup(new);
180
161
  if(copy == NULL){
181
162
    return false;
182
163
  }
208
189
    if(strncmp(*e, def, namelen + 1) == 0){
209
190
      /* It already exists */
210
191
      if(replace){
211
 
        char *new;
212
 
        do {
213
 
          new = realloc(*e, strlen(def) + 1);
214
 
        } while(new == NULL and errno == EINTR);
 
192
        char *new = realloc(*e, strlen(def) + 1);
215
193
        if(new == NULL){
216
194
          return false;
217
195
        }
227
205
/*
228
206
 * Based on the example in the GNU LibC manual chapter 13.13 "File
229
207
 * Descriptor Flags".
230
 
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
 
208
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
231
209
 */
232
210
static int set_cloexec_flag(int fd){
233
 
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
 
211
  int ret = fcntl(fd, F_GETFD, 0);
234
212
  /* If reading the flags failed, return error indication now. */
235
213
  if(ret < 0){
236
214
    return ret;
237
215
  }
238
216
  /* Store modified flag word in the descriptor. */
239
 
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
240
 
                                       ret | FD_CLOEXEC));
 
217
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
241
218
}
242
219
 
243
220
 
244
221
/* Mark processes as completed when they exit, and save their exit
245
222
   status. */
246
223
static void handle_sigchld(__attribute__((unused)) int sig){
247
 
  int old_errno = errno;
248
224
  while(true){
249
225
    plugin *proc = plugin_list;
250
226
    int status;
254
230
      break;
255
231
    }
256
232
    if(pid == -1){
257
 
      if(errno == ECHILD){
258
 
        /* No child processes */
259
 
        break;
 
233
      if (errno != ECHILD){
 
234
        perror("waitpid");
260
235
      }
261
 
      perror("waitpid");
 
236
      /* No child processes */
 
237
      break;
262
238
    }
263
239
    
264
240
    /* A child exited, find it in process_list */
270
246
      continue;
271
247
    }
272
248
    proc->status = status;
273
 
    proc->completed = 1;
 
249
    proc->completed = true;
274
250
  }
275
 
  errno = old_errno;
276
251
}
277
252
 
278
253
/* Prints out a password to stdout */
300
275
  }
301
276
  free(plugin_node->environ);
302
277
  free(plugin_node->buffer);
303
 
  
 
278
 
304
279
  /* Removes the plugin from the singly-linked list */
305
280
  if(plugin_node == plugin_list){
306
281
    /* First one - simple */
334
309
  struct stat st;
335
310
  fd_set rfds_all;
336
311
  int ret, maxfd = 0;
337
 
  ssize_t sret;
338
312
  uid_t uid = 65534;
339
313
  gid_t gid = 65534;
340
314
  bool debug = false;
397
371
    { .name = NULL }
398
372
  };
399
373
  
400
 
  error_t parse_opt(int key, char *arg, __attribute__((unused))
401
 
                    struct argp_state *state){
402
 
    switch(key){
403
 
      char *tmp;
404
 
      intmax_t tmpmax;
 
374
  error_t parse_opt (int key, char *arg, __attribute__((unused))
 
375
                     struct argp_state *state) {
 
376
    switch (key) {
405
377
    case 'g':                   /* --global-options */
406
 
      if(arg != NULL){
407
 
        char *plugin_option;
408
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
409
 
          if(plugin_option[0] == '\0'){
 
378
      if (arg != NULL){
 
379
        char *p;
 
380
        while((p = strsep(&arg, ",")) != NULL){
 
381
          if(p[0] == '\0'){
410
382
            continue;
411
383
          }
412
 
          if(not add_argument(getplugin(NULL), plugin_option)){
 
384
          if(not add_argument(getplugin(NULL), p)){
413
385
            perror("add_argument");
414
386
            return ARGP_ERR_UNKNOWN;
415
387
          }
425
397
      }
426
398
      break;
427
399
    case 'o':                   /* --options-for */
428
 
      if(arg != NULL){
429
 
        char *plugin_name = strsep(&arg, ":");
430
 
        if(plugin_name[0] == '\0'){
431
 
          break;
432
 
        }
433
 
        char *plugin_option;
434
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
435
 
          if(not add_argument(getplugin(plugin_name), plugin_option)){
 
400
      if (arg != NULL){
 
401
        char *p_name = strsep(&arg, ":");
 
402
        if(p_name[0] == '\0' or arg == NULL){
 
403
          break;
 
404
        }
 
405
        char *opt = strsep(&arg, ":");
 
406
        if(opt[0] == '\0' or opt == NULL){
 
407
          break;
 
408
        }
 
409
        char *p;
 
410
        while((p = strsep(&opt, ",")) != NULL){
 
411
          if(p[0] == '\0'){
 
412
            continue;
 
413
          }
 
414
          if(not add_argument(getplugin(p_name), p)){
436
415
            perror("add_argument");
437
416
            return ARGP_ERR_UNKNOWN;
438
417
          }
455
434
      }
456
435
      break;
457
436
    case 'd':                   /* --disable */
458
 
      if(arg != NULL){
 
437
      if (arg != NULL){
459
438
        plugin *p = getplugin(arg);
460
439
        if(p == NULL){
461
440
          return ARGP_ERR_UNKNOWN;
464
443
      }
465
444
      break;
466
445
    case 'e':                   /* --enable */
467
 
      if(arg != NULL){
 
446
      if (arg != NULL){
468
447
        plugin *p = getplugin(arg);
469
448
        if(p == NULL){
470
449
          return ARGP_ERR_UNKNOWN;
477
456
      plugindir = strdup(arg);
478
457
      if(plugindir == NULL){
479
458
        perror("strdup");
480
 
      }
 
459
      }      
481
460
      break;
482
461
    case 129:                   /* --config-file */
483
462
      /* This is already done by parse_opt_config_file() */
484
463
      break;
485
464
    case 130:                   /* --userid */
486
 
      errno = 0;
487
 
      tmpmax = strtoimax(arg, &tmp, 10);
488
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
489
 
         or tmpmax != (uid_t)tmpmax){
490
 
        fprintf(stderr, "Bad user ID number: \"%s\", using %"
491
 
                PRIdMAX "\n", arg, (intmax_t)uid);
492
 
      } else {
493
 
        uid = (uid_t)tmpmax;
494
 
      }
 
465
      uid = (uid_t)strtol(arg, NULL, 10);
495
466
      break;
496
467
    case 131:                   /* --groupid */
497
 
      errno = 0;
498
 
      tmpmax = strtoimax(arg, &tmp, 10);
499
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
500
 
         or tmpmax != (gid_t)tmpmax){
501
 
        fprintf(stderr, "Bad group ID number: \"%s\", using %"
502
 
                PRIdMAX "\n", arg, (intmax_t)gid);
503
 
      } else {
504
 
        gid = (gid_t)tmpmax;
505
 
      }
 
468
      gid = (gid_t)strtol(arg, NULL, 10);
506
469
      break;
507
470
    case 132:                   /* --debug */
508
471
      debug = true;
509
472
      break;
510
 
/*
511
 
 * When adding more options before this line, remember to also add a
512
 
 * "case" to the "parse_opt_config_file" function below.
513
 
 */
514
473
    case ARGP_KEY_ARG:
515
474
      /* Cryptsetup always passes an argument, which is an empty
516
475
         string if "none" was specified in /etc/crypttab.  So if
529
488
  
530
489
  /* This option parser is the same as parse_opt() above, except it
531
490
     ignores everything but the --config-file option. */
532
 
  error_t parse_opt_config_file(int key, char *arg,
533
 
                                __attribute__((unused))
534
 
                                struct argp_state *state){
535
 
    switch(key){
 
491
  error_t parse_opt_config_file (int key, char *arg,
 
492
                                 __attribute__((unused))
 
493
                                 struct argp_state *state) {
 
494
    switch (key) {
536
495
    case 'g':                   /* --global-options */
537
496
    case 'G':                   /* --global-env */
538
497
    case 'o':                   /* --options-for */
547
506
      if(argfile == NULL){
548
507
        perror("strdup");
549
508
      }
550
 
      break;
 
509
      break;      
551
510
    case 130:                   /* --userid */
552
511
    case 131:                   /* --groupid */
553
512
    case 132:                   /* --debug */
565
524
                       .args_doc = "",
566
525
                       .doc = "Mandos plugin runner -- Run plugins" };
567
526
  
568
 
  /* Parse using parse_opt_config_file() in order to get the custom
 
527
  /* Parse using the parse_opt_config_file in order to get the custom
569
528
     config file location, if any. */
570
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
571
 
  if(ret == ARGP_ERR_UNKNOWN){
 
529
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
530
  if (ret == ARGP_ERR_UNKNOWN){
572
531
    fprintf(stderr, "Unknown error while parsing arguments\n");
573
532
    exitstatus = EXIT_FAILURE;
574
533
    goto fallback;
578
537
  argp.parser = parse_opt;
579
538
  
580
539
  /* Open the configfile if available */
581
 
  if(argfile == NULL){
 
540
  if (argfile == NULL){
582
541
    conffp = fopen(AFILE, "r");
583
542
  } else {
584
543
    conffp = fopen(argfile, "r");
585
 
  }
 
544
  }  
586
545
  if(conffp != NULL){
587
546
    char *org_line = NULL;
588
547
    char *p, *arg, *new_arg, *line;
589
548
    size_t size = 0;
 
549
    ssize_t sret;
590
550
    const char whitespace_delims[] = " \r\t\f\v\n";
591
551
    const char comment_delim[] = "#";
592
 
    
 
552
 
593
553
    custom_argc = 1;
594
554
    custom_argv = malloc(sizeof(char*) * 2);
595
555
    if(custom_argv == NULL){
599
559
    }
600
560
    custom_argv[0] = argv[0];
601
561
    custom_argv[1] = NULL;
602
 
    
 
562
 
603
563
    /* for each line in the config file, strip whitespace and ignore
604
564
       commented text */
605
565
    while(true){
607
567
      if(sret == -1){
608
568
        break;
609
569
      }
610
 
      
 
570
 
611
571
      line = org_line;
612
572
      arg = strsep(&line, comment_delim);
613
573
      while((p = strsep(&arg, whitespace_delims)) != NULL){
632
592
          goto fallback;
633
593
        }
634
594
        custom_argv[custom_argc-1] = new_arg;
635
 
        custom_argv[custom_argc] = NULL;
 
595
        custom_argv[custom_argc] = NULL;        
636
596
      }
637
597
    }
638
 
    do {
639
 
      ret = fclose(conffp);
640
 
    } while(ret == EOF and errno == EINTR);
641
 
    if(ret == EOF){
642
 
      perror("fclose");
643
 
      exitstatus = EXIT_FAILURE;
644
 
      goto fallback;
645
 
    }
646
598
    free(org_line);
647
599
  } else {
648
600
    /* Check for harmful errors and go to fallback. Other errors might
649
601
       not affect opening plugins */
650
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
602
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
651
603
      perror("fopen");
652
604
      exitstatus = EXIT_FAILURE;
653
605
      goto fallback;
656
608
  /* If there was any arguments from configuration file,
657
609
     pass them to parser as command arguments */
658
610
  if(custom_argv != NULL){
659
 
    ret = argp_parse(&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
660
 
                     0, NULL);
661
 
    if(ret == ARGP_ERR_UNKNOWN){
 
611
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
 
612
                      0, NULL);
 
613
    if (ret == ARGP_ERR_UNKNOWN){
662
614
      fprintf(stderr, "Unknown error while parsing arguments\n");
663
615
      exitstatus = EXIT_FAILURE;
664
616
      goto fallback;
667
619
  
668
620
  /* Parse actual command line arguments, to let them override the
669
621
     config file */
670
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
671
 
  if(ret == ARGP_ERR_UNKNOWN){
 
622
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
623
  if (ret == ARGP_ERR_UNKNOWN){
672
624
    fprintf(stderr, "Unknown error while parsing arguments\n");
673
625
    exitstatus = EXIT_FAILURE;
674
626
    goto fallback;
681
633
      for(char **a = p->argv; *a != NULL; a++){
682
634
        fprintf(stderr, "\tArg: %s\n", *a);
683
635
      }
684
 
      fprintf(stderr, "...and %d environment variables\n", p->envc);
 
636
      fprintf(stderr, "...and %u environment variables\n", p->envc);
685
637
      for(char **a = p->environ; *a != NULL; a++){
686
638
        fprintf(stderr, "\t%s\n", *a);
687
639
      }
689
641
  }
690
642
  
691
643
  /* Strip permissions down to nobody */
 
644
  ret = setuid(uid);
 
645
  if (ret == -1){
 
646
    perror("setuid");
 
647
  }  
692
648
  setgid(gid);
693
 
  if(ret == -1){
 
649
  if (ret == -1){
694
650
    perror("setgid");
695
651
  }
696
 
  ret = setuid(uid);
697
 
  if(ret == -1){
698
 
    perror("setuid");
699
 
  }
700
652
  
701
 
  if(plugindir == NULL){
 
653
  if (plugindir == NULL){
702
654
    dir = opendir(PDIR);
703
655
  } else {
704
656
    dir = opendir(plugindir);
727
679
  
728
680
  /* Read and execute any executable in the plugin directory*/
729
681
  while(true){
730
 
    do {
731
 
      dirst = readdir(dir);
732
 
    } while(dirst == NULL and errno == EINTR);
 
682
    dirst = readdir(dir);
733
683
    
734
684
    /* All directory entries have been processed */
735
685
    if(dirst == NULL){
736
 
      if(errno == EBADF){
 
686
      if (errno == EBADF){
737
687
        perror("readdir");
738
688
        exitstatus = EXIT_FAILURE;
739
689
        goto fallback;
771
721
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
772
722
        size_t suf_len = strlen(*suf);
773
723
        if((d_name_len >= suf_len)
774
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
 
724
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
775
725
                == 0)){
776
726
          if(debug){
777
727
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
786
736
        continue;
787
737
      }
788
738
    }
789
 
    
 
739
 
790
740
    char *filename;
791
741
    if(plugindir == NULL){
792
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
793
 
                                             dirst->d_name));
 
742
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
794
743
    } else {
795
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
796
 
                                             plugindir,
797
 
                                             dirst->d_name));
 
744
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
798
745
    }
799
746
    if(ret < 0){
800
747
      perror("asprintf");
801
748
      continue;
802
749
    }
803
750
    
804
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
805
 
    if(ret == -1){
 
751
    ret = stat(filename, &st);
 
752
    if (ret == -1){
806
753
      perror("stat");
807
754
      free(filename);
808
755
      continue;
809
756
    }
810
 
    
 
757
 
811
758
    /* Ignore non-executable files */
812
 
    if(not S_ISREG(st.st_mode)
813
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
759
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
814
760
      if(debug){
815
761
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
816
762
                " with bad type or mode\n", filename);
862
808
    }
863
809
    
864
810
    int pipefd[2];
865
 
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
866
 
    if(ret == -1){
 
811
    ret = pipe(pipefd);
 
812
    if (ret == -1){
867
813
      perror("pipe");
868
814
      exitstatus = EXIT_FAILURE;
869
815
      goto fallback;
882
828
      goto fallback;
883
829
    }
884
830
    /* Block SIGCHLD until process is safely in process list */
885
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
886
 
                                              &sigchld_action.sa_mask,
887
 
                                              NULL));
 
831
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
888
832
    if(ret < 0){
889
833
      perror("sigprocmask");
890
834
      exitstatus = EXIT_FAILURE;
891
835
      goto fallback;
892
836
    }
893
837
    /* Starting a new process to be watched */
894
 
    pid_t pid;
895
 
    do {
896
 
      pid = fork();
897
 
    } while(pid == -1 and errno == EINTR);
 
838
    pid_t pid = fork();
898
839
    if(pid == -1){
899
840
      perror("fork");
900
841
      exitstatus = EXIT_FAILURE;
938
879
      /* no return */
939
880
    }
940
881
    /* Parent process */
941
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
942
 
                                             pipe */
 
882
    close(pipefd[1]);           /* Close unused write end of pipe */
943
883
    free(filename);
944
884
    plugin *new_plugin = getplugin(dirst->d_name);
945
 
    if(new_plugin == NULL){
 
885
    if (new_plugin == NULL){
946
886
      perror("getplugin");
947
 
      ret = (int)(TEMP_FAILURE_RETRY
948
 
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
949
 
                               NULL)));
 
887
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
950
888
      if(ret < 0){
951
889
        perror("sigprocmask");
952
890
      }
959
897
    
960
898
    /* Unblock SIGCHLD so signal handler can be run if this process
961
899
       has already completed */
962
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
963
 
                                              &sigchld_action.sa_mask,
964
 
                                              NULL));
 
900
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
965
901
    if(ret < 0){
966
902
      perror("sigprocmask");
967
903
      exitstatus = EXIT_FAILURE;
968
904
      goto fallback;
969
905
    }
970
906
    
971
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
972
 
                                          -Wconversion */
 
907
    FD_SET(new_plugin->fd, &rfds_all);
973
908
    
974
 
    if(maxfd < new_plugin->fd){
 
909
    if (maxfd < new_plugin->fd){
975
910
      maxfd = new_plugin->fd;
976
911
    }
977
912
  }
978
913
  
979
 
  TEMP_FAILURE_RETRY(closedir(dir));
 
914
  closedir(dir);
980
915
  dir = NULL;
981
 
  free_plugin(getplugin(NULL));
982
916
  
983
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
984
918
    if(p->pid != 0){
995
929
  while(plugin_list){
996
930
    fd_set rfds = rfds_all;
997
931
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
998
 
    if(select_ret == -1 and errno != EINTR){
 
932
    if (select_ret == -1){
999
933
      perror("select");
1000
934
      exitstatus = EXIT_FAILURE;
1001
935
      goto fallback;
1004
938
       from one of them */
1005
939
    for(plugin *proc = plugin_list; proc != NULL;){
1006
940
      /* Is this process completely done? */
1007
 
      if(proc->completed and proc->eof){
 
941
      if(proc->eof and proc->completed){
1008
942
        /* Only accept the plugin output if it exited cleanly */
1009
943
        if(not WIFEXITED(proc->status)
1010
944
           or WEXITSTATUS(proc->status) != 0){
1011
945
          /* Bad exit by plugin */
1012
 
          
 
946
 
1013
947
          if(debug){
1014
948
            if(WIFEXITED(proc->status)){
1015
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
1016
 
                      " status %d\n", proc->name,
1017
 
                      (intmax_t) (proc->pid),
 
949
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
950
                      (unsigned int) (proc->pid),
1018
951
                      WEXITSTATUS(proc->status));
1019
 
            } else if(WIFSIGNALED(proc->status)){
1020
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
1021
 
                      " signal %d: %s\n", proc->name,
1022
 
                      (intmax_t) (proc->pid),
1023
 
                      WTERMSIG(proc->status),
1024
 
                      strsignal(WTERMSIG(proc->status)));
 
952
            } else if(WIFSIGNALED(proc->status)) {
 
953
              fprintf(stderr, "Plugin %u killed by signal %d\n",
 
954
                      (unsigned int) (proc->pid),
 
955
                      WTERMSIG(proc->status));
1025
956
            } else if(WCOREDUMP(proc->status)){
1026
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1027
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
 
957
              fprintf(stderr, "Plugin %d dumped core\n",
 
958
                      (unsigned int) (proc->pid));
1028
959
            }
1029
960
          }
1030
961
          
1031
962
          /* Remove the plugin */
1032
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1033
 
                                          -Wconversion */
1034
 
          
 
963
          FD_CLR(proc->fd, &rfds_all);
 
964
 
1035
965
          /* Block signal while modifying process_list */
1036
 
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1037
 
                                        (SIG_BLOCK,
1038
 
                                         &sigchld_action.sa_mask,
1039
 
                                         NULL));
 
966
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1040
967
          if(ret < 0){
1041
968
            perror("sigprocmask");
1042
969
            exitstatus = EXIT_FAILURE;
1048
975
          proc = next_plugin;
1049
976
          
1050
977
          /* We are done modifying process list, so unblock signal */
1051
 
          ret = (int)(TEMP_FAILURE_RETRY
1052
 
                      (sigprocmask(SIG_UNBLOCK,
1053
 
                                   &sigchld_action.sa_mask, NULL)));
 
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
979
                             NULL);
1054
980
          if(ret < 0){
1055
981
            perror("sigprocmask");
1056
982
            exitstatus = EXIT_FAILURE;
1076
1002
      }
1077
1003
      
1078
1004
      /* This process has not completed.  Does it have any output? */
1079
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1080
 
                                                         warning from
1081
 
                                                         -Wconversion */
 
1005
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1082
1006
        /* This process had nothing to say at this time */
1083
1007
        proc = proc->next;
1084
1008
        continue;
1087
1011
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1088
1012
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1089
1013
                               + (size_t) BUFFER_SIZE);
1090
 
        if(proc->buffer == NULL){
 
1014
        if (proc->buffer == NULL){
1091
1015
          perror("malloc");
1092
1016
          exitstatus = EXIT_FAILURE;
1093
1017
          goto fallback;
1095
1019
        proc->buffer_size += BUFFER_SIZE;
1096
1020
      }
1097
1021
      /* Read from the process */
1098
 
      sret = TEMP_FAILURE_RETRY(read(proc->fd,
1099
 
                                     proc->buffer
1100
 
                                     + proc->buffer_length,
1101
 
                                     BUFFER_SIZE));
1102
 
      if(sret < 0){
 
1022
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
1023
                 BUFFER_SIZE);
 
1024
      if(ret < 0){
1103
1025
        /* Read error from this process; ignore the error */
1104
1026
        proc = proc->next;
1105
1027
        continue;
1106
1028
      }
1107
 
      if(sret == 0){
 
1029
      if(ret == 0){
1108
1030
        /* got EOF */
1109
1031
        proc->eof = true;
1110
1032
      } else {
1111
 
        proc->buffer_length += (size_t) sret;
 
1033
        proc->buffer_length += (size_t) ret;
1112
1034
      }
1113
1035
    }
1114
1036
  }
1115
 
  
1116
 
  
 
1037
 
 
1038
 
1117
1039
 fallback:
1118
1040
  
1119
1041
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1166
1088
  }
1167
1089
  
1168
1090
  /* Wait for any remaining child processes to terminate */
1169
 
  do {
 
1091
  do{
1170
1092
    ret = wait(NULL);
1171
1093
  } while(ret >= 0);
1172
1094
  if(errno != ECHILD){