/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: 2011-12-31 20:07:11 UTC
  • mfrom: (535.1.9 wireless-network-hook)
  • Revision ID: teddy@recompile.se-20111231200711-6dli3r8drftem57r
Merge new wireless network hook.  Fix bridge network hook to use
hardware addresses instead of interface names.  Implement and document
new "CONNECT" environment variable for network hooks.

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-2011 Teddy Hogeborn
 
6
 * Copyright © 2008-2011 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, pipe2() */
 
26
                                   asprintf(), O_CLOEXEC */
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, fclose() */
33
 
#include <sys/types.h>          /* fstat(), struct stat, waitpid(),
34
 
                                   WIFEXITED(), WEXITSTATUS(), wait(),
35
 
                                   pid_t, uid_t, gid_t, getuid(),
36
 
                                   getgid() */
 
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() */
37
38
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
38
39
                                   FD_SET(), FD_ISSET(), FD_CLR */
39
40
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
40
41
                                   WEXITSTATUS(), WTERMSIG(),
41
42
                                   WCOREDUMP() */
42
 
#include <sys/stat.h>           /* struct stat, fstat(), S_ISREG() */
 
43
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
43
44
#include <iso646.h>             /* and, or, not */
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
 
                                */
 
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() */
52
54
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
 
                                   FD_CLOEXEC, openat(), scandirat(),
54
 
                                   pipe2() */
55
 
#include <string.h>             /* strsep, strlen(), strsignal(),
56
 
                                   strcmp(), strncmp() */
 
55
                                   FD_CLOEXEC */
 
56
#include <string.h>             /* strsep, strlen(), asprintf(),
 
57
                                   strsignal(), strcmp(), strncmp() */
57
58
#include <errno.h>              /* errno */
58
59
#include <argp.h>               /* struct argp_option, struct
59
60
                                   argp_state, struct argp,
