/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: 2019-03-12 20:13:34 UTC
  • Revision ID: teddy@recompile.se-20190312201334-my3htrprewjosuw5
mandos-ctl: Refactor

* mandos-ctl: Reorder everything into logical order; put main() first,
              and put every subsequent definition as soon as possible
              after its first use, except superclasses which need to
              be placed before the classes inheriting from them.
              Reorder all tests to match.

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-2013 Teddy Hogeborn
6
 
 * Copyright © 2008-2013 Björn Påhlsson
7
 
 * 
8
 
 * This program is free software: you can redistribute it and/or
9
 
 * modify it under the terms of the GNU General Public License as
10
 
 * published by the Free Software Foundation, either version 3 of the
11
 
 * License, or (at your option) any later version.
12
 
 * 
13
 
 * This program is distributed in the hope that it will be useful, but
 
5
 * Copyright © 2008-2018 Teddy Hogeborn
 
6
 * Copyright © 2008-2018 Björn Påhlsson
 
7
 * 
 
8
 * This file is part of Mandos.
 
9
 * 
 
10
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 * under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 * 
 
15
 * Mandos is distributed in the hope that it will be useful, but
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
24
25
 
25
26
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
 
                                   O_CLOEXEC */
 
27
                                   O_CLOEXEC, pipe2() */
27
28
#include <stddef.h>             /* size_t, NULL */
28
29
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
30
                                   realloc() */
30
31
#include <stdbool.h>            /* bool, true, false */
31
32
#include <stdio.h>              /* fileno(), fprintf(),
32
33
                                   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() */
 
34
#include <sys/types.h>          /* fstat(), struct stat, waitpid(),
 
35
                                   WIFEXITED(), WEXITSTATUS(), wait(),
 
36
                                   pid_t, uid_t, gid_t, getuid(),
 
37
                                   getgid() */
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(), WTERMSIG() */
43
42
#include <sys/stat.h>           /* struct stat, fstat(), S_ISREG() */
44
43
#include <iso646.h>             /* and, or, not */
45
 
#include <dirent.h>             /* DIR, struct dirent, fdopendir(),
46
 
                                   readdir(), closedir(), dirfd() */
 
44
#include <dirent.h>             /* struct dirent, scandirat() */
47
45
#include <unistd.h>             /* fcntl(), F_GETFD, F_SETFD,
48
46
                                   FD_CLOEXEC, write(), STDOUT_FILENO,
49
47
                                   struct stat, fstat(), close(),
50
48
                                   setgid(), setuid(), S_ISREG(),
51
 
                                   faccessat() pipe(), fork(),
 
49
                                   faccessat() pipe2(), fork(),
52
50
                                   _exit(), dup2(), fexecve(), read()
53
51
                                */
54
52
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
 
                                   FD_CLOEXEC, openat() */
 
53
                                   FD_CLOEXEC, openat(), scandirat(),
 
54
                                   pipe2() */
56
55
#include <string.h>             /* strsep, strlen(), strsignal(),
57
56
                                   strcmp(), strncmp() */
58
57
#include <errno.h>              /* errno */
72
71
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
73
72
#include <errno.h>              /* errno */
74
73
#include <error.h>              /* error() */
 
74
#include <fnmatch.h>            /* fnmatch() */
75
75
 
76
76
#define BUFFER_SIZE 256
77
77
 
78
78
#define PDIR "/lib/mandos/plugins.d"
 
79
#define PHDIR "/lib/mandos/plugin-helpers"
79
80
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
80
81
 
81
82
const char *argp_program_version = "plugin-runner " VERSION;
240
241
  return add_to_char_array(def, &(p->environ), &(p->envc));
241
242
}
242
243
 
 
244
#ifndef O_CLOEXEC
243
245
/*
244
246
 * Based on the example in the GNU LibC manual chapter 13.13 "File
245
247
 * Descriptor Flags".
256
258
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
257
259
                                       ret | FD_CLOEXEC));
258
260
}
 
261
#endif  /* not O_CLOEXEC */
259
262
 
