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