/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-2016 Teddy Hogeborn
6
 
 * Copyright © 2008-2016 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
 
78
78
#define PDIR "/lib/mandos/plugins.d"
79
 
#define PHDIR "/lib/mandos/plugin-helpers"
80
79
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
81
80
 
82
81
const char *argp_program_version = "plugin-runner " VERSION;
106
105
 
107
106
/* Gets an existing plugin based on name,
108
107
   or if none is found, creates a new one */
109
 
__attribute__((warn_unused_result))
110
108
static plugin *getplugin(char *name){
111
109
  /* Check for existing plugin with that name */
112
110
  for(plugin *p = plugin_list; p != NULL; p = p->next){
173
171
}
174
172
 
175
173
/* Helper function for add_argument and add_environment */
176
 
__attribute__((nonnull, warn_unused_result))
 
174
__attribute__((nonnull))
177
175
static bool add_to_char_array(const char *new, char ***array,
178
176
                              int *len){
179
177
  /* Resize the pointed-to array to hold one more pointer */
180
 
  char **new_array = NULL;
181
178
  do {
182
 
    new_array = realloc(*array, sizeof(char *)
183
 
                        * (size_t) ((*len) + 2));
184
 
  } 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);
185
182
  /* Malloc check */
186
 
  if(new_array == NULL){
 
183
  if(*array == NULL){
187
184
    return false;
188
185
  }
189
 
  *array = new_array;
190
186
  /* Make a copy of the new string */
191
187
  char *copy;
192
188
  do {
204
200
}
205
201
 
206
202
/* Add to a plugin's argument vector */
207
 
__attribute__((nonnull(2), warn_unused_result))
 
203
__attribute__((nonnull(2)))
208
204
static bool add_argument(plugin *p, const char *arg){
209
205
  if(p == NULL){
210
206
    return false;
213
209
}
214
210
 
215
211
/* Add to a plugin's environment */
216
 
__attribute__((nonnull(2), warn_unused_result))
 
212
__attribute__((nonnull(2)))
217
213
static bool add_environment(plugin *p, const char *def, bool replace){
218
214
  if(p == NULL){
219
215
    return false;
221
217
  /* namelen = length of name of environment variable */
222
218
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
223
219
  /* Search for this environment variable */
224
 
  for(char **envdef = p->environ; *envdef != NULL; envdef++){
225
 
    if(strncmp(*envdef, def, namelen + 1) == 0){
 
220
  for(char **e = p->environ; *e != NULL; e++){
 
221
    if(strncmp(*e, def, namelen + 1) == 0){
226
222
      /* It already exists */
227
223
      if(replace){
228
 
        char *new_envdef;
 
224
        char *new;
229
225
        do {
230
 
          new_envdef = realloc(*envdef, strlen(def) + 1);
231
 
        } while(new_envdef == NULL and errno == EINTR);
232
 
        if(new_envdef == NULL){
 
226
          new = realloc(*e, strlen(def) + 1);
 
227
        } while(new == NULL and errno == EINTR);
 
228
        if(new == NULL){
233
229
          return false;
234
230
        }
235
 
        *envdef = new_envdef;
236
 
        strcpy(*envdef, def);
 
231
        *e = new;
 
232
        strcpy(*e, def);
237
233
      }
238
234
      return true;
239
235
    }
241
237
  return add_to_char_array(def, &(p->environ), &(p->envc));
242
238
}
243
239
 
244
 
#ifndef O_CLOEXEC
245
240
/*
246
241
 * Based on the example in the GNU LibC manual chapter 13.13 "File
247
242
 * Descriptor Flags".
248
243
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
249
244
 */
250
 
__attribute__((warn_unused_result))
251
245
static int set_cloexec_flag(int fd){
252
246
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
253
247
  /* If reading the flags failed, return error indication now. */
258
252
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
259
253
                                       ret | FD_CLOEXEC));
260
254
}
261
 
#endif  /* not O_CLOEXEC */
262
255
 
263
256
 
264
257
/* Mark processes as completed when they exit, and save their exit
296
289
}
297
290
 
298
291
/* Prints out a password to stdout */
299
 
__attribute__((nonnull, warn_unused_result))
 
292
__attribute__((nonnull))
300
293
static bool print_out_password(const char *buffer, size_t length){
301
294
  ssize_t ret;
302
295
  for(size_t written = 0; written < length; written += (size_t)ret){
348
341
 
349
342
int main(int argc, char *argv[]){
350
343
  char *plugindir = NULL;
351
 
  char *pluginhelperdir = NULL;
352
344
  char *argfile = NULL;
353
345
  FILE *conffp;
354
 
  struct dirent **direntries = NULL;
 
346
  size_t d_name_len;
 
347
  DIR *dir = NULL;
 
348
  struct dirent *dirst;
355
349
  struct stat st;
356
350
  fd_set rfds_all;
357
351
  int ret, maxfd = 0;
365
359
                                      .sa_flags = SA_NOCLDSTOP };
366
360
  char **custom_argv = NULL;
367
361
  int custom_argc = 0;
368
 
  int dir_fd = -1;
369
362
  
370
363
  /* Establish a signal handler */
371
364
  sigemptyset(&sigchld_action.sa_mask);
416
409
      .doc = "Group ID the plugins will run as", .group = 3 },
417
410
    { .name = "debug", .key = 132,
418
411
      .doc = "Debug mode", .group = 4 },
419
 
    { .name = "plugin-helper-dir", .key = 133,
420
 
      .arg = "DIRECTORY",
421
 
      .doc = "Specify a different plugin helper directory",
422
 
      .group = 2 },
423
412
    /*
424
413
     * These reproduce what we would get without ARGP_NO_HELP
425
414
     */
446
435
            break;
447
436
          }
448
437
        }
449
 
        errno = 0;
450
438
      }
451
439
      break;
452
440
    case 'G':                   /* --global-env */
453
 
      if(add_environment(getplugin(NULL), arg, true)){
454
 
        errno = 0;
455
 
      }
 
441
      add_environment(getplugin(NULL), arg, true);
456
442
      break;
457
443
    case 'o':                   /* --options-for */
458
444
      {
475
461
            break;
476
462
          }
477
463
        }
478
 
        errno = 0;
479
464
      }
480
465
      break;
481
466
    case 'E':                   /* --env-for */
493
478
          errno = EINVAL;
494
479
          break;
495
480
        }
