/mandos/release

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2021-02-04 17:59:45 UTC
  • mto: This revision was merged to the branch mainline in revision 406.
  • Revision ID: teddy@recompile.se-20210204175945-8druo6d88ipc1z58
Fix issue with french translation

Initial white space was missing in both msgid and msgstr of the french
translation, leading to checking tools reporing an incomplete
translation.  The string is a raw key id, and therefore did not need
translation, so this was never a user-visible issue.

* debian/po/fr.po: Add missing whitespace to the id and translation
  for msgid " ${key_id}".

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Mandos plugin runner - Run Mandos plugins
4
4
 *
5
 
 * Copyright © 2008-2013 Teddy Hogeborn
6
 
 * Copyright © 2008-2013 Björn Påhlsson
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
 
5
 * Copyright © 2008-2021 Teddy Hogeborn
 
6
 * Copyright © 2008-2021 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
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
24
25
 
25
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
 
                                   asprintf(), O_CLOEXEC */
 
26
#define _GNU_SOURCE             /* strchrnul(), TEMP_FAILURE_RETRY(),
 
27
                                   getline(), asprintf(), O_CLOEXEC,
 
28
                                   scandirat(), pipe2() */
 
29
#include <argp.h>               /* argp_program_version,
 
30
                                   argp_program_bug_address,
 
31
                                   struct argp_option,
 
32
                                   struct argp_state, argp_error(),
 
33
                                   ARGP_NO_EXIT, argp_state_help,
 
34
                                   ARGP_HELP_STD_HELP,
 
35
                                   ARGP_HELP_USAGE, ARGP_HELP_EXIT_OK,
 
36
                                   ARGP_KEY_ARG, ARGP_ERR_UNKNOWN,
 
37
                                   struct argp, argp_parse(),
 
38
                                   ARGP_IN_ORDER, ARGP_NO_HELP */
 
39
#include <stdbool.h>            /* bool, false, true */
 
40
#include <sys/types.h>          /* pid_t, sig_atomic_t, uid_t, gid_t,
 
41
                                   getuid(), setgid(), setuid() */
27
42
#include <stddef.h>             /* size_t, NULL */
28
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
 
                                   realloc() */
30
 
#include <stdbool.h>            /* bool, true, false */
31
 
#include <stdio.h>              /* fileno(), fprintf(),
32
 
                                   stderr, STDOUT_FILENO */
33
 
#include <sys/types.h>          /* DIR, fdopendir(), stat(), struct
34
 
                                   stat, waitpid(), WIFEXITED(),
35
 
                                   WEXITSTATUS(), wait(), pid_t,
36
 
                                   uid_t, gid_t, getuid(), getgid(),
37
 
                                   dirfd() */
38
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
39
 
                                   FD_SET(), FD_ISSET(), FD_CLR */
40
 
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
41
 
                                   WEXITSTATUS(), WTERMSIG(),
42
 
                                   WCOREDUMP() */
43
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
44
 
#include <iso646.h>             /* and, or, not */
45
 
#include <dirent.h>             /* DIR, struct dirent, fdopendir(),
46
 
                                   readdir(), closedir(), dirfd() */
47
 
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
48
 
                                   fcntl(), setuid(), setgid(),
49
 
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
50
 
                                   access(), pipe(), fork(), close()
51
 
                                   dup2(), STDOUT_FILENO, _exit(),
52
 
                                   execv(), write(), read(),
53
 
                                   close() */
 
43
#include <iso646.h>             /* or, and, not */
 
44
#include <string.h>             /* strcmp(), strdup(), strchrnul(),
 
45
                                   strncmp(), strlen(), strcpy(),
 
46
                                   strsep(), strchr(), strsignal() */
 
47
#include <stdlib.h>             /* malloc(), free(), reallocarray(),
 
48
                                   realloc(), EXIT_SUCCESS */
 
49
#include <errno.h>              /* errno, EINTR, ENOMEM, ECHILD,
 
50
                                   error_t, EINVAL, EMFILE, ENFILE,
 
51
                                   ENOENT, ESRCH */
 
52
#include <stdint.h>             /* SIZE_MAX */
 