71
72
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
72
73
#include <errno.h>              /* errno */
73
74
#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))
109
108
static plugin *getplugin(char *name){
110
109
  /* Check for existing plugin with that name */
111
110
  for(plugin *p = plugin_list; p != NULL; p = p->next){
172
171
}
173
172
 
174
173
/* Helper function for add_argument and add_environment */
175
 
__attribute__((nonnull, warn_unused_result))
 
174
__attribute__((nonnull))
176
175
static bool add_to_char_array(const char *new, char ***array,
177
176
                              int *len){
178
177
  /* Resize the pointed-to array to hold one more pointer */
179
 
  char **new_array = NULL;
180
178
  do {
181
 
    new_array = realloc(*array, sizeof(char *)
182
 
                        * (size_t) ((*len) + 2));
183
 
  } while(new_array == NULL and errno == EINTR);
 
179
    *array = realloc(*array, sizeof(char *)
 
180
                     * (size_t) ((*len) + 2));
 
181
  } while(*array == NULL and errno == EINTR);
184
182
  /* Malloc check */
185
 
  if(new_array == NULL){
 
183
  if(*array == NULL){
186
184
    return false;
187
185
  }
188
 
  *array = new_array;
189
186
  /* Make a copy of the new string */
190
187
  char *copy;
191
188
  do {
203
200
}
204
201
 
205
202
/* Add to a plugin's argument vector */
206
 
__attribute__((nonnull(2), warn_unused_result))
 
203
__attribute__((nonnull(2)))
207
204
static bool add_argument(plugin *p, const char *arg){
208
205
  if(p == NULL){
209
206
    return false;
212
209
}
213
210
 
214
211
/* Add to a plugin's environment */
215
 
__attribute__((nonnull(2), warn_unused_result))
 
212
__attribute__((nonnull(2)))
216
213
static bool add_environment(plugin *p, const char *def, bool replace){
217
214
  if(p == NULL){
218
215
    return false;
220
217
  /* namelen = length of name of environment variable */
221
218
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
222
219
  /* Search for this environment variable */
223
 
  for(char **envdef = p->environ; *envdef != NULL; envdef++){
224
 
    if(strncmp(*envdef, def, namelen + 1) == 0){
 
220
  for(char **e = p->environ; *e != NULL; e++){
 
221
    if(strncmp(*e, def, namelen + 1) == 0){
225
222
      /* It already exists */
226
223
      if(replace){
227
 
        char *new_envdef;
 
224
        char *new;
228
225
        do {
229
 
          new_envdef = realloc(*envdef, strlen(def) + 1);
230
 
        } while(new_envdef == NULL and errno == EINTR);
231
 
        if(new_envdef == NULL){
 
226
          new = realloc(*e, strlen(def) + 1);
 
227
        } while(new == NULL and errno == EINTR);
 
228
        if(new == NULL){
232
229
          return false;
233
230
        }
234
 
        *envdef = new_envdef;
235
 
        strcpy(*envdef, def);
 
231
        *e = new;
 
232
        strcpy(*e, def);
236
233
      }
237
234
      return true;
238
235
    }
240
237
  return add_to_char_array(def, &(p->environ), &(p->envc));
241
238
}
242
239
 
243
 
#ifndef O_CLOEXEC
244
240
/*
245
241
 * Based on the example in the GNU LibC manual chapter 13.13 "File
246
242
 * Descriptor Flags".
247
243
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
248
244
 */
249
 
__attribute__((warn_unused_result))
250
245
static int set_cloexec_flag(int fd){
251
246
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
252
247
  /* If reading the flags failed, return error indication now. */
257
252
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
258
253
                                       ret | FD_CLOEXEC));
259
254
}
260
 
#endif  /* not O_CLOEXEC */
261
255
 
262
256
 
263
257
/* Mark processes as completed when they exit, and save their exit
295
289
}
296
290
 
297
291
/* Prints out a password to stdout */
298
 
__attribute__((nonnull, warn_unused_result))
 
292
__attribute__((nonnull))
299
293
static bool print_out_password(const char *buffer, size_t length){
300
294
  ssize_t ret;
301
295
  for(size_t written = 0; written < length; written += (size_t)ret){
349
343
  char *plugindir = NULL;
350
344
  char *argfile = NULL;
351
345
  FILE *conffp;
352
 
  struct dirent **direntries = NULL;
 
346
  size_t d_name_len;
 
347
  DIR *dir = NULL;
 
348
  struct dirent *dirst;
353
349
  struct stat st;
354
350
  fd_set rfds_all;
355
351
  int ret, maxfd = 0;
363
359
                                      .sa_flags = SA_NOCLDSTOP };
364
360
  char **custom_argv = NULL;
365
361
  int custom_argc = 0;
366
 
  int dir_fd = -1;
367
362
  
368
363
  /* Establish a signal handler */
369
364
  sigemptyset(&sigchld_action.sa_mask);
440
435
            break;
441
436
          }
442
437
        }
443
 
        errno = 0;
444
438
      }
445
439
      break;
446
440
    case 'G':                   /* --global-env */
447
 
      if(add_environment(getplugin(NULL), arg, true)){
448
 
        errno = 0;
449
 
      }
 
441
      add_environment(getplugin(NULL), arg, true);
450
442
      break;
451
443
    case 'o':                   /* --options-for */
452
444
      {
469
461
            break;
470
462
          }
471
463
        }
472
 
        errno = 0;
473
464
      }
474
465
      break;
475
466
    case 'E':                   /* --env-for */
487
478
          errno = EINVAL;
488
479
          break;
489
480
        }
490
 
        if(add_environment(getplugin(arg), envdef, true)){
491
 
          errno = 0;
492
 
        }
 
481
        add_environment(getplugin(arg), envdef, true);
493
482
      }
494
483
      break;
495
484
    case 'd':                   /* --disable */
497
486
        plugin *p = getplugin(arg);
498
487
        if(p != NULL){
499
488
          p->disabled = true;
500
 
          errno = 0;
501
489
        }
502
490
      }
503
491
      break;
506
494
        plugin *p = getplugin(arg);
507
495
        if(p != NULL){
508
496
          p->disabled = false;
509
 
          errno = 0;
510
497
        }
511
498
      }
512
499
      break;
513
500
    case 128:                   /* --plugin-dir */
514
501
      free(plugindir);
