/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){
313
306
__attribute__((nonnull))
314
307
static void free_plugin(plugin *plugin_node){
315
308
  
316
 
  for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){
 
309
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
317
310
    free(*arg);
318
311
  }
319
 
  free(plugin_node->name);
320
312
  free(plugin_node->argv);
321
313
  for(char **env = plugin_node->environ; *env != NULL; env++){
322
314
    free(*env);
349
341
 
350
342
int main(int argc, char *argv[]){
351
343
  char *plugindir = NULL;
352
 
  char *pluginhelperdir = NULL;
353
344
  char *argfile = NULL;
354
345
  FILE *conffp;
355
 
  struct dirent **direntries = NULL;
 
346
  size_t d_name_len;
 
347
  DIR *dir = NULL;
 
348
  struct dirent *dirst;
356
349
  struct stat st;
357
350
  fd_set rfds_all;
358
351
  int ret, maxfd = 0;
366
359
                                      .sa_flags = SA_NOCLDSTOP };
367
360
  char **custom_argv = NULL;
368
361
  int custom_argc = 0;
369
 
  int dir_fd = -1;
370
362
  
371
363
  /* Establish a signal handler */
372
364
  sigemptyset(&sigchld_action.sa_mask);
417
409
      .doc = "Group ID the plugins will run as", .group = 3 },
418
410
    { .name = "debug", .key = 132,
419
411
      .doc = "Debug mode", .group = 4 },
420
 
    { .name = "plugin-helper-dir", .key = 133,
421
 
      .arg = "DIRECTORY",
422
 
      .doc = "Specify a different plugin helper directory",
423
 
      .group = 2 },
424
412
    /*
425
413
     * These reproduce what we would get without ARGP_NO_HELP
426
414
     */
447
435
            break;
448
436
          }
449
437
        }
450
 
        errno = 0;
451
438
      }
452
439
      break;
453
440
    case 'G':                   /* --global-env */
454
 
      if(add_environment(getplugin(NULL), arg, true)){
455
 
        errno = 0;
456
 
      }
 
441
      add_environment(getplugin(NULL), arg, true);
457
442
      break;
458
443
    case 'o':                   /* --options-for */
459
444
      {
476
461
            break;
477
462
          }
478
463
        }
479
 
        errno = 0;
480
464
      }
481
465
      break;
482
466
    case 'E':                   /* --env-for */
494
478
          errno = EINVAL;
495
479
          break;
496
480
        }
497
 
        if(add_environment(getplugin(arg), envdef, true)){
498
 
          errno = 0;
499
 
        }
 
481
        add_environment(getplugin(arg), envdef, true);
500
482
      }
501
483
      break;
502
484
    case 'd':                   /* --disable */
504
486
        plugin *p = getplugin(arg);
505
487
        if(p != NULL){
506
488
          p->disabled = true;
507
 
          errno = 0;
508
489
        }
509
490
      }
510
491
      break;
513
494
        plugin *p = getplugin(arg);
514
495
        if(p != NULL){
515
496
          p->disabled = false;
516
 
          errno = 0;
517
497
        }
518
498
      }
519
499
      break;
520
500
    case 128:                   /* --plugin-dir */
521
501
      free(plugindir);
522
502
      plugindir = strdup(arg);
523
 
      if(plugindir != NULL){
524
 
        errno = 0;
525
 
      }
526
503
      break;
527
504
    case 129:                   /* --config-file */
528
505
      /* This is already done by parse_opt_config_file() */
536
513
        break;
537
514
      }
538
515
      uid = (uid_t)tmp_id;
539
 
      errno = 0;
540
516
      break;
541
517
    case 131:                   /* --groupid */
542
518
      tmp_id = strtoimax(arg, &tmp, 10);
547
523
        break;
548
524
      }
549
525
      gid = (gid_t)tmp_id;
550
 
      errno = 0;
551
526
      break;
552
527
    case 132:                   /* --debug */
553
528
      debug = true;
554
529
      break;