496
 
        if(add_environment(getplugin(arg), envdef, true)){
497
 
          errno = 0;
498
 
        }
 
481
        add_environment(getplugin(arg), envdef, true);
499
482
      }
500
483
      break;
501
484
    case 'd':                   /* --disable */
503
486
        plugin *p = getplugin(arg);
504
487
        if(p != NULL){
505
488
          p->disabled = true;
506
 
          errno = 0;
507
489
        }
508
490
      }
509
491
      break;
512
494
        plugin *p = getplugin(arg);
513
495
        if(p != NULL){
514
496
          p->disabled = false;
515
 
          errno = 0;
516
497
        }
517
498
      }
518
499
      break;
519
500
    case 128:                   /* --plugin-dir */
520
501
      free(plugindir);
521
502
      plugindir = strdup(arg);
522
 
      if(plugindir != NULL){
523
 
        errno = 0;
524
 
      }
525
503
      break;
526
504
    case 129:                   /* --config-file */
527
505
      /* This is already done by parse_opt_config_file() */
535
513
        break;
536
514
      }
537
515
      uid = (uid_t)tmp_id;
538
 
      errno = 0;
539
516
      break;
540
517
    case 131:                   /* --groupid */
541
518
      tmp_id = strtoimax(arg, &tmp, 10);
546
523
        break;
547
524
      }
548
525
      gid = (gid_t)tmp_id;
549
 
      errno = 0;
550
526
      break;
551
527
    case 132:                   /* --debug */
552
528
      debug = true;
553
529
      break;
554
 
    case 133:                   /* --plugin-helper-dir */
555
 
      free(pluginhelperdir);
556
 
      pluginhelperdir = strdup(arg);
557
 
      if(pluginhelperdir != NULL){
558
 
        errno = 0;
559
 
      }