515
502
      plugindir = strdup(arg);
516
 
      if(plugindir != NULL){
517
 
        errno = 0;
518
 
      }
519
503
      break;
520
504
    case 129:                   /* --config-file */
521
505
      /* This is already done by parse_opt_config_file() */
529
513
        break;
530
514
      }
531
515
      uid = (uid_t)tmp_id;
532
 
      errno = 0;
533
516
      break;
534
517
    case 131:                   /* --groupid */
535
518
      tmp_id = strtoimax(arg, &tmp, 10);
540
523
        break;
541
524
      }
542
525
      gid = (gid_t)tmp_id;
543
 
      errno = 0;
544
526
      break;
545
527
    case 132:                   /* --debug */
546
528
      debug = true;
594
576
    case 129:                   /* --config-file */
595
577
      free(argfile);
596
578
      argfile = strdup(arg);
597
 
      if(argfile != NULL){
598
 
        errno = 0;
599
 
      }
600
579
      break;
601
580
    case 130:                   /* --userid */
602
581
    case 131:                   /* --groupid */
685
664
        }
686
665
        
687
666
        custom_argc += 1;
688
 
        {
689
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
690
 
                                    * ((unsigned int)
691
 
                                       custom_argc + 1));
692
 
          if(new_argv == NULL){
693
 
            error(0, errno, "realloc");
694
 
            exitstatus = EX_OSERR;
695
 
            free(new_arg);
696
 
            free(org_line);
697
 
            goto fallback;
698
 
          } else {
699
 
            custom_argv = new_argv;
700
 
          }
 
667
        custom_argv = realloc(custom_argv, sizeof(char *)
 
668
                              * ((unsigned int) custom_argc + 1));
 
669
        if(custom_argv == NULL){
 
670
          error(0, errno, "realloc");
 
671
          exitstatus = EX_OSERR;
 
672
          free(org_line);
 
673
          goto fallback;
701
674
        }
702
675
        custom_argv[custom_argc-1] = new_arg;
703
676
        custom_argv[custom_argc] = NULL;
780
753
       <http://bugs.debian.org/633582> */
781
754
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
782
755
    if(plugindir_fd == -1){
783
 
      if(errno != ENOENT){
784
 
        error(0, errno, "open(\"" PDIR "\")");
785
 
      }
 
756
      error(0, errno, "open");
786
757
    } else {
787
758
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
788
759
      if(ret == -1){
800
771
  }
801
772
  
802
773
  /* Lower permissions */
803
 
  ret = setgid(gid);
 
774
  setgid(gid);
804
775
  if(ret == -1){
805
776
    error(0, errno, "setgid");
806
777
  }
811
782
  
812
783
  /* Open plugin directory with close_on_exec flag */
813
784
  {
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
 
                  );
 
785
    int dir_fd = -1;
 
786
    if(plugindir == NULL){
 
787
      dir_fd = open(PDIR, O_RDONLY |
 
788
#ifdef O_CLOEXEC
 
789
                    O_CLOEXEC
 
790
#else  /* not O_CLOEXEC */
 
791
                    0
 
792
#endif  /* not O_CLOEXEC */
 
793
                    );
 
794
    } else {
 
795
      dir_fd = open(plugindir, O_RDONLY |
 
796
#ifdef O_CLOEXEC
 
797
                    O_CLOEXEC
 
798
#else  /* not O_CLOEXEC */
 
799
                    0
 
800
#endif  /* not O_CLOEXEC */
 
801
                    );
 
802
    }
821
803
    if(dir_fd == -1){
822
804
      error(0, errno, "Could not open plugin dir");
823
805
      exitstatus = EX_UNAVAILABLE;
829
811
    ret = set_cloexec_flag(dir_fd);
830
812
    if(ret < 0){
831
813
      error(0, errno, "set_cloexec_flag");
 
814
      TEMP_FAILURE_RETRY(close(dir_fd));
832
815
      exitstatus = EX_OSERR;
833
816
      goto fallback;
834
817
    }
835
818
#endif  /* O_CLOEXEC */
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
 
      }
 
819
    
 
820
    dir = fdopendir(dir_fd);
 
821
    if(dir == NULL){
 
822
      error(0, errno, "Could not open plugin dir");
 
823
      TEMP_FAILURE_RETRY(close(dir_fd));
 
824
      exitstatus = EX_OSERR;
 
825
      goto fallback;
859
826
    }
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;
880
827
  }