555
 
    case 133:                   /* --plugin-helper-dir */
556
 
      free(pluginhelperdir);
557
 
      pluginhelperdir = strdup(arg);
558
 
      if(pluginhelperdir != NULL){
559
 
        errno = 0;
560
 
      }
561
 
      break;
562
530
      /*
563
531
       * These reproduce what we would get without ARGP_NO_HELP
564
532
       */
565
533
    case '?':                   /* --help */
566
534
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
567
535
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
568
 
      __builtin_unreachable();
569
536
    case -3:                    /* --usage */
570
537
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
571
538
      argp_state_help(state, state->out_stream,
572
539
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
573
 
      __builtin_unreachable();
574
540
    case 'V':                   /* --version */
575
541
      fprintf(state->out_stream, "%s\n", argp_program_version);
576
542
      exit(EXIT_SUCCESS);
586
552
      if(arg[0] == '\0'){
587
553
        break;
588
554
      }
589
 
#if __GNUC__ >= 7
590
 
      __attribute__((fallthrough));
591
 
#else
592
 
          /* FALLTHROUGH */
593
 
#endif
594
555
    default:
595
556
      return ARGP_ERR_UNKNOWN;
596
557
    }
615
576
    case 129:                   /* --config-file */
616
577
      free(argfile);
617
578
      argfile = strdup(arg);
618
 
      if(argfile != NULL){
619
 
        errno = 0;
620
 
      }
621
579
      break;
622
580
    case 130:                   /* --userid */
623
581
    case 131:                   /* --groupid */
624
582
    case 132:                   /* --debug */
625
 
    case 133:                   /* --plugin-helper-dir */
626
583
    case '?':                   /* --help */
627
584
    case -3:                    /* --usage */
628
585
    case 'V':                   /* --version */
707
664
        }
708
665
        
709
666
        custom_argc += 1;
710
 
        {
711
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
712
 
                                    * ((size_t)custom_argc + 1));
713
 
          if(new_argv == NULL){
714
 
            error(0, errno, "realloc");
715
 
            exitstatus = EX_OSERR;
716
 
            free(new_arg);
717
 
            free(org_line);
718
 
            goto fallback;
719
 
          } else {
720
 
            custom_argv = new_argv;
721
 
          }
 
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;
722
674
        }
723
675
        custom_argv[custom_argc-1] = new_arg;
724
676
        custom_argv[custom_argc] = NULL;
782
734
    goto fallback;
783
735
  }
784
736
  
785
 
  {
786
 
    char *pluginhelperenv;
787
 
    bool bret = true;
788
 
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
789
 
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
790
 
    if(ret != -1){
791
 
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
792
 
    }
793
 
    if(ret == -1 or not bret){
794
 
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
795
 
            " environment variable to \"%s\" for all plugins\n",
796
 
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
797
 
    }
798
 
    if(ret != -1){
799
 
      free(pluginhelperenv);
800
 
    }
801
 
  }
