/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-2018 Teddy Hogeborn
6
 
 * Copyright © 2008-2018 Björn Påhlsson
7
 
 * 
8
 
 * This file is part of Mandos.
9
 
 * 
10
 
 * Mandos is free software: you can redistribute it and/or modify it
11
 
 * under the terms of the GNU General Public License as published by
12
 
 * the Free Software Foundation, either version 3 of the License, or
13
 
 * (at your option) any later version.
14
 
 * 
15
 
 * Mandos is distributed in the hope that it will be useful, but
 
5
 * Copyright © 2008-2011 Teddy Hogeborn
 
6
 * Copyright © 2008-2011 Björn Påhlsson
 
7
 * 
 
8
 * This program is free software: you can redistribute it and/or
 
9
 * modify it under the terms of the GNU General Public License as
 
10
 * published by the Free Software Foundation, either version 3 of the
 
11
 * License, or (at your option) any later version.
 
12
 * 
 
13
 * This program is distributed in the hope that it will be useful, but
16
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of
17
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
16
 * General Public License for more details.
19
17
 * 
20
18
 * You should have received a copy of the GNU General Public License
21
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
19
 * along with this program.  If not, see
 
20
 * <http://www.gnu.org/licenses/>.
22
21
 * 
23
22
 * Contact the authors at <mandos@recompile.se>.
24
23
 */
25
24
 
26
25
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
27
 
                                   O_CLOEXEC, pipe2() */
 
26
                                   asprintf(), O_CLOEXEC */
28
27
#include <stddef.h>             /* size_t, NULL */
29
28
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
30
29
                                   realloc() */
31
30
#include <stdbool.h>            /* bool, true, false */
32
31
#include <stdio.h>              /* fileno(), fprintf(),
33
 
                                   stderr, STDOUT_FILENO, fclose() */
34
 
#include <sys/types.h>          /* fstat(), struct stat, waitpid(),
35
 
                                   WIFEXITED(), WEXITSTATUS(), wait(),
36
 
                                   pid_t, uid_t, gid_t, getuid(),
37
 
                                   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() */
38
38
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
39
39
                                   FD_SET(), FD_ISSET(), FD_CLR */
40
40
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
41
 
                                   WEXITSTATUS(), WTERMSIG() */
42
 
#include <sys/stat.h>           /* struct stat, fstat(), S_ISREG() */
 
41
                                   WEXITSTATUS(), WTERMSIG(),
 
42
                                   WCOREDUMP() */
 
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
       */
564
533
    case '?':                   /* --help */
565
534
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
566
535
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
567
 
      __builtin_unreachable();
568
536
    case -3:                    /* --usage */
569
537
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
570
538
      argp_state_help(state, state->out_stream,
571
539
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
572
 
      __builtin_unreachable();
573
540
    case 'V':                   /* --version */
574
541
      fprintf(state->out_stream, "%s\n", argp_program_version);
575
542
      exit(EXIT_SUCCESS);
585
552
      if(arg[0] == '\0'){
586
553
        break;
587
554
      }
588
 
      /* FALLTHROUGH */
589
555
    default:
590
556
      return ARGP_ERR_UNKNOWN;
591
557
    }
610
576
    case 129:                   /* --config-file */
611
577
      free(argfile);
612
578
      argfile = strdup(arg);
613
 
      if(argfile != NULL){
614
 
        errno = 0;
615
 
      }
616
579
      break;
617
580
    case 130:                   /* --userid */
618
581
    case 131:                   /* --groupid */
619
582
    case 132:                   /* --debug */
620
 
    case 133:                   /* --plugin-helper-dir */
621
583
    case '?':                   /* --help */
622
584
    case -3:                    /* --usage */
623
585
    case 'V':                   /* --version */
702
664
        }
703
665
        
704
666
        custom_argc += 1;
705
 
        {
706
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
707
 
                                    * ((size_t)custom_argc + 1));
708
 
          if(new_argv == NULL){
709
 
            error(0, errno, "realloc");
710
 
            exitstatus = EX_OSERR;
711
 
            free(new_arg);
712
 
            free(org_line);
713
 
            goto fallback;
714
 
          } else {
715
 
            custom_argv = new_argv;
716
 
          }
 
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;
717
674
        }
718
675
        custom_argv[custom_argc-1] = new_arg;
719
676
        custom_argv[custom_argc] = NULL;
777
734
    goto fallback;
778
735
  }
779
736
  