53
#define _GNU_SOURCE             /* strchrnul(), TEMP_FAILURE_RETRY(),
 
54
                                   getline(), asprintf(), O_CLOEXEC,
 
55
                                   scandirat(), pipe2() */
 
56
#include <unistd.h>             /* TEMP_FAILURE_RETRY(), ssize_t,
 
57
                                   write(), STDOUT_FILENO, uid_t,
 
58
                                   gid_t, getuid(), fchown(), close(),
 
59
                                   symlink(), setgid(), setuid(),
 
60
                                   faccessat(), X_OK, pipe(), pipe2(),
 
61
                                   fork(), _exit(), dup2(), fexecve(),
 
62
                                   read(), getpass() */
54
63
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
 
                                   FD_CLOEXEC */
56
 
#include <string.h>             /* strsep, strlen(), asprintf(),
57
 
                                   strsignal(), strcmp(), strncmp() */
58
 
#include <errno.h>              /* errno */
59
 
#include <argp.h>               /* struct argp_option, struct
60
 
                                   argp_state, struct argp,
61
 
                                   argp_parse(), ARGP_ERR_UNKNOWN,
62
 
                                   ARGP_KEY_END, ARGP_KEY_ARG,
63
 
                                   error_t */
64
 
#include <signal.h>             /* struct sigaction, sigemptyset(),
65
 
                                   sigaddset(), sigaction(),
66
 
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
67
 
                                   SIG_UNBLOCK, kill(), sig_atomic_t
68
 
                                */
69
 
#include <errno.h>              /* errno, EBADF */
70
 
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
 
64
                                   FD_CLOEXEC, open(), O_RDONLY,
 
65
                                   O_CLOEXEC, openat() */
 
66
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
 
67
                                   WEXITSTATUS(), WIFSIGNALED(),
 
68
                                   WTERMSIG(), wait() */
 
69
#include <error.h>              /* error() */
 
70
#include <stdio.h>              /* FILE, fprintf(), fopen(),
 
71
                                   getline(), fclose(), EOF,
 
72
                                   asprintf(), stderr */
 
73
#include <dirent.h>             /* struct dirent, scandirat(),
 
74
                                   alphasort() */
 
75
#include <sys/stat.h>           /* struct stat, fstat(), S_ISDIR(),
 
76
                                   lstat(), S_ISREG() */
 
77
#include <sys/select.h>         /* fd_set, FD_ZERO(), FD_SETSIZE,
 
78
                                   FD_SET(), select(), FD_CLR(),
 
79
                                   FD_ISSET() */
 
80
#include <signal.h>             /* struct sigaction, SA_NOCLDSTOP,
 
81
                                   sigemptyset(), sigaddset(),
 
82
                                   SIGCHLD, sigprocmask(), SIG_BLOCK,
 
83
                                   SIG_UNBLOCK, kill(), SIGTERM */
71
84
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_IOERR,
72
85
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
73
 
#include <errno.h>              /* errno */
74
 
#include <error.h>              /* error() */
 
86
#include <inttypes.h>           /* intmax_t, strtoimax(), PRIdMAX */
 
87
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME,
 
88
                                   FNM_PERIOD, FNM_NOMATCH */
75
89
 
76
90
#define BUFFER_SIZE 256
77
91
 
78
92
#define PDIR "/lib/mandos/plugins.d"
 
93
#define PHDIR "/lib/mandos/plugin-helpers"
79
94
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
80
95
 
81
96
const char *argp_program_version = "plugin-runner " VERSION;
178
193
  /* Resize the pointed-to array to hold one more pointer */
179
194
  char **new_array = NULL;
180
195
  do {
181
 
    new_array = realloc(*array, sizeof(char *)
182
 
                        * (size_t) ((*len) + 2));
 
196
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
197
    new_array = reallocarray(*array, (size_t)((*len) + 2),
 
198
                             sizeof(char *));
 
199
#else
 
200
    if(((size_t)((*len) + 2)) > (SIZE_MAX / sizeof(char *))){
 
201
      /* overflow */
 
202
      new_array = NULL;
 
203
      errno = ENOMEM;
 
204
    } else {
 
205
      new_array = realloc(*array, (size_t)((*len) + 2)
 
206
                          * sizeof(char *));
 
207
    }
 
208
#endif
183
209
  } while(new_array == NULL and errno == EINTR);