560
 
      break;
561
530
      /*
562
531
       * These reproduce what we would get without ARGP_NO_HELP
563
532
       */
607
576
    case 129:                   /* --config-file */
608
577
      free(argfile);
609
578
      argfile = strdup(arg);
610
 
      if(argfile != NULL){
611
 
        errno = 0;
612
 
      }
613
579
      break;
614
580
    case 130:                   /* --userid */
615
581
    case 131:                   /* --groupid */
616
582
    case 132:                   /* --debug */
617
 
    case 133:                   /* --plugin-helper-dir */
618
583
    case '?':                   /* --help */
619
584
    case -3:                    /* --usage */
620
585
    case 'V':                   /* --version */
699
664
        }
700
665
        
701
666
        custom_argc += 1;
702
 
        {
703
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
704
 
                                    * ((size_t)custom_argc + 1));
705
 
          if(new_argv == NULL){
706
 
            error(0, errno, "realloc");
707
 
            exitstatus = EX_OSERR;
708
 
            free(new_arg);
709
 
            free(org_line);
710
 
            goto fallback;
711
 
          } else {
712
 
            custom_argv = new_argv;
713
 
          }
 
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;
714
674
        }
715
675
        custom_argv[custom_argc-1] = new_arg;
716
676
        custom_argv[custom_argc] = NULL;
774
734
    goto fallback;
775
735
  }
776
736
  
777
 
  {
778
 
    char *pluginhelperenv;
779
 
    bool bret = true;
780
 
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
781
 
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
782
 
    if(ret != -1){
783
 
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
784
 
    }
785
 
    if(ret == -1 or not bret){
786
 
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
787
 
            " environment variable to \"%s\" for all plugins\n",
788
 
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
789
 
    }
790
 
    if(ret != -1){
791
 
      free(pluginhelperenv);
792
 
    }
793
 
  }
794
 
  
795
737
  if(debug){
796
738
    for(plugin *p = plugin_list; p != NULL; p=p->next){
797
739
      fprintf(stderr, "Plugin: %s has %d arguments\n",
811
753
       <http://bugs.debian.org/633582> */
812
754
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
813
755
    if(plugindir_fd == -1){
814
 
      if(errno != ENOENT){
815
 
        error(0, errno, "open(\"" PDIR "\")");
816
 
      }
 
756
      error(0, errno, "open");
817
757
    } else {
818
758
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
819
759
      if(ret == -1){
826
766
          }
827
767
        }
828
768
      }
829
 
      close(plugindir_fd);
 
769
      TEMP_FAILURE_RETRY(close(plugindir_fd));
830
770
    }
831
771
  }
832
772
  
833
773
  /* Lower permissions */
834
 
  ret = setgid(gid);
 
774
  setgid(gid);
835
775
  if(ret == -1){
836
776
    error(0, errno, "setgid");
837
777
  }
842
782
  
843
783
  /* Open plugin directory with close_on_exec flag */
844
784
  {
845
 
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
846
 
#ifdef O_CLOEXEC
847
 
                  O_CLOEXEC
848
 
#else  /* not O_CLOEXEC */
849
 
                  0
850
 
#endif  /* not O_CLOEXEC */
851
 
                  );
 
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
    }
852
803
    if(dir_fd == -1){
853
804
      error(0, errno, "Could not open plugin dir");
854
805
      exitstatus = EX_UNAVAILABLE;
860
811
    ret = set_cloexec_flag(dir_fd);
861
812
    if(ret < 0){
862
813
      error(0, errno, "set_cloexec_flag");
 
814
      TEMP_FAILURE_RETRY(close(dir_fd));
863
815
      exitstatus = EX_OSERR;
864
816
      goto fallback;
865
817
    }
866
818
#endif  /* O_CLOEXEC */
867
 
  }
868
 
  
869
 
  int good_name(const struct dirent * const dirent){
870
 
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
871
 
                                      "*.dpkg-old", "*.dpkg-bak",
872
 
                                      "*.dpkg-divert", NULL };
873
 
#ifdef __GNUC__
874
 
#pragma GCC diagnostic push
875
 
