/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: 2015-05-23 20:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 756.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

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
 
5
 * Copyright © 2008-2014 Teddy Hogeborn
 
6
 * Copyright © 2008-2014 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
 
                                   asprintf(), 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
 
                                   stderr, STDOUT_FILENO */
33
 
#include <sys/types.h>          /* DIR, fdopendir(), stat(), struct
34
 
                                   stat, waitpid(), WIFEXITED(),
35
 
                                   WEXITSTATUS(), wait(), pid_t,
36
 
                                   uid_t, gid_t, getuid(), getgid(),
37
 
                                   dirfd() */
 
32
                                   stderr, STDOUT_FILENO, fclose() */
 
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
40
                                   WEXITSTATUS(), WTERMSIG(),
42
41
                                   WCOREDUMP() */
43
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
 
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() */
47
 
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
48
 
                                   fcntl(), setuid(), setgid(),
49
 
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
50
 
                                   access(), pipe(), fork(), close()
51
 
                                   dup2(), STDOUT_FILENO, _exit(),
52
 
                                   execv(), write(), read(),
53
 
                                   close() */
 
44
#include <dirent.h>             /* struct dirent, scandirat() */
 
45
#include <unistd.h>             /* fcntl(), F_GETFD, F_SETFD,
 
46
                                   FD_CLOEXEC, write(), STDOUT_FILENO,
 
47
                                   struct stat, fstat(), close(),
 
48
                                   setgid(), setuid(), S_ISREG(),
 
49
                                   faccessat() pipe2(), fork(),
 
50
                                   _exit(), dup2(), fexecve(), read()
 
51
                                */
54
52
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
 
                                   FD_CLOEXEC */
56
 
#include <string.h>             /* strsep, strlen(), asprintf(),
57
 
                                   strsignal(), strcmp(), strncmp() */
 
53
                                   FD_CLOEXEC, openat(), scandirat(),
 
54
                                   pipe2() */
 
55
#include <string.h>             /* strsep, strlen(), strsignal(),
 
56
                                   strcmp(), strncmp() */
58
57
#include <errno.h>              /* errno */
59
58
#include <argp.h>               /* struct argp_option, struct
60
59
                                   argp_state, struct argp,
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
 
105
105
 
106
106
/* Gets an existing plugin based on name,
107
107
   or if none is found, creates a new one */
 
108
__attribute__((warn_unused_result))
108
109
static plugin *getplugin(char *name){
109
110
  /* Check for existing plugin with that name */
110
111
  for(plugin *p = plugin_list; p != NULL; p = p->next){
171
172
}
172
173
 
173
174
/* Helper function for add_argument and add_environment */
174
 
__attribute__((nonnull))
 
175
__attribute__((nonnull, warn_unused_result))
175
176
static bool add_to_char_array(const char *new, char ***array,
176
177
                              int *len){
177
178
  /* Resize the pointed-to array to hold one more pointer */
202
203
}
203
204
 
204
205
/* Add to a plugin's argument vector */
205
 
__attribute__((nonnull(2)))
 
206
__attribute__((nonnull(2), warn_unused_result))
206
207
static bool add_argument(plugin *p, const char *arg){
207
208
  if(p == NULL){
208
209
    return false;
211
212
}
212
213
 
213
214
/* Add to a plugin's environment */
214
 
__attribute__((nonnull(2)))
 
215
__attribute__((nonnull(2), warn_unused_result))
215
216
static bool add_environment(plugin *p, const char *def, bool replace){
216
217
  if(p == NULL){
217
218
    return false;
239
240
  return add_to_char_array(def, &(p->environ), &(p->envc));
240
241
}
241
242
 
 
243
#ifndef O_CLOEXEC
242
244
/*
243
245
 * Based on the example in the GNU LibC manual chapter 13.13 "File
244
246
 * Descriptor Flags".
245
247
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
246
248
 */
 
249
__attribute__((warn_unused_result))
247
250
static int set_cloexec_flag(int fd){
248
251
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
249
252
  /* If reading the flags failed, return error indication now. */
254
257
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
255
258
                                       ret | FD_CLOEXEC));
256
259
}
 
260
#endif  /* not O_CLOEXEC */
257
261
 
258
262
 
259
263
/* Mark processes as completed when they exit, and save their exit
291
295
}
292
296
 
293
297
/* Prints out a password to stdout */
294
 
__attribute__((nonnull))
 
298
__attribute__((nonnull, warn_unused_result))
295
299
static bool print_out_password(const char *buffer, size_t length){
296
300
  ssize_t ret;
297
301
  for(size_t written = 0; written < length; written += (size_t)ret){
345
349
  char *plugindir = NULL;
346
350
  char *argfile = NULL;
347
351
  FILE *conffp;
348
 
  size_t d_name_len;
349
 
  DIR *dir = NULL;
350
 
  struct dirent *dirst;
 
352
  struct dirent **direntries = NULL;
351
353
  struct stat st;
352
354
  fd_set rfds_all;
353
355
  int ret, maxfd = 0;
361
363
                                      .sa_flags = SA_NOCLDSTOP };
362
364
  char **custom_argv = NULL;
363
365
  int custom_argc = 0;
 
366
  int dir_fd = -1;
364
367
  
365
368
  /* Establish a signal handler */
366
369
  sigemptyset(&sigchld_action.sa_mask);
437
440
            break;
438
441
          }
439
442
        }
 