802
 
  
803
737
  if(debug){
804
 
    for(plugin *p = plugin_list; p != NULL; p = p->next){
 
738
    for(plugin *p = plugin_list; p != NULL; p=p->next){
805
739
      fprintf(stderr, "Plugin: %s has %d arguments\n",
806
740
              p->name ? p->name : "Global", p->argc - 1);
807
741
      for(char **a = p->argv; *a != NULL; a++){
816
750
  
817
751
  if(getuid() == 0){
818
752
    /* Work around Debian bug #633582:
819
 
       <https://bugs.debian.org/633582> */
 
753
       <http://bugs.debian.org/633582> */
820
754
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
821
755
    if(plugindir_fd == -1){
822
 
      if(errno != ENOENT){
823
 
        error(0, errno, "open(\"" PDIR "\")");
824
 
      }
 
756
      error(0, errno, "open");
825
757
    } else {
826
758
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
827
759
      if(ret == -1){
834
766
          }
835
767
        }
836
768
      }
837
 
      close(plugindir_fd);
 
769
      TEMP_FAILURE_RETRY(close(plugindir_fd));
838
770
    }
839
771
  }
840
772
  
841
773
  /* Lower permissions */
842
 
  ret = setgid(gid);
 
774
  setgid(gid);
843
775
  if(ret == -1){
844
776
    error(0, errno, "setgid");
845
777
  }
850
782
  
851
783
  /* Open plugin directory with close_on_exec flag */
852
784
  {
853
 
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
854
 
#ifdef O_CLOEXEC
855
 
                  O_CLOEXEC
856
 
#else  /* not O_CLOEXEC */
857
 
                  0
858
 
#endif  /* not O_CLOEXEC */
859
 
                  );
 
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
    }
860
803
    if(dir_fd == -1){
861
804
      error(0, errno, "Could not open plugin dir");
862
805
      exitstatus = EX_UNAVAILABLE;
868
811
    ret = set_cloexec_flag(dir_fd);
869
812
    if(ret < 0){
870
813
      error(0, errno, "set_cloexec_flag");
 
814
      TEMP_FAILURE_RETRY(close(dir_fd));
871
815
      exitstatus = EX_OSERR;
872
816
      goto fallback;
873
817
    }
874
818
#endif  /* O_CLOEXEC */
875
 
  }
876
 
  
877
 
  int good_name(const struct dirent * const dirent){
878
 
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
879
 
                                      "*.dpkg-old", "*.dpkg-bak",
880
 
                                      "*.dpkg-divert", NULL };
881
 
#ifdef __GNUC__
882
 
#pragma GCC diagnostic push
883
 
#pragma GCC diagnostic ignored "-Wcast-qual"
884
 
#endif
885
 
    for(const char **pat = (const char **)patterns;
886
 
        *pat != NULL; pat++){
887
 
#ifdef __GNUC__
888
 
#pragma GCC diagnostic pop
889
 
#endif
890
 
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
891
 
         != FNM_NOMATCH){
892
 
        if(debug){
893
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
894
 
                    " matching pattern %s\n", dirent->d_name, *pat);
895
 
        }
896
 
        return 0;
897
 
      }
 
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;
898
826
    }
899
 
    return 1;
900
 
  }
901
 
  
902
 
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
903
 
                             alphasort);
904
 
  if(numplugins == -1){
905
 
    error(0, errno, "Could not scan plugin dir");
906
 
    direntries = NULL;
907
 
    exitstatus = EX_OSERR;
908
 
    goto fallback;
909
827
  }
910
828
  
911
829
  FD_ZERO(&rfds_all);
912
830
  
913
831
  /* Read and execute any executable in the plugin directory*/
914
 
  for(int i = 0; i < numplugins; i++){
915
 
    
916
 
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
917
 
    if(plugin_fd == -1){
918
 
      error(0, errno, "Could not open plugin");
919
 
      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");
920
904
      continue;
921
905
    }
922
 
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
 
906
    
 
907
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
923
908
    if(ret == -1){
924
909
      error(0, errno, "stat");
925
 
      close(plugin_fd);
926
 
      free(direntries[i]);
 
910
      free(filename);
927
911
      continue;
928
912
    }
929
913
    
930
914
    /* Ignore non-executable files */
931
915
    if(not S_ISREG(st.st_mode)
932
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
933
 
                                        X_OK, 0)) != 0)){
 
916
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
934
917
      if(debug){
935
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
936
 
                " with bad type or mode\n",
937
 
                plugindir != NULL ? plugindir : PDIR,
938
 
                direntries[i]->d_name);
 
918
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
919
                " with bad type or mode\n", filename);
939
920
      }
940
 
      close(plugin_fd);
941
 
      free(direntries[i]);
 
921
      free(filename);
942
922
      continue;
943
923
    }
944
924
    
945
 
    plugin *p = getplugin(direntries[i]->d_name);
 
925
    plugin *p = getplugin(dirst->d_name);
946
926
    if(p == NULL){
947
927
      error(0, errno, "getplugin");
948
 
      close(plugin_fd);
949
 
      free(direntries[i]);
 
928
      free(filename);
950
929
      continue;
951
930
    }