780
 
  {
781
 
    char *pluginhelperenv;
782
 
    bool bret = true;
783
 
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
784
 
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
785
 
    if(ret != -1){
786
 
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
787
 
    }
788
 
    if(ret == -1 or not bret){
789
 
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
790
 
            " environment variable to \"%s\" for all plugins\n",
791
 
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
792
 
    }
793
 
    if(ret != -1){
794
 
      free(pluginhelperenv);
795
 
    }
796
 
  }
797
 
  
798
737
  if(debug){
799
 
    for(plugin *p = plugin_list; p != NULL; p = p->next){
 
738
    for(plugin *p = plugin_list; p != NULL; p=p->next){
800
739
      fprintf(stderr, "Plugin: %s has %d arguments\n",
801
740
              p->name ? p->name : "Global", p->argc - 1);
802
741
      for(char **a = p->argv; *a != NULL; a++){
811
750
  
812
751
  if(getuid() == 0){
813
752
    /* Work around Debian bug #633582:
814
 
       <https://bugs.debian.org/633582> */
 
753
       <http://bugs.debian.org/633582> */
815
754
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
816
755
    if(plugindir_fd == -1){
817
 
      if(errno != ENOENT){
818
 
        error(0, errno, "open(\"" PDIR "\")");
819
 
      }
 
756
      error(0, errno, "open");
820
757
    } else {
821
758
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
822
759
      if(ret == -1){
829
766
          }
830
767
        }
831
768
      }
832
 
      close(plugindir_fd);
 
769
      TEMP_FAILURE_RETRY(close(plugindir_fd));
833
770
    }
834
771
  }
835
772
  
836
773
  /* Lower permissions */
837
 
  ret = setgid(gid);
 
774
  setgid(gid);
838
775
  if(ret == -1){
839
776
    error(0, errno, "setgid");
840
777
  }
845
782
  
846
783
  /* Open plugin directory with close_on_exec flag */
847
784
  {
848
 
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
849
 
#ifdef O_CLOEXEC
850
 
                  O_CLOEXEC
851
 
#else  /* not O_CLOEXEC */
852
 
                  0
853
 
#endif  /* not O_CLOEXEC */
854
 
                  );
 
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
    }
855
803
    if(dir_fd == -1){
856
804
      error(0, errno, "Could not open plugin dir");
857
805
      exitstatus = EX_UNAVAILABLE;
863
811
    ret = set_cloexec_flag(dir_fd);
864
812
    if(ret < 0){
865
813
      error(0, errno, "set_cloexec_flag");
 
814
      TEMP_FAILURE_RETRY(close(dir_fd));
866
815
      exitstatus = EX_OSERR;
867
816
      goto fallback;
868
817
    }
869
818
#endif  /* O_CLOEXEC */
870
 
  }
871
 
  
872
 
  int good_name(const struct dirent * const dirent){
873
 
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
874
 
                                      "*.dpkg-old", "*.dpkg-bak",
875
 
                                      "*.dpkg-divert", NULL };
876
 
#ifdef __GNUC__
877
 
#pragma GCC diagnostic push
878
 
#pragma GCC diagnostic ignored "-Wcast-qual"
879
 
#endif
880
 
    for(const char **pat = (const char **)patterns;
881
 
        *pat != NULL; pat++){
882
 
#ifdef __GNUC__
883
 
#pragma GCC diagnostic pop
884
 
#endif
885
 
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
886
 
         != FNM_NOMATCH){
887
 
        if(debug){
888
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
889
 
                    " matching pattern %s\n", dirent->d_name, *pat);
890
 
        }
891
 
        return 0;
892
 
      }
 
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;
893
826
    }
894
 
    return 1;
895
 
  }
896
 
  
897
 
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
898
 
                             alphasort);
899
 
  if(numplugins == -1){
900
 
    error(0, errno, "Could not scan plugin dir");
901
 
    direntries = NULL;
902
 
    exitstatus = EX_OSERR;
903
 
    goto fallback;
904
827
  }
905
828
  
906
829
  FD_ZERO(&rfds_all);
907
830
  
908
831
  /* Read and execute any executable in the plugin directory*/