184
210
  /* Malloc check */
185
211
  if(new_array == NULL){
240
266
  return add_to_char_array(def, &(p->environ), &(p->envc));
241
267
}
242
268
 
 
269
#ifndef O_CLOEXEC
243
270
/*
244
271
 * Based on the example in the GNU LibC manual chapter 13.13 "File
245
272
 * Descriptor Flags".
256
283
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
257
284
                                       ret | FD_CLOEXEC));
258
285
}
 
286
#endif  /* not O_CLOEXEC */
259
287
 
260
288
 
261
289
/* Mark processes as completed when they exit, and save their exit
310
338
__attribute__((nonnull))
311
339
static void free_plugin(plugin *plugin_node){
312
340
  
313
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
 
341
  for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){
314
342
    free(*arg);
315
343
  }
 
344
  free(plugin_node->name);
316
345
  free(plugin_node->argv);
317
346
  for(char **env = plugin_node->environ; *env != NULL; env++){
318
347
    free(*env);
345
374
 
346
375
int main(int argc, char *argv[]){
347
376
  char *plugindir = NULL;
 
377
  char *pluginhelperdir = NULL;
348
378
  char *argfile = NULL;
349
379
  FILE *conffp;
350
 
  size_t d_name_len;
351
 
  DIR *dir = NULL;
352
 
  struct dirent *dirst;
 
380
  struct dirent **direntries = NULL;
353
381
  struct stat st;
354
382
  fd_set rfds_all;
355
383
  int ret, maxfd = 0;
363
391
                                      .sa_flags = SA_NOCLDSTOP };
364
392
  char **custom_argv = NULL;
365
393
  int custom_argc = 0;
 
394
  int dir_fd = -1;
366
395
  
367
396
  /* Establish a signal handler */
368
397
  sigemptyset(&sigchld_action.sa_mask);
413
442
      .doc = "Group ID the plugins will run as", .group = 3 },
414
443
    { .name = "debug", .key = 132,
415
444
      .doc = "Debug mode", .group = 4 },
 
445
    { .name = "plugin-helper-dir", .key = 133,
 
446
      .arg = "DIRECTORY",
 
447
      .doc = "Specify a different plugin helper directory",
 
448
      .group = 2 },
416
449
    /*
417
450
     * These reproduce what we would get without ARGP_NO_HELP
418
451
     */
544
577
    case 132:                   /* --debug */
545
578
      debug = true;
546
579
      break;
 
580
    case 133:                   /* --plugin-helper-dir */
 
581
      free(pluginhelperdir);
 
582
      pluginhelperdir = strdup(arg);
 
583
      if(pluginhelperdir != NULL){
 
584
        errno = 0;
 
585
      }
 
586
      break;
547
587
      /*
548
588
       * These reproduce what we would get without ARGP_NO_HELP
549
589
       */
550
590
    case '?':                   /* --help */
551
591
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
552
592
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
 
593
      __builtin_unreachable();
553
594
    case -3:                    /* --usage */
554
595
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
555
596
      argp_state_help(state, state->out_stream,
556
597
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
 
598
      __builtin_unreachable();
557
599
    case 'V':                   /* --version */
558
600
      fprintf(state->out_stream, "%s\n", argp_program_version);
559
601
      exit(EXIT_SUCCESS);
569
611
      if(arg[0] == '\0'){
570
612
        break;
571
613
      }
 
614
#if __GNUC__ >= 7
 
615
      __attribute__((fallthrough));
 
616
#else
 
617
          /* FALLTHROUGH */
 
618
#endif
572
619
    default:
573
620
      return ARGP_ERR_UNKNOWN;
574
621
    }
600
647
    case 130:                   /* --userid */
601
648
    case 131:                   /* --groupid */
602
649
    case 132:                   /* --debug */
 
650
    case 133:                   /* --plugin-helper-dir */
603
651
    case '?':                   /* --help */
604
652
    case -3:                    /* --usage */
