/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: 2016-06-03 17:27:03 UTC
  • Revision ID: teddy@recompile.se-20160603172703-mc6tjor6rhq4xy74
mandos: Bug fix: Do multiprocessing cleanup correctly on exit

* mandos (main): Save module "multiprocessing" and open file "wnull"
                 as scope variables accessible by function cleanup(),
                 since the module and global variable may not be
                 accessible when the cleanup() function is run as
                 scheduled by atexit().

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-2014 Teddy Hogeborn
6
 
 * Copyright © 2008-2014 Björn Påhlsson
 
5
 * Copyright © 2008-2016 Teddy Hogeborn
 
6
 * Copyright © 2008-2016 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
23
23
 */
24
24
 
25
25
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
 
                                   O_CLOEXEC */
 
26
                                   O_CLOEXEC, pipe2() */
27
27
#include <stddef.h>             /* size_t, NULL */
28
28
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
29
                                   realloc() */
30
30
#include <stdbool.h>            /* bool, true, false */
31
31
#include <stdio.h>              /* fileno(), fprintf(),
32
32
                                   stderr, STDOUT_FILENO, fclose() */
33
 
#include <sys/types.h>          /* DIR, fdopendir(), fstat(), struct
34
 
                                   stat, waitpid(), WIFEXITED(),
35
 
                                   WEXITSTATUS(), wait(), pid_t,
36
 
                                   uid_t, gid_t, getuid(), getgid(),
37
 
                                   dirfd() */
 
33
#include <sys/types.h>          /* fstat(), struct stat, waitpid(),
 
34
                                   WIFEXITED(), WEXITSTATUS(), wait(),
 
35
                                   pid_t, uid_t, gid_t, getuid(),
 
36
                                   getgid() */
38
37
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
39
38
                                   FD_SET(), FD_ISSET(), FD_CLR */
40
39
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
41
 
                                   WEXITSTATUS(), WTERMSIG(),
42
 
                                   WCOREDUMP() */
 
40
                                   WEXITSTATUS(), WTERMSIG() */
43
41
#include <sys/stat.h>           /* struct stat, fstat(), S_ISREG() */
44
42
#include <iso646.h>             /* and, or, not */
45
 
#include <dirent.h>             /* DIR, struct dirent, fdopendir(),
46
 
                                   readdir(), closedir(), dirfd() */
 
43
#include <dirent.h>             /* struct dirent, scandirat() */
47
44
#include <unistd.h>             /* fcntl(), F_GETFD, F_SETFD,
48
45
                                   FD_CLOEXEC, write(), STDOUT_FILENO,
49
46
                                   struct stat, fstat(), close(),
50
47
                                   setgid(), setuid(), S_ISREG(),
51
 
                                   faccessat() pipe(), fork(),
 
48
                                   faccessat() pipe2(), fork(),
52
49
                                   _exit(), dup2(), fexecve(), read()
53
50
                                */
54
51
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
 
                                   FD_CLOEXEC, openat() */
 
52
                                   FD_CLOEXEC, openat(), scandirat(),
 
53
                                   pipe2() */
56
54
#include <string.h>             /* strsep, strlen(), strsignal(),
57
55
                                   strcmp(), strncmp() */
58
56
#include <errno.h>              /* errno */
77
75
#define BUFFER_SIZE 256
78
76
 
79
77
#define PDIR "/lib/mandos/plugins.d"
 
78
#define PHDIR "/lib/mandos/plugin-helpers"
80
79
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
81
80
 
82
81
const char *argp_program_version = "plugin-runner " VERSION;
241
240
  return add_to_char_array(def, &(p->environ), &(p->envc));
242
241
}
243
242
 
 
243
#ifndef O_CLOEXEC
244
244
/*
245
245
 * Based on the example in the GNU LibC manual chapter 13.13 "File
246
246
 * Descriptor Flags".
257
257
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
258
258
                                       ret | FD_CLOEXEC));
259
259
}
 
260
#endif  /* not O_CLOEXEC */
260
261
 
261
262
 
262
263
/* Mark processes as completed when they exit, and save their exit
346
347
 
347
348
int main(int argc, char *argv[]){
348
349
  char *plugindir = NULL;
 
350
  char *pluginhelperdir = NULL;
349
351
  char *argfile = NULL;
350
352
  FILE *conffp;
351
 
  DIR *dir = NULL;
352
 
  struct dirent *dirst;
 
353
  struct dirent **direntries = NULL;
353
354
  struct stat st;
354
355
  fd_set rfds_all;
355
356
  int ret, maxfd = 0;
363
364
                                      .sa_flags = SA_NOCLDSTOP };
364
365
  char **custom_argv = NULL;
365
366
  int custom_argc = 0;
366
 
  int dir_fd;
 
367
  int dir_fd = -1;
367
368
  
368
369
  /* Establish a signal handler */
