/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: 2021-02-01 19:30:45 UTC
  • Revision ID: teddy@recompile.se-20210201193045-lpg6aprpc4srem6k
Fix issue with french translation

Initial white space was missing in both msgid and msgstr of the french
translation, leading to checking tools reporing an incomplete
translation.  The string is a raw command line command, and therefore
did not need translation, so this was never a user-visible issue.

* debian/po/fr.po: Add missing whitespace to the id and translation
  for msgid " mandos-keygen -F/dev/null|grep ^key_id".

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
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-2020 Teddy Hogeborn
 
6
 * Copyright © 2008-2020 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
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
 
                                   realloc() */
 
29
#include <stdlib.h>             /* malloc(), reallocarray(), realloc(),
 
30
                                   EXIT_SUCCESS, exit() */
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(),
52
 
                                   _exit(), dup2(), fexecve(), read()
53
 
                                */
 
49
                                   faccessat() pipe2(), fork(),
 
50
                                   _exit(), dup2(), fexecve(), read(),
 
51
                                   lstat(), symlink() */
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 */
77
76
#define BUFFER_SIZE 256
78
77
 
79
78
#define PDIR "/lib/mandos/plugins.d"
 
79
#define PHDIR "/lib/mandos/plugin-helpers"
80
80
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
81
81
 
82
82
const char *argp_program_version = "plugin-runner " VERSION;
179
179
  /* Resize the pointed-to array to hold one more pointer */
180
180
  char **new_array = NULL;
181
181
  do {
182
 
    new_array = realloc(*array, sizeof(char *)
183
 
                        * (size_t) ((*len) + 2));
 
182
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
183
    new_array = reallocarray(*array, (size_t)((*len) + 2),
 
184
                             sizeof(char *));
 
185
#else
 
186
    if(((size_t)((*len) + 2)) > (SIZE_MAX / sizeof(char *))){
 
187
      /* overflow */
 
188
      new_array = NULL;
 
189
      errno = ENOMEM;
 
190
    } else {
 
191
      new_array = realloc(*array, (size_t)((*len) + 2)
 
192
                          * sizeof(char *));
 
193
    }
 
194
#endif
184
195
  } while(new_array == NULL and errno == EINTR);
185
196
  /* Malloc check */
186
197
  if(new_array == NULL){
241
252
  return add_to_char_array(def, &(p->environ), &(p->envc));
242
253
}
243
254
 
 
255
#ifndef O_CLOEXEC
244
256
/*
245
257
 * Based on the example in the GNU LibC manual chapter 13.13 "File
246
258
 * Descriptor Flags".
257
269
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
258
270
                                       ret | FD_CLOEXEC));
259
271
}
 
272
#endif  /* not O_CLOEXEC */
260
273
 
261
274
 
262
275
/* Mark processes as completed when they exit, and save their exit
311
324
__attribute__((nonnull))
312
325
static void free_plugin(plugin *plugin_node){
313
326
  
314
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
 
327
  for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){
315
328
    free(*arg);
316
329
  }
 
330
  free(plugin_node->name);
317
331
  free(plugin_node->argv);
318
332
  for(char **env = plugin_node->environ; *env != NULL; env++){
319
333
    free(*env);
346
360
 
347
361
int main(int argc, char *argv[]){
348
362
  char *plugindir = NULL;
 
363
  char *pluginhelperdir = NULL;
349
364
  char *argfile = NULL;
350
365
  FILE *conffp;
351
 
  DIR *dir = NULL;
352
 
  struct dirent *dirst;
 
366
  struct dirent **direntries = NULL;
353
367
  struct stat st;
354
368
  fd_set rfds_all;
355
369
  int ret, maxfd = 0;
363
377
                                      .sa_flags = SA_NOCLDSTOP };
364
378
  char **custom_argv = NULL;
365
379
  int custom_argc = 0;
366
 
  int dir_fd;
 
380
  int dir_fd = -1;
367
381
  
368
382
  /* Establish a signal handler */
369
383
  sigemptyset(&sigchld_action.sa_mask);
414
428
      .doc = "Group ID the plugins will run as", .group = 3 },
415
429
    { .name = "debug", .key = 132,
416
430
      .doc = "Debug mode", .group = 4 },
 
431
    { .name = "plugin-helper-dir", .key = 133,
 
432
      .arg = "DIRECTORY",
 
433
      .doc = "Specify a different plugin helper directory",
 
434
      .group = 2 },
417
435
    /*
418
436
     * These reproduce what we would get without ARGP_NO_HELP
419
437
     */
545
563
    case 132:                   /* --debug */
546
564
      debug = true;
547
565
      break;
 