605
653
    case 'V':                   /* --version */
685
733
        
686
734
        custom_argc += 1;
687
735
        {
688
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
689
 
                                    * ((unsigned int)
690
 
                                       custom_argc + 1));
 
736
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
737
          char **new_argv = reallocarray(custom_argv, (size_t)custom_argc + 1,
 
738
                                         sizeof(char *));
 
739
#else
 
740
          char **new_argv = NULL;
 
741
          if(((size_t)custom_argc + 1) > (SIZE_MAX / sizeof(char *))){
 
742
            /* overflow */
 
743
            errno = ENOMEM;
 
744
          } else {
 
745
            new_argv = realloc(custom_argv, ((size_t)custom_argc + 1)
 
746
                               * sizeof(char *));
 
747
          }
 
748
#endif
691
749
          if(new_argv == NULL){
692
 
            error(0, errno, "realloc");
 
750
            error(0, errno, "reallocarray");
693
751
            exitstatus = EX_OSERR;
694
752
            free(new_arg);
695
753
            free(org_line);
760
818
    goto fallback;
761
819
  }
762
820
  
 
821
  {
 
822
    char *pluginhelperenv;
 
823
    bool bret = true;
 
824
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
 
825
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
826
    if(ret != -1){
 
827
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
 
828
    }
 
829
    if(ret == -1 or not bret){
 
830
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
 
831
            " environment variable to \"%s\" for all plugins\n",
 
832
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
833
    }
 
834
    if(ret != -1){
 
835
      free(pluginhelperenv);
 
836
    }
 
837
  }
 
838
  
763
839
  if(debug){
764
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
 
840
    for(plugin *p = plugin_list; p != NULL; p = p->next){
765
841
      fprintf(stderr, "Plugin: %s has %d arguments\n",
766
842
              p->name ? p->name : "Global", p->argc - 1);
767
843
      for(char **a = p->argv; *a != NULL; a++){
776
852
  
777
853
  if(getuid() == 0){
778
854
    /* Work around Debian bug #633582:
779
 
       <http://bugs.debian.org/633582> */
 
855
       <https://bugs.debian.org/633582> */
780
856
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
781
857
    if(plugindir_fd == -1){
782
 
      error(0, errno, "open");
 
858
      if(errno != ENOENT){
 
859
        error(0, errno, "open(\"" PDIR "\")");
 
860
      }
783
861
    } else {
784
862
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
785
863
      if(ret == -1){
792
870
          }
793
871
        }
794
872
      }
795
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
873
      close(plugindir_fd);
 
874
    }
 
875
 
 
876
    /* Work around Debian bug #981302
 
877
       <https://bugs.debian.org/981302> */
 
878
    if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){
 
879
      ret = symlink("/proc/self/fd", "/dev/fd");
 
880
      if(ret == -1){
 
881
        error(0, errno, "Failed to create /dev/fd symlink");
 
882
      }
796
883
    }
797
884
  }
798
885
  
808
895
  
809
896
  /* Open plugin directory with close_on_exec flag */
810
897
  {
811
 
    int dir_fd = -1;
812
 
    if(plugindir == NULL){
813
 
      dir_fd = open(PDIR, O_RDONLY |
814
 
#ifdef O_CLOEXEC
815
 
                    O_CLOEXEC
816
 
#else  /* not O_CLOEXEC */
817
 
                    0
818
 
#endif  /* not O_CLOEXEC */
819
 
                    );
820
 
    } else {
821
 
      dir_fd = open(plugindir, O_RDONLY |
822
 
#ifdef O_CLOEXEC
823
 
                    O_CLOEXEC
824
 
#else  /* not O_CLOEXEC */
825
 
                    0
826
 
#endif  /* not O_CLOEXEC */
827
 
                    );
828
 
    }
 
898
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
 
899
#ifdef O_CLOEXEC
 
900
                  O_CLOEXEC
 
901
#else  /* not O_CLOEXEC */
 
902
                  0
 
903
#endif  /* not O_CLOEXEC */
 
904
                  );