369
370
  sigemptyset(&sigchld_action.sa_mask);
414
415
      .doc = "Group ID the plugins will run as", .group = 3 },
415
416
    { .name = "debug", .key = 132,
416
417
      .doc = "Debug mode", .group = 4 },
 
418
    { .name = "plugin-helper-dir", .key = 133,
 
419
      .arg = "DIRECTORY",
 
420
      .doc = "Specify a different plugin helper directory",
 
421
      .group = 2 },
417
422
    /*
418
423
     * These reproduce what we would get without ARGP_NO_HELP
419
424
     */
545
550
    case 132:                   /* --debug */
546
551
      debug = true;
547
552
      break;
 
553
    case 133:                   /* --plugin-helper-dir */
 
554
      free(pluginhelperdir);
 
555
      pluginhelperdir = strdup(arg);
 
556
      if(pluginhelperdir != NULL){
 
557
        errno = 0;
 
558
      }
 
559
      break;
548
560
      /*
549
561
       * These reproduce what we would get without ARGP_NO_HELP
550
562
       */
601
613
    case 130:                   /* --userid */
602
614
    case 131:                   /* --groupid */
603
615
    case 132:                   /* --debug */
 
616
    case 133:                   /* --plugin-helper-dir */
604
617
    case '?':                   /* --help */
605
618
    case -3:                    /* --usage */
606
619
    case 'V':                   /* --version */
687
700
        custom_argc += 1;
688
701
        {
689
702
          char **new_argv = realloc(custom_argv, sizeof(char *)
690
 
                                    * ((unsigned int)
691
 
                                       custom_argc + 1));
 
703
                                    * ((size_t)custom_argc + 1));
692
704
          if(new_argv == NULL){
693
705
            error(0, errno, "realloc");
694
706
            exitstatus = EX_OSERR;
761
773
    goto fallback;
762
774
  }
763
775
  
 
776
  {
 
777
    char *pluginhelperenv;
 
778
    bool bret = true;
 
779
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
 
780
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
781
    if(ret != -1){
 
782
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
 
783
    }
 
784
    if(ret == -1 or not bret){
 
785
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
 
786
            " environment variable to \"%s\" for all plugins\n",
 
787
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
788
    }
 
789
    if(ret != -1){
 
790
      free(pluginhelperenv);
 
791
    }
 
792
  }
 
793
  
764
794
  if(debug){
765
795
    for(plugin *p = plugin_list; p != NULL; p=p->next){
766
796
      fprintf(stderr, "Plugin: %s has %d arguments\n",
795
825
          }
796
826
        }
797
827
      }
798
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
828
      close(plugindir_fd);
799
829
    }
800
830
  }
801
831
  
829
859
    ret = set_cloexec_flag(dir_fd);
830
860
    if(ret < 0){
831
861
      error(0, errno, "set_cloexec_flag");
832
 
      TEMP_FAILURE_RETRY(close(dir_fd));
833
862
      exitstatus = EX_OSERR;
834
863
      goto fallback;
835
864
    }
836
865
#endif  /* O_CLOEXEC */
837
 
    
838
 
    dir = fdopendir(dir_fd);
839
 
    if(dir == NULL){
840
 
      error(0, errno, "Could not open plugin dir");
841
 
      TEMP_FAILURE_RETRY(close(dir_fd));
842
 
      exitstatus = EX_OSERR;
843
 
      goto fallback;
 
866
  }
 
867
  
 
868
  int good_name(const struct dirent * const dirent){
 
869
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
 
870
                                      "*.dpkg-old", "*.dpkg-bak",
 
871
                                      "*.dpkg-divert", NULL };
 
872
#ifdef __GNUC__
 
873
#pragma GCC diagnostic push
 
874
#pragma GCC diagnostic ignored "-Wcast-qual"
 
875
#endif
 
876
    for(const char **pat = (const char **)patterns;
 
877
        *pat != NULL; pat++){
 
878
#ifdef __GNUC__
 
879
#pragma GCC diagnostic pop
 
880
#endif
 
881
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
 
882
         != FNM_NOMATCH){
 
883
        if(debug){
 
884
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
885
                    " matching pattern %s\n", dirent->d_name, *pat);
 
886
        }
 
