/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2015-01-25 00:02:51 UTC
  • mto: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • Revision ID: teddy@recompile.se-20150125000251-j2bw50gfq9smqyxe
mandos.xml (SEE ALSO): Update links.

Update link to GnuPG home page, change reference from TLS 1.1 to TLS
1.2, and change to latest RFC for using OpenPGP keys with TLS (and use
its correct title).

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 */
 
179
  char **new_array = NULL;
178
180
  do {
179
 
    *array = realloc(*array, sizeof(char *)
180
 
                     * (size_t) ((*len) + 2));
181
 
  } while(*array == NULL and errno == EINTR);
 
181
    new_array = realloc(*array, sizeof(char *)
 
182
                        * (size_t) ((*len) + 2));
 
183
  } while(new_array == NULL and errno == EINTR);
182
184
  /* Malloc check */
183
 
  if(*array == NULL){
 
185
  if(new_array == NULL){
184
186
    return false;
185
187
  }
 
188
  *array = new_array;
186
189
  /* Make a copy of the new string */
187
190
  char *copy;
188
191
  do {
200
203
}
201
204
 
202
205
/* Add to a plugin's argument vector */
203
 
__attribute__((nonnull(2)))
 
206
__attribute__((nonnull(2), warn_unused_result))
204
207
static bool add_argument(plugin *p, const char *arg){
205
208
  if(p == NULL){
206
209
    return false;
209
212
}
210
213
 
211
214
/* Add to a plugin's environment */
212
 
__attribute__((nonnull(2)))
 
215
__attribute__((nonnull(2), warn_unused_result))
213
216
static bool add_environment(plugin *p, const char *def, bool replace){
214
217
  if(p == NULL){
215
218
    return false;
217
220
  /* namelen = length of name of environment variable */
218
221
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
219
222
  /* Search for this environment variable */
220
 
  for(char **e = p->environ; *e != NULL; e++){
221
 
    if(strncmp(*e, def, namelen + 1) == 0){
 
223
  for(char **envdef = p->environ; *envdef != NULL; envdef++){
 
224
    if(strncmp(*envdef, def, namelen + 1) == 0){
222
225
      /* It already exists */
223
226
      if(replace){
224
 
        char *new;
 
227
        char *new_envdef;
225
228
        do {
226
 
          new = realloc(*e, strlen(def) + 1);
227
 
        } while(new == NULL and errno == EINTR);
228
 
        if(new == NULL){
 
229
          new_envdef = realloc(*envdef, strlen(def) + 1);
 
230
        } while(new_envdef == NULL and errno == EINTR);
 
231
        if(new_envdef == NULL){
229
232
          return false;
230
233
        }
231
 
        *e = new;
232
 
        strcpy(*e, def);
 
234
        *envdef = new_envdef;
 
235
        strcpy(*envdef, def);
233
236
      }
234
237
      return true;
235
238
    }
237
240
  return add_to_char_array(def, &(p->environ), &(p->envc));
238
241
}
239
242
 
 
243
#ifndef O_CLOEXEC
240
244
/*
241
245
 * Based on the example in the GNU LibC manual chapter 13.13 "File
242
246
 * Descriptor Flags".
243
247
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
244
248
 */
 
249
__attribute__((warn_unused_result))
245
250
static int set_cloexec_flag(int fd){
246
251
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
247
252
  /* If reading the flags failed, return error indication now. */
252
257
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
253
258
                                       ret | FD_CLOEXEC));
254
259
}
 
260
#endif  /* not O_CLOEXEC */
255
261
 
256
262
 
257
263
/* Mark processes as completed when they exit, and save their exit
289
295
}
290
296
 
291
297
/* Prints out a password to stdout */
292
 
__attribute__((nonnull))
 
298
__attribute__((nonnull, warn_unused_result))
293
299
static bool print_out_password(const char *buffer, size_t length){
294
300
  ssize_t ret;
295
301
  for(size_t written = 0; written < length; written += (size_t)ret){
343
349
  char *plugindir = NULL;
344
350
  char *argfile = NULL;
345
351
  FILE *conffp;
346
 
  size_t d_name_len;
347
 
  DIR *dir = NULL;
348
 
  struct dirent *dirst;
 
352
  struct dirent **direntries = NULL;
349
353
  struct stat st;
350
354
  fd_set rfds_all;
351
355
  int ret, maxfd = 0;
359
363
                                      .sa_flags = SA_NOCLDSTOP };
360
364
  char **custom_argv = NULL;
361
365
  int custom_argc = 0;
 
366
  int dir_fd = -1;
362
367
  
363
368
  /* Establish a signal handler */
364
369
  sigemptyset(&sigchld_action.sa_mask);
435
440
            break;
436
441
          }
437
442
        }
 
443
        errno = 0;
438
444
      }
439
445
      break;
440
446
    case 'G':                   /* --global-env */
441
 
      add_environment(getplugin(NULL), arg, true);
 
447
      if(add_environment(getplugin(NULL), arg, true)){
 
448
        errno = 0;
 
449
      }
442
450
      break;
443
451
    case 'o':                   /* --options-for */
444
452
      {
461
469
            break;
462
470
          }
463
471
        }
 
472
        errno = 0;
464
473
      }
