/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:
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
62
62
#include <signal.h>             /* struct sigaction, sigemptyset(),
63
63
                                   sigaddset(), sigaction(),
64
64
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
65
 
                                   SIG_UNBLOCK, kill(), sig_atomic_t
66
 
                                */
 
65
                                   SIG_UNBLOCK, kill() */
67
66
#include <errno.h>              /* errno, EBADF */
68
 
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
69
67
 
70
68
#define BUFFER_SIZE 256
71
69
 
82
80
  char **environ;
83
81
  int envc;
84
82
  bool disabled;
85
 
  
 
83
 
86
84
  /* Variables used for running processes*/
87
85
  pid_t pid;
88
86
  int fd;
90
88
  size_t buffer_size;
91
89
  size_t buffer_length;
92
90
  bool eof;
93
 
  volatile sig_atomic_t completed;
94
 
  int status;
 
91
  volatile bool completed;
 
92
  volatile int status;
95
93
  struct plugin *next;
96
94
} plugin;
97
95
 
101
99
   or if none is found, creates a new one */
102
100
static plugin *getplugin(char *name){
103
101
  /* Check for exiting plugin with that name */
104
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
105
 
    if((p->name == name)
106
 
       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))){
107
105
      return p;
108
106
    }
109
107
  }
110
108
  /* Create a new plugin */
111
109
  plugin *new_plugin = malloc(sizeof(plugin));