881
828
  
882
829
  FD_ZERO(&rfds_all);
883
830
  
884
831
  /* Read and execute any executable in the plugin directory*/
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");
 
832
  while(true){
 
833
    do {
 
834
      dirst = readdir(dir);
 
835
    } while(dirst == NULL and errno == EINTR);
 
836
    
 
837
    /* All directory entries have been processed */
 
838
    if(dirst == NULL){
 
839
      if(errno == EBADF){
 
840
        error(0, errno, "readdir");
 
841
        exitstatus = EX_IOERR;
 
842
        goto fallback;
 
843
      }
 
844
      break;
 
845
    }
 
846
    
 
847
    d_name_len = strlen(dirst->d_name);
 
848
    
 
849
    /* Ignore dotfiles, backup files and other junk */
 
850
    {
 
851
      bool bad_name = false;
 
852
      
 
853
      const char const *bad_prefixes[] = { ".", "#", NULL };
 
854
      
 
855
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
 
856
                                           ".dpkg-old",
 
857
                                           ".dpkg-bak",
 
858
                                           ".dpkg-divert", NULL };
 
859
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
 
860
        size_t pre_len = strlen(*pre);
 
861
        if((d_name_len >= pre_len)
 
862
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
 
863
          if(debug){
 
864
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
865
                    " with bad prefix %s\n", dirst->d_name, *pre);
 
866
          }
 
867
          bad_name = true;
 
868
          break;
 
869
        }
 
870
      }
 
871
      if(bad_name){
 
872
        continue;
 
873
      }
 
874
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
 
875
        size_t suf_len = strlen(*suf);
 
876
        if((d_name_len >= suf_len)
 
877
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
 
878
                == 0)){
 
879
          if(debug){
 
880
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
881
                    " with bad suffix %s\n", dirst->d_name, *suf);
 
882
          }
 
883
          bad_name = true;
 
884
          break;
 
885
        }
 
886
      }
 
887
      
 
888
      if(bad_name){
 
889
        continue;
 
890
      }
 
891
    }
 
892
    
 
893
    char *filename;
 
894
    if(plugindir == NULL){
 
895
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
 
896
                                             dirst->d_name));
 
897
    } else {
 
898
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
 
899
                                             plugindir,
 
900
                                             dirst->d_name));
 
901
    }
 
902
    if(ret < 0){
 
903
      error(0, errno, "asprintf");
890
904
      continue;
891
905
    }
892
 
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
 
906
    
 
907
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
893
908
    if(ret == -1){
894
909
      error(0, errno, "stat");
895
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
910
      free(filename);
896
911
      continue;
897
912
    }
898
913
    
899
914
    /* Ignore non-executable files */
900
915
    if(not S_ISREG(st.st_mode)
901
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
902
 
                                        X_OK, 0)) != 0)){
 
916
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
903
917
      if(debug){
904
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
905
 
                " with bad type or mode\n",
906
 
                plugindir != NULL ? plugindir : PDIR,
907
 
                direntries[i]->d_name);
 
918
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
919
                " with bad type or mode\n", filename);
908
920
      }
909
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
921
      free(filename);
910
922
      continue;
911
923
    }
912
924
    
913
 
    plugin *p = getplugin(direntries[i]->d_name);
 
925
    plugin *p = getplugin(dirst->d_name);
914
926
    if(p == NULL){
915
927
      error(0, errno, "getplugin");
916
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
928
      free(filename);
917
929
      continue;
918
930
    }
919
931
    if(p->disabled){
920
932
      if(debug){
921
933
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
922
 
                direntries[i]->d_name);
 
934
                dirst->d_name);
923
935
      }
924
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
936
      free(filename);
925
937
      continue;
926
938
    }
927
939
    {
941
953
        }
942
954
      }
943
955
    }