260
263
 
261
264
/* Mark processes as completed when they exit, and save their exit
345
348
 
346
349
int main(int argc, char *argv[]){
347
350
  char *plugindir = NULL;
 
351
  char *pluginhelperdir = NULL;
348
352
  char *argfile = NULL;
349
353
  FILE *conffp;
350
 
  size_t d_name_len;
351
 
  DIR *dir = NULL;
352
 
  struct dirent *dirst;
 
354
  struct dirent **direntries = NULL;
353
355
  struct stat st;
354
356
  fd_set rfds_all;
355
357
  int ret, maxfd = 0;
363
365
                                      .sa_flags = SA_NOCLDSTOP };
364
366
  char **custom_argv = NULL;
365
367
  int custom_argc = 0;
366
 
  int dir_fd;
 
368
  int dir_fd = -1;
367
369
  
368
370
  /* Establish a signal handler */
369
371
  sigemptyset(&sigchld_action.sa_mask);
414
416
      .doc = "Group ID the plugins will run as", .group = 3 },
415
417
    { .name = "debug", .key = 132,
416
418
      .doc = "Debug mode", .group = 4 },
 
419
    { .name = "plugin-helper-dir", .key = 133,
 
420
      .arg = "DIRECTORY",
 
421
      .doc = "Specify a different plugin helper directory",
 
422
      .group = 2 },
417
423
    /*
418
424
     * These reproduce what we would get without ARGP_NO_HELP
419
425
     */
545
551
    case 132:                   /* --debug */
546
552
      debug = true;
547
553
      break;
 
554
    case 133:                   /* --plugin-helper-dir */
 
555
      free(pluginhelperdir);
 
556
      pluginhelperdir = strdup(arg);
 
557
      if(pluginhelperdir != NULL){
 
558
        errno = 0;
 
559
      }
 
560
      break;
548
561
      /*
549
562
       * These reproduce what we would get without ARGP_NO_HELP
550
563
       */
551
564
    case '?':                   /* --help */
552
565
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
553
566
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
 
567
      __builtin_unreachable();
554
568
    case -3:                    /* --usage */
555
569
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
556
570
      argp_state_help(state, state->out_stream,
557
571
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
 
572
      __builtin_unreachable();
558
573
    case 'V':                   /* --version */
559
574
      fprintf(state->out_stream, "%s\n", argp_program_version);
560
575
      exit(EXIT_SUCCESS);
570
585
      if(arg[0] == '\0'){
571
586
        break;
572
587
      }
 
588
      /* FALLTHROUGH */
573
589
    default:
574
590
      return ARGP_ERR_UNKNOWN;
575
591
    }
601
617
    case 130:                   /* --userid */
602
618
    case 131:                   /* --groupid */
603
619
    case 132:                   /* --debug */
 
620
    case 133:                   /* --plugin-helper-dir */
604
621
    case '?':                   /* --help */
605
622
    case -3:                    /* --usage */
606
623
    case 'V':                   /* --version */
687
704
        custom_argc += 1;
688
705
        {
689
706
          char **new_argv = realloc(custom_argv, sizeof(char *)
690
 
                                    * ((unsigned int)
691
 
                                       custom_argc + 1));
 
707
                                    * ((size_t)custom_argc + 1));
692
708
          if(new_argv == NULL){
693
709
            error(0, errno, "realloc");
694
710
            exitstatus = EX_OSERR;
761
777
    goto fallback;
762
778
  }
763
779
  
 
780
  {
 
781
    char *pluginhelperenv;
 
782
    bool bret = true;
 
783
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
 
784
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
785
    if(ret != -1){
 
786
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
 
787
    }
 
788
    if(ret == -1 or not bret){
 
789
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
 
790
            " environment variable to \"%s\" for all plugins\n",
 
791
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
792
    }
 
793
    if(ret != -1){
 
794
      free(pluginhelperenv);
 
795
    }
 
796
  }
 