952
931
    if(p->disabled){
953
932
      if(debug){
954
933
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
955
 
                direntries[i]->d_name);
 
934
                dirst->d_name);
956
935
      }
957
 
      close(plugin_fd);
958
 
      free(direntries[i]);
 
936
      free(filename);
959
937
      continue;
960
938
    }
961
939
    {
975
953
        }
976
954
      }
977
955
    }
978
 
    /* If this plugin has any environment variables, we need to
979
 
       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. */
980
959
    if(p->environ[0] != NULL){
981
960
      for(char **e = environ; *e != NULL; e++){
982
961
        if(not add_environment(p, *e, false)){
986
965
    }
987
966
    
988
967
    int pipefd[2];
989
 
#ifndef O_CLOEXEC
990
968
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
991
 
#else  /* O_CLOEXEC */
992
 
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
993
 
#endif  /* O_CLOEXEC */
994
969
    if(ret == -1){
995
970
      error(0, errno, "pipe");
996
971
      exitstatus = EX_OSERR;
997
 
      free(direntries[i]);
998
 
      goto fallback;
999
 
    }
1000
 
    if(pipefd[0] >= FD_SETSIZE){
1001
 
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
1002
 
              FD_SETSIZE);
1003
 
      close(pipefd[0]);
1004
 
      close(pipefd[1]);
1005
 
      exitstatus = EX_OSERR;
1006
 
      free(direntries[i]);
1007
 
      goto fallback;
1008
 
    }
1009
 
#ifndef O_CLOEXEC
 
972
      goto fallback;
 
973
    }
1010
974
    /* Ask OS to automatic close the pipe on exec */
1011
975
    ret = set_cloexec_flag(pipefd[0]);
1012
976
    if(ret < 0){
1013
977
      error(0, errno, "set_cloexec_flag");
1014
 
      close(pipefd[0]);
1015
 
      close(pipefd[1]);
1016
978
      exitstatus = EX_OSERR;
1017
 
      free(direntries[i]);
1018
979
      goto fallback;
1019
980
    }
1020
981
    ret = set_cloexec_flag(pipefd[1]);
1021
982
    if(ret < 0){
1022
983
      error(0, errno, "set_cloexec_flag");
1023
 
      close(pipefd[0]);
1024
 
      close(pipefd[1]);
1025
984
      exitstatus = EX_OSERR;
1026
 
      free(direntries[i]);
1027
985
      goto fallback;
1028
986
    }
1029
 
#endif  /* not O_CLOEXEC */
1030
987
    /* Block SIGCHLD until process is safely in process list */
1031
988
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1032
989
                                              &sigchld_action.sa_mask,
1034
991
    if(ret < 0){
1035
992
      error(0, errno, "sigprocmask");
1036
993
      exitstatus = EX_OSERR;
1037
 
      free(direntries[i]);
1038
994
      goto fallback;
1039
995
    }
1040
996
    /* Starting a new process to be watched */
1044
1000
    } while(pid == -1 and errno == EINTR);
1045
1001
    if(pid == -1){
1046
1002
      error(0, errno, "fork");
1047
 
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1048
 
                                     &sigchld_action.sa_mask, NULL));
1049
 
      close(pipefd[0]);
1050
 
      close(pipefd[1]);
1051
1003
      exitstatus = EX_OSERR;
1052
 
      free(direntries[i]);
1053
1004
      goto fallback;
1054
1005
    }
1055
1006
    if(pid == 0){
1071
1022
        _exit(EX_OSERR);
1072
1023
      }
1073
1024
      
1074
 
      if(fexecve(plugin_fd, p->argv,
1075
 
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1076
 
        error(0, errno, "fexecve for %s/%s",
1077
 
              plugindir != NULL ? plugindir : PDIR,
1078
 
              direntries[i]->d_name);
1079
 
        _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
        }
1080
1040
      }
1081
1041
      /* no return */