566
    case 133:                   /* --plugin-helper-dir */
 
567
      free(pluginhelperdir);
 
568
      pluginhelperdir = strdup(arg);
 
569
      if(pluginhelperdir != NULL){
 
570
        errno = 0;
 
571
      }
 
572
      break;
548
573
      /*
549
574
       * These reproduce what we would get without ARGP_NO_HELP
550
575
       */
551
576
    case '?':                   /* --help */
552
577
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
553
578
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
 
579
      __builtin_unreachable();
554
580
    case -3:                    /* --usage */
555
581
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
556
582
      argp_state_help(state, state->out_stream,
557
583
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
 
584
      __builtin_unreachable();
558
585
    case 'V':                   /* --version */
559
586
      fprintf(state->out_stream, "%s\n", argp_program_version);
560
587
      exit(EXIT_SUCCESS);
570
597
      if(arg[0] == '\0'){
571
598
        break;
572
599
      }
 
600
#if __GNUC__ >= 7
 
601
      __attribute__((fallthrough));
 
602
#else
 
603
          /* FALLTHROUGH */
 
604
#endif
573
605
    default:
574
606
      return ARGP_ERR_UNKNOWN;
575
607
    }
601
633
    case 130:                   /* --userid */
602
634
    case 131:                   /* --groupid */
603
635
    case 132:                   /* --debug */
 
636
    case 133:                   /* --plugin-helper-dir */
604
637
    case '?':                   /* --help */
605
638
    case -3:                    /* --usage */
606
639
    case 'V':                   /* --version */
686
719
        
687
720
        custom_argc += 1;
688
721
        {
689
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
690
 
                                    * ((unsigned int)
691
 
                                       custom_argc + 1));
 
722
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
723
          char **new_argv = reallocarray(custom_argv, (size_t)custom_argc + 1,
 
724
                                         sizeof(char *));
 
725
#else
 
726
          char **new_argv = NULL;
 
727
          if(((size_t)custom_argc + 1) > (SIZE_MAX / sizeof(char *))){
 
728
            /* overflow */
 
729
            errno = ENOMEM;
 
730
          } else {
 
731
            new_argv = realloc(custom_argv, ((size_t)custom_argc + 1)
 
732
                               * sizeof(char *));
 
733
          }
 
734
#endif
692
735
          if(new_argv == NULL){
693
 
            error(0, errno, "realloc");
 
736
            error(0, errno, "reallocarray");
694
737
            exitstatus = EX_OSERR;
695
738
            free(new_arg);
696
739
            free(org_line);
761
804
    goto fallback;
762
805
  }
763
806
  
 
807
  {
 
808
    char *pluginhelperenv;
 
809
    bool bret = true;
 
810
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
 
811
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
812
    if(ret != -1){
 
813
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
 
814
    }
 
815
    if(ret == -1 or not bret){
 
816
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
 
817
            " environment variable to \"%s\" for all plugins\n",
 
818
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
819
    }
 
820
    if(ret != -1){
 
821
      free(pluginhelperenv);
 
822
    }
 
823
  }
 
824
  
764
825
  if(debug){
765
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
 
826
    for(plugin *p = plugin_list; p != NULL; p = p->next){
766
827
      fprintf(stderr, "Plugin: %s has %d arguments\n",
767
828
              p->name ? p->name : "Global", p->argc - 1);
768
829
      for(char **a = p->argv; *a != NULL; a++){
777
838
  
778
839
  if(getuid() == 0){
779
840
    /* Work around Debian bug #633582:
780
 
       <http://bugs.debian.org/633582> */
 
841
       <https://bugs.debian.org/633582> */
781
842
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
782
843
    if(plugindir_fd == -1){
783
844
      if(errno != ENOENT){
795
856
          }
796
857
        }
797
858
      }
798
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
859
      close(plugindir_fd);
 
860
    }
 
861
 
 
862
    /* Work around Debian bug #981302
 
863
       <https://bugs.debian.org/981302> */
 
864
    if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){
 
865
      ret = symlink("/proc/self/fd", "/dev/fd");
 
866
      if(ret == -1){
 
867
        error(0, errno, "Failed to create /dev/fd symlink");
 
868
      }
799
869
    }
800
870
  }
801
871
  
829
899
    ret = set_cloexec_flag(dir_fd);
830
900
    if(ret < 0){
831
901
      error(0, errno, "set_cloexec_flag");
832
 
      TEMP_FAILURE_RETRY(close(dir_fd));
833
902
      exitstatus = EX_OSERR;
834
903
      goto fallback;
835
904
    }
836
905
#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;
 
906
  }
 