#pragma GCC diagnostic ignored "-Wcast-qual"
876
 
#endif
877
 
    for(const char **pat = (const char **)patterns;
878
 
        *pat != NULL; pat++){
879
 
#ifdef __GNUC__
880
 
#pragma GCC diagnostic pop
881
 
#endif
882
 
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
883
 
         != FNM_NOMATCH){
884
 
        if(debug){
885
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
886
 
                    " matching pattern %s\n", dirent->d_name, *pat);
887
 
        }
888
 
        return 0;
889
 
      }
 
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;
890
826
    }
891
 
    return 1;
892
 
  }
893
 
  
894
 
#ifdef __GLIBC__
895
 
#if __GLIBC_PREREQ(2, 15)
896
 
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
897
 
                             alphasort);
898
 
#else  /* not __GLIBC_PREREQ(2, 15) */
899
 
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
900
 
                           &direntries, good_name, alphasort);
901
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
902
 
#else   /* not __GLIBC__ */
903
 
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
904
 
                           &direntries, good_name, alphasort);
905
 
#endif  /* not __GLIBC__ */
906
 
  if(numplugins == -1){
907
 
    error(0, errno, "Could not scan plugin dir");
908
 
    direntries = NULL;
909
 
    exitstatus = EX_OSERR;
910
 
    goto fallback;
911
827
  }
912
828
  
913
829
  FD_ZERO(&rfds_all);
914
830
  
915
831
  /* Read and execute any executable in the plugin directory*/
916
 
  for(int i = 0; i < numplugins; i++){
917
 
    
918
 
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
919
 
    if(plugin_fd == -1){
920
 
      error(0, errno, "Could not open plugin");
921
 
      free(direntries[i]);
 
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");
922
904
      continue;
923
905
    }
924
 
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
 
906
    
 
907
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
925
908
    if(ret == -1){
926
909
      error(0, errno, "stat");
927
 
      close(plugin_fd);
928
 
      free(direntries[i]);
 
910
      free(filename);
929
911
      continue;
930
912
    }
931
913
    
932
914
    /* Ignore non-executable files */
933
915
    if(not S_ISREG(st.st_mode)
934
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
935
 
                                        X_OK, 0)) != 0)){
 
916
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
936
917
      if(debug){
937
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
938
 
                " with bad type or mode\n",
939
 
                plugindir != NULL ? plugindir : PDIR,
940
 
                direntries[i]->d_name);
 
918
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
919
                " with bad type or mode\n", filename);
941
920
      }
942
 
      close(plugin_fd);
943
 
      free(direntries[i]);
 
921
      free(filename);
944
922
      continue;
945
923
    }
946
924
    
947
 
    plugin *p = getplugin(direntries[i]->d_name);
 
925
    plugin *p = getplugin(dirst->d_name);
948
926
    if(p == NULL){
949
927
      error(0, errno, "getplugin");
950
 
      close(plugin_fd);
951
 
      free(direntries[i]);
 
928
      free(filename);
952
929
      continue;
953
930
    }
954
931
    if(p->disabled){
955
932
      if(debug){
956
933
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
957
 
                direntries[i]->d_name);
 
934
                dirst->d_name);
958
935
      }
959
 
      close(plugin_fd);
960
 
      free(direntries[i]);
 
936
      free(filename);
961
937
      continue;
962
938
    }
963
939
    {
977
953
        }
978
954
      }
979
955
    }
980
 
    /* If this plugin has any environment variables, we need to
981
 
       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. */
982
959
    if(p->environ[0] != NULL){
983
960
      for(char **e = environ; *e != NULL; e++){
984
961
        if(not add_environment(p, *e, false)){
988
965
    }
989
966
    
990
967
    int pipefd[2];
991
 
#ifndef O_CLOEXEC
992
968
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
993
 
#else  /* O_CLOEXEC */
994
 
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
995
 
#endif  /* O_CLOEXEC */
996
969
    if(ret == -1){
997
970
      error(0, errno, "pipe");
998
971
      exitstatus = EX_OSERR;
999
 
      free(direntries[i]);
1000
 
      goto fallback;
1001
 
    }
1002
 
    if(pipefd[0] >= FD_SETSIZE){
1003
 
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
1004
 
              FD_SETSIZE);
1005
 
      close(pipefd[0]);
1006
 
      close(pipefd[1]);
1007
 
      exitstatus = EX_OSERR;
1008
 
      free(direntries[i]);
1009
 
      goto fallback;
1010
 
    }
1011
 
#ifndef O_CLOEXEC
 
972
      goto fallback;
 
973
    }