465
474
      break;
466
475
    case 'E':                   /* --env-for */
478
487
          errno = EINVAL;
479
488
          break;
480
489
        }
481
 
        add_environment(getplugin(arg), envdef, true);
 
490
        if(add_environment(getplugin(arg), envdef, true)){
 
491
          errno = 0;
 
492
        }
482
493
      }
483
494
      break;
484
495
    case 'd':                   /* --disable */
486
497
        plugin *p = getplugin(arg);
487
498
        if(p != NULL){
488
499
          p->disabled = true;
 
500
          errno = 0;
489
501
        }
490
502
      }
491
503
      break;
494
506
        plugin *p = getplugin(arg);
495
507
        if(p != NULL){
496
508
          p->disabled = false;
 
509
          errno = 0;
497
510
        }
498
511
      }
499
512
      break;
500
513
    case 128:                   /* --plugin-dir */
501
514
      free(plugindir);
502
515
      plugindir = strdup(arg);
 
516
      if(plugindir != NULL){
 
517
        errno = 0;
 
518
      }
503
519
      break;
504
520
    case 129:                   /* --config-file */
505
521
      /* This is already done by parse_opt_config_file() */
513
529
        break;
514
530
      }
515
531
      uid = (uid_t)tmp_id;
 
532
      errno = 0;
516
533
      break;
517
534
    case 131:                   /* --groupid */
518
535
      tmp_id = strtoimax(arg, &tmp, 10);
523
540
        break;
524
541
      }
525
542
      gid = (gid_t)tmp_id;
 
543
      errno = 0;
526
544
      break;
527
545
    case 132:                   /* --debug */
528
546
      debug = true;
576
594
    case 129:                   /* --config-file */
577
595
      free(argfile);
578
596
      argfile = strdup(arg);
 
597
      if(argfile != NULL){
 
598
        errno = 0;
 
599
      }
579
600
      break;
580
601
    case 130:                   /* --userid */
581
602
    case 131:                   /* --groupid */
664
685
        }
665
686
        
666
687
        custom_argc += 1;
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;
 
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
          }
674
701
        }
675
702
        custom_argv[custom_argc-1] = new_arg;
676
703
        custom_argv[custom_argc] = NULL;
753
780
       <http://bugs.debian.org/633582> */
754
781
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
755
782
    if(plugindir_fd == -1){
756
 
      error(0, errno, "open");
 
783
      if(errno != ENOENT){
 
784
        error(0, errno, "open(\"" PDIR "\")");
 
785
      }
757
786
    } else {
758
787
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
759
788
      if(ret == -1){
782
811
  
783
812
  /* Open plugin directory with close_on_exec flag */
784
813
  {
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
 
    }
 
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
                  );
803
821
    if(dir_fd == -1){
804
822
      error(0, errno, "Could not open plugin dir");
805
823
      exitstatus = EX_UNAVAILABLE;
811
829
    ret = set_cloexec_flag(dir_fd);
812
830
    if(ret < 0){
813
831
      error(0, errno, "set_cloexec_flag");
814
 
      TEMP_FAILURE_RETRY(close(dir_fd));
815
832
      exitstatus = EX_OSERR;
816
833
      goto fallback;
817
834
    }
818
835
#endif  /* O_CLOEXEC */
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;
 
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
      }
826
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;
827
880
  }
828
881
  
829
882
  FD_ZERO(&rfds_all);
830
883
  
