/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() */
 
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 = 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 TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD, ret | FD_CLOEXEC));
 
217
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
240
218
}
241
219
 
242
220
 
243
221
/* Mark processes as completed when they exit, and save their exit
244
222
   status. */
245
223
static void handle_sigchld(__attribute__((unused)) int sig){
246
 
  int old_errno = errno;
247
224
  while(true){
248
225
    plugin *proc = plugin_list;
249
226
    int status;
253
230
      break;
254
231
    }
255
232
    if(pid == -1){
256
 
      if(errno == ECHILD){
257
 
        /* No child processes */
258
 
        break;
 
233
      if (errno != ECHILD){
 
234
        perror("waitpid");
259
235
      }
260
 
      perror("waitpid");
 
236
      /* No child processes */
 
237
      break;
261
238
    }
262
239
    
263
240
    /* A child exited, find it in process_list */
269
246
      continue;
270
247
    }
271
248
    proc->status = status;
272
 
    proc->completed = 1;
 
249
    proc->completed = true;
273
250
  }
274
 
  errno = old_errno;
275
251
}
276
252
 
277
253
/* Prints out a password to stdout */
299
275
  }
300
276
  free(plugin_node->environ);
301
277
  free(plugin_node->buffer);
302
 
  
 
278
 
303
279
  /* Removes the plugin from the singly-linked list */
304
280
  if(plugin_node == plugin_list){
305
281
    /* First one - simple */
333
309
  struct stat st;
334
310
  fd_set rfds_all;
335
311
  int ret, maxfd = 0;
336
 
  ssize_t sret;
337
312
  uid_t uid = 65534;
338
313
  gid_t gid = 65534;
339
314
  bool debug = false;
396
371
    { .name = NULL }
397
372
  };
398
373
  