887
        return 0;
 
888
      }
844
889
    }
 
890
    return 1;
 
891
  }
 
892
  
 
893
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
 
894
                             alphasort);
 
895
  if(numplugins == -1){
 
896
    error(0, errno, "Could not scan plugin dir");
 
897
    direntries = NULL;
 
898
    exitstatus = EX_OSERR;
 
899
    goto fallback;
845
900
  }
846
901
  
847
902
  FD_ZERO(&rfds_all);
848
903
  
849
904
  /* Read and execute any executable in the plugin directory*/
850
 
  while(true){
851
 
    do {
852
 
      dirst = readdir(dir);
853
 
    } while(dirst == NULL and errno == EINTR);
854
 
    
855
 
    /* All directory entries have been processed */
856
 
    if(dirst == NULL){
857
 
      if(errno == EBADF){
858
 
        error(0, errno, "readdir");
859
 
        exitstatus = EX_IOERR;
860
 
        goto fallback;
861
 
      }
862
 
      break;
863
 
    }
864
 
    
865
 
    /* Ignore dotfiles, backup files and other junk */
866
 
    {
867
 
      bool bad_name = false;
868
 
      const char * const patterns[] = { ".*", "#*#", "*~",
869
 
                                        "*.dpkg-new", "*.dpkg-old",
870
 
                                        "*.dpkg-bak", "*.dpkg-divert",
871
 
                                        NULL };
872
 
#ifdef __GNUC__
873
 
#pragma GCC diagnostic push
874
 
#pragma GCC diagnostic ignored "-Wcast-qual"
875
 
#endif
876
 
      for(const char **pat = (const char **)patterns;
877
 
          *pat != NULL; pat++){
878
 
#ifdef __GNUC__
879
 
#pragma GCC diagnostic pop
880
 
#endif
881
 
        if(fnmatch(*pat, dirst->d_name,
882
 
                   FNM_FILE_NAME | FNM_PERIOD) != FNM_NOMATCH){
883
 
          if(debug){
884
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
885
 
                    " matching pattern %s\n", dirst->d_name, *pat);
886
 
          }
887
 
          bad_name = true;
888
 
          break;
889
 
        }
890
 
      }
891
 
      if(bad_name){
892
 
        continue;
893
 
      }
894
 
    }
895
 
    
896
 
    int plugin_fd = openat(dir_fd, dirst->d_name, O_RDONLY |
897
 
#ifdef O_CLOEXEC
898
 
                            O_CLOEXEC
899
 
#else  /* not O_CLOEXEC */
900
 
                            0
901
 
#endif  /* not O_CLOEXEC */
902
 
                            );
 
905
  for(int i = 0; i < numplugins; i++){
 
906
    
 
907
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
903
908
    if(plugin_fd == -1){
904
909
      error(0, errno, "Could not open plugin");
905
 
      continue;
906
 
    }
907
 
#ifndef O_CLOEXEC
908
 
  /* Set the FD_CLOEXEC flag on the plugin FD */
909
 
    ret = set_cloexec_flag(plugin_fd);
910
 
    if(ret < 0){
911
 
      error(0, errno, "set_cloexec_flag");
912
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
913
 
      continue;
914
 
    }
915
 
#endif  /* O_CLOEXEC */
 
910
      free(direntries[i]);
 
911
      continue;
 
912
    }
916
913
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
917
914
    if(ret == -1){
918
915
      error(0, errno, "stat");
919
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
916
      close(plugin_fd);
 
917
      free(direntries[i]);
920
918
      continue;
921
919
    }
922
920
    
923
921
    /* Ignore non-executable files */
924
922
    if(not S_ISREG(st.st_mode)
925
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, dirst->d_name, X_OK,
926
 
                                        0)) != 0)){
 
923
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
924
                                        X_OK, 0)) != 0)){
927
925
      if(debug){
928
926
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
929
927
                " with bad type or mode\n",
930
 
                plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
928
                plugindir != NULL ? plugindir : PDIR,
 
929
                direntries[i]->d_name);
931
930
      }
932
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
931
      close(plugin_fd);
 
932
      free(direntries[i]);
933
933
      continue;
934
934
    }
935
935
    
936
 
    plugin *p = getplugin(dirst->d_name);
 