907
  
 
908
  int good_name(const struct dirent * const dirent){
 
909
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
 
910
                                      "*.dpkg-old", "*.dpkg-bak",
 
911
                                      "*.dpkg-divert", NULL };
 
912
#ifdef __GNUC__
 
913
#pragma GCC diagnostic push
 
914
#pragma GCC diagnostic ignored "-Wcast-qual"
 
915
#endif
 
916
    for(const char **pat = (const char **)patterns;
 
917
        *pat != NULL; pat++){
 
918
#ifdef __GNUC__
 
919
#pragma GCC diagnostic pop
 
920
#endif
 
921
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
 
922
         != FNM_NOMATCH){
 
923
        if(debug){
 
924
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
925
                    " matching pattern %s\n", dirent->d_name, *pat);
 
926
        }
 
927
        return 0;
 
928
      }
844
929
    }
 
930
    return 1;
 
931
  }
 
932
  
 
933
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
 
934
                             alphasort);
 
935
  if(numplugins == -1){
 
936
    error(0, errno, "Could not scan plugin dir");
 
937
    direntries = NULL;
 
938
    exitstatus = EX_OSERR;
 
939
    goto fallback;
845
940
  }
846
941
  
847
942
  FD_ZERO(&rfds_all);
848
943
  
849
944
  /* 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
 
                            );
 
945
  for(int i = 0; i < numplugins; i++){
 
946
    
 
947
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
903
948
    if(plugin_fd == -1){
904
949
      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 */
 
950
      free(direntries[i]);
 
951
      continue;
 
952
    }
916
953
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
917
954
    if(ret == -1){
918
955
      error(0, errno, "stat");
919
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
956
      close(plugin_fd);
 
957
      free(direntries[i]);
920
958
      continue;
921
959
    }
922
960
    
923
961
    /* Ignore non-executable files */
924
962
    if(not S_ISREG(st.st_mode)
925
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, dirst->d_name, X_OK,
926
 
                                        0)) != 0)){
 
963
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
964
                                        X_OK, 0)) != 0)){
927
965
      if(debug){
928
966
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
929
967
                " with bad type or mode\n",
930
 
                plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
968
                plugindir != NULL ? plugindir : PDIR,
 
969
                direntries[i]->d_name);
931
970
      }
932
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
971
      close(plugin_fd);
 
972
      free(direntries[i]);
933
973
      continue;
934
974
    }
935
975
    
936
 
    plugin *p = getplugin(dirst->d_name);
 
976
    plugin *p = getplugin(direntries[i]->d_name);
937
977
    if(p == NULL){
938
978
      error(0, errno, "getplugin");
939
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
979
      close(plugin_fd);
 
980
      free(direntries[i]);
940
981
      continue;
941
982
    }
942
983
    if(p->disabled){
943
984
      if(debug){
944
985
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
945
 
                dirst->d_name);
 
986
                direntries[i]->d_name);
946
987
      }
947
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
988
      close(plugin_fd);
 
989
      free(direntries[i]);
948
990
      continue;
949
991
    }
950
992
    {
975
1017
    }
976
1018
    
977
1019
    int pipefd[2];
 
1020
#ifndef O_CLOEXEC
978
1021
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
 
1022
#else  /* O_CLOEXEC */
 
1023
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
 
1024
#endif  /* O_CLOEXEC */
979
1025
    if(ret == -1){
980
1026
      error(0, errno, "pipe");
981
1027
      exitstatus = EX_OSERR;
982
 
      goto fallback;
983
 
    }
 
1028
      free(direntries[i]);
 
1029
      goto fallback;
 
1030
    }
 
1031
    if(pipefd[0] >= FD_SETSIZE){
 
1032
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
 
1033
              FD_SETSIZE);
 
1034
      close(pipefd[0]);
 
1035
      close(pipefd[1]);
 
1036
      exitstatus = EX_OSERR;
 
1037
      free(direntries[i]);
 
1038
      goto fallback;
 
1039
    }
 
1040
#ifndef O_CLOEXEC
984
1041
    /* Ask OS to automatic close the pipe on exec */
985
1042
    ret = set_cloexec_flag(pipefd[0]);
986
1043
    if(ret < 0){
987
1044
      error(0, errno, "set_cloexec_flag");
 
1045
      close(pipefd[0]);
 
1046
      close(pipefd[1]);
988
1047
      exitstatus = EX_OSERR;
 
1048
      free(direntries[i]);
989
1049
      goto fallback;
990
1050
    }
991
1051
    ret = set_cloexec_flag(pipefd[1]);