797
  
764
798
  if(debug){
765
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
 
799
    for(plugin *p = plugin_list; p != NULL; p = p->next){
766
800
      fprintf(stderr, "Plugin: %s has %d arguments\n",
767
801
              p->name ? p->name : "Global", p->argc - 1);
768
802
      for(char **a = p->argv; *a != NULL; a++){
777
811
  
778
812
  if(getuid() == 0){
779
813
    /* Work around Debian bug #633582:
780
 
       <http://bugs.debian.org/633582> */
 
814
       <https://bugs.debian.org/633582> */
781
815
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
782
816
    if(plugindir_fd == -1){
783
817
      if(errno != ENOENT){
795
829
          }
796
830
        }
797
831
      }
798
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
832
      close(plugindir_fd);
799
833
    }
800
834
  }
801
835
  
829
863
    ret = set_cloexec_flag(dir_fd);
830
864
    if(ret < 0){
831
865
      error(0, errno, "set_cloexec_flag");
832
 
      TEMP_FAILURE_RETRY(close(dir_fd));
833
866
      exitstatus = EX_OSERR;
834
867
      goto fallback;
835
868
    }
836
869
#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;
 
870
  }
 
871
  
 
872
  int good_name(const struct dirent * const dirent){
 
873
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
 
874
                                      "*.dpkg-old", "*.dpkg-bak",
 
875
                                      "*.dpkg-divert", NULL };
 
876
#ifdef __GNUC__
 
877
#pragma GCC diagnostic push
 
878
#pragma GCC diagnostic ignored "-Wcast-qual"
 
879
#endif
 
880
    for(const char **pat = (const char **)patterns;
 
881
        *pat != NULL; pat++){
 
882
#ifdef __GNUC__
 
883
#pragma GCC diagnostic pop
 
884
#endif
 
885
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
 
886
         != FNM_NOMATCH){
 
887
        if(debug){
 
888
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
889
                    " matching pattern %s\n", dirent->d_name, *pat);
 
890
        }
 
891
        return 0;
 
892
      }
844
893
    }
 
894
    return 1;
 
895
  }
 
896
  
 
897
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
 
898
                             alphasort);
 
899
  if(numplugins == -1){
 
900
    error(0, errno, "Could not scan plugin dir");
 
901
    direntries = NULL;
 
902
    exitstatus = EX_OSERR;
 
903
    goto fallback;
845
904
  }
846
905
  
847
906
  FD_ZERO(&rfds_all);
848
907
  
849
908
  /* 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
 
    d_name_len = strlen(dirst->d_name);
866
 
    
867
 
    /* Ignore dotfiles, backup files and other junk */
868
 
    {
869
 
      bool bad_name = false;
870
 
      
871
 
      const char * const bad_prefixes[] = { ".", "#", NULL };
872
 
      
873
 
      const char * const bad_suffixes[] = { "~", "#", ".dpkg-new",
874
 
                                           ".dpkg-old",
875
 
                                           ".dpkg-bak",
876
 
                                           ".dpkg-divert", NULL };
877
 
#ifdef __GNUC__
878
 
#pragma GCC diagnostic push
879
 
#pragma GCC diagnostic ignored "-Wcast-qual"
880
 
#endif
881
 
      for(const char **pre = (const char **)bad_prefixes;
882
 
          *pre != NULL; pre++){
883
 
#ifdef __GNUC__
884
 
#pragma GCC diagnostic pop
885
 
#endif
886
 
        size_t pre_len = strlen(*pre);
887
 
        if((d_name_len >= pre_len)
888
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
889
 
          if(debug){
890
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
891
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
892
 
          }
893
 
          bad_name = true;
894
 
          break;
895
 
        }
896
 
      }
897
 
      if(bad_name){
898
 
        continue;
899
 
      }
900
 
#ifdef __GNUC__
901
 
#pragma GCC diagnostic push
902
 
#pragma GCC diagnostic ignored "-Wcast-qual"
903
 
#endif
904
 
      for(const char **suf = (const char **)bad_suffixes;
905
 
          *suf != NULL; suf++){
906
 
#ifdef __GNUC__
907
 
#pragma GCC diagnostic pop
908
 
#endif
909
 
        size_t suf_len = strlen(*suf);
910
 
        if((d_name_len >= suf_len)
911
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
912
 
                == 0)){
913
 
          if(debug){
914
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
915
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
916
 
          }
917
 
          bad_name = true;
918
 
          break;
919
 
        }
920
 
      }