1012
974
    /* Ask OS to automatic close the pipe on exec */
1013
975
    ret = set_cloexec_flag(pipefd[0]);
1014
976
    if(ret < 0){
1015
977
      error(0, errno, "set_cloexec_flag");
1016
 
      close(pipefd[0]);
1017
 
      close(pipefd[1]);
1018
978
      exitstatus = EX_OSERR;
1019
 
      free(direntries[i]);
1020
979
      goto fallback;
1021
980
    }
1022
981
    ret = set_cloexec_flag(pipefd[1]);
1023
982
    if(ret < 0){
1024
983
      error(0, errno, "set_cloexec_flag");
1025
 
      close(pipefd[0]);
1026
 
      close(pipefd[1]);
1027
984
      exitstatus = EX_OSERR;
1028
 
      free(direntries[i]);
1029
985
      goto fallback;
1030
986
    }
1031
 
#endif  /* not O_CLOEXEC */
1032
987
    /* Block SIGCHLD until process is safely in process list */
1033
988
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1034
989
                                              &sigchld_action.sa_mask,
1036
991
    if(ret < 0){
1037
992
      error(0, errno, "sigprocmask");
1038
993
      exitstatus = EX_OSERR;
1039
 
      free(direntries[i]);
1040
994
      goto fallback;
1041
995
    }
1042
996
    /* Starting a new process to be watched */
1046
1000
    } while(pid == -1 and errno == EINTR);
1047
1001
    if(pid == -1){
1048
1002
      error(0, errno, "fork");
1049
 
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1050
 
                                     &sigchld_action.sa_mask, NULL));
1051
 
      close(pipefd[0]);
1052
 
      close(pipefd[1]);
1053
1003
      exitstatus = EX_OSERR;
1054
 
      free(direntries[i]);
1055
1004
      goto fallback;
1056
1005
    }
1057
1006
    if(pid == 0){
1073
1022
        _exit(EX_OSERR);
1074
1023
      }
1075
1024
      
1076
 
      if(fexecve(plugin_fd, p->argv,
1077
 
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1078
 
        error(0, errno, "fexecve for %s/%s",
1079
 
              plugindir != NULL ? plugindir : PDIR,
1080
 
              direntries[i]->d_name);
1081
 
        _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
        }
1082
1040
      }
1083
1041
      /* no return */
1084
1042
    }
1085
1043
    /* Parent process */
1086
 
    close(pipefd[1]);           /* Close unused write end of pipe */
1087
 
    close(plugin_fd);
1088
 
    plugin *new_plugin = getplugin(direntries[i]->d_name);
 
1044
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
 
1045
                                             pipe */
 
1046
    free(filename);
 
1047
    plugin *new_plugin = getplugin(dirst->d_name);
1089
1048
    if(new_plugin == NULL){
1090
1049
      error(0, errno, "getplugin");
1091
1050
      ret = (int)(TEMP_FAILURE_RETRY
1095
1054
        error(0, errno, "sigprocmask");
1096
1055
      }
1097
1056
      exitstatus = EX_OSERR;
1098
 
      free(direntries[i]);
1099
1057
      goto fallback;
1100
1058
    }
1101
 
    free(direntries[i]);
1102
1059
    
1103
1060
    new_plugin->pid = pid;
1104
1061
    new_plugin->fd = pipefd[0];
1114
1071
      goto fallback;
1115
1072
    }
1116
1073
    
1117
 
#if defined (__GNUC__) and defined (__GLIBC__)
1118
 
#if not __GLIBC_PREREQ(2, 16)
1119
 
#pragma GCC diagnostic push
1120
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1121
 
#endif
1122
 