944
 
    /* If this plugin has any environment variables, we need to
945
 
       duplicate the environment from this process, too. */
 
956
    /* If this plugin has any environment variables, we will call
 
957
       using execve and need to duplicate the environment from this
 
958
       process, too. */
946
959
    if(p->environ[0] != NULL){
947
960
      for(char **e = environ; *e != NULL; e++){
948
961
        if(not add_environment(p, *e, false)){
952
965
    }
953
966
    
954
967
    int pipefd[2];
955
 
#ifndef O_CLOEXEC
956
968
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
957
 
#else  /* O_CLOEXEC */
958
 
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
959
 
#endif  /* O_CLOEXEC */
960
969
    if(ret == -1){
961
970
      error(0, errno, "pipe");
962
971
      exitstatus = EX_OSERR;
963
972
      goto fallback;
964
973
    }
965
 
    if(pipefd[0] >= FD_SETSIZE){
966
 
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
967
 
              FD_SETSIZE);
968
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
969
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
970
 
      exitstatus = EX_OSERR;
971
 
      goto fallback;
972
 
    }
973
 
#ifndef O_CLOEXEC
974
974
    /* Ask OS to automatic close the pipe on exec */
975
975
    ret = set_cloexec_flag(pipefd[0]);
976
976
    if(ret < 0){
977
977
      error(0, errno, "set_cloexec_flag");
978
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
979
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
980
978
      exitstatus = EX_OSERR;
981
979
      goto fallback;
982
980
    }
983
981
    ret = set_cloexec_flag(pipefd[1]);
984
982
    if(ret < 0){
985
983
      error(0, errno, "set_cloexec_flag");
986
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
987
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
988
984
      exitstatus = EX_OSERR;
989
985
      goto fallback;
990
986
    }
991
 
#endif  /* not O_CLOEXEC */
992
987
    /* Block SIGCHLD until process is safely in process list */
993
988
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
994
989
                                              &sigchld_action.sa_mask,
1005
1000
    } while(pid == -1 and errno == EINTR);
1006
1001
    if(pid == -1){
1007
1002
      error(0, errno, "fork");
1008
 
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1009
 
                                     &sigchld_action.sa_mask, NULL));
1010
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1011
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
1012
1003
      exitstatus = EX_OSERR;
1013
1004
      goto fallback;
1014
1005
    }
1031
1022
        _exit(EX_OSERR);
1032
1023
      }
1033
1024
      
1034
 
      if(fexecve(plugin_fd, p->argv,
1035
 
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1036
 
        error(0, errno, "fexecve for %s/%s",
1037
 
              plugindir != NULL ? plugindir : PDIR,
1038
 
              direntries[i]->d_name);
1039
 
        _exit(EX_OSERR);
 
1025
      if(dirfd(dir) < 0){
 
1026
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
 
1027
           above and must now close it manually here. */
 
1028
        closedir(dir);
 
1029
      }
 
1030
      if(p->environ[0] == NULL){
 
1031
        if(execv(filename, p->argv) < 0){
 
1032
          error(0, errno, "execv for %s", filename);
 
1033
          _exit(EX_OSERR);
 
1034
        }
 
1035
      } else {
 
1036
        if(execve(filename, p->argv, p->environ) < 0){
 
1037
          error(0, errno, "execve for %s", filename);
 
1038
          _exit(EX_OSERR);
 
1039
        }
1040
1040
      }
1041
1041
      /* no return */
1042
1042
    }
1043
1043
    /* Parent process */
1044
1044
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1045
1045
                                             pipe */
1046
 
    TEMP_FAILURE_RETRY(close(plugin_fd));
1047
 
    plugin *new_plugin = getplugin(direntries[i]->d_name);
 
1046
    free(filename);
 
1047
    plugin *new_plugin = getplugin(dirst->d_name);
1048
1048
    if(new_plugin == NULL){
1049
1049
      error(0, errno, "getplugin");
1050
1050
      ret = (int)(TEMP_FAILURE_RETRY
1071
1071
      goto fallback;
1072
1072
    }
1073
1073
    
1074
 
#if defined (__GNUC__) and defined (__GLIBC__)
1075
 
#if not __GLIBC_PREREQ(2, 16)
1076
 
#pragma GCC diagnostic push
1077
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1078
 
#endif
1079
 
#endif
1080
1074
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1081
 
                                          -Wconversion in GNU libc
1082
 
                                          before 2.16 */
1083
 
#if defined (__GNUC__) and defined (__GLIBC__)
1084
 
#if not __GLIBC_PREREQ(2, 16)
1085
 
#pragma GCC diagnostic pop
1086
 
#endif
1087
 
#endif
 
1075
                                          -Wconversion */
1088
1076
    
1089
1077
    if(maxfd < new_plugin->fd){
1090
1078
      maxfd = new_plugin->fd;
1091
1079
    }
1092
1080
  }
1093
1081
  
1094
 
  free(direntries);
1095
 
  direntries = NULL;
1096
 
  TEMP_FAILURE_RETRY(close(dir_fd));
1097
 
  dir_fd = -1;
 
1082
  TEMP_FAILURE_RETRY(closedir(dir));
 
1083
  dir = NULL;
1098
1084
  free_plugin(getplugin(NULL));
1099
1085
  
1100
1086
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1146
1132
          }