443
        errno = 0;
440
444
      }
441
445
      break;
442
446
    case 'G':                   /* --global-env */
443
 
      add_environment(getplugin(NULL), arg, true);
 
447
      if(add_environment(getplugin(NULL), arg, true)){
 
448
        errno = 0;
 
449
      }
444
450
      break;
445
451
    case 'o':                   /* --options-for */
446
452
      {
463
469
            break;
464
470
          }
465
471
        }
 
472
        errno = 0;
466
473
      }
467
474
      break;
468
475
    case 'E':                   /* --env-for */
480
487
          errno = EINVAL;
481
488
          break;
482
489
        }
483
 
        add_environment(getplugin(arg), envdef, true);
 
490
        if(add_environment(getplugin(arg), envdef, true)){
 
491
          errno = 0;
 
492
        }
484
493
      }
485
494
      break;
486
495
    case 'd':                   /* --disable */
488
497
        plugin *p = getplugin(arg);
489
498
        if(p != NULL){
490
499
          p->disabled = true;
 
500
          errno = 0;
491
501
        }
492
502
      }
493
503
      break;
496
506
        plugin *p = getplugin(arg);
497
507
        if(p != NULL){
498
508
          p->disabled = false;
 
509
          errno = 0;
499
510
        }
500
511
      }
501
512
      break;
502
513
    case 128:                   /* --plugin-dir */
503
514
      free(plugindir);
504
515
      plugindir = strdup(arg);
 
516
      if(plugindir != NULL){
 
517
        errno = 0;
 
518
      }
505
519
      break;
506
520
    case 129:                   /* --config-file */
507
521
      /* This is already done by parse_opt_config_file() */
515
529
        break;
516
530
      }
517
531
      uid = (uid_t)tmp_id;
 
532
      errno = 0;
518
533
      break;
519
534
    case 131:                   /* --groupid */
520
535
      tmp_id = strtoimax(arg, &tmp, 10);
525
540
        break;
526
541
      }
527
542
      gid = (gid_t)tmp_id;
 
543
      errno = 0;
528
544
      break;
529
545
    case 132:                   /* --debug */
530
546
      debug = true;
578
594
    case 129:                   /* --config-file */
579
595
      free(argfile);
580
596
      argfile = strdup(arg);
 
597
      if(argfile != NULL){
 
598
        errno = 0;
 
599
      }
581
600
      break;
582
601
    case 130:                   /* --userid */
583
602
    case 131:                   /* --groupid */
761
780
       <http://bugs.debian.org/633582> */