112
 
  if(new_plugin == NULL){
 
110
  if (new_plugin == NULL){
113
111
    return NULL;
114
112
  }
115
113
  char *copy_name = NULL;
116
114
  if(name != NULL){
117
115
    copy_name = strdup(name);
118
116
    if(copy_name == NULL){
119
 
      free(new_plugin);
120
117
      return NULL;
121
118
    }
122
119
  }
123
120
  
124
 
  *new_plugin = (plugin){ .name = copy_name,
125
 
                          .argc = 1,
126
 
                          .disabled = false,
127
 
                          .next = plugin_list };
 
121
  *new_plugin = (plugin) { .name = copy_name,
 
122
                           .argc = 1,
 
123
                           .disabled = false,
 
124
                           .next = plugin_list };
128
125
  
129
126
  new_plugin->argv = malloc(sizeof(char *) * 2);
130
 
  if(new_plugin->argv == NULL){
 
127
  if (new_plugin->argv == NULL){
131
128
    free(copy_name);
132
129
    free(new_plugin);
133
130
    return NULL;
224
221
/* Mark processes as completed when they exit, and save their exit
225
222
   status. */
226
223
static void handle_sigchld(__attribute__((unused)) int sig){
227
 
  int old_errno = errno;
228
224
  while(true){
229
225
    plugin *proc = plugin_list;
230
226
    int status;
234
230
      break;
235
231
    }
236
232
    if(pid == -1){
237
 
      if(errno == ECHILD){
238
 
        /* No child processes */
239
 
        break;
 
233
      if (errno != ECHILD){
 
234
        perror("waitpid");
240
235
      }
241
 
      perror("waitpid");
 
236
      /* No child processes */
 
237
      break;
242
238
    }
243
239
    
244
240
    /* A child exited, find it in process_list */
250
246
      continue;
251
247
    }
252
248
    proc->status = status;
253
 
    proc->completed = 1;
 
249
    proc->completed = true;
254
250
  }
255
 
  errno = old_errno;
256
251
}
257
252
 
258
253
/* Prints out a password to stdout */
280
275
  }
281
276
  free(plugin_node->environ);
282
277
  free(plugin_node->buffer);
283
 
  
 
278
 
284
279
  /* Removes the plugin from the singly-linked list */
285
280
  if(plugin_node == plugin_list){
286
281
    /* First one - simple */
314
309
  struct stat st;
315
310
  fd_set rfds_all;
316
311
  int ret, maxfd = 0;
317
 
  ssize_t sret;
318
 
  intmax_t tmpmax;
319
312
  uid_t uid = 65534;
320
313
  gid_t gid = 65534;
321
314
  bool debug = false;
378
371
    { .name = NULL }
379
372
  };
380
373
  
381
 
  error_t parse_opt(int key, char *arg, __attribute__((unused))
382
 
                    struct argp_state *state){
383
 
    char *tmp;
384
 
    switch(key){
 
374
  error_t parse_opt (int key, char *arg, __attribute__((unused))
 
375
                     struct argp_state *state) {
 
376
    switch (key) {
385
377
    case 'g':                   /* --global-options */
386
 
      if(arg != NULL){
387
 
        char *plugin_option;
388
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
389
 
          if(plugin_option[0] == '\0'){
 
378
      if (arg != NULL){
 
379
        char *p;
 
380
        while((p = strsep(&arg, ",")) != NULL){
 
381
          if(p[0] == '\0'){
390
382
            continue;
391
383
          }
392
 
          if(not add_argument(getplugin(NULL), plugin_option)){
 
384
          if(not add_argument(getplugin(NULL), p)){
393
385
            perror("add_argument");
394
386
            return ARGP_ERR_UNKNOWN;
395
387
          }
405
397
      }
406
398
      break;
407
399
    case 'o':                   /* --options-for */
408
 
      if(arg != NULL){
409
 
        char *plugin_name = strsep(&arg, ":");
410
 
        if(plugin_name[0] == '\0'){
411
 
          break;
412
 
        }
413
 
        char *plugin_option;
414
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
415
 
          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)){
416
415
            perror("add_argument");
417
416
            return ARGP_ERR_UNKNOWN;
418
417
          }
435
434
      }
436
435
      break;
437
436
    case 'd':                   /* --disable */
438
 
      if(arg != NULL){
 
437
      if (arg != NULL){
439
438
        plugin *p = getplugin(arg);
440
439
        if(p == NULL){
441
440
          return ARGP_ERR_UNKNOWN;
444
443
      }
445
444
      break;
446
445
    case 'e':                   /* --enable */
447
 
      if(arg != NULL){
 
446
      if (arg != NULL){
448
447
        plugin *p = getplugin(arg);
449
448
        if(p == NULL){
450
449
          return ARGP_ERR_UNKNOWN;
463
462
      /* This is already done by parse_opt_config_file() */
464
463
      break;
465
464
    case 130:                   /* --userid */
466
 
      errno = 0;
467
 
      tmpmax = strtoimax(arg, &tmp, 10);
468
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
469
 
         or tmpmax != (uid_t)tmpmax){
470
 
        fprintf(stderr, "Bad user ID number: \"%s\", using %"
471
 
                PRIdMAX "\n", arg, (intmax_t)uid);
472
 
      } else {
473
 
        uid = (uid_t)tmpmax;
474
 
      }
 
465
      uid = (uid_t)strtol(arg, NULL, 10);
475
466
      break;
476
467
    case 131:                   /* --groupid */
477
 
      errno = 0;
478
 
      tmpmax = strtoimax(arg, &tmp, 10);
479
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
480
 
         or tmpmax != (gid_t)tmpmax){
481
 
        fprintf(stderr, "Bad group ID number: \"%s\", using %"
482
 
                PRIdMAX "\n", arg, (intmax_t)gid);
483
 
      } else {
484
 
        gid = (gid_t)tmpmax;
485
 
      }
 
468
      gid = (gid_t)strtol(arg, NULL, 10);
486
469
      break;
487
470
    case 132:                   /* --debug */
488
471
      debug = true;
489
472
      break;
490
 
/*
491
 
 * When adding more options before this line, remember to also add a
492
 
 * "case" to the "parse_opt_config_file" function below.
493
 
 */
494
473
    case ARGP_KEY_ARG:
495
474
      /* Cryptsetup always passes an argument, which is an empty
496
475
         string if "none" was specified in /etc/crypttab.  So if
509
488
  
510
489
  /* This option parser is the same as parse_opt() above, except it
511
490
     ignores everything but the --config-file option. */
512
 
  error_t parse_opt_config_file(int key, char *arg,
513
 
                                __attribute__((unused))
514
 
                                struct argp_state *state){
515
 
    switch(key){
 
491
  error_t parse_opt_config_file (int key, char *arg,
 
492
                                 __attribute__((unused))
 
493
                                 struct argp_state *state) {
 
494
    switch (key) {
516
495
    case 'g':                   /* --global-options */
517
496
    case 'G':                   /* --global-env */
518
497
    case 'o':                   /* --options-for */
545
524
                       .args_doc = "",
546
525
                       .doc = "Mandos plugin runner -- Run plugins" };
547
526
  
548
 
  /* 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
549
528
     config file location, if any. */
550
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
551
 
  if(ret == ARGP_ERR_UNKNOWN){
 
529
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
530
  if (ret == ARGP_ERR_UNKNOWN){
552
531
    fprintf(stderr, "Unknown error while parsing arguments\n");
553
532
    exitstatus = EXIT_FAILURE;
554
533
    goto fallback;
558
537
  argp.parser = parse_opt;
559
538
  
560
539
  /* Open the configfile if available */
561
 
  if(argfile == NULL){
 
540
  if (argfile == NULL){
562
541
    conffp = fopen(AFILE, "r");
563
542
  } else {
564
543
    conffp = fopen(argfile, "r");
567
546
    char *org_line = NULL;
568
547
    char *p, *arg, *new_arg, *line;
569
548
    size_t size = 0;
 
549
    ssize_t sret;
570
550
    const char whitespace_delims[] = " \r\t\f\v\n";
571
551
    const char comment_delim[] = "#";
572
 
    
 
552
 
573
553
    custom_argc = 1;
574
554
    custom_argv = malloc(sizeof(char*) * 2);
575
555
    if(custom_argv == NULL){
579
559
    }
580
560
    custom_argv[0] = argv[0];
581
561
    custom_argv[1] = NULL;
582
 
    
 
562
 
583
563
    /* for each line in the config file, strip whitespace and ignore
584
564
       commented text */
585
565
    while(true){
587
567
      if(sret == -1){
588
568
        break;
589
569
      }
590
 
      
 
570
 
591
571
      line = org_line;
592
572
      arg = strsep(&line, comment_delim);
593
573
      while((p = strsep(&arg, whitespace_delims)) != NULL){
619
599
  } else {
620
600
    /* Check for harmful errors and go to fallback. Other errors might
621
601
       not affect opening plugins */
622
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
602
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
623
603
      perror("fopen");
624
604
      exitstatus = EXIT_FAILURE;
625
605
      goto fallback;
628
608
  /* If there was any arguments from configuration file,
629
609
     pass them to parser as command arguments */
630
610
  if(custom_argv != NULL){
631
 
    ret = argp_parse(&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
632
 
                     0, NULL);
633
 
    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){
634
614
      fprintf(stderr, "Unknown error while parsing arguments\n");
635
615
      exitstatus = EXIT_FAILURE;
636
616
      goto fallback;
639
619
  
640
620
  /* Parse actual command line arguments, to let them override the
641
621
     config file */
642
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
643
 
  if(ret == ARGP_ERR_UNKNOWN){
 
622
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
623
  if (ret == ARGP_ERR_UNKNOWN){
644
624
    fprintf(stderr, "Unknown error while parsing arguments\n");
645
625
    exitstatus = EXIT_FAILURE;
646
626
    goto fallback;
653
633
      for(char **a = p->argv; *a != NULL; a++){
654
634
        fprintf(stderr, "\tArg: %s\n", *a);
655
635
      }
656
 
      fprintf(stderr, "...and %d environment variables\n", p->envc);
 
636
      fprintf(stderr, "...and %u environment variables\n", p->envc);
657
637
      for(char **a = p->environ; *a != NULL; a++){
658
638
        fprintf(stderr, "\t%s\n", *a);
659
639
      }
661
641
  }
662
642
  
663
643
  /* Strip permissions down to nobody */
 
644
  ret = setuid(uid);
 
645
  if (ret == -1){
 
646
    perror("setuid");
 
647
  }  
664
648
  setgid(gid);
665
 
  if(ret == -1){
 
649
  if (ret == -1){
666
650
    perror("setgid");
667
651
  }
668
 
  ret = setuid(uid);
669
 
  if(ret == -1){
670
 
    perror("setuid");
671
 
  }
672
652
  
673
 
  if(plugindir == NULL){
 
653
  if (plugindir == NULL){
674
654
    dir = opendir(PDIR);
675
655
  } else {
676
656
    dir = opendir(plugindir);
703
683
    
704
684
    /* All directory entries have been processed */
705
685
    if(dirst == NULL){
706
 
      if(errno == EBADF){
 
686
      if (errno == EBADF){
707
687
        perror("readdir");
708
688
        exitstatus = EXIT_FAILURE;
709
689
        goto fallback;
756
736
        continue;
757
737
      }
758
738
    }
759
 
    
 
739
 
760
740
    char *filename;
761
741
    if(plugindir == NULL){
762
742
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
769
749
    }
770
750
    
771
751
    ret = stat(filename, &st);
772
 
    if(ret == -1){
 
752
    if (ret == -1){
773
753
      perror("stat");
774
754
      free(filename);
775
755
      continue;
776
756
    }
777
 
    
 
757
 
778
758
    /* Ignore non-executable files */
779
 
    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)){
780
760
      if(debug){
781
761
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
782
762
                " with bad type or mode\n", filename);
829
809
    
830
810
    int pipefd[2];
831
811
    ret = pipe(pipefd);
832
 
    if(ret == -1){
 
812
    if (ret == -1){
833
813
      perror("pipe");
834
814
      exitstatus = EXIT_FAILURE;
835
815
      goto fallback;
848
828
      goto fallback;
849
829
    }
850
830
    /* Block SIGCHLD until process is safely in process list */
851
 
    ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
831
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
852
832
    if(ret < 0){
853
833
      perror("sigprocmask");
854
834
      exitstatus = EXIT_FAILURE;
902
882
    close(pipefd[1]);           /* Close unused write end of pipe */
903
883
    free(filename);
904
884
    plugin *new_plugin = getplugin(dirst->d_name);
905
 
    if(new_plugin == NULL){
 
885
    if (new_plugin == NULL){
906
886
      perror("getplugin");
907
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
887
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
908
888
      if(ret < 0){
909
889
        perror("sigprocmask");
910
890
      }
917
897
    
918
898
    /* Unblock SIGCHLD so signal handler can be run if this process
919
899
       has already completed */
920
 
    ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
900
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
921
901
    if(ret < 0){
922
902
      perror("sigprocmask");
923
903
      exitstatus = EXIT_FAILURE;
926
906
    
927
907
    FD_SET(new_plugin->fd, &rfds_all);
928
908
    
929
 
    if(maxfd < new_plugin->fd){
 
909
    if (maxfd < new_plugin->fd){
930
910
      maxfd = new_plugin->fd;
931
911
    }
932
912
  }
933
913
  
934
914
  closedir(dir);
935
915
  dir = NULL;
936
 
  free_plugin(getplugin(NULL));
937
916
  
938
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
939
918
    if(p->pid != 0){
950
929
  while(plugin_list){
951
930
    fd_set rfds = rfds_all;
952
931
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
953
 
    if(select_ret == -1){
 
932
    if (select_ret == -1){
954
933
      perror("select");
955
934
      exitstatus = EXIT_FAILURE;
956
935
      goto fallback;
959
938
       from one of them */
960
939
    for(plugin *proc = plugin_list; proc != NULL;){
961
940
      /* Is this process completely done? */
962
 
      if(proc->completed and proc->eof){
 
941
      if(proc->eof and proc->completed){
963
942
        /* Only accept the plugin output if it exited cleanly */
964
943
        if(not WIFEXITED(proc->status)
965
944
           or WEXITSTATUS(proc->status) != 0){
966
945
          /* Bad exit by plugin */
967
 
          
 
946
 
968
947
          if(debug){
969
948
            if(WIFEXITED(proc->status)){
970
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
971
 
                      " status %d\n", proc->name,
972
 
                      (intmax_t) (proc->pid),
 
949
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
950
                      (unsigned int) (proc->pid),
973
951
                      WEXITSTATUS(proc->status));
974
 
            } else if(WIFSIGNALED(proc->status)){
975
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
976
 
                      " signal %d\n", proc->name,
977
 
                      (intmax_t) (proc->pid),
 
952
            } else if(WIFSIGNALED(proc->status)) {
 
953
              fprintf(stderr, "Plugin %u killed by signal %d\n",
 
954
                      (unsigned int) (proc->pid),
978
955
                      WTERMSIG(proc->status));
979
956
            } else if(WCOREDUMP(proc->status)){
980
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
981
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
 
957
              fprintf(stderr, "Plugin %d dumped core\n",
 
958
                      (unsigned int) (proc->pid));
982
959
            }
983
960
          }
984
961
          
985
962
          /* Remove the plugin */
986
963
          FD_CLR(proc->fd, &rfds_all);
987
 
          
 
964
 
988
965
          /* Block signal while modifying process_list */
989
966
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
990
967
          if(ret < 0){
998
975
          proc = next_plugin;
999
976
          
1000
977
          /* We are done modifying process list, so unblock signal */
1001
 
          ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1002
 
                            NULL);
 
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
979
                             NULL);
1003
980
          if(ret < 0){
1004
981
            perror("sigprocmask");
1005
982
            exitstatus = EXIT_FAILURE;
1034
1011
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1035
1012
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1036
1013
                               + (size_t) BUFFER_SIZE);
1037
 
        if(proc->buffer == NULL){
 
1014
        if (proc->buffer == NULL){
1038
1015
          perror("malloc");
1039
1016
          exitstatus = EXIT_FAILURE;
1040
1017
          goto fallback;
1042
1019
        proc->buffer_size += BUFFER_SIZE;
1043
1020
      }
1044
1021
      /* Read from the process */
1045
 
      sret = read(proc->fd, proc->buffer + proc->buffer_length,
1046
 
                  BUFFER_SIZE);
1047
 
      if(sret < 0){
 
1022
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
1023
                 BUFFER_SIZE);
 
1024
      if(ret < 0){
1048
1025
        /* Read error from this process; ignore the error */
1049
1026
        proc = proc->next;
1050
1027
        continue;
1051
1028
      }
1052
 
      if(sret == 0){
 
1029
      if(ret == 0){
1053
1030
        /* got EOF */
1054
1031
        proc->eof = true;
1055
1032
      } else {
1056
 
        proc->buffer_length += (size_t) sret;
 
1033
        proc->buffer_length += (size_t) ret;
1057
1034
      }
1058
1035
    }
1059
1036
  }
1060
 
  
1061
 
  
 
1037
 
 
1038
 
1062
1039
 fallback:
1063
1040
  
1064
1041
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){