831
884
  /* Read and execute any executable in the plugin directory*/
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
 
#pragma GCC diagnostic push
860
 
#pragma GCC diagnostic ignored "-Wcast-qual"
861
 
      for(const char **pre = (const char **)bad_prefixes;
862
 
          *pre != NULL; pre++){
863
 
#pragma GCC diagnostic pop
864
 
        size_t pre_len = strlen(*pre);
865
 
        if((d_name_len >= pre_len)
866
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
867
 
          if(debug){
868
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
869
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
870
 
          }
871
 
          bad_name = true;
872
 
          break;
873
 
        }
874
 
      }
875
 
      if(bad_name){
876
 
        continue;
877
 
      }
878
 
#pragma GCC diagnostic push
879
 
#pragma GCC diagnostic ignored "-Wcast-qual"
880
 
      for(const char **suf = (const char **)bad_suffixes;
881
 
          *suf != NULL; suf++){
882
 
#pragma GCC diagnostic pop
883
 
        size_t suf_len = strlen(*suf);
884
 
        if((d_name_len >= suf_len)
885
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
886
 
                == 0)){
887
 
          if(debug){
888
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
889
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
890
 
          }
891
 
          bad_name = true;
892
 
          break;
893
 
        }
894
 
      }
895
 
      
896
 
      if(bad_name){
897
 
        continue;
898
 
      }
899
 
    }
900
 
    
901
 
    char *filename;
902
 
    if(plugindir == NULL){
903
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
904
 
                                             dirst->d_name));
905
 
    } else {
906
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
907
 
                                             plugindir,
908
 
                                             dirst->d_name));
909
 
    }
910
 
    if(ret < 0){
911
 
      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]);
912
891
      continue;
913
892
    }
914
 
    
915
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
 
893
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
916
894
    if(ret == -1){
917
895
      error(0, errno, "stat");
918
 
      free(filename);
 
896
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
897
      free(direntries[i]);
919
898
      continue;
920
899
    }
921
900
    
922
901
    /* Ignore non-executable files */
923
902
    if(not S_ISREG(st.st_mode)
924
 
       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)){
925
905
      if(debug){
926
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
927
 
                " 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);
928
910
      }
929
 
      free(filename);
 
911
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
912
      free(direntries[i]);
930
913
      continue;
931
914
    }
932
915
    
933
 
    plugin *p = getplugin(dirst->d_name);
 
916
    plugin *p = getplugin(direntries[i]->d_name);
934
917
    if(p == NULL){
935
918
      error(0, errno, "getplugin");
936
 
      free(filename);
 
919
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
920
      free(direntries[i]);
937
921
      continue;
938
922
    }
939
923
    if(p->disabled){
940
924
      if(debug){
941
925
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
942
 
                dirst->d_name);
 
926
                direntries[i]->d_name);
943
927
      }
944
 
      free(filename);
 
928
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
929
      free(direntries[i]);
945
930
      continue;
946
931
    }
947
932
    {
961
946
        }
962
947
      }
963
948
    }
964
 
    /* If this plugin has any environment variables, we will call
965
 
       using execve and need to duplicate the environment from this
966
 
       process, too. */
 
949
    /* If this plugin has any environment variables, we need to
 
950
       duplicate the environment from this process, too. */
967
951
    if(p->environ[0] != NULL){
968
952
      for(char **e = environ; *e != NULL; e++){
969
953
        if(not add_environment(p, *e, false)){
973
957
    }
974
958
    
975
959
    int pipefd[2];
 
960
#ifndef O_CLOEXEC
976
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 */
977
965
    if(ret == -1){
978
966
      error(0, errno, "pipe");
979
967
      exitstatus = EX_OSERR;
980
 
      goto fallback;
981
 
    }
 
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
982
981
    /* Ask OS to automatic close the pipe on exec */
983
982
    ret = set_cloexec_flag(pipefd[0]);
984
983
    if(ret < 0){
985
984
      error(0, errno, "set_cloexec_flag");
 
985
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
986
      TEMP_FAILURE_RETRY(close(pipefd[1]));
986
987
      exitstatus = EX_OSERR;
 
988
      free(direntries[i]);
987
989
      goto fallback;
988
990
    }
989
991
    ret = set_cloexec_flag(pipefd[1]);
990
992
    if(ret < 0){
991
993
      error(0, errno, "set_cloexec_flag");
 
994
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
995
      TEMP_FAILURE_RETRY(close(pipefd[1]));
992
996
      exitstatus = EX_OSERR;
 
997
      free(direntries[i]);
993
998
      goto fallback;
994
999
    }
 
1000
#endif  /* not O_CLOEXEC */
995
1001
    /* Block SIGCHLD until process is safely in process list */
996
1002
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
997
1003
                                              &sigchld_action.sa_mask,
999
1005
    if(ret < 0){
1000
1006
      error(0, errno, "sigprocmask");
1001
1007
      exitstatus = EX_OSERR;
 
1008
      free(direntries[i]);
1002
1009
      goto fallback;
1003
1010
    }
1004
1011
    /* Starting a new process to be watched */
1008
1015
    } while(pid == -1 and errno == EINTR);
1009
1016
    if(pid == -1){
1010
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]));
1011
1022
      exitstatus = EX_OSERR;
 