762
781
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
763
782
    if(plugindir_fd == -1){
764
 
      error(0, errno, "open");
 
783
      if(errno != ENOENT){
 
784
        error(0, errno, "open(\"" PDIR "\")");
 
785
      }
765
786
    } else {
766
787
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
767
788
      if(ret == -1){
790
811
  
791
812
  /* Open plugin directory with close_on_exec flag */
792
813
  {
793
 
    int dir_fd = -1;
794
 
    if(plugindir == NULL){
795
 
      dir_fd = open(PDIR, O_RDONLY |
796
 
#ifdef O_CLOEXEC
797
 
                    O_CLOEXEC
798
 
#else  /* not O_CLOEXEC */
799
 
                    0
800
 
#endif  /* not O_CLOEXEC */
801
 
                    );
802
 
    } else {
803
 
      dir_fd = open(plugindir, O_RDONLY |
804
 
#ifdef O_CLOEXEC
805
 
                    O_CLOEXEC
806
 
#else  /* not O_CLOEXEC */
807
 
                    0
808
 
#endif  /* not O_CLOEXEC */
809
 
                    );
810
 
    }
 
814
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
 
815
#ifdef O_CLOEXEC
 
816
                  O_CLOEXEC
 
817
#else  /* not O_CLOEXEC */
 
818
                  0
 
819
#endif  /* not O_CLOEXEC */
 
820
                  );
811
821
    if(dir_fd == -1){
812
822
      error(0, errno, "Could not open plugin dir");
813
823
      exitstatus = EX_UNAVAILABLE;
819
829
    ret = set_cloexec_flag(dir_fd);
820
830
    if(ret < 0){
821
831
      error(0, errno, "set_cloexec_flag");
822
 
      TEMP_FAILURE_RETRY(close(dir_fd));
823
832
      exitstatus = EX_OSERR;
824
833
      goto fallback;
825
834
    }
826
835
#endif  /* O_CLOEXEC */
827
 
    
828
 
    dir = fdopendir(dir_fd);
829
 
    if(dir == NULL){
830
 
      error(0, errno, "Could not open plugin dir");
831
 
      TEMP_FAILURE_RETRY(close(dir_fd));
832
 
      exitstatus = EX_OSERR;
833
 
      goto fallback;
 
836
  }
 
837
  
 
838
  int good_name(const struct dirent * const dirent){
 
839
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
 
840
                                      "*.dpkg-old", "*.dpkg-bak",
 
841
                                      "*.dpkg-divert", NULL };
 
842
#ifdef __GNUC__
 
843
#pragma GCC diagnostic push
 
844
#pragma GCC diagnostic ignored "-Wcast-qual"
 
845
#endif
 
846
    for(const char **pat = (const char **)patterns;
 
847
        *pat != NULL; pat++){
 
848
#ifdef __GNUC__
 
849
#pragma GCC diagnostic pop
 
850
#endif
 
851
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
 
852
         != FNM_NOMATCH){
 
853
        if(debug){
 
854
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
855
                    " matching pattern %s\n", dirent->d_name, *pat);
 
856
        }
 
857
        return 0;
 
858
      }
834
859
    }
 
860
    return 1;
 
861
  }
 
862
  
 
863
#ifdef __GLIBC__
 
864
#if __GLIBC_PREREQ(2, 15)
 
865
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
 
866
                             alphasort);
 
867
#else  /* not __GLIBC_PREREQ(2, 15) */
 
868
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
 
869
                           &direntries, good_name, alphasort);
 
870
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
871
#else   /* not __GLIBC__ */
 
872
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
 
873
                           &direntries, good_name, alphasort);
 
874
#endif  /* not __GLIBC__ */
 
875
  if(numplugins == -1){
 
876
    error(0, errno, "Could not scan plugin dir");
 
877
    direntries = NULL;
 
878
    exitstatus = EX_OSERR;
 
879
    goto fallback;
835
880
  }
836
881
  
837
882
  FD_ZERO(&rfds_all);
838
883
  
839
884
  /* Read and execute any executable in the plugin directory*/
840
 
  while(true){
841
 
    do {
842
 
      dirst = readdir(dir);
843
 
    } while(dirst == NULL and errno == EINTR);
844
 
    
845
 
    /* All directory entries have been processed */
846
 
    if(dirst == NULL){
847
 
      if(errno == EBADF){
848
 
        error(0, errno, "readdir");
849
 
        exitstatus = EX_IOERR;
850
 
        goto fallback;
851
 
      }
852
 
      break;
853
 
    }
854
 
    
855
 
    d_name_len = strlen(dirst->d_name);
856
 
    
857
 
    /* Ignore dotfiles, backup files and other junk */
858
 
    {
859
 
      bool bad_name = false;
860
 
      
861
 
      const char * const bad_prefixes[] = { ".", "#", NULL };
862
 
      
863
 
      const char * const bad_suffixes[] = { "~", "#", ".dpkg-new",
864
 
                                           ".dpkg-old",
865
 
                                           ".dpkg-bak",
866
 
                                           ".dpkg-divert", NULL };
867
 
#ifdef __GNUC__
868
 
#pragma GCC diagnostic push
869
 
#pragma GCC diagnostic ignored "-Wcast-qual"
870
 
#endif
871
 
      for(const char **pre = (const char **)bad_prefixes;
872
 
          *pre != NULL; pre++){
873
 
#ifdef __GNUC__
874
 
#pragma GCC diagnostic pop
875
 
#endif
876
 
        size_t pre_len = strlen(*pre);
877
 
        if((d_name_len >= pre_len)
878
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
879
 
          if(debug){
880
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
881
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
882
 
          }
883
 
          bad_name = true;
884
 
          break;
885
 
        }
886
 
      }
887
 
      if(bad_name){
888
 
        continue;
889
 
      }
890
 
#ifdef __GNUC__
891
 
#pragma GCC diagnostic push
892
 
#pragma GCC diagnostic ignored "-Wcast-qual"
893
 
#endif
894
 
      for(const char **suf = (const char **)bad_suffixes;
895
 
          *suf != NULL; suf++){
896
 
#ifdef __GNUC__
897
 
#pragma GCC diagnostic pop
898
 
#endif
899
 
        size_t suf_len = strlen(*suf);
900
 
        if((d_name_len >= suf_len)
901
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
902
 
                == 0)){
903
 
          if(debug){
904
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
905
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
906
 
          }
907
 
          bad_name = true;
908
 
          break;
909
 
        }
910
 
      }
911
 
      
912
 
      if(bad_name){
913
 
        continue;
914
 
      }
915
 
    }