992
1052
    if(ret < 0){
993
1053
      error(0, errno, "set_cloexec_flag");
 
1054
      close(pipefd[0]);
 
1055
      close(pipefd[1]);
994
1056
      exitstatus = EX_OSERR;
 
1057
      free(direntries[i]);
995
1058
      goto fallback;
996
1059
    }
 
1060
#endif  /* not O_CLOEXEC */
997
1061
    /* Block SIGCHLD until process is safely in process list */
998
1062
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
999
1063
                                              &sigchld_action.sa_mask,
1001
1065
    if(ret < 0){
1002
1066
      error(0, errno, "sigprocmask");
1003
1067
      exitstatus = EX_OSERR;
 
1068
      free(direntries[i]);
1004
1069
      goto fallback;
1005
1070
    }
1006
1071
    /* Starting a new process to be watched */
1010
1075
    } while(pid == -1 and errno == EINTR);
1011
1076
    if(pid == -1){
1012
1077
      error(0, errno, "fork");
 
1078
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
 
1079
                                     &sigchld_action.sa_mask, NULL));
 
1080
      close(pipefd[0]);
 
1081
      close(pipefd[1]);
1013
1082
      exitstatus = EX_OSERR;
 
1083
      free(direntries[i]);
1014
1084
      goto fallback;
1015
1085
    }
1016
1086
    if(pid == 0){
1032
1102
        _exit(EX_OSERR);
1033
1103
      }
1034
1104
      
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
1105
      if(fexecve(plugin_fd, p->argv,
1041
1106
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1042
1107
        error(0, errno, "fexecve for %s/%s",
1043
 
              plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
1108
              plugindir != NULL ? plugindir : PDIR,
 
1109
              direntries[i]->d_name);
1044
1110
        _exit(EX_OSERR);
1045
1111
      }
1046
1112
      /* no return */
1047
1113
    }
1048
1114
    /* 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);
 
1115
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1116
    close(plugin_fd);
 
1117
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1053
1118
    if(new_plugin == NULL){
1054
1119
      error(0, errno, "getplugin");
1055
1120
      ret = (int)(TEMP_FAILURE_RETRY
1059
1124
        error(0, errno, "sigprocmask");
1060
1125
      }
1061
1126
      exitstatus = EX_OSERR;
 
1127
      free(direntries[i]);
1062
1128
      goto fallback;
1063
1129
    }
 
1130
    free(direntries[i]);
1064
1131
    
1065
1132
    new_plugin->pid = pid;
1066
1133
    new_plugin->fd = pipefd[0];
1067
 
    
 
1134
 
 
1135
    if(debug){
 
1136
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
 
1137
              new_plugin->name, (intmax_t) (new_plugin->pid));
 
1138
    }
 
1139
 
1068
1140
    /* Unblock SIGCHLD so signal handler can be run if this process
1069
1141
       has already completed */
1070
1142
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1076
1148
      goto fallback;
1077
1149
    }
1078
1150
    
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
 
1151
    FD_SET(new_plugin->fd, &rfds_all);
1093
1152
    
1094
1153
    if(maxfd < new_plugin->fd){
1095
1154
      maxfd = new_plugin->fd;
1096
1155
    }
1097
1156
  }
1098
1157
  
1099
 
  TEMP_FAILURE_RETRY(closedir(dir));
1100
 
  dir = NULL;
 
1158
  free(direntries);
 
1159
  direntries = NULL;
 
1160
  close(dir_fd);
 
1161
  dir_fd = -1;
1101
1162
  free_plugin(getplugin(NULL));
1102
1163
  
1103
1164
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1142
1203
                      (intmax_t) (proc->pid),
1143
1204
                      WTERMSIG(proc->status),
1144
1205
                      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
1206
            }
1149
1207
          }
1150
1208
          
1151
1209
          /* 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
 
1210
          FD_CLR(proc->fd, &rfds_all);
1166
1211
          
1167
1212
          /* Block signal while modifying process_list */
1168
1213
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1208
1253
      }
1209
1254
      
1210
1255
      /* 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
 
1256
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1228
1257
        /* This process had nothing to say at this time */
1229
1258
        proc = proc->next;
1230
1259
        continue;
1297
1326
    free(custom_argv);
1298
1327
  }
1299
1328
  
1300
 
  if(dir != NULL){
1301
 
    closedir(dir);
 
1329
  free(direntries);
 
1330
  
 
1331
  if(dir_fd != -1){
 
1332
    close(dir_fd);
1302
1333
  }
1303
1334
  
1304
1335
  /* Kill the processes */
1324
1355
  free_plugin_list();
1325
1356
  
1326
1357
  free(plugindir);
 
1358
  free(pluginhelperdir);
1327
1359
  free(argfile);
1328
1360
  
1329
1361
  return exitstatus;