829
905
    if(dir_fd == -1){
830
906
      error(0, errno, "Could not open plugin dir");
831
907
      exitstatus = EX_UNAVAILABLE;
837
913
    ret = set_cloexec_flag(dir_fd);
838
914
    if(ret < 0){
839
915
      error(0, errno, "set_cloexec_flag");
840
 
      TEMP_FAILURE_RETRY(close(dir_fd));
841
916
      exitstatus = EX_OSERR;
842
917
      goto fallback;
843
918
    }
844
919
#endif  /* O_CLOEXEC */
845
 
    
846
 
    dir = fdopendir(dir_fd);
847
 
    if(dir == NULL){
848
 
      error(0, errno, "Could not open plugin dir");
849
 
      TEMP_FAILURE_RETRY(close(dir_fd));
850
 
      exitstatus = EX_OSERR;
851
 
      goto fallback;
 
920
  }
 
921
  
 
922
  int good_name(const struct dirent * const dirent){
 
923
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
 
924
                                      "*.dpkg-old", "*.dpkg-bak",
 
925
                                      "*.dpkg-divert", NULL };
 
926
#ifdef __GNUC__
 
927
#pragma GCC diagnostic push
 
928
#pragma GCC diagnostic ignored "-Wcast-qual"
 
929
#endif
 
930
    for(const char **pat = (const char **)patterns;
 
931
        *pat != NULL; pat++){
 
932
#ifdef __GNUC__
 
933
#pragma GCC diagnostic pop
 
934
#endif
 
935
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
 
936
         != FNM_NOMATCH){
 
937
        if(debug){
 
938
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
939
                    " matching pattern %s\n", dirent->d_name, *pat);
 
940
        }
 
941
        return 0;
 
942
      }
852
943
    }
 
944
    return 1;
 
945
  }
 
946
  
 
947
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
 
948
                             alphasort);
 
949
  if(numplugins == -1){
 
950
    error(0, errno, "Could not scan plugin dir");
 
951
    direntries = NULL;
 
952
    exitstatus = EX_OSERR;
 
953
    goto fallback;
853
954
  }
854
955
  
855
956
  FD_ZERO(&rfds_all);
856
957
  
857
958
  /* Read and execute any executable in the plugin directory*/
858
 
  while(true){
859
 
    do {
860
 
      dirst = readdir(dir);
861
 
    } while(dirst == NULL and errno == EINTR);
862
 
    
863
 
    /* All directory entries have been processed */
864
 
    if(dirst == NULL){
865
 
      if(errno == EBADF){
866
 
        error(0, errno, "readdir");
867
 
        exitstatus = EX_IOERR;
868
 
        goto fallback;
869
 
      }
870
 
      break;
871
 
    }
872
 
    
873
 
    d_name_len = strlen(dirst->d_name);
874
 
    
875
 
    /* Ignore dotfiles, backup files and other junk */
876
 
    {
877
 
      bool bad_name = false;
878
 
      
879
 
      const char * const bad_prefixes[] = { ".", "#", NULL };
880
 
      
881
 
      const char * const bad_suffixes[] = { "~", "#", ".dpkg-new",
882
 
                                           ".dpkg-old",
883
 
                                           ".dpkg-bak",
884
 
                                           ".dpkg-divert", NULL };
885
 
#ifdef __GNUC__
886
 
#pragma GCC diagnostic push
887
 
#pragma GCC diagnostic ignored "-Wcast-qual"
888
 
#endif
889
 
      for(const char **pre = (const char **)bad_prefixes;
890
 
          *pre != NULL; pre++){
891
 
#ifdef __GNUC__
892
 
#pragma GCC diagnostic pop
893
 
#endif
894
 
        size_t pre_len = strlen(*pre);
895
 
        if((d_name_len >= pre_len)
896
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
897
 
          if(debug){
898
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
899
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
900
 
          }
901
 
          bad_name = true;
902
 
          break;
903
 
        }
904
 
      }
905
 
      if(bad_name){
906
 
        continue;
907
 
      }
908
 
#ifdef __GNUC__
909
 
#pragma GCC diagnostic push
910
 
#pragma GCC diagnostic ignored "-Wcast-qual"
911
 
#endif
912
 
      for(const char **suf = (const char **)bad_suffixes;
913
 
          *suf != NULL; suf++){
914
 
#ifdef __GNUC__
915
 
#pragma GCC diagnostic pop
916
 
#endif
917
 
        size_t suf_len = strlen(*suf);
918
 
        if((d_name_len >= suf_len)
919
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
920
 
                == 0)){
921
 
          if(debug){
922
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
923
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
924
 
          }
925
 
          bad_name = true;
926
 
          break;
927
 
        }
928
 
      }
929
 
      
930
 
      if(bad_name){
931
 
        continue;
932
 
      }
933
 
    }