#endif
1123
1074
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1124
 
                                          -Wconversion in GNU libc
1125
 
                                          before 2.16 */
1126
 
#if defined (__GNUC__) and defined (__GLIBC__)
1127
 
#if not __GLIBC_PREREQ(2, 16)
1128
 
#pragma GCC diagnostic pop
1129
 
#endif
1130
 
#endif
 
1075
                                          -Wconversion */
1131
1076
    
1132
1077
    if(maxfd < new_plugin->fd){
1133
1078
      maxfd = new_plugin->fd;
1134
1079
    }
1135
1080
  }
1136
1081
  
1137
 
  free(direntries);
1138
 
  direntries = NULL;
1139
 
  close(dir_fd);
1140
 
  dir_fd = -1;
 
1082
  TEMP_FAILURE_RETRY(closedir(dir));
 
1083
  dir = NULL;
1141
1084
  free_plugin(getplugin(NULL));
1142
1085
  
1143
1086
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1189
1132
          }
1190
1133
          
1191
1134
          /* Remove the plugin */
1192
 
#if defined (__GNUC__) and defined (__GLIBC__)
1193
 
#if not __GLIBC_PREREQ(2, 16)
1194
 
#pragma GCC diagnostic push
1195
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1196
 
#endif
1197
 
#endif
1198
1135
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1199
 
                                          -Wconversion in GNU libc
1200
 
                                          before 2.16 */
1201
 
#if defined (__GNUC__) and defined (__GLIBC__)
1202
 
#if not __GLIBC_PREREQ(2, 16)
1203
 
#pragma GCC diagnostic pop
1204
 
#endif
1205
 
#endif
 
1136
                                          -Wconversion */
1206
1137
          
1207
1138
          /* Block signal while modifying process_list */
1208
1139
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1248
1179
      }
1249
1180
      
1250
1181
      /* This process has not completed.  Does it have any output? */
1251
 
#if defined (__GNUC__) and defined (__GLIBC__)
1252
 
#if not __GLIBC_PREREQ(2, 16)
1253
 
#pragma GCC diagnostic push
1254
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1255
 
#endif
1256
 
#endif
1257
1182
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1258
1183
                                                         warning from
1259
 
                                                         -Wconversion
1260
 
                                                         in GNU libc
1261
 
                                                         before
1262
 
                                                         2.16 */
1263
 
#if defined (__GNUC__) and defined (__GLIBC__)
1264
 
#if not __GLIBC_PREREQ(2, 16)
1265
 
#pragma GCC diagnostic pop
1266
 
#endif
1267
 
#endif
 
1184
                                                         -Wconversion */
1268
1185
        /* This process had nothing to say at this time */
1269
1186
        proc = proc->next;
1270
1187
        continue;
1271
1188
      }
1272
1189
      /* Before reading, make the process' data buffer large enough */
1273
1190
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1274
 
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
1275
 
                                   + (size_t) BUFFER_SIZE);
1276
 
        if(new_buffer == NULL){
 
1191
        proc->buffer = realloc(proc->buffer, proc->buffer_size
 
1192
                               + (size_t) BUFFER_SIZE);
 
1193
        if(proc->buffer == NULL){
1277
1194
          error(0, errno, "malloc");
1278
1195
          exitstatus = EX_OSERR;
1279
1196
          goto fallback;
1280
1197
        }
1281
 
        proc->buffer = new_buffer;
1282
1198
        proc->buffer_size += BUFFER_SIZE;
1283
1199
      }
1284
1200
      /* Read from the process */
1337
1253
    free(custom_argv);
1338
1254
  }
1339
1255
  
1340
 
  free(direntries);
1341
 
  
1342
 
  if(dir_fd != -1){
1343
 
    close(dir_fd);
 
1256
  if(dir != NULL){
 
1257
    closedir(dir);
1344
1258
  }
1345
1259
  
1346
1260
  /* Kill the processes */
1366
1280
  free_plugin_list();
1367
1281
  
1368
1282
  free(plugindir);
1369
 
  free(pluginhelperdir);
1370
1283
  free(argfile);
1371
1284
  
1372
1285
  return exitstatus;