921
 
      
922
 
      if(bad_name){
923
 
        continue;
924
 
      }
925
 
    }
926
 
    
927
 
    int plugin_fd = openat(dir_fd, dirst->d_name, O_RDONLY |
928
 
#ifdef O_CLOEXEC
929
 
                            O_CLOEXEC
930
 
#else  /* not O_CLOEXEC */
931
 
                            0
932
 
#endif  /* not O_CLOEXEC */
933
 
                            );
 
909
  for(int i = 0; i < numplugins; i++){
 
910
    
 
911
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
934
912
    if(plugin_fd == -1){
935
913
      error(0, errno, "Could not open plugin");
936
 
      continue;
937
 
    }
938
 
#ifndef O_CLOEXEC
939
 
  /* Set the FD_CLOEXEC flag on the plugin FD */
940
 
    ret = set_cloexec_flag(plugin_fd);
941
 
    if(ret < 0){
942
 
      error(0, errno, "set_cloexec_flag");
943
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
944
 
      continue;
945
 
    }
946
 
#endif  /* O_CLOEXEC */
 
914
      free(direntries[i]);
 
915
      continue;
 
916
    }
947
917
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
948
918
    if(ret == -1){
949
919
      error(0, errno, "stat");
950
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
920
      close(plugin_fd);
 
921
      free(direntries[i]);
951
922
      continue;
952
923
    }
953
924
    
954
925
    /* Ignore non-executable files */
955
926
    if(not S_ISREG(st.st_mode)
956
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, dirst->d_name, X_OK,
957
 
                                        0)) != 0)){
 
927
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
928
                                        X_OK, 0)) != 0)){
958
929
      if(debug){
959
930
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
960
931
                " with bad type or mode\n",
961
 
                plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
932
                plugindir != NULL ? plugindir : PDIR,
 
933
                direntries[i]->d_name);
962
934
      }
963
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
935
      close(plugin_fd);
 
936
      free(direntries[i]);
964
937
      continue;
965
938
    }
966
939
    
967
 
    plugin *p = getplugin(dirst->d_name);
 
940
    plugin *p = getplugin(direntries[i]->d_name);
968
941
    if(p == NULL){
969
942
      error(0, errno, "getplugin");
970
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
943
      close(plugin_fd);
 
944
      free(direntries[i]);
971
945
      continue;
972
946
    }
973
947
    if(p->disabled){
974
948
      if(debug){
975
949
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
976
 
                dirst->d_name);
 
950
                direntries[i]->d_name);
977
951
      }
978
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
952
      close(plugin_fd);
 
953
      free(direntries[i]);
979
954
      continue;
980
955
    }
981
956
    {
1006
981
    }
1007
982
    
1008
983
    int pipefd[2];
 
984
#ifndef O_CLOEXEC
1009
985
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
 
986
#else  /* O_CLOEXEC */
 
987
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
 
988
#endif  /* O_CLOEXEC */
1010
989
    if(ret == -1){
1011
990
      error(0, errno, "pipe");
1012
991
      exitstatus = EX_OSERR;
1013
 
      goto fallback;
1014
 
    }
 
992
      free(direntries[i]);
 
993
      goto fallback;
 
994
    }
 
995
    if(pipefd[0] >= FD_SETSIZE){
 
996
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
 
997
              FD_SETSIZE);
 
998
      close(pipefd[0]);
 
999
      close(pipefd[1]);
 
1000
      exitstatus = EX_OSERR;
 