934
 
    
935
 
    char *filename;
936
 
    if(plugindir == NULL){
937
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
938
 
                                             dirst->d_name));
939
 
    } else {
940
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
941
 
                                             plugindir,
942
 
                                             dirst->d_name));
943
 
    }
944
 
    if(ret < 0){
945
 
      error(0, errno, "asprintf");
 
959
  for(int i = 0; i < numplugins; i++){
 
960
    
 
961
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
 
962
    if(plugin_fd == -1){
 
963
      error(0, errno, "Could not open plugin");
 
964
      free(direntries[i]);
946
965
      continue;
947
966
    }
948
 
    
949
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
 
967
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
950
968
    if(ret == -1){
951
969
      error(0, errno, "stat");
952
 
      free(filename);
 
970
      close(plugin_fd);
 
971
      free(direntries[i]);
953
972
      continue;
954
973
    }
955
974
    
956
975
    /* Ignore non-executable files */
957
976
    if(not S_ISREG(st.st_mode)
958
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
977
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
978
                                        X_OK, 0)) != 0)){
959
979
      if(debug){
960
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
961
 
                " with bad type or mode\n", filename);
 
980
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
 
981
                " with bad type or mode\n",
 
982
                plugindir != NULL ? plugindir : PDIR,
 
983
                direntries[i]->d_name);
962
984
      }
963
 
      free(filename);
 
985
      close(plugin_fd);
 
986
      free(direntries[i]);
964
987
      continue;
965
988
    }
966
989
    
967
 
    plugin *p = getplugin(dirst->d_name);
 
990
    plugin *p = getplugin(direntries[i]->d_name);
968
991
    if(p == NULL){
969
992
      error(0, errno, "getplugin");
970
 
      free(filename);
 
993
      close(plugin_fd);
 
994
      free(direntries[i]);
971
995
      continue;
972
996
    }
973
997
    if(p->disabled){
974
998
      if(debug){
975
999
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
976
 
                dirst->d_name);
 
1000
                direntries[i]->d_name);
977
1001
      }
978
 
      free(filename);
 
1002
      close(plugin_fd);
 
1003
      free(direntries[i]);
979
1004
      continue;
980
1005
    }
981
1006
    {
995
1020
        }
996
1021
      }
997
1022
    }
998
 
    /* If this plugin has any environment variables, we will call
999
 
       using execve and need to duplicate the environment from this
1000
 
       process, too. */
 
1023
    /* If this plugin has any environment variables, we need to
 
1024
       duplicate the environment from this process, too. */
1001
1025
    if(p->environ[0] != NULL){
1002
1026
      for(char **e = environ; *e != NULL; e++){
1003
1027
        if(not add_environment(p, *e, false)){
1007
1031
    }
1008
1032
    
1009
1033
    int pipefd[2];
 
1034
#ifndef O_CLOEXEC
1010
1035
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
 
1036
#else  /* O_CLOEXEC */
 
1037
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
 
1038
#endif  /* O_CLOEXEC */
1011
1039
    if(ret == -1){
1012
1040
      error(0, errno, "pipe");
1013
1041
      exitstatus = EX_OSERR;
1014
 
      goto fallback;
1015
 
    }
 
1042
      free(direntries[i]);
 
1043
      goto fallback;
 
1044
    }
 
1045
    if(pipefd[0] >= FD_SETSIZE){
 
1046
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
 
1047
              FD_SETSIZE);
 
1048
      close(pipefd[0]);
 
1049
      close(pipefd[1]);
 
1050
      exitstatus = EX_OSERR;
 
1051
      free(direntries[i]);
 
1052
      goto fallback;
 
1053
    }
 