916
 
    
917
 
    char *filename;
918
 
    if(plugindir == NULL){
919
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
920
 
                                             dirst->d_name));
921
 
    } else {
922
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
923
 
                                             plugindir,
924
 
                                             dirst->d_name));
925
 
    }
926
 
    if(ret < 0){
927
 
      error(0, errno, "asprintf");
 
885
  for(int i = 0; i < numplugins; i++){
 
886
    
 
887
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
 
888
    if(plugin_fd == -1){
 
889
      error(0, errno, "Could not open plugin");
 
890
      free(direntries[i]);
928
891
      continue;
929
892
    }
930
 
    
931
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
 
893
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
932
894
    if(ret == -1){
933
895
      error(0, errno, "stat");
934
 
      free(filename);
 
896
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
897
      free(direntries[i]);
935
898
      continue;
936
899
    }
937
900
    
938
901
    /* Ignore non-executable files */
939
902
    if(not S_ISREG(st.st_mode)
940
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
903
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
904
                                        X_OK, 0)) != 0)){
941
905
      if(debug){
942
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
943
 
                " with bad type or mode\n", filename);
 
906
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
 
907
                " with bad type or mode\n",
 
908
                plugindir != NULL ? plugindir : PDIR,
 
909
                direntries[i]->d_name);
944
910
      }
945
 
      free(filename);
 
911
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
912
      free(direntries[i]);
946
913
      continue;
947
914
    }
948
915
    
949
 
    plugin *p = getplugin(dirst->d_name);
 
916
    plugin *p = getplugin(direntries[i]->d_name);
950
917
    if(p == NULL){
951
918
      error(0, errno, "getplugin");
952
 
      free(filename);
 
919
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
920
      free(direntries[i]);
953
921
      continue;
954
922
    }
955
923
    if(p->disabled){
956
924
      if(debug){
957
925
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
958
 
                dirst->d_name);
 
926
                direntries[i]->d_name);
959
927
      }
960
 
      free(filename);
 
928
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
929
      free(direntries[i]);
961
930
      continue;
962
931
    }
963
932
    {
977
946
        }
978
947
      }
979
948
    }
980
 
    /* If this plugin has any environment variables, we will call
981
 
       using execve and need to duplicate the environment from this
982
 
       process, too. */
 
949
    /* If this plugin has any environment variables, we need to
 
950
       duplicate the environment from this process, too. */
983
951
    if(p->environ[0] != NULL){
984
952
      for(char **e = environ; *e != NULL; e++){
985
953
        if(not add_environment(p, *e, false)){
989
957
    }
990
958
    
991
959
    int pipefd[2];
 
960
#ifndef O_CLOEXEC
992
961
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
 
962
#else  /* O_CLOEXEC */
 
963
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
 
964
#endif  /* O_CLOEXEC */
993
965
    if(ret == -1){
994
966
      error(0, errno, "pipe");
995
967
      exitstatus = EX_OSERR;
996
 
      goto fallback;
997
 
    }
 
968
      free(direntries[i]);
 
969
      goto fallback;
 
970
    }
 
971
    if(pipefd[0] >= FD_SETSIZE){
 
972
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
 
973
              FD_SETSIZE);
 
974
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
975
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
976
      exitstatus = EX_OSERR;
 
977
      free(direntries[i]);
 
978
      goto fallback;
 
979
    }
 
980
#ifndef O_CLOEXEC
998
981
    /* Ask OS to automatic close the pipe on exec */
999
982
    ret = set_cloexec_flag(pipefd[0]);
