/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
109
  plugin *new_plugin = malloc(sizeof(plugin));
114
 
  if(new_plugin == NULL){
 
110
  if (new_plugin == NULL){
115
111
    return NULL;
116
112
  }
117
113
  char *copy_name = NULL;
118
114
  if(name != NULL){
119
115
    copy_name = strdup(name);
120
116
    if(copy_name == NULL){
121
 
      free(new_plugin);
122
117
      return NULL;
123
118
    }
124
119
  }
125
120
  
126
 
  *new_plugin = (plugin){ .name = copy_name,
127
 
                          .argc = 1,
128
 
                          .disabled = false,
129
 
                          .next = plugin_list };
 
121
  *new_plugin = (plugin) { .name = copy_name,
 
122
                           .argc = 1,
 
123
                           .disabled = false,
 
124
                           .next = plugin_list };
130
125
  
131
126
  new_plugin->argv = malloc(sizeof(char *) * 2);
132
 
  if(new_plugin->argv == NULL){
 
127
  if (new_plugin->argv == NULL){
133
128
    free(copy_name);
134
129
    free(new_plugin);
135
130
    return NULL;
210
205
/*
211
206
 * Based on the example in the GNU LibC manual chapter 13.13 "File
212
207
 * Descriptor Flags".
213
 
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
 
208
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
214
209
 */
215
210
static int set_cloexec_flag(int fd){
216
211
  int ret = fcntl(fd, F_GETFD, 0);
226
221
/* Mark processes as completed when they exit, and save their exit
227
222
   status. */
228
223
static void handle_sigchld(__attribute__((unused)) int sig){
229
 
  int old_errno = errno;
230
224
  while(true){
231
225
    plugin *proc = plugin_list;
232
226
    int status;
236
230
      break;
237
231
    }
238
232
    if(pid == -1){
239
 
      if(errno == ECHILD){
240
 
        /* No child processes */
241
 
        break;
 
233
      if (errno != ECHILD){
 
234
        perror("waitpid");
242
235
      }
243
 
      perror("waitpid");
 
236
      /* No child processes */
 
237
      break;
244
238
    }
245
239
    
246
240
    /* A child exited, find it in process_list */
252
246
      continue;
253
247
    }
254
248
    proc->status = status;
255
 
    proc->completed = 1;
 
249
    proc->completed = true;
256
250
  }
257
 
  errno = old_errno;
258
251
}
259
252
 
260
253
/* Prints out a password to stdout */
282
275
  }
283
276
  free(plugin_node->environ);
284
277
  free(plugin_node->buffer);
285
 
  
 
278
 
286
279
  /* Removes the plugin from the singly-linked list */
287
280
  if(plugin_node == plugin_list){
288
281
    /* First one - simple */
316
309
  struct stat st;
317
310
  fd_set rfds_all;
318
311
  int ret, maxfd = 0;
319
 
  ssize_t sret;
320
 
  intmax_t tmpmax;
321
312
  uid_t uid = 65534;
322
313
  gid_t gid = 65534;
323
314
  bool debug = false;
380
371
    { .name = NULL }
381
372
  };
382
373
  
383
 
  error_t parse_opt(int key, char *arg, __attribute__((unused))
384
 
                    struct argp_state *state){
385
 
    char *tmp;
386
 
    switch(key){
 
374
  error_t parse_opt (int key, char *arg, __attribute__((unused))
 
375
                     struct argp_state *state) {
 
376
    switch (key) {
387
377
    case 'g':                   /* --global-options */
388
 
      if(arg != NULL){
389
 
        char *plugin_option;
390
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
391
 
          if(plugin_option[0] == '\0'){
 
378
      if (arg != NULL){
 
379
        char *p;
 
380
        while((p = strsep(&arg, ",")) != NULL){
 
381
          if(p[0] == '\0'){
392
382
            continue;
393
383
          }
394
 
          if(not add_argument(getplugin(NULL), plugin_option)){
 
384
          if(not add_argument(getplugin(NULL), p)){
395
385
            perror("add_argument");
396
386
            return ARGP_ERR_UNKNOWN;
397
387
          }
407
397
      }
408
398
      break;
409
399
    case 'o':                   /* --options-for */
410
 
      if(arg != NULL){
411
 
        char *plugin_name = strsep(&arg, ":");
412
 
        if(plugin_name[0] == '\0'){
413
 
          break;
414
 
        }
415
 
        char *plugin_option;
416
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
417
 
          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)){
418
415
            perror("add_argument");
419
416
            return ARGP_ERR_UNKNOWN;
420
417
          }
437
434
      }
438
435
      break;
439
436
    case 'd':                   /* --disable */
440
 
      if(arg != NULL){
 
437
      if (arg != NULL){
441
438
        plugin *p = getplugin(arg);
442
439
        if(p == NULL){
443
440
          return ARGP_ERR_UNKNOWN;
446
443
      }
447
444
      break;
448
445
    case 'e':                   /* --enable */
449
 
      if(arg != NULL){
 
446
      if (arg != NULL){
450
447
        plugin *p = getplugin(arg);
451
448
        if(p == NULL){
452
449
          return ARGP_ERR_UNKNOWN;
459
456
      plugindir = strdup(arg);
460
457
      if(plugindir == NULL){
461
458
        perror("strdup");
462
 
      }
 
459
      }      
463
460
      break;
464
461
    case 129:                   /* --config-file */
465
462
      /* This is already done by parse_opt_config_file() */
466
463
      break;
467
464
    case 130:                   /* --userid */
468
 
      errno = 0;
469
 
      tmpmax = strtoimax(arg, &tmp, 10);
470
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
471
 
         or tmpmax != (uid_t)tmpmax){
472
 
        fprintf(stderr, "Bad user ID number: \"%s\", using %"
473
 
                PRIdMAX "\n", arg, (intmax_t)uid);
474
 
      } else {
475
 
        uid = (uid_t)tmpmax;
476
 
      }
 
465
      uid = (uid_t)strtol(arg, NULL, 10);
477
466
      break;
478
467
    case 131:                   /* --groupid */
479
 
      errno = 0;
480
 
      tmpmax = strtoimax(arg, &tmp, 10);
481
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
482
 
         or tmpmax != (gid_t)tmpmax){
483
 
        fprintf(stderr, "Bad group ID number: \"%s\", using %"
484
 
                PRIdMAX "\n", arg, (intmax_t)gid);
485
 
      } else {
486
 
        gid = (gid_t)tmpmax;
487
 
      }
 
468
      gid = (gid_t)strtol(arg, NULL, 10);
488
469
      break;
489
470
    case 132:                   /* --debug */
490
471
      debug = true;
491
472
      break;
492
 
/*
493
 
 * When adding more options before this line, remember to also add a
494
 
 * "case" to the "parse_opt_config_file" function below.
495
 
 */
496
473
    case ARGP_KEY_ARG:
497
474
      /* Cryptsetup always passes an argument, which is an empty
498
475
         string if "none" was specified in /etc/crypttab.  So if
511
488
  
512
489
  /* This option parser is the same as parse_opt() above, except it
513
490
     ignores everything but the --config-file option. */
514
 
  error_t parse_opt_config_file(int key, char *arg,
515
 
                                __attribute__((unused))
516
 
                                struct argp_state *state){
517
 
    switch(key){
 
491
  error_t parse_opt_config_file (int key, char *arg,
 
492
                                 __attribute__((unused))
 
493
                                 struct argp_state *state) {
 
494
    switch (key) {
518
495
    case 'g':                   /* --global-options */
519
496
    case 'G':                   /* --global-env */
520
497
    case 'o':                   /* --options-for */
529
506
      if(argfile == NULL){
530
507
        perror("strdup");
531
508
      }
532
 
      break;
 
509
      break;      
533
510
    case 130:                   /* --userid */
534
511
    case 131:                   /* --groupid */
535
512
    case 132:                   /* --debug */
547
524
                       .args_doc = "",
548
525
                       .doc = "Mandos plugin runner -- Run plugins" };
549
526
  
550
 
  /* 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
551
528
     config file location, if any. */
552
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
553
 
  if(ret == ARGP_ERR_UNKNOWN){
 
529
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
530
  if (ret == ARGP_ERR_UNKNOWN){
554
531
    fprintf(stderr, "Unknown error while parsing arguments\n");
555
532
    exitstatus = EXIT_FAILURE;
556
533
    goto fallback;
560
537
  argp.parser = parse_opt;
561
538
  
562
539
  /* Open the configfile if available */
563
 
  if(argfile == NULL){
 
540
  if (argfile == NULL){
564
541
    conffp = fopen(AFILE, "r");
565
542
  } else {
566
543
    conffp = fopen(argfile, "r");
567
 
  }
 
544
  }  
568
545
  if(conffp != NULL){
569
546
    char *org_line = NULL;
570
547
    char *p, *arg, *new_arg, *line;
571
548
    size_t size = 0;
 
549
    ssize_t sret;
572
550
    const char whitespace_delims[] = " \r\t\f\v\n";
573
551
    const char comment_delim[] = "#";
574
 
    
 
552
 
575
553
    custom_argc = 1;
576
554
    custom_argv = malloc(sizeof(char*) * 2);
577
555
    if(custom_argv == NULL){
581
559
    }
582
560
    custom_argv[0] = argv[0];
583
561
    custom_argv[1] = NULL;
584
 
    
 
562
 
585
563
    /* for each line in the config file, strip whitespace and ignore
586
564
       commented text */
587
565
    while(true){
589
567
      if(sret == -1){
590
568
        break;
591
569
      }
592
 
      
 
570
 
593
571
      line = org_line;
594
572
      arg = strsep(&line, comment_delim);
595
573
      while((p = strsep(&arg, whitespace_delims)) != NULL){
614
592
          goto fallback;
615
593
        }
616
594
        custom_argv[custom_argc-1] = new_arg;
617
 
        custom_argv[custom_argc] = NULL;
 
595
        custom_argv[custom_argc] = NULL;        
618
596
      }
619
597
    }
620
 
    do{
621
 
      ret = fclose(conffp);
622
 
    }while(ret == EOF and errno == EINTR);
623
 
    if(ret == EOF){
624
 
      perror("fclose");
625
 
      exitstatus = EXIT_FAILURE;
626
 
      goto fallback;
627
 
    }
628
598
    free(org_line);
629
599
  } else {
630
600
    /* Check for harmful errors and go to fallback. Other errors might
631
601
       not affect opening plugins */
632
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
602
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
633
603
      perror("fopen");
634
604
      exitstatus = EXIT_FAILURE;
635
605
      goto fallback;
638
608
  /* If there was any arguments from configuration file,
639
609
     pass them to parser as command arguments */
640
610
  if(custom_argv != NULL){
641
 
    ret = argp_parse(&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
642
 
                     0, NULL);
643
 
    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){
644
614
      fprintf(stderr, "Unknown error while parsing arguments\n");
645
615
      exitstatus = EXIT_FAILURE;
646
616
      goto fallback;
649
619
  
650
620
  /* Parse actual command line arguments, to let them override the
651
621
     config file */
652
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
653
 
  if(ret == ARGP_ERR_UNKNOWN){
 
622
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
623
  if (ret == ARGP_ERR_UNKNOWN){
654
624
    fprintf(stderr, "Unknown error while parsing arguments\n");
655
625
    exitstatus = EXIT_FAILURE;
656
626
    goto fallback;
663
633
      for(char **a = p->argv; *a != NULL; a++){
664
634
        fprintf(stderr, "\tArg: %s\n", *a);
665
635
      }
666
 
      fprintf(stderr, "...and %d environment variables\n", p->envc);
 
636
      fprintf(stderr, "...and %u environment variables\n", p->envc);
667
637
      for(char **a = p->environ; *a != NULL; a++){
668
638
        fprintf(stderr, "\t%s\n", *a);
669
639
      }
671
641
  }
672
642
  
673
643
  /* Strip permissions down to nobody */
 
644
  ret = setuid(uid);
 
645
  if (ret == -1){
 
646
    perror("setuid");
 
647
  }  
674
648
  setgid(gid);
675
 
  if(ret == -1){
 
649
  if (ret == -1){
676
650
    perror("setgid");
677
651
  }
678
 
  ret = setuid(uid);
679
 
  if(ret == -1){
680
 
    perror("setuid");
681
 
  }
682
652
  
683
 
  if(plugindir == NULL){
 
653
  if (plugindir == NULL){
684
654
    dir = opendir(PDIR);
685
655
  } else {
686
656
    dir = opendir(plugindir);
713
683
    
714
684
    /* All directory entries have been processed */
715
685
    if(dirst == NULL){
716
 
      if(errno == EBADF){
 
686
      if (errno == EBADF){
717
687
        perror("readdir");
718
688
        exitstatus = EXIT_FAILURE;
719
689
        goto fallback;
766
736
        continue;
767
737
      }
768
738
    }
769
 
    
 
739
 
770
740
    char *filename;
771
741
    if(plugindir == NULL){
772
742
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
779
749
    }
780
750
    
781
751
    ret = stat(filename, &st);
782
 
    if(ret == -1){
 
752
    if (ret == -1){
783
753
      perror("stat");
784
754
      free(filename);
785
755
      continue;
786
756
    }
787
 
    
 
757
 
788
758
    /* Ignore non-executable files */
789
 
    if(not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
 
759
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
790
760
      if(debug){
791
761
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
792
762
                " with bad type or mode\n", filename);
839
809
    
840
810
    int pipefd[2];
841
811
    ret = pipe(pipefd);
842
 
    if(ret == -1){
 
812
    if (ret == -1){
843
813
      perror("pipe");
844
814
      exitstatus = EXIT_FAILURE;
845
815
      goto fallback;
858
828
      goto fallback;
859
829
    }
860
830
    /* Block SIGCHLD until process is safely in process list */
861
 
    ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
831
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
862
832
    if(ret < 0){
863
833
      perror("sigprocmask");
864
834
      exitstatus = EXIT_FAILURE;
912
882
    close(pipefd[1]);           /* Close unused write end of pipe */
913
883
    free(filename);
914
884
    plugin *new_plugin = getplugin(dirst->d_name);
915
 
    if(new_plugin == NULL){
 
885
    if (new_plugin == NULL){
916
886
      perror("getplugin");
917
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
887
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
918
888
      if(ret < 0){
919
889
        perror("sigprocmask");
920
890
      }
927
897
    
928
898
    /* Unblock SIGCHLD so signal handler can be run if this process
929
899
       has already completed */
930
 
    ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
900
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
931
901
    if(ret < 0){
932
902
      perror("sigprocmask");
933
903
      exitstatus = EXIT_FAILURE;
936
906
    
937
907
    FD_SET(new_plugin->fd, &rfds_all);
938
908
    
939
 
    if(maxfd < new_plugin->fd){
 
909
    if (maxfd < new_plugin->fd){
940
910
      maxfd = new_plugin->fd;
941
911
    }
942
912
  }
943
913
  
944
914
  closedir(dir);
945
915
  dir = NULL;
946
 
  free_plugin(getplugin(NULL));
947
916
  
948
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
949
918
    if(p->pid != 0){
960
929
  while(plugin_list){
961
930
    fd_set rfds = rfds_all;
962
931
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
963
 
    if(select_ret == -1){
 
932
    if (select_ret == -1){
964
933
      perror("select");
965
934
      exitstatus = EXIT_FAILURE;
966
935
      goto fallback;
969
938
       from one of them */
970
939
    for(plugin *proc = plugin_list; proc != NULL;){
971
940
      /* Is this process completely done? */
972
 
      if(proc->completed and proc->eof){
 
941
      if(proc->eof and proc->completed){
973
942
        /* Only accept the plugin output if it exited cleanly */
974
943
        if(not WIFEXITED(proc->status)
975
944
           or WEXITSTATUS(proc->status) != 0){
976
945
          /* Bad exit by plugin */
977
 
          
 
946
 
978
947
          if(debug){
979
948
            if(WIFEXITED(proc->status)){
980
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
981
 
                      " status %d\n", proc->name,
982
 
                      (intmax_t) (proc->pid),
 
949
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
950
                      (unsigned int) (proc->pid),
983
951
                      WEXITSTATUS(proc->status));
984
 
            } else if(WIFSIGNALED(proc->status)){
985
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
986
 
                      " signal %d: %s\n", proc->name,
987
 
                      (intmax_t) (proc->pid),
988
 
                      WTERMSIG(proc->status),
989
 
                      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));
990
956
            } else if(WCOREDUMP(proc->status)){
991
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
992
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
 
957
              fprintf(stderr, "Plugin %d dumped core\n",
 
958
                      (unsigned int) (proc->pid));
993
959
            }
994
960
          }
995
961
          
996
962
          /* Remove the plugin */
997
963
          FD_CLR(proc->fd, &rfds_all);
998
 
          
 
964
 
999
965
          /* Block signal while modifying process_list */
1000
966
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1001
967
          if(ret < 0){
1009
975
          proc = next_plugin;
1010
976
          
1011
977
          /* We are done modifying process list, so unblock signal */
1012
 
          ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1013
 
                            NULL);
 
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
979
                             NULL);
1014
980
          if(ret < 0){
1015
981
            perror("sigprocmask");
1016
982
            exitstatus = EXIT_FAILURE;
1045
1011
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1046
1012
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1047
1013
                               + (size_t) BUFFER_SIZE);
1048
 
        if(proc->buffer == NULL){
 
1014
        if (proc->buffer == NULL){
1049
1015
          perror("malloc");
1050
1016
          exitstatus = EXIT_FAILURE;
1051
1017
          goto fallback;
1053
1019
        proc->buffer_size += BUFFER_SIZE;
1054
1020
      }
1055
1021
      /* Read from the process */
1056
 
      sret = read(proc->fd, proc->buffer + proc->buffer_length,
1057
 
                  BUFFER_SIZE);
1058
 
      if(sret < 0){
 
1022
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
1023
                 BUFFER_SIZE);
 
1024
      if(ret < 0){
1059
1025
        /* Read error from this process; ignore the error */
1060
1026
        proc = proc->next;
1061
1027
        continue;
1062
1028
      }
1063
 
      if(sret == 0){
 
1029
      if(ret == 0){
1064
1030
        /* got EOF */
1065
1031
        proc->eof = true;
1066
1032
      } else {
1067
 
        proc->buffer_length += (size_t) sret;
 
1033
        proc->buffer_length += (size_t) ret;
1068
1034
      }
1069
1035
    }
1070
1036
  }
1071
 
  
1072
 
  
 
1037
 
 
1038
 
1073
1039
 fallback:
1074
1040
  
1075
1041
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){