/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 plugin-runner.c

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 = (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;
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;
970
906
    
971
907
    FD_SET(new_plugin->fd, &rfds_all);
972
908
    
973
 
    if(maxfd < new_plugin->fd){
 
909
    if (maxfd < new_plugin->fd){
974
910
      maxfd = new_plugin->fd;
975
911
    }
976
912
  }
977
913
  
978
 
  TEMP_FAILURE_RETRY(closedir(dir));
 
914
  closedir(dir);
979
915
  dir = NULL;
980
 
  free_plugin(getplugin(NULL));
981
916
  
982
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
983
918
    if(p->pid != 0){
994
929
  while(plugin_list){
995
930
    fd_set rfds = rfds_all;
996
931
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
997
 
    if(select_ret == -1 and errno != EINTR){
 
932
    if (select_ret == -1){
998
933
      perror("select");
999
934
      exitstatus = EXIT_FAILURE;
1000
935
      goto fallback;
1003
938
       from one of them */
1004
939
    for(plugin *proc = plugin_list; proc != NULL;){
1005
940
      /* Is this process completely done? */
1006
 
      if(proc->completed and proc->eof){
 
941
      if(proc->eof and proc->completed){
1007
942
        /* Only accept the plugin output if it exited cleanly */
1008
943
        if(not WIFEXITED(proc->status)
1009
944
           or WEXITSTATUS(proc->status) != 0){
1010
945
          /* Bad exit by plugin */
1011
 
          
 
946
 
1012
947
          if(debug){
1013
948
            if(WIFEXITED(proc->status)){
1014
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
1015
 
                      " status %d\n", proc->name,
1016
 
                      (intmax_t) (proc->pid),
 
949
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
950
                      (unsigned int) (proc->pid),
1017
951
                      WEXITSTATUS(proc->status));
1018
 
            } else if(WIFSIGNALED(proc->status)){
1019
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
1020
 
                      " signal %d: %s\n", proc->name,
1021
 
                      (intmax_t) (proc->pid),
1022
 
                      WTERMSIG(proc->status),
1023
 
                      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));
1024
956
            } else if(WCOREDUMP(proc->status)){
1025
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1026
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
 
957
              fprintf(stderr, "Plugin %d dumped core\n",
 
958
                      (unsigned int) (proc->pid));
1027
959
            }
1028
960
          }
1029
961
          
1030
962
          /* Remove the plugin */
1031
963
          FD_CLR(proc->fd, &rfds_all);
1032
 
          
 
964
 
1033
965
          /* Block signal while modifying process_list */
1034
 
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1035
 
                                        (SIG_BLOCK,
1036
 
                                         &sigchld_action.sa_mask,
1037
 
                                         NULL));
 
966
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1038
967
          if(ret < 0){
1039
968
            perror("sigprocmask");
1040
969
            exitstatus = EXIT_FAILURE;
1046
975
          proc = next_plugin;
1047
976
          
1048
977
          /* We are done modifying process list, so unblock signal */
1049
 
          ret = (int)(TEMP_FAILURE_RETRY
1050
 
                      (sigprocmask(SIG_UNBLOCK,
1051
 
                                   &sigchld_action.sa_mask, NULL)));
 
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
979
                             NULL);
1052
980
          if(ret < 0){
1053
981
            perror("sigprocmask");
1054
982
            exitstatus = EXIT_FAILURE;
1083
1011
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1084
1012
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1085
1013
                               + (size_t) BUFFER_SIZE);
1086
 
        if(proc->buffer == NULL){
 
1014
        if (proc->buffer == NULL){
1087
1015
          perror("malloc");
1088
1016
          exitstatus = EXIT_FAILURE;
1089
1017
          goto fallback;
1091
1019
        proc->buffer_size += BUFFER_SIZE;
1092
1020
      }
1093
1021
      /* Read from the process */
1094
 
      sret = TEMP_FAILURE_RETRY(read(proc->fd,
1095
 
                                     proc->buffer
1096
 
                                     + proc->buffer_length,
1097
 
                                     BUFFER_SIZE));
1098
 
      if(sret < 0){
 
1022
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
1023
                 BUFFER_SIZE);
 
1024
      if(ret < 0){
1099
1025
        /* Read error from this process; ignore the error */
1100
1026
        proc = proc->next;
1101
1027
        continue;
1102
1028
      }
1103
 
      if(sret == 0){
 
1029
      if(ret == 0){
1104
1030
        /* got EOF */
1105
1031
        proc->eof = true;
1106
1032
      } else {
1107
 
        proc->buffer_length += (size_t) sret;
 
1033
        proc->buffer_length += (size_t) ret;
1108
1034
      }
1109
1035
    }
1110
1036
  }
1111
 
  
1112
 
  
 
1037
 
 
1038
 
1113
1039
 fallback:
1114
1040
  
1115
1041
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1162
1088
  }
1163
1089
  
1164
1090
  /* Wait for any remaining child processes to terminate */
1165
 
  do {
 
1091
  do{
1166
1092
    ret = wait(NULL);
1167
1093
  } while(ret >= 0);
1168
1094
  if(errno != ECHILD){