936
    plugin *p = getplugin(direntries[i]->d_name);
937
937
    if(p == NULL){
938
938
      error(0, errno, "getplugin");
939
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
939
      close(plugin_fd);
 
940
      free(direntries[i]);
940
941
      continue;
941
942
    }
942
943
    if(p->disabled){
943
944
      if(debug){
944
945
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
945
 
                dirst->d_name);
 
946
                direntries[i]->d_name);
946
947
      }
947
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
948
      close(plugin_fd);
 
949
      free(direntries[i]);
948
950
      continue;
949
951
    }
950
952
    {
975
977
    }
976
978
    
977
979
    int pipefd[2];
 
980
#ifndef O_CLOEXEC
978
981
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
 
982
#else  /* O_CLOEXEC */
 
983
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
 
984
#endif  /* O_CLOEXEC */
979
985
    if(ret == -1){
980
986
      error(0, errno, "pipe");
981
987
      exitstatus = EX_OSERR;
982
 
      goto fallback;
983
 
    }
 
988
      free(direntries[i]);
 
989
      goto fallback;
 
990
    }
 
991
    if(pipefd[0] >= FD_SETSIZE){
 
992
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
 
993
              FD_SETSIZE);
 
994
      close(pipefd[0]);
 
995
      close(pipefd[1]);
 
996
      exitstatus = EX_OSERR;
 
997
      free(direntries[i]);
 
998
      goto fallback;
 
999
    }
 
1000
#ifndef O_CLOEXEC
984
1001
    /* Ask OS to automatic close the pipe on exec */
985
1002
    ret = set_cloexec_flag(pipefd[0]);
986
1003
    if(ret < 0){
987
1004
      error(0, errno, "set_cloexec_flag");
 
1005
      close(pipefd[0]);
 
1006
      close(pipefd[1]);
988
1007
      exitstatus = EX_OSERR;
 
1008
      free(direntries[i]);
989
1009
      goto fallback;
990
1010
    }
991
1011
    ret = set_cloexec_flag(pipefd[1]);
992
1012
    if(ret < 0){
993
1013
      error(0, errno, "set_cloexec_flag");
 
1014
      close(pipefd[0]);
 
1015
      close(pipefd[1]);
994
1016
      exitstatus = EX_OSERR;
 
1017
      free(direntries[i]);
995
1018
      goto fallback;
996
1019
    }
 
1020
#endif  /* not O_CLOEXEC */
997
1021
    /* Block SIGCHLD until process is safely in process list */
998
1022
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
999
1023
                                              &sigchld_action.sa_mask,
1001
1025
    if(ret < 0){
1002
1026
      error(0, errno, "sigprocmask");
1003
1027
      exitstatus = EX_OSERR;
 
1028
      free(direntries[i]);
1004
1029
      goto fallback;
1005
1030
    }
1006
1031
    /* Starting a new process to be watched */
1010
1035
    } while(pid == -1 and errno == EINTR);
1011
1036
    if(pid == -1){
1012
1037
      error(0, errno, "fork");
 
1038
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
 
1039
                                     &sigchld_action.sa_mask, NULL));
 
1040
      close(pipefd[0]);
 
1041
      close(pipefd[1]);
1013
1042
      exitstatus = EX_OSERR;
 
1043
      free(direntries[i]);
1014
1044
      goto fallback;
1015
1045
    }
1016
1046
    if(pid == 0){
1032
1062
        _exit(EX_OSERR);
1033
1063
      }
1034
1064
      
1035
 
      if(dirfd(dir) < 0){
1036
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
1037
 
           above and must now close it manually here. */
1038
 
        closedir(dir);
1039
 
      }
1040
1065
      if(fexecve(plugin_fd, p->argv,
1041
1066
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1042
1067
        error(0, errno, "fexecve for %s/%s",
1043
 
              plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
1068
              plugindir != NULL ? plugindir : PDIR,
 
1069
              direntries[i]->d_name);
1044
1070
        _exit(EX_OSERR);
1045
1071
      }
1046
1072
      /* no return */
1047
1073
    }
1048
1074
    /* Parent process */
1049
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1050
 
                                             pipe */
1051
 
    TEMP_FAILURE_RETRY(close(plugin_fd));
1052
 
    plugin *new_plugin = getplugin(dirst->d_name);
 
1075
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1076
    close(plugin_fd);
 
