/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
 
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
 
                                    * ((unsigned int)
705
 
                                       custom_argc + 1));
706
 
          if(new_argv == NULL){
707
 
            error(0, errno, "realloc");
708
 
            exitstatus = EX_OSERR;
709
 
            free(new_arg);
710
 
            free(org_line);
711
 
            goto fallback;
712
 
          } else {
713
 
            custom_argv = new_argv;
714
 
          }
 
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;
715
674
        }
716
675
        custom_argv[custom_argc-1] = new_arg;
717
676
        custom_argv[custom_argc] = NULL;
775
734
    goto fallback;
776
735
  }
777
736
  
778
 
  {
779
 
    char *pluginhelperenv;
780
 
    bool bret = true;
781
 
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
782
 
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
783
 
    if(ret != -1){
784
 
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
785
 
    }
786
 
    if(ret == -1 or not bret){
787
 
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
788
 
            " environment variable to \"%s\" for all plugins\n",
789
 
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
790
 
    }
791
 
    if(ret != -1){
792
 
      free(pluginhelperenv);
793
 
    }
794
 
  }
795
 
  
796
737
  if(debug){
797
738
    for(plugin *p = plugin_list; p != NULL; p=p->next){
798
739
      fprintf(stderr, "Plugin: %s has %d arguments\n",
812
753
       <http://bugs.debian.org/633582> */
813
754
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
814
755
    if(plugindir_fd == -1){
815
 
      if(errno != ENOENT){
816
 
        error(0, errno, "open(\"" PDIR "\")");
817
 
      }
 
756
      error(0, errno, "open");
818
757
    } else {
819
758
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
820
759
      if(ret == -1){
827
766
          }
828
767
        }
829
768
      }
830
 
      close(plugindir_fd);
 
769
      TEMP_FAILURE_RETRY(close(plugindir_fd));
831
770
    }
832
771
  }
833
772
  
834
773
  /* Lower permissions */
835
 
  ret = setgid(gid);
 
774
  setgid(gid);
836
775
  if(ret == -1){
837
776
    error(0, errno, "setgid");
838
777
  }
843
782
  
844
783
  /* Open plugin directory with close_on_exec flag */
845
784
  {
846
 
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
847
 
#ifdef O_CLOEXEC
848
 
                  O_CLOEXEC
849
 
#else  /* not O_CLOEXEC */
850
 
                  0
851
 
#endif  /* not O_CLOEXEC */
852
 
                  );
 
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
    }
853
803
    if(dir_fd == -1){
854
804
      error(0, errno, "Could not open plugin dir");
855
805
      exitstatus = EX_UNAVAILABLE;
861
811
    ret = set_cloexec_flag(dir_fd);
862
812
    if(ret < 0){
863
813
      error(0, errno, "set_cloexec_flag");
 
814
      TEMP_FAILURE_RETRY(close(dir_fd));
864
815
      exitstatus = EX_OSERR;
865
816
      goto fallback;
866
817
    }
867
818
#endif  /* O_CLOEXEC */
868
 
  }
869
 
  
870
 
  int good_name(const struct dirent * const dirent){
871
 
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
872
 
                                      "*.dpkg-old", "*.dpkg-bak",
873
 
                                      "*.dpkg-divert", NULL };
874
 
#ifdef __GNUC__
875
 
#pragma GCC diagnostic push
876
 
#pragma GCC diagnostic ignored "-Wcast-qual"
877
 
#endif
878
 
    for(const char **pat = (const char **)patterns;
879
 
        *pat != NULL; pat++){
880
 
#ifdef __GNUC__
881
 
#pragma GCC diagnostic pop
882
 
#endif
883
 
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
884
 
         != FNM_NOMATCH){
885
 
        if(debug){
886
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
887
 
                    " matching pattern %s\n", dirent->d_name, *pat);
888
 
        }
889
 
        return 0;
890
 
      }
 
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;
891
826
    }
892
 
    return 1;
893
 
  }
894
 
  
895
 
#ifdef __GLIBC__
896
 
#if __GLIBC_PREREQ(2, 15)
897
 
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
898
 
                             alphasort);
899
 
#else  /* not __GLIBC_PREREQ(2, 15) */
900
 
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
901
 
                           &direntries, good_name, alphasort);
902
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
903
 
#else   /* not __GLIBC__ */
904
 
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
905
 
                           &direntries, good_name, alphasort);
906
 
#endif  /* not __GLIBC__ */
907
 
  if(numplugins == -1){
908
 
    error(0, errno, "Could not scan plugin dir");
909
 
    direntries = NULL;
910
 
    exitstatus = EX_OSERR;
911
 
    goto fallback;
912
827
  }
913
828
  
914
829
  FD_ZERO(&rfds_all);
915
830
  
916
831
  /* Read and execute any executable in the plugin directory*/