1001
      free(direntries[i]);
 
1002
      goto fallback;
 
1003
    }
 
1004
#ifndef O_CLOEXEC
1015
1005
    /* Ask OS to automatic close the pipe on exec */
1016
1006
    ret = set_cloexec_flag(pipefd[0]);
1017
1007
    if(ret < 0){
1018
1008
      error(0, errno, "set_cloexec_flag");
 
1009
      close(pipefd[0]);
 
1010
      close(pipefd[1]);
1019
1011
      exitstatus = EX_OSERR;
 
1012
      free(direntries[i]);
1020
1013
      goto fallback;
1021
1014
    }
1022
1015
    ret = set_cloexec_flag(pipefd[1]);
1023
1016
    if(ret < 0){
1024
1017
      error(0, errno, "set_cloexec_flag");
 
1018
      close(pipefd[0]);
 
1019
      close(pipefd[1]);
1025
1020
      exitstatus = EX_OSERR;
 
1021
      free(direntries[i]);
1026
1022
      goto fallback;
1027
1023
    }
 
1024
#endif  /* not O_CLOEXEC */
1028
1025
    /* Block SIGCHLD until process is safely in process list */
1029
1026
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1030
1027
                                              &sigchld_action.sa_mask,
1032
1029
    if(ret < 0){
1033
1030
      error(0, errno, "sigprocmask");
1034
1031
      exitstatus = EX_OSERR;
 
1032
      free(direntries[i]);
1035
1033
      goto fallback;
1036
1034
    }
1037
1035
    /* Starting a new process to be watched */
1041
1039
    } while(pid == -1 and errno == EINTR);
1042
1040
    if(pid == -1){
1043
1041
      error(0, errno, "fork");
 
1042
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
 
1043
                                     &sigchld_action.sa_mask, NULL));
 
1044
      close(pipefd[0]);
 
1045
      close(pipefd[1]);
1044
1046
      exitstatus = EX_OSERR;
 
1047
      free(direntries[i]);
1045
1048
      goto fallback;
1046
1049
    }
1047
1050
    if(pid == 0){
1063
1066
        _exit(EX_OSERR);
1064
1067
      }
1065
1068
      
1066
 
      if(dirfd(dir) < 0){
1067
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
1068
 
           above and must now close it manually here. */
1069
 
        closedir(dir);
1070
 
      }
1071
1069
      if(fexecve(plugin_fd, p->argv,
1072
1070
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1073
1071
        error(0, errno, "fexecve for %s/%s",
1074
 
              plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
1072
              plugindir != NULL ? plugindir : PDIR,
 
1073
              direntries[i]->d_name);
1075
1074
        _exit(EX_OSERR);
1076
1075
      }
1077
1076
      /* no return */
1078
1077
    }
1079
1078
    /* Parent process */
1080
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1081
 
                                             pipe */
1082
 
    TEMP_FAILURE_RETRY(close(plugin_fd));
1083
 
    plugin *new_plugin = getplugin(dirst->d_name);
 
1079
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1080
    close(plugin_fd);
 
1081
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1084
1082
    if(new_plugin == NULL){
1085
1083
      error(0, errno, "getplugin");
1086
1084
      ret = (int)(TEMP_FAILURE_RETRY
1090
1088
        error(0, errno, "sigprocmask");
1091
1089
      }
1092
1090
      exitstatus = EX_OSERR;
 
1091
      free(direntries[i]);
1093
1092
      goto fallback;
1094
1093
    }
 
1094
    free(direntries[i]);
1095
1095
    
1096
1096
    new_plugin->pid = pid;
1097
1097
    new_plugin->fd = pipefd[0];
1098
 
    
 
1098
 
 
1099
    if(debug){
 
1100
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
 
1101
              new_plugin->name, (intmax_t) (new_plugin->pid));
 
1102
    }
 
1103
 
1099
1104
    /* Unblock SIGCHLD so signal handler can be run if this process
1100
1105
       has already completed */