909
 
  for(int i = 0; i < numplugins; i++){
910
 
    
911
 
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
912
 
    if(plugin_fd == -1){
913
 
      error(0, errno, "Could not open plugin");
914
 
      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");
915
904
      continue;
916
905
    }
917
 
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
 
906
    
 
907
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
918
908
    if(ret == -1){
919
909
      error(0, errno, "stat");
920
 
      close(plugin_fd);
921
 
      free(direntries[i]);
 
910
      free(filename);
922
911
      continue;
923
912
    }
924
913
    
925
914
    /* Ignore non-executable files */
926
915
    if(not S_ISREG(st.st_mode)
927
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
928
 
                                        X_OK, 0)) != 0)){
 
916
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
929
917
      if(debug){
930
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
931
 
                " with bad type or mode\n",
932
 
                plugindir != NULL ? plugindir : PDIR,
933
 
                direntries[i]->d_name);
 
918
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
919
                " with bad type or mode\n", filename);
934
920
      }
935
 
      close(plugin_fd);
936
 
      free(direntries[i]);
 
921
      free(filename);
937
922
      continue;
938
923
    }
939
924
    
940
 
    plugin *p = getplugin(direntries[i]->d_name);
 
925
    plugin *p = getplugin(dirst->d_name);
941
926
    if(p == NULL){
942
927
      error(0, errno, "getplugin");
943
 
      close(plugin_fd);
944
 
      free(direntries[i]);
 
928
      free(filename);
945
929
      continue;
946
930
    }
947
931
    if(p->disabled){
948
932
      if(debug){
949
933
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
950
 
                direntries[i]->d_name);
 
934
                dirst->d_name);
951
935
      }
952
 
      close(plugin_fd);
953
 
      free(direntries[i]);
 
936
      free(filename);
954
937
      continue;
955
938
    }
956
939
    {
970
953
        }
971
954
      }
972
955
    }
973
 
    /* If this plugin has any environment variables, we need to
974
 
       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. */
975
959
    if(p->environ[0] != NULL){
976
960
      for(char **e = environ; *e != NULL; e++){
977
961
        if(not add_environment(p, *e, false)){
981
965
    }
982
966
    
983
967
    int pipefd[2];
984
 
#ifndef O_CLOEXEC
985
968
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
986
 
#else  /* O_CLOEXEC */
987
 
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
988
 
#endif  /* O_CLOEXEC */
989
969
    if(ret == -1){
990
970
      error(0, errno, "pipe");
991
971
      exitstatus = EX_OSERR;
992
 
      free(direntries[i]);
993
 
      goto fallback;
994
 
    }
995
 
    if(pipefd[0] >= FD_SETSIZE){
996
 
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
997
 
              FD_SETSIZE);
998
 
      close(pipefd[0]);
999
 
      close(pipefd[1]);
1000
 
      exitstatus = EX_OSERR;
1001
 
      free(direntries[i]);
1002
 
      goto fallback;
1003
 
    }
1004
 
#ifndef O_CLOEXEC
 
972
      goto fallback;
 
973
    }
1005
974
    /* Ask OS to automatic close the pipe on exec */
1006
975
    ret = set_cloexec_flag(pipefd[0]);
1007
976
    if(ret < 0){
1008
977
      error(0, errno, "set_cloexec_flag");
1009
 
      close(pipefd[0]);
1010
 
      close(pipefd[1]);
1011
978
      exitstatus = EX_OSERR;
1012
 
      free(direntries[i]);
1013
979
      goto fallback;
1014
980
    }
1015
981
    ret = set_cloexec_flag(pipefd[1]);
1016
982
    if(ret < 0){
1017
983
      error(0, errno, "set_cloexec_flag");
1018
 
      close(pipefd[0]);
1019
 
      close(pipefd[1]);
1020
984
      exitstatus = EX_OSERR;
1021
 
      free(direntries[i]);
1022
985
      goto fallback;
1023
986
    }
1024
 
#endif  /* not O_CLOEXEC */
1025
987
    /* Block SIGCHLD until process is safely in process list */
1026
988
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1027
989
                                              &sigchld_action.sa_mask,
1029
991
    if(ret < 0){
1030
992
      error(0, errno, "sigprocmask");
1031
993
      exitstatus = EX_OSERR;
1032
 
      free(direntries[i]);
1033
994
      goto fallback;
1034
995
    }
1035
996
    /* Starting a new process to be watched */
1039
1000
    } while(pid == -1 and errno == EINTR);
1040
1001
    if(pid == -1){
1041
1002
      error(0, errno, "fork");
1042
 
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1043
 
                                     &sigchld_action.sa_mask, NULL));
1044
 
      close(pipefd[0]);
1045
 
      close(pipefd[1]);
1046
1003
      exitstatus = EX_OSERR;
1047
 
      free(direntries[i]);
1048
1004
      goto fallback;
1049
1005
    }