917
 
  for(int i = 0; i < numplugins; i++){
918
 
    
919
 
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
920
 
    if(plugin_fd == -1){
921
 
      error(0, errno, "Could not open plugin");
922
 
      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");
923
904
      continue;
924
905
    }
925
 
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
 
906
    
 
907
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
926
908
    if(ret == -1){
927
909
      error(0, errno, "stat");
928
 
      close(plugin_fd);
929
 
      free(direntries[i]);
 
910
      free(filename);
930
911
      continue;
931
912
    }
932
913
    
933
914
    /* Ignore non-executable files */
934
915
    if(not S_ISREG(st.st_mode)
935
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
936
 
                                        X_OK, 0)) != 0)){
 
916
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
937
917
      if(debug){
938
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
939
 
                " with bad type or mode\n",
940
 
                plugindir != NULL ? plugindir : PDIR,
941
 
                direntries[i]->d_name);
 
918
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
919
                " with bad type or mode\n", filename);
942
920
      }
943
 
      close(plugin_fd);
944
 
      free(direntries[i]);
 
921
      free(filename);
945
922
      continue;
946
923
    }
947
924
    
948
 
    plugin *p = getplugin(direntries[i]->d_name);
 
925
    plugin *p = getplugin(dirst->d_name);
949
926
    if(p == NULL){
950
927
      error(0, errno, "getplugin");
951
 
      close(plugin_fd);
952
 
      free(direntries[i]);
 
928
      free(filename);
953
929
      continue;
954
930
    }
955
931
    if(p->disabled){
956
932
      if(debug){
957
933
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
958
 
                direntries[i]->d_name);
 
934
                dirst->d_name);
959
935
      }
960
 
      close(plugin_fd);
961
 
      free(direntries[i]);
 
936
      free(filename);
962
937
      continue;
963
938
    }
964
939
    {
978
953
        }
979
954
      }
980
955
    }
981
 
    /* If this plugin has any environment variables, we need to
982
 
       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. */
983
959
    if(p->environ[0] != NULL){
984
960
      for(char **e = environ; *e != NULL; e++){
985
961
        if(not add_environment(p, *e, false)){
989
965
    }
990
966
    
991
967
    int pipefd[2];
992
 
#ifndef O_CLOEXEC
993
968
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
994
 
#else  /* O_CLOEXEC */
995
 
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
996
 
#endif  /* O_CLOEXEC */
997
969
    if(ret == -1){
998
970
      error(0, errno, "pipe");
999
971
      exitstatus = EX_OSERR;
1000
 
      free(direntries[i]);
1001
 
      goto fallback;
1002
 
    }
1003
 
    if(pipefd[0] >= FD_SETSIZE){
1004
 
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
1005
 
              FD_SETSIZE);
1006
 
      close(pipefd[0]);
1007
 
      close(pipefd[1]);
1008
 
      exitstatus = EX_OSERR;
1009
 
      free(direntries[i]);
1010
 
      goto fallback;
1011
 
    }
1012
 
#ifndef O_CLOEXEC
 
972
      goto fallback;
 
973
    }
1013
974
    /* Ask OS to automatic close the pipe on exec */
1014
975
    ret = set_cloexec_flag(pipefd[0]);
1015
976
    if(ret < 0){
1016
977
      error(0, errno, "set_cloexec_flag");
1017
 
      close(pipefd[0]);
1018
 
      close(pipefd[1]);
1019
978
      exitstatus = EX_OSERR;
1020
 
      free(direntries[i]);
1021
979
      goto fallback;
1022
980
    }
1023
981
    ret = set_cloexec_flag(pipefd[1]);
1024
982
    if(ret < 0){
1025
983
      error(0, errno, "set_cloexec_flag");
1026
 
      close(pipefd[0]);
1027
 
      close(pipefd[1]);
1028
984
      exitstatus = EX_OSERR;
1029
 
      free(direntries[i]);
1030
985
      goto fallback;
1031
986
    }
1032
 
#endif  /* not O_CLOEXEC */
1033
987
    /* Block SIGCHLD until process is safely in process list */
1034
988
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1035
989
                                              &sigchld_action.sa_mask,
1037
991
    if(ret < 0){
1038
992
      error(0, errno, "sigprocmask");
1039
993
      exitstatus = EX_OSERR;
1040
 
      free(direntries[i]);
1041
994
      goto fallback;
1042
995
    }
1043
996
    /* Starting a new process to be watched */
1047
1000
    } while(pid == -1 and errno == EINTR);
1048
1001
    if(pid == -1){
1049
1002
      error(0, errno, "fork");
1050
 
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1051
 
                                     &sigchld_action.sa_mask, NULL));
1052
 
      close(pipefd[0]);
1053
 
      close(pipefd[1]);
1054
1003
      exitstatus = EX_OSERR;
1055
 
      free(direntries[i]);
1056
1004
      goto fallback;
1057
1005
    }
1058
1006
    if(pid == 0){
1074
1022
        _exit(EX_OSERR);
1075
1023
      }
1076
1024
      
1077
 
      if(fexecve(plugin_fd, p->argv,
1078
 
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1079
 
        error(0, errno, "fexecve for %s/%s",
1080
 
              plugindir != NULL ? plugindir : PDIR,
1081
 
              direntries[i]->d_name);
1082
 
        _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
        }
1083
1040
      }
1084
1041
      /* no return */
1085
1042
    }
1086
1043
    /* Parent process */