1082
1042
    }
1083
1043
    /* Parent process */
1084
 
    close(pipefd[1]);           /* Close unused write end of pipe */
1085
 
    close(plugin_fd);
1086
 
    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);
1087
1048
    if(new_plugin == NULL){
1088
1049
      error(0, errno, "getplugin");
1089
1050
      ret = (int)(TEMP_FAILURE_RETRY
1093
1054
        error(0, errno, "sigprocmask");
1094
1055
      }
1095
1056
      exitstatus = EX_OSERR;
1096
 
      free(direntries[i]);
1097
1057
      goto fallback;
1098
1058
    }
1099
 
    free(direntries[i]);
1100
1059
    
1101
1060
    new_plugin->pid = pid;
1102
1061
    new_plugin->fd = pipefd[0];
1103
 
 
1104
 
    if(debug){
1105
 
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
1106
 
              new_plugin->name, (intmax_t) (new_plugin->pid));
1107
 
    }
1108
 
 
 
1062
    
1109
1063
    /* Unblock SIGCHLD so signal handler can be run if this process
1110
1064
       has already completed */
1111
1065
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1117
1071
      goto fallback;
1118
1072
    }
1119
1073
    
1120
 
    FD_SET(new_plugin->fd, &rfds_all);
 
1074
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
 
1075
                                          -Wconversion */
1121
1076
    
1122
1077
    if(maxfd < new_plugin->fd){
1123
1078
      maxfd = new_plugin->fd;
1124
1079
    }
1125
1080
  }
1126
1081
  
1127
 
  free(direntries);
1128
 
  direntries = NULL;
1129
 
  close(dir_fd);
1130
 
  dir_fd = -1;
 
1082
  TEMP_FAILURE_RETRY(closedir(dir));
 
1083
  dir = NULL;
1131
1084
  free_plugin(getplugin(NULL));
1132
1085
  
1133
1086
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1172
1125
                      (intmax_t) (proc->pid),
1173
1126
                      WTERMSIG(proc->status),
1174
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));
1175
1131
            }
1176
1132
          }
1177
1133
          
1178
1134
          /* Remove the plugin */
1179
 
          FD_CLR(proc->fd, &rfds_all);
 
1135
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
 
1136
                                          -Wconversion */
1180
1137
          
1181
1138
          /* Block signal while modifying process_list */
1182
1139
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1222
1179
      }
1223
1180
      
1224
1181
      /* This process has not completed.  Does it have any output? */
1225
 
      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 */
1226
1185
        /* This process had nothing to say at this time */
1227
1186
        proc = proc->next;
1228
1187
        continue;
1229
1188
      }
1230
1189
      /* Before reading, make the process' data buffer large enough */
1231
1190
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1232
 
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
1233
 
                                   + (size_t) BUFFER_SIZE);
1234
 
        if(new_buffer == NULL){
 
1191
        proc->buffer = realloc(proc->buffer, proc->buffer_size
 
1192
                               + (size_t) BUFFER_SIZE);
 
1193
        if(proc->buffer == NULL){
1235
1194
          error(0, errno, "malloc");
1236
1195
          exitstatus = EX_OSERR;
1237
1196
          goto fallback;
1238
1197
        }
1239
 
        proc->buffer = new_buffer;
1240
1198
        proc->buffer_size += BUFFER_SIZE;
1241
1199
      }
1242
1200
      /* Read from the process */
1295
1253
    free(custom_argv);
1296
1254
  }
1297
1255
  
1298
 
  free(direntries);
1299
 
  
1300
 
  if(dir_fd != -1){
1301
 
    close(dir_fd);
 
1256
  if(dir != NULL){
 
1257
    closedir(dir);
1302
1258
  }
1303
1259
  
1304
1260
  /* Kill the processes */
1324
1280
  free_plugin_list();
1325
1281
  
1326
1282
  free(plugindir);
1327
 
  free(pluginhelperdir);
1328
1283
  free(argfile);
1329
1284
  
1330
1285
  return exitstatus;