1054
#ifndef O_CLOEXEC
1016
1055
    /* Ask OS to automatic close the pipe on exec */
1017
1056
    ret = set_cloexec_flag(pipefd[0]);
1018
1057
    if(ret < 0){
1019
1058
      error(0, errno, "set_cloexec_flag");
 
1059
      close(pipefd[0]);
 
1060
      close(pipefd[1]);
1020
1061
      exitstatus = EX_OSERR;
 
1062
      free(direntries[i]);
1021
1063
      goto fallback;
1022
1064
    }
1023
1065
    ret = set_cloexec_flag(pipefd[1]);
1024
1066
    if(ret < 0){
1025
1067
      error(0, errno, "set_cloexec_flag");
 
1068
      close(pipefd[0]);
 
1069
      close(pipefd[1]);
1026
1070
      exitstatus = EX_OSERR;
 
1071
      free(direntries[i]);
1027
1072
      goto fallback;
1028
1073
    }
 
1074
#endif  /* not O_CLOEXEC */
1029
1075
    /* Block SIGCHLD until process is safely in process list */
1030
1076
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1031
1077
                                              &sigchld_action.sa_mask,
1033
1079
    if(ret < 0){
1034
1080
      error(0, errno, "sigprocmask");
1035
1081
      exitstatus = EX_OSERR;
 
1082
      free(direntries[i]);
1036
1083
      goto fallback;
1037
1084
    }
1038
1085
    /* Starting a new process to be watched */
1042
1089
    } while(pid == -1 and errno == EINTR);
1043
1090
    if(pid == -1){
1044
1091
      error(0, errno, "fork");
 
1092
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
 
1093
                                     &sigchld_action.sa_mask, NULL));
 
1094
      close(pipefd[0]);
 
1095
      close(pipefd[1]);
1045
1096
      exitstatus = EX_OSERR;
 
1097
      free(direntries[i]);
1046
1098
      goto fallback;
1047
1099
    }
1048
1100
    if(pid == 0){
1064
1116
        _exit(EX_OSERR);
1065
1117
      }
1066
1118
      
1067
 
      if(dirfd(dir) < 0){
1068
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
1069
 
           above and must now close it manually here. */
1070
 
        closedir(dir);
1071
 
      }
1072
 
      if(p->environ[0] == NULL){
1073
 
        if(execv(filename, p->argv) < 0){
1074
 
          error(0, errno, "execv for %s", filename);
1075
 
          _exit(EX_OSERR);
1076
 
        }
1077
 
      } else {
1078
 
        if(execve(filename, p->argv, p->environ) < 0){
1079
 
          error(0, errno, "execve for %s", filename);
1080
 
          _exit(EX_OSERR);
1081
 
        }
 
1119
      if(fexecve(plugin_fd, p->argv,
 
1120
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
 
1121
        error(0, errno, "fexecve for %s/%s",
 
1122
              plugindir != NULL ? plugindir : PDIR,
 
1123
              direntries[i]->d_name);
 
1124
        _exit(EX_OSERR);
1082
1125
      }
1083
1126
      /* no return */
1084
1127
    }
1085
1128
    /* Parent process */
1086
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1087
 
                                             pipe */
1088
 
    free(filename);
1089
 
    plugin *new_plugin = getplugin(dirst->d_name);
 
1129
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1130
    close(plugin_fd);
 
1131
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1090
1132
    if(new_plugin == NULL){
1091
1133
      error(0, errno, "getplugin");
1092
1134
      ret = (int)(TEMP_FAILURE_RETRY
1096
1138
        error(0, errno, "sigprocmask");
1097
1139
      }
1098
1140
      exitstatus = EX_OSERR;
 
1141
      free(direntries[i]);
1099
1142
      goto fallback;
1100
1143
    }
 
1144
    free(direntries[i]);
1101
1145
    
1102
1146
    new_plugin->pid = pid;
1103
1147
    new_plugin->fd = pipefd[0];
1104
 
    
 
1148
 
 
1149
    if(debug){
 
1150
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
 
1151
              new_plugin->name, (intmax_t) (new_plugin->pid));
 
1152
    }
 