1050
1006
    if(pid == 0){
1066
1022
        _exit(EX_OSERR);
1067
1023
      }
1068
1024
      
1069
 
      if(fexecve(plugin_fd, p->argv,
1070
 
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1071
 
        error(0, errno, "fexecve for %s/%s",
1072
 
              plugindir != NULL ? plugindir : PDIR,
1073
 
              direntries[i]->d_name);
1074
 
        _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
        }
1075
1040
      }
1076
1041
      /* no return */
1077
1042
    }
1078
1043
    /* Parent process */
1079
 
    close(pipefd[1]);           /* Close unused write end of pipe */
1080
 
    close(plugin_fd);
1081
 
    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);
1082
1048
    if(new_plugin == NULL){
1083
1049
      error(0, errno, "getplugin");
1084
1050
      ret = (int)(TEMP_FAILURE_RETRY
1088
1054
        error(0, errno, "sigprocmask");
1089
1055
      }
1090
1056
      exitstatus = EX_OSERR;
1091
 
      free(direntries[i]);
1092
1057
      goto fallback;
1093
1058
    }
1094
 
    free(direntries[i]);
1095
1059
    
1096
1060
    new_plugin->pid = pid;
1097
1061
    new_plugin->fd = pipefd[0];
1098
 
 
1099
 
    if(debug){
1100
 
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
1101
 
              new_plugin->name, (intmax_t) (new_plugin->pid));
1102
 
    }
1103
 
 
 
1062
    
1104
1063
    /* Unblock SIGCHLD so signal handler can be run if this process
1105
1064
       has already completed */
1106
1065
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1112
1071
      goto fallback;
1113
1072
    }
1114
1073
    
1115
 
    FD_SET(new_plugin->fd, &rfds_all);
 
1074
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
 
1075
                                          -Wconversion */
1116
1076
    
1117
1077
    if(maxfd < new_plugin->fd){
1118
1078
      maxfd = new_plugin->fd;
1119
1079
    }
1120
1080
  }
1121
1081
  
1122
 
  free(direntries);
1123
 
  direntries = NULL;
1124
 
  close(dir_fd);
1125
 
  dir_fd = -1;
 
1082
  TEMP_FAILURE_RETRY(closedir(dir));
 
1083
  dir = NULL;
1126
1084
  free_plugin(getplugin(NULL));
1127
1085
  
1128
1086
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1167
1125
                      (intmax_t) (proc->pid),
1168
1126
                      WTERMSIG(proc->status),
1169
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));
1170
1131
            }
1171
1132
          }
1172
1133
          
1173
1134
          /* Remove the plugin */
1174
 
          FD_CLR(proc->fd, &rfds_all);
 
1135
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
 
1136
                                          -Wconversion */
1175
1137
          
1176
1138
          /* Block signal while modifying process_list */
1177
1139
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1217
1179
      }
1218
1180
      
1219
1181
      /* This process has not completed.  Does it have any output? */
1220
 
      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 */
1221
1185
        /* This process had nothing to say at this time */
1222
1186
        proc = proc->next;
1223
1187
        continue;
1224
1188
      }
1225
1189
      /* Before reading, make the process' data buffer large enough */
1226
1190
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1227
 
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
1228
 
                                   + (size_t) BUFFER_SIZE);
1229
 
        if(new_buffer == NULL){
 
1191
        proc->buffer = realloc(proc->buffer, proc->buffer_size
 
1192
                               + (size_t) BUFFER_SIZE);
 
1193
        if(proc->buffer == NULL){
1230
1194
          error(0, errno, "malloc");
1231
1195
          exitstatus = EX_OSERR;
1232
1196
          goto fallback;
1233
1197
        }
1234
 
        proc->buffer = new_buffer;
1235
1198
        proc->buffer_size += BUFFER_SIZE;
1236
1199
      }
1237
1200
      /* Read from the process */
1290
1253
    free(custom_argv);
1291
1254
  }
1292
1255
  
1293
 
  free(direntries);
1294
 
  
1295
 
  if(dir_fd != -1){
1296
 
    close(dir_fd);
 
1256
  if(dir != NULL){
 
1257
    closedir(dir);
1297
1258
  }
1298
1259
  
1299
1260
  /* Kill the processes */
1319
1280
  free_plugin_list();
1320
1281
  
1321
1282
  free(plugindir);
1322
 
  free(pluginhelperdir);
1323
1283
  free(argfile);
1324
1284
  
1325
1285
  return exitstatus;