1023
      free(direntries[i]);
1012
1024
      goto fallback;
1013
1025
    }
1014
1026
    if(pid == 0){
1030
1042
        _exit(EX_OSERR);
1031
1043
      }
1032
1044
      
1033
 
      if(dirfd(dir) < 0){
1034
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
1035
 
           above and must now close it manually here. */
1036
 
        closedir(dir);
1037
 
      }
1038
 
      if(p->environ[0] == NULL){
1039
 
        if(execv(filename, p->argv) < 0){
1040
 
          error(0, errno, "execv for %s", filename);
1041
 
          _exit(EX_OSERR);
1042
 
        }
1043
 
      } else {
1044
 
        if(execve(filename, p->argv, p->environ) < 0){
1045
 
          error(0, errno, "execve for %s", filename);
1046
 
          _exit(EX_OSERR);
1047
 
        }
 
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);
1048
1051
      }
1049
1052
      /* no return */
1050
1053
    }
1051
1054
    /* Parent process */
1052
1055
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1053
1056
                                             pipe */
1054
 
    free(filename);
1055
 
    plugin *new_plugin = getplugin(dirst->d_name);
 
1057
    TEMP_FAILURE_RETRY(close(plugin_fd));
 
1058
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1056
1059
    if(new_plugin == NULL){
1057
1060
      error(0, errno, "getplugin");
1058
1061
      ret = (int)(TEMP_FAILURE_RETRY
1062
1065
        error(0, errno, "sigprocmask");
1063
1066
      }
1064
1067
      exitstatus = EX_OSERR;
 
1068
      free(direntries[i]);
1065
1069
      goto fallback;
1066
1070
    }
 
1071
    free(direntries[i]);
1067
1072
    
1068
1073
    new_plugin->pid = pid;
1069
1074
    new_plugin->fd = pipefd[0];
1099
1104
    }
1100
1105
  }
1101
1106
  
1102
 
  TEMP_FAILURE_RETRY(closedir(dir));
1103
 
  dir = NULL;
 
1107
  free(direntries);
 
1108
  direntries = NULL;
 
1109
  TEMP_FAILURE_RETRY(close(dir_fd));
 
1110
  dir_fd = -1;
1104
1111
  free_plugin(getplugin(NULL));
1105
1112
  
1106
1113
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1234
1241
      }
1235
1242
      /* Before reading, make the process' data buffer large enough */
1236
1243
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1237
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1238
 
                               + (size_t) BUFFER_SIZE);
1239
 
        if(proc->buffer == NULL){
 
1244
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
 
1245
                                   + (size_t) BUFFER_SIZE);
 
1246
        if(new_buffer == NULL){
1240
1247
          error(0, errno, "malloc");
1241
1248
          exitstatus = EX_OSERR;
1242
1249
          goto fallback;
1243
1250
        }
 
1251
        proc->buffer = new_buffer;
1244
1252
        proc->buffer_size += BUFFER_SIZE;
1245
1253
      }
1246
1254
      /* Read from the process */
1299
1307
    free(custom_argv);
1300
1308
  }
1301
1309
  
1302
 
  if(dir != NULL){
1303
 
    closedir(dir);
 
1310
  free(direntries);
 
1311
  
 
1312
  if(dir_fd != -1){
 
1313
    TEMP_FAILURE_RETRY(close(dir_fd));
1304
1314
  }
1305
1315
  
1306
1316
  /* Kill the processes */