1101
1106
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1107
1112
      goto fallback;
1108
1113
    }
1109
1114
    
1110
 
#if defined (__GNUC__) and defined (__GLIBC__)
1111
 
#if not __GLIBC_PREREQ(2, 16)
1112
 
#pragma GCC diagnostic push
1113
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1114
 
#endif
1115
 
#endif
1116
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1117
 
                                          -Wconversion in GNU libc
1118
 
                                          before 2.16 */
1119
 
#if defined (__GNUC__) and defined (__GLIBC__)
1120
 
#if not __GLIBC_PREREQ(2, 16)
1121
 
#pragma GCC diagnostic pop
1122
 
#endif
1123
 
#endif
 
1115
    FD_SET(new_plugin->fd, &rfds_all);
1124
1116
    
1125
1117
    if(maxfd < new_plugin->fd){
1126
1118
      maxfd = new_plugin->fd;
1127
1119
    }
1128
1120
  }
1129
1121
  
1130
 
  TEMP_FAILURE_RETRY(closedir(dir));
1131
 
  dir = NULL;
 
1122
  free(direntries);
 
1123
  direntries = NULL;
 
1124
  close(dir_fd);
 
1125
  dir_fd = -1;
1132
1126
  free_plugin(getplugin(NULL));
1133
1127
  
1134
1128
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1173
1167
                      (intmax_t) (proc->pid),
1174
1168
                      WTERMSIG(proc->status),
1175
1169
                      strsignal(WTERMSIG(proc->status)));
1176
 
            } else if(WCOREDUMP(proc->status)){
1177
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1178
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
1179
1170
            }
1180
1171
          }
1181
1172
          
1182
1173
          /* Remove the plugin */
1183
 
#if defined (__GNUC__) and defined (__GLIBC__)
1184
 
#if not __GLIBC_PREREQ(2, 16)
1185
 
#pragma GCC diagnostic push
1186
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1187
 
#endif
1188
 
#endif
1189
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1190
 
                                          -Wconversion in GNU libc
1191
 
                                          before 2.16 */
1192
 
#if defined (__GNUC__) and defined (__GLIBC__)
1193
 
#if not __GLIBC_PREREQ(2, 16)
1194
 
#pragma GCC diagnostic pop
1195
 
#endif
1196
 
#endif
 
1174
          FD_CLR(proc->fd, &rfds_all);
1197
1175
          
1198
1176
          /* Block signal while modifying process_list */
1199
1177
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1239
1217
      }
1240
1218
      
1241
1219
      /* This process has not completed.  Does it have any output? */
1242
 
#if defined (__GNUC__) and defined (__GLIBC__)
1243
 
#if not __GLIBC_PREREQ(2, 16)
1244
 
#pragma GCC diagnostic push
1245
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1246
 
#endif
1247
 
#endif
1248
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1249
 
                                                         warning from
1250
 
                                                         -Wconversion
1251
 
                                                         in GNU libc
1252
 
                                                         before
1253
 
                                                         2.16 */
1254
 
#if defined (__GNUC__) and defined (__GLIBC__)
1255
 
#if not __GLIBC_PREREQ(2, 16)
1256
 
#pragma GCC diagnostic pop
1257
 
#endif
1258
 
#endif
 
1220
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1259
1221
        /* This process had nothing to say at this time */
1260
1222
        proc = proc->next;
1261
1223
        continue;
1328
1290
    free(custom_argv);
1329
1291
  }
1330
1292
  
1331
 
  if(dir != NULL){
1332
 
    closedir(dir);
 
1293
  free(direntries);
 
1294
  
 
1295
  if(dir_fd != -1){
 
1296
    close(dir_fd);
1333
1297
  }
1334
1298
  
1335
1299
  /* Kill the processes */
1355
1319
  free_plugin_list();
1356
1320
  
1357
1321
  free(plugindir);
 
1322
  free(pluginhelperdir);
1358
1323
  free(argfile);
1359
1324
  
1360
1325
  return exitstatus;