399
 
  error_t parse_opt(int key, char *arg, __attribute__((unused))
400
 
                    struct argp_state *state){
401
 
    switch(key){
402
 
      char *tmp;
403
 
      intmax_t tmpmax;
 
374
  error_t parse_opt (int key, char *arg, __attribute__((unused))
 
375
                     struct argp_state *state) {
 
376
    switch (key) {
404
377
    case 'g':                   /* --global-options */
405
 
      if(arg != NULL){
406
 
        char *plugin_option;
407
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
408
 
          if(plugin_option[0] == '\0'){
 
378
      if (arg != NULL){
 
379
        char *p;
 
380
        while((p = strsep(&arg, ",")) != NULL){
 
381
          if(p[0] == '\0'){
409
382
            continue;
410
383
          }
411
 
          if(not add_argument(getplugin(NULL), plugin_option)){
 
384
          if(not add_argument(getplugin(NULL), p)){
412
385
            perror("add_argument");
413
386
            return ARGP_ERR_UNKNOWN;
414
387
          }
424
397
      }
425
398
      break;
426
399
    case 'o':                   /* --options-for */
427
 
      if(arg != NULL){
428
 
        char *plugin_name = strsep(&arg, ":");
429
 
        if(plugin_name[0] == '\0'){
430
 
          break;
431
 
        }
432
 
        char *plugin_option;
433
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
434
 
          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)){
435
415
            perror("add_argument");
436
416
            return ARGP_ERR_UNKNOWN;
437
417
          }
454
434
      }
455
435
      break;
456
436
    case 'd':                   /* --disable */
457
 
      if(arg != NULL){
 
437
      if (arg != NULL){
458
438
        plugin *p = getplugin(arg);
459
439
        if(p == NULL){
460
440
          return ARGP_ERR_UNKNOWN;
463
443
      }
464
444
      break;
465
445
    case 'e':                   /* --enable */
466
 
      if(arg != NULL){
 
446
      if (arg != NULL){
467
447
        plugin *p = getplugin(arg);
468
448
        if(p == NULL){
469
449
          return ARGP_ERR_UNKNOWN;
476
456
      plugindir = strdup(arg);
477
457
      if(plugindir == NULL){
478
458
        perror("strdup");
479
 
      }
 
459
      }      
480
460
      break;
481
461
    case 129:                   /* --config-file */
482
462
      /* This is already done by parse_opt_config_file() */
483
463
      break;
484
464
    case 130:                   /* --userid */
485
 
      errno = 0;
486
 
      tmpmax = strtoimax(arg, &tmp, 10);
487
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
488
 
         or tmpmax != (uid_t)tmpmax){
489
 
        fprintf(stderr, "Bad user ID number: \"%s\", using %"
490
 
                PRIdMAX "\n", arg, (intmax_t)uid);
491
 
      } else {
492
 
        uid = (uid_t)tmpmax;
493
 
      }
 
465
      uid = (uid_t)strtol(arg, NULL, 10);
494
466
      break;
495
467
    case 131:                   /* --groupid */
496
 
      errno = 0;
497
 
      tmpmax = strtoimax(arg, &tmp, 10);
498
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
499
 
         or tmpmax != (gid_t)tmpmax){
500
 
        fprintf(stderr, "Bad group ID number: \"%s\", using %"
501
 
                PRIdMAX "\n", arg, (intmax_t)gid);
502
 
      } else {
503
 
        gid = (gid_t)tmpmax;
504
 
      }
 
468
      gid = (gid_t)strtol(arg, NULL, 10);
505
469
      break;
506
470
    case 132:                   /* --debug */
507
471
      debug = true;
508
472
      break;
509
 
/*
510
 
 * When adding more options before this line, remember to also add a
511
 
 * "case" to the "parse_opt_config_file" function below.
512
 
 */
513
473
    case ARGP_KEY_ARG:
514
474
      /* Cryptsetup always passes an argument, which is an empty
515
475
         string if "none" was specified in /etc/crypttab.  So if
528
488
  
529
489
  /* This option parser is the same as parse_opt() above, except it
530
490
     ignores everything but the --config-file option. */
531
 
  error_t parse_opt_config_file(int key, char *arg,
532
 
                                __attribute__((unused))
533
 
                                struct argp_state *state){
534
 
    switch(key){
 
491
  error_t parse_opt_config_file (int key, char *arg,
 
492
                                 __attribute__((unused))
 
493
                                 struct argp_state *state) {
 
494
    switch (key) {
535
495
    case 'g':                   /* --global-options */
536
496
    case 'G':                   /* --global-env */
537
497
    case 'o':                   /* --options-for */
546
506
      if(argfile == NULL){
547
507
        perror("strdup");
548
508
      }
549
 
      break;
 
509
      break;      
550
510
    case 130:                   /* --userid */
551
511
    case 131:                   /* --groupid */
552
512
    case 132:                   /* --debug */
564
524
                       .args_doc = "",
565
525
                       .doc = "Mandos plugin runner -- Run plugins" };
566
526
  
567
 
  /* 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
568
528
     config file location, if any. */
569
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
570
 
  if(ret == ARGP_ERR_UNKNOWN){
 
529
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
530
  if (ret == ARGP_ERR_UNKNOWN){
571
531
    fprintf(stderr, "Unknown error while parsing arguments\n");
572
532
    exitstatus = EXIT_FAILURE;
573
533
    goto fallback;
577
537
  argp.parser = parse_opt;
578
538
  
579
539
  /* Open the configfile if available */
580
 
  if(argfile == NULL){
 
540
  if (argfile == NULL){
581
541
    conffp = fopen(AFILE, "r");
582
542
  } else {
583
543
    conffp = fopen(argfile, "r");
584
 
  }
 
544
  }  
585
545
  if(conffp != NULL){
586
546
    char *org_line = NULL;
587
547
    char *p, *arg, *new_arg, *line;
588
548
    size_t size = 0;
 
549
    ssize_t sret;
589
550
    const char whitespace_delims[] = " \r\t\f\v\n";
590
551
    const char comment_delim[] = "#";
591
 
    
 
552
 
592
553
    custom_argc = 1;
593
554
    custom_argv = malloc(sizeof(char*) * 2);
594
555
    if(custom_argv == NULL){
598
559
    }
599
560
    custom_argv[0] = argv[0];
600
561
    custom_argv[1] = NULL;
601
 
    
 
562
 
602
563
    /* for each line in the config file, strip whitespace and ignore
603
564
       commented text */
604
565
    while(true){
606
567
      if(sret == -1){
607
568
        break;
608
569
      }
609
 
      
 
570
 
610
571
      line = org_line;
611
572
      arg = strsep(&line, comment_delim);
612
573
      while((p = strsep(&arg, whitespace_delims)) != NULL){
631
592
          goto fallback;
632
593
        }
633
594
        custom_argv[custom_argc-1] = new_arg;
634
 
        custom_argv[custom_argc] = NULL;
 
595
        custom_argv[custom_argc] = NULL;        
635
596
      }
636
597
    }
637
 
    do {
638
 
      ret = fclose(conffp);
639
 
    } while(ret == EOF and errno == EINTR);
640
 
    if(ret == EOF){
641
 
      perror("fclose");
642
 
      exitstatus = EXIT_FAILURE;
643
 
      goto fallback;
644
 
    }
645
598
    free(org_line);
646
599
  } else {
647
600
    /* Check for harmful errors and go to fallback. Other errors might
648
601
       not affect opening plugins */
649
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
602
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
650
603
      perror("fopen");
651
604
      exitstatus = EXIT_FAILURE;
652
605
      goto fallback;
655
608
  /* If there was any arguments from configuration file,
656
609
     pass them to parser as command arguments */
657
610
  if(custom_argv != NULL){
658
 
    ret = argp_parse(&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
659
 
                     0, NULL);
660
 
    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){
661
614
      fprintf(stderr, "Unknown error while parsing arguments\n");
662
615
      exitstatus = EXIT_FAILURE;
663
616
      goto fallback;
666
619
  
667
620
  /* Parse actual command line arguments, to let them override the
668
621
     config file */
669
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
670
 
  if(ret == ARGP_ERR_UNKNOWN){
 
622
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
623
  if (ret == ARGP_ERR_UNKNOWN){
671
624
    fprintf(stderr, "Unknown error while parsing arguments\n");
672
625
    exitstatus = EXIT_FAILURE;
673
626
    goto fallback;
680
633
      for(char **a = p->argv; *a != NULL; a++){
681
634
        fprintf(stderr, "\tArg: %s\n", *a);
682
635
      }
683
 
      fprintf(stderr, "...and %d environment variables\n", p->envc);
 
636
      fprintf(stderr, "...and %u environment variables\n", p->envc);
684
637
      for(char **a = p->environ; *a != NULL; a++){
685
638
        fprintf(stderr, "\t%s\n", *a);
686
639
      }
688
641
  }
689
642
  
690
643
  /* Strip permissions down to nobody */
 
644
  ret = setuid(uid);
 
645
  if (ret == -1){
 
646
    perror("setuid");
 
647
  }  
691
648
  setgid(gid);
692
 
  if(ret == -1){
 
649
  if (ret == -1){
693
650
    perror("setgid");
694
651
  }
695
 
  ret = setuid(uid);
696
 
  if(ret == -1){
697
 
    perror("setuid");
698
 
  }
699
652
  
700
 
  if(plugindir == NULL){
 
653
  if (plugindir == NULL){
701
654
    dir = opendir(PDIR);
702
655
  } else {
703
656
    dir = opendir(plugindir);
726
679
  
727
680
  /* Read and execute any executable in the plugin directory*/
728
681
  while(true){
729
 
    do {
730
 
      dirst = readdir(dir);
731
 
    } while(dirst == NULL and errno == EINTR);
 
682
    dirst = readdir(dir);
732
683
    
733
684
    /* All directory entries have been processed */
734
685
    if(dirst == NULL){
735
 
      if(errno == EBADF){
 
686
      if (errno == EBADF){
736
687
        perror("readdir");
737
688
        exitstatus = EXIT_FAILURE;
738
689
        goto fallback;
785
736
        continue;
786
737
      }
787
738
    }
788
 
    
 
739
 
789
740
    char *filename;
790
741
    if(plugindir == NULL){
791
 
      ret = TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
792
 
                                        dirst->d_name));
 
742
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
793
743
    } else {
794
 
      ret = TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s", plugindir,
795
 
                                        dirst->d_name));
 
744
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
796
745
    }
797
746
    if(ret < 0){
798
747
      perror("asprintf");
799
748
      continue;
800
749
    }
801
750
    
802
 
    ret = TEMP_FAILURE_RETRY(stat(filename, &st));
803
 
    if(ret == -1){
 
751
    ret = stat(filename, &st);
 
752
    if (ret == -1){
804
753
      perror("stat");
805
754
      free(filename);
806
755
      continue;
807
756
    }
808
 
    
 
757
 
809
758
    /* Ignore non-executable files */
810
 
    if(not S_ISREG(st.st_mode)
811
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
759
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
812
760
      if(debug){
813
761
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
814
762
                " with bad type or mode\n", filename);
860
808
    }
861
809
    
862
810
    int pipefd[2];
863
 
    ret = TEMP_FAILURE_RETRY(pipe(pipefd));
864
 
    if(ret == -1){
 
811
    ret = pipe(pipefd);
 
812
    if (ret == -1){
865
813
      perror("pipe");
866
814
      exitstatus = EXIT_FAILURE;
867
815
      goto fallback;
880
828
      goto fallback;
881
829
    }
882
830
    /* Block SIGCHLD until process is safely in process list */
883
 
    ret = TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
884
 
                                         &sigchld_action.sa_mask,
885
 
                                         NULL));
 
831
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
886
832
    if(ret < 0){
887
833
      perror("sigprocmask");
888
834
      exitstatus = EXIT_FAILURE;
889
835
      goto fallback;
890
836
    }
891
837
    /* Starting a new process to be watched */
892
 
    pid_t pid;
893
 
    do {
894
 
      pid = fork();
895
 
    } while(pid == -1 and errno == EINTR);
 
838
    pid_t pid = fork();
896
839
    if(pid == -1){
897
840
      perror("fork");
898
841
      exitstatus = EXIT_FAILURE;
936
879
      /* no return */
937
880
    }
938
881
    /* Parent process */
939
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
940
 
                                             pipe */
 
882
    close(pipefd[1]);           /* Close unused write end of pipe */
941
883
    free(filename);
942
884
    plugin *new_plugin = getplugin(dirst->d_name);
943
 
    if(new_plugin == NULL){
 
885
    if (new_plugin == NULL){
944
886
      perror("getplugin");
945
 
      ret = TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
946
 
                                           &sigchld_action.sa_mask,
947
 
                                           NULL));
 
887
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
948
888
      if(ret < 0){
949
889
        perror("sigprocmask");
950
890
      }
957
897
    
958
898
    /* Unblock SIGCHLD so signal handler can be run if this process
959
899
       has already completed */
960
 
    ret = TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
961
 
                                         &sigchld_action.sa_mask,
962
 
                                         NULL));
 
900
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
963
901
    if(ret < 0){
964
902
      perror("sigprocmask");
965
903
      exitstatus = EXIT_FAILURE;
968
906
    
969
907
    FD_SET(new_plugin->fd, &rfds_all);
970
908
    
971
 
    if(maxfd < new_plugin->fd){
 
909
    if (maxfd < new_plugin->fd){
972
910
      maxfd = new_plugin->fd;
973
911
    }
974
912
  }
975
913
  
976
 
  TEMP_FAILURE_RETRY(closedir(dir));
 
914
  closedir(dir);
977
915
  dir = NULL;
978
 
  free_plugin(getplugin(NULL));
979
916
  
980
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
981
918
    if(p->pid != 0){
992
929
  while(plugin_list){
993
930
    fd_set rfds = rfds_all;
994
931
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
995
 
    if(select_ret == -1 and errno != EINTR){
 
932
    if (select_ret == -1){
996
933
      perror("select");
997
934
      exitstatus = EXIT_FAILURE;
998
935
      goto fallback;
1001
938
       from one of them */
1002
939
    for(plugin *proc = plugin_list; proc != NULL;){
1003
940
      /* Is this process completely done? */
1004
 
      if(proc->completed and proc->eof){
 
941
      if(proc->eof and proc->completed){
1005
942
        /* Only accept the plugin output if it exited cleanly */
1006
943
        if(not WIFEXITED(proc->status)
1007
944
           or WEXITSTATUS(proc->status) != 0){
1008
945
          /* Bad exit by plugin */
1009
 
          
 
946
 
1010
947
          if(debug){
1011
948
            if(WIFEXITED(proc->status)){
1012
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
1013
 
                      " status %d\n", proc->name,
1014
 
                      (intmax_t) (proc->pid),
 
949
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
950
                      (unsigned int) (proc->pid),
1015
951
                      WEXITSTATUS(proc->status));
1016
 
            } else if(WIFSIGNALED(proc->status)){
1017
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
1018
 
                      " signal %d: %s\n", proc->name,
1019
 
                      (intmax_t) (proc->pid),
1020
 
                      WTERMSIG(proc->status),
1021
 
                      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));
1022
956
            } else if(WCOREDUMP(proc->status)){
1023
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1024
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
 
957
              fprintf(stderr, "Plugin %d dumped core\n",
 
958
                      (unsigned int) (proc->pid));
1025
959
            }
1026
960
          }
1027
961
          
1028
962
          /* Remove the plugin */
1029
963
          FD_CLR(proc->fd, &rfds_all);
1030
 
          
 
964
 
1031
965
          /* Block signal while modifying process_list */
1032
 
          ret = TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1033
 
                                               &sigchld_action.sa_mask,
1034
 
                                               NULL));
 
966
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1035
967
          if(ret < 0){
1036
968
            perror("sigprocmask");
1037
969
            exitstatus = EXIT_FAILURE;
1043
975
          proc = next_plugin;
1044
976
          
1045
977
          /* We are done modifying process list, so unblock signal */
1046
 
          ret = TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1047
 
                                               &sigchld_action.sa_mask,
1048
 
                                               NULL));
 
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
979
                             NULL);
1049
980
          if(ret < 0){
1050
981
            perror("sigprocmask");
1051
982
            exitstatus = EXIT_FAILURE;
1080
1011
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1081
1012
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1082
1013
                               + (size_t) BUFFER_SIZE);
1083
 
        if(proc->buffer == NULL){
 
1014
        if (proc->buffer == NULL){
1084
1015
          perror("malloc");
1085
1016
          exitstatus = EXIT_FAILURE;
1086
1017
          goto fallback;
1088
1019
        proc->buffer_size += BUFFER_SIZE;
1089
1020
      }
1090
1021
      /* Read from the process */
1091
 
      sret = TEMP_FAILURE_RETRY(read(proc->fd,
1092
 
                                     proc->buffer
1093
 
                                     + proc->buffer_length,
1094
 
                                     BUFFER_SIZE));
1095
 
      if(sret < 0){
 
1022
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
1023
                 BUFFER_SIZE);
 
1024
      if(ret < 0){
1096
1025
        /* Read error from this process; ignore the error */
1097
1026
        proc = proc->next;
1098
1027
        continue;
1099
1028
      }
1100
 
      if(sret == 0){
 
1029
      if(ret == 0){
1101
1030
        /* got EOF */
1102
1031
        proc->eof = true;
1103
1032
      } else {
1104
 
        proc->buffer_length += (size_t) sret;
 
1033
        proc->buffer_length += (size_t) ret;
1105
1034
      }
1106
1035
    }
1107
1036
  }
1108
 
  
1109
 
  
 
1037
 
 
1038
 
1110
1039
 fallback:
1111
1040
  
1112
1041
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1159
1088
  }
1160
1089
  
1161
1090
  /* Wait for any remaining child processes to terminate */
1162
 
  do {
 
1091
  do{
1163
1092
    ret = wait(NULL);
1164
1093
  } while(ret >= 0);
1165
1094
  if(errno != ECHILD){