1077
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1053
1078
    if(new_plugin == NULL){
1054
1079
      error(0, errno, "getplugin");
1055
1080
      ret = (int)(TEMP_FAILURE_RETRY
1059
1084
        error(0, errno, "sigprocmask");
1060
1085
      }
1061
1086
      exitstatus = EX_OSERR;
 
1087
      free(direntries[i]);
1062
1088
      goto fallback;
1063
1089
    }
 
1090
    free(direntries[i]);
1064
1091
    
1065
1092
    new_plugin->pid = pid;
1066
1093
    new_plugin->fd = pipefd[0];
1076
1103
      goto fallback;
1077
1104
    }
1078
1105
    
1079
 
#if defined (__GNUC__) and defined (__GLIBC__)
1080
 
#if not __GLIBC_PREREQ(2, 16)
1081
 
#pragma GCC diagnostic push
1082
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1083
 
#endif
1084
 
#endif
1085
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1086
 
                                          -Wconversion in GNU libc
1087
 
                                          before 2.16 */
1088
 
#if defined (__GNUC__) and defined (__GLIBC__)
1089
 
#if not __GLIBC_PREREQ(2, 16)
1090
 
#pragma GCC diagnostic pop
1091
 
#endif
1092
 
#endif
 
1106
    FD_SET(new_plugin->fd, &rfds_all);
1093
1107
    
1094
1108
    if(maxfd < new_plugin->fd){
1095
1109
      maxfd = new_plugin->fd;
1096
1110
    }
1097
1111
  }
1098
1112
  
1099
 
  TEMP_FAILURE_RETRY(closedir(dir));
1100
 
  dir = NULL;
 
1113
  free(direntries);
 
1114
  direntries = NULL;
 
1115
  close(dir_fd);
 
1116
  dir_fd = -1;
1101
1117
  free_plugin(getplugin(NULL));
1102
1118
  
1103
1119
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1142
1158
                      (intmax_t) (proc->pid),
1143
1159
                      WTERMSIG(proc->status),
1144
1160
                      strsignal(WTERMSIG(proc->status)));
1145
 
            } else if(WCOREDUMP(proc->status)){
1146
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1147
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
1148
1161
            }
1149
1162
          }
1150
1163
          
1151
1164
          /* Remove the plugin */
1152
 
#if defined (__GNUC__) and defined (__GLIBC__)
1153
 
#if not __GLIBC_PREREQ(2, 16)
1154
 
#pragma GCC diagnostic push
1155
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1156
 
#endif
1157
 
#endif
1158
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1159
 
                                          -Wconversion in GNU libc
1160
 
                                          before 2.16 */
1161
 
#if defined (__GNUC__) and defined (__GLIBC__)
1162
 
#if not __GLIBC_PREREQ(2, 16)
1163
 
#pragma GCC diagnostic pop
1164
 
#endif
1165
 
#endif
 
1165
          FD_CLR(proc->fd, &rfds_all);
1166
1166
          
1167
1167
          /* Block signal while modifying process_list */
1168
1168
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1208
1208
      }
1209
1209
      
1210
1210
      /* This process has not completed.  Does it have any output? */
1211
 
#if defined (__GNUC__) and defined (__GLIBC__)
1212
 
#if not __GLIBC_PREREQ(2, 16)
1213
 
#pragma GCC diagnostic push
1214
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1215
 
#endif
1216
 
#endif
1217
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1218
 
                                                         warning from
1219
 
                                                         -Wconversion
1220
 
                                                         in GNU libc
1221
 
                                                         before
1222
 
                                                         2.16 */
1223
 
#if defined (__GNUC__) and defined (__GLIBC__)
1224
 
#if not __GLIBC_PREREQ(2, 16)
1225
 
#pragma GCC diagnostic pop
1226
 
#endif
1227
 
#endif
 
1211
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1228
1212
        /* This process had nothing to say at this time */
1229
1213
        proc = proc->next;
1230
1214
        continue;
1297
1281
    free(custom_argv);
1298
1282
  }
1299
1283
  
1300
 
  if(dir != NULL){
1301
 
    closedir(dir);
 
1284
  free(direntries);
 
1285
  
 
1286
  if(dir_fd != -1){
 
1287
    close(dir_fd);
1302
1288
  }
1303
1289
  
1304
1290
  /* Kill the processes */
1324
1310
  free_plugin_list();
1325
1311
  
1326
1312
  free(plugindir);
 
1313
  free(pluginhelperdir);
1327
1314
  free(argfile);
1328
1315
  
1329
1316
  return exitstatus;