1087
 
    close(pipefd[1]);           /* Close unused write end of pipe */
1088
 
    close(plugin_fd);
1089
 
    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);
1090
1048
    if(new_plugin == NULL){
1091
1049
      error(0, errno, "getplugin");
1092
1050
      ret = (int)(TEMP_FAILURE_RETRY
1096
1054
        error(0, errno, "sigprocmask");
1097
1055
      }
1098
1056
      exitstatus = EX_OSERR;
1099
 
      free(direntries[i]);
1100
1057
      goto fallback;
1101
1058
    }
1102
 
    free(direntries[i]);
1103
1059
    
1104
1060
    new_plugin->pid = pid;
1105
1061
    new_plugin->fd = pipefd[0];
1115
1071
      goto fallback;
1116
1072
    }
1117
1073
    
1118
 
#if defined (__GNUC__) and defined (__GLIBC__)
1119
 
#if not __GLIBC_PREREQ(2, 16)
1120
 
#pragma GCC diagnostic push
1121
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1122
 
#endif
1123
 
#endif
1124
1074
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1125
 
                                          -Wconversion in GNU libc
1126
 
                                          before 2.16 */
1127
 
#if defined (__GNUC__) and defined (__GLIBC__)
1128
 
#if not __GLIBC_PREREQ(2, 16)
1129
 
#pragma GCC diagnostic pop
1130
 
#endif
1131
 
#endif
 
1075
                                          -Wconversion */
1132
1076
    
1133
1077
    if(maxfd < new_plugin->fd){
1134
1078
      maxfd = new_plugin->fd;
1135
1079
    }
1136
1080
  }
1137
1081
  
1138
 
  free(direntries);
1139
 
  direntries = NULL;
1140
 
  close(dir_fd);
1141
 
  dir_fd = -1;
 
1082
  TEMP_FAILURE_RETRY(closedir(dir));
 
1083
  dir = NULL;
1142
1084
  free_plugin(getplugin(NULL));
1143
1085
  
1144
1086
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1190
1132
          }
1191
1133
          
1192
1134
          /* Remove the plugin */
1193
 
#if defined (__GNUC__) and defined (__GLIBC__)
1194
 
#if not __GLIBC_PREREQ(2, 16)
1195
 
#pragma GCC diagnostic push
1196
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1197
 
#endif
1198
 
#endif
1199
1135
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1200
 
                                          -Wconversion in GNU libc
1201
 
                                          before 2.16 */
1202
 
#if defined (__GNUC__) and defined (__GLIBC__)
1203
 
#if not __GLIBC_PREREQ(2, 16)
1204
 
#pragma GCC diagnostic pop
1205
 
#endif
1206
 
#endif
 
1136
                                          -Wconversion */
1207
1137
          
1208
1138
          /* Block signal while modifying process_list */
1209
1139
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1249
1179
      }
1250
1180
      
1251
1181
      /* This process has not completed.  Does it have any output? */
1252
 
#if defined (__GNUC__) and defined (__GLIBC__)
1253
 
#if not __GLIBC_PREREQ(2, 16)
1254
 
#pragma GCC diagnostic push
1255
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1256
 
#endif
1257
 
#endif
1258
1182
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1259
1183
                                                         warning from
1260
 
                                                         -Wconversion
1261
 
                                                         in GNU libc
1262
 
                                                         before
1263
 
                                                         2.16 */
1264
 
#if defined (__GNUC__) and defined (__GLIBC__)
1265
 
#if not __GLIBC_PREREQ(2, 16)
1266
 
#pragma GCC diagnostic pop
1267
 
#endif
1268
 
#endif
 
1184
                                                         -Wconversion */
1269
1185
        /* This process had nothing to say at this time */
1270
1186
        proc = proc->next;
1271
1187
        continue;
1272
1188
      }
1273
1189
      /* Before reading, make the process' data buffer large enough */
1274
1190
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1275
 
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
1276
 
                                   + (size_t) BUFFER_SIZE);
1277
 
        if(new_buffer == NULL){
 
1191
        proc->buffer = realloc(proc->buffer, proc->buffer_size
 
1192
                               + (size_t) BUFFER_SIZE);
 
1193
        if(proc->buffer == NULL){
1278
1194
          error(0, errno, "malloc");
1279
1195
          exitstatus = EX_OSERR;
1280
1196
          goto fallback;
1281
1197
        }
1282
 
        proc->buffer = new_buffer;
1283
1198
        proc->buffer_size += BUFFER_SIZE;
1284
1199
      }
1285
1200
      /* Read from the process */
1338
1253
    free(custom_argv);
1339
1254
  }
1340
1255
  
1341
 
  free(direntries);
1342
 
  
1343
 
  if(dir_fd != -1){
1344
 
    close(dir_fd);
 
1256
  if(dir != NULL){
 
1257
    closedir(dir);
1345
1258
  }
1346
1259
  
1347
1260
  /* Kill the processes */
1367
1280
  free_plugin_list();
1368
1281
  
1369
1282
  free(plugindir);
1370
 
  free(pluginhelperdir);
1371
1283
  free(argfile);
1372
1284
  
1373
1285
  return exitstatus;