1000
983
    if(ret < 0){
1001
984
      error(0, errno, "set_cloexec_flag");
 
985
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
986
      TEMP_FAILURE_RETRY(close(pipefd[1]));
1002
987
      exitstatus = EX_OSERR;
 
988
      free(direntries[i]);
1003
989
      goto fallback;
1004
990
    }
1005
991
    ret = set_cloexec_flag(pipefd[1]);
1006
992
    if(ret < 0){
1007
993
      error(0, errno, "set_cloexec_flag");
 
994
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
995
      TEMP_FAILURE_RETRY(close(pipefd[1]));
1008
996
      exitstatus = EX_OSERR;
 
997
      free(direntries[i]);
1009
998
      goto fallback;
1010
999
    }
 
1000
#endif  /* not O_CLOEXEC */
1011
1001
    /* Block SIGCHLD until process is safely in process list */
1012
1002
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1013
1003
                                              &sigchld_action.sa_mask,
1015
1005
    if(ret < 0){
1016
1006
      error(0, errno, "sigprocmask");
1017
1007
      exitstatus = EX_OSERR;
 
1008
      free(direntries[i]);
1018
1009
      goto fallback;
1019
1010
    }
1020
1011
    /* Starting a new process to be watched */
1024
1015
    } while(pid == -1 and errno == EINTR);
1025
1016
    if(pid == -1){
1026
1017
      error(0, errno, "fork");
 
1018
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
 
1019
                                     &sigchld_action.sa_mask, NULL));
 
1020
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
1021
      TEMP_FAILURE_RETRY(close(pipefd[1]));
1027
1022
      exitstatus = EX_OSERR;
 
1023
      free(direntries[i]);
1028
1024
      goto fallback;
1029
1025
    }
1030
1026
    if(pid == 0){
1046
1042
        _exit(EX_OSERR);
1047
1043
      }
1048
1044
      
1049
 
      if(dirfd(dir) < 0){
1050
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
1051
 
           above and must now close it manually here. */
1052
 
        closedir(dir);
1053
 
      }
1054
 
      if(p->environ[0] == NULL){
1055
 
        if(execv(filename, p->argv) < 0){
1056
 
          error(0, errno, "execv for %s", filename);
1057
 
          _exit(EX_OSERR);
1058
 
        }
1059
 
      } else {
1060
 
        if(execve(filename, p->argv, p->environ) < 0){
1061
 
          error(0, errno, "execve for %s", filename);
1062
 
          _exit(EX_OSERR);
1063
 
        }
 
1045
      if(fexecve(plugin_fd, p->argv,
 
1046
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
 
1047
        error(0, errno, "fexecve for %s/%s",
 
1048
              plugindir != NULL ? plugindir : PDIR,
 
1049
              direntries[i]->d_name);
 
1050
        _exit(EX_OSERR);
1064
1051
      }
1065
1052
      /* no return */
1066
1053
    }
1067
1054
    /* Parent process */
1068
1055
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1069
1056
                                             pipe */
1070
 
    free(filename);
1071
 
    plugin *new_plugin = getplugin(dirst->d_name);
 
1057
    TEMP_FAILURE_RETRY(close(plugin_fd));
 
1058
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1072
1059
    if(new_plugin == NULL){
1073
1060
      error(0, errno, "getplugin");
1074
1061
      ret = (int)(TEMP_FAILURE_RETRY
1078
1065
        error(0, errno, "sigprocmask");
1079
1066
      }
1080
1067
      exitstatus = EX_OSERR;
 
1068
      free(direntries[i]);
1081
1069
      goto fallback;
1082
1070
    }
 
1071
    free(direntries[i]);
1083
1072
    
1084
1073
    new_plugin->pid = pid;
1085
1074
    new_plugin->fd = pipefd[0];
1115
1104
    }
1116
1105
  }
1117
1106
  
1118
 
  TEMP_FAILURE_RETRY(closedir(dir));
1119
 
  dir = NULL;
 
1107
  free(direntries);
 
1108
  direntries = NULL;
 
1109
  TEMP_FAILURE_RETRY(close(dir_fd));
 
1110
  dir_fd = -1;
1120
1111
  free_plugin(getplugin(NULL));
1121
1112
  
1122
1113
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1316
1307
    free(custom_argv);
1317
1308
  }
1318
1309
  
1319
 
  if(dir != NULL){
1320
 
    closedir(dir);
 
1310
  free(direntries);
 
1311
  
 
1312
  if(dir_fd != -1){
 
1313
    TEMP_FAILURE_RETRY(close(dir_fd));
1321
1314
  }
1322
1315
  
1323
1316
  /* Kill the processes */