1147
1133
          
1148
1134
          /* Remove the plugin */
1149
 
#if defined (__GNUC__) and defined (__GLIBC__)
1150
 
#if not __GLIBC_PREREQ(2, 16)
1151
 
#pragma GCC diagnostic push
1152
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1153
 
#endif
1154
 
#endif
1155
1135
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1156
 
                                          -Wconversion in GNU libc
1157
 
                                          before 2.16 */
1158
 
#if defined (__GNUC__) and defined (__GLIBC__)
1159
 
#if not __GLIBC_PREREQ(2, 16)
1160
 
#pragma GCC diagnostic pop
1161
 
#endif
1162
 
#endif
 
1136
                                          -Wconversion */
1163
1137
          
1164
1138
          /* Block signal while modifying process_list */
1165
1139
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1205
1179
      }
1206
1180
      
1207
1181
      /* This process has not completed.  Does it have any output? */
1208
 
#if defined (__GNUC__) and defined (__GLIBC__)
1209
 
#if not __GLIBC_PREREQ(2, 16)
1210
 
#pragma GCC diagnostic push
1211
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1212
 
#endif
1213
 
#endif
1214
1182
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1215
1183
                                                         warning from
1216
 
                                                         -Wconversion
1217
 
                                                         in GNU libc
1218
 
                                                         before
1219
 
                                                         2.16 */
1220
 
#if defined (__GNUC__) and defined (__GLIBC__)
1221
 
#if not __GLIBC_PREREQ(2, 16)
1222
 
#pragma GCC diagnostic pop
1223
 
#endif
1224
 
#endif
 
1184
                                                         -Wconversion */
1225
1185
        /* This process had nothing to say at this time */
1226
1186
        proc = proc->next;
1227
1187
        continue;
1228
1188
      }
1229
1189
      /* Before reading, make the process' data buffer large enough */
1230
1190
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1231
 
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
1232
 
                                   + (size_t) BUFFER_SIZE);
1233
 
        if(new_buffer == NULL){
 
1191
        proc->buffer = realloc(proc->buffer, proc->buffer_size
 
1192
                               + (size_t) BUFFER_SIZE);
 
1193
        if(proc->buffer == NULL){
1234
1194
          error(0, errno, "malloc");
1235
1195
          exitstatus = EX_OSERR;
1236
1196
          goto fallback;
1237
1197
        }
1238
 
        proc->buffer = new_buffer;
1239
1198
        proc->buffer_size += BUFFER_SIZE;
1240
1199
      }
1241
1200
      /* Read from the process */
1294
1253
    free(custom_argv);
1295
1254
  }
1296
1255
  
1297
 
  free(direntries);
1298
 
  
1299
 
  if(dir_fd != -1){
1300
 
    TEMP_FAILURE_RETRY(close(dir_fd));
 
1256
  if(dir != NULL){
 
1257
    closedir(dir);
1301
1258
  }
1302
1259
  
1303
1260
  /* Kill the processes */