1153
 
1105
1154
    /* Unblock SIGCHLD so signal handler can be run if this process
1106
1155
       has already completed */
1107
1156
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1113
1162
      goto fallback;
1114
1163
    }
1115
1164
    
1116
 
#if defined (__GNUC__) and defined (__GLIBC__)
1117
 
#if not __GLIBC_PREREQ(2, 16)
1118
 
#pragma GCC diagnostic push
1119
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1120
 
#endif
1121
 
#endif
1122
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1123
 
                                          -Wconversion in GNU libc
1124
 
                                          before 2.16 */
1125
 
#if defined (__GNUC__) and defined (__GLIBC__)
1126
 
#if not __GLIBC_PREREQ(2, 16)
1127
 
#pragma GCC diagnostic pop
1128
 
#endif
1129
 
#endif
 
1165
    FD_SET(new_plugin->fd, &rfds_all);
1130
1166
    
1131
1167
    if(maxfd < new_plugin->fd){
1132
1168
      maxfd = new_plugin->fd;
1133
1169
    }
1134
1170
  }
1135
1171
  
1136
 
  TEMP_FAILURE_RETRY(closedir(dir));
1137
 
  dir = NULL;
 
1172
  free(direntries);
 
1173
  direntries = NULL;
 
1174
  close(dir_fd);
 
1175
  dir_fd = -1;
1138
1176
  free_plugin(getplugin(NULL));
1139
1177
  
1140
1178
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1179
1217
                      (intmax_t) (proc->pid),
1180
1218
                      WTERMSIG(proc->status),
1181
1219
                      strsignal(WTERMSIG(proc->status)));
1182
 
            } else if(WCOREDUMP(proc->status)){
1183
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1184
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
1185
1220
            }
1186
1221
          }
1187
1222
          
1188
1223
          /* Remove the plugin */
1189
 
#if defined (__GNUC__) and defined (__GLIBC__)
1190
 
#if not __GLIBC_PREREQ(2, 16)
1191
 
#pragma GCC diagnostic push
1192
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1193
 
#endif
1194
 
#endif
1195
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1196
 
                                          -Wconversion in GNU libc
1197
 
                                          before 2.16 */
1198
 
#if defined (__GNUC__) and defined (__GLIBC__)
1199
 
#if not __GLIBC_PREREQ(2, 16)
1200
 
#pragma GCC diagnostic pop
1201
 
#endif
1202
 
#endif
 
1224
          FD_CLR(proc->fd, &rfds_all);
1203
1225
          
1204
1226
          /* Block signal while modifying process_list */
1205
1227
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1245
1267
      }
1246
1268
      
1247
1269
      /* This process has not completed.  Does it have any output? */
1248
 
#if defined (__GNUC__) and defined (__GLIBC__)
1249
 
#if not __GLIBC_PREREQ(2, 16)
1250
 
#pragma GCC diagnostic push
1251
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1252
 
#endif
1253
 
#endif
1254
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1255
 
                                                         warning from
1256
 
                                                         -Wconversion
1257
 
                                                         in GNU libc
1258
 
                                                         before
1259
 
                                                         2.16 */
1260
 
#if defined (__GNUC__) and defined (__GLIBC__)
1261
 
#if not __GLIBC_PREREQ(2, 16)
1262
 
#pragma GCC diagnostic pop
1263
 
#endif
1264
 
#endif
 
1270
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1265
1271
        /* This process had nothing to say at this time */
1266
1272
        proc = proc->next;
1267
1273
        continue;
1334
1340
    free(custom_argv);
1335
1341
  }
1336
1342
  
1337
 
  if(dir != NULL){
1338
 
    closedir(dir);
 
1343
  free(direntries);
 
1344
  
 
1345
  if(dir_fd != -1){
 
1346
    close(dir_fd);
1339
1347
  }
1340
1348
  
1341
1349
  /* Kill the processes */
1361
1369
  free_plugin_list();
1362
1370
  
1363
1371
  free(plugindir);
 
1372
  free(pluginhelperdir);
1364
1373
  free(argfile);
1365
1374
  
1366
1375
  return exitstatus;