/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
 
      /* FALLTHROUGH */
590
555
    default:
591
556
      return ARGP_ERR_UNKNOWN;
592
557
    }
611
576
    case 129:                   /* --config-file */
612
577
      free(argfile);
613
578
      argfile = strdup(arg);
614
 
      if(argfile != NULL){
615
 
        errno = 0;
616
 
      }
617
579
      break;
618
580
    case 130:                   /* --userid */
619
581
    case 131:                   /* --groupid */
620
582
    case 132:                   /* --debug */
621
 
    case 133:                   /* --plugin-helper-dir */
622
583
    case '?':                   /* --help */
623
584
    case -3:                    /* --usage */
624
585
    case 'V':                   /* --version */
703
664
        }
704
665
        
705
666
        custom_argc += 1;
706
 
        {
707
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
708
 
                                    * ((size_t)custom_argc + 1));
709
 
          if(new_argv == NULL){
710
 
            error(0, errno, "realloc");
711
 
            exitstatus = EX_OSERR;
712
 
            free(new_arg);
713
 
            free(org_line);
714
 
            goto fallback;
715
 
          } else {
716
 
            custom_argv = new_argv;
717
 
          }
 
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;
718
674
        }
719
675
        custom_argv[custom_argc-1] = new_arg;
720
676
        custom_argv[custom_argc] = NULL;
778
734
    goto fallback;
779
735
  }
780
736
  
781
 
  {
782
 
    char *pluginhelperenv;
783
 
    bool bret = true;
784
 
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
785
 
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
786
 
    if(ret != -1){
787
 
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
788
 
    }
789
 
    if(ret == -1 or not bret){
790
 
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
791
 
            " environment variable to \"%s\" for all plugins\n",
792
 
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
793
 
    }
794
 
    if(ret != -1){
795
 
      free(pluginhelperenv);
796
 
    }
797
 
  }
798
 
  
799
737
  if(debug){
800
 
    for(plugin *p = plugin_list; p != NULL; p = p->next){
 
738
    for(plugin *p = plugin_list; p != NULL; p=p->next){
801
739
      fprintf(stderr, "Plugin: %s has %d arguments\n",
802
740
              p->name ? p->name : "Global", p->argc - 1);
803
741
      for(char **a = p->argv; *a != NULL; a++){
812
750
  
813
751
  if(getuid() == 0){
814
752
    /* Work around Debian bug #633582:
815
 
       <https://bugs.debian.org/633582> */
 
753
       <http://bugs.debian.org/633582> */
816
754
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
817
755
    if(plugindir_fd == -1){
818
 
      if(errno != ENOENT){
819
 
        error(0, errno, "open(\"" PDIR "\")");
820
 
      }
 
756
      error(0, errno, "open");
821
757
    } else {
822
758
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
823
759
      if(ret == -1){
830
766
          }
831
767
        }
832
768
      }
833
 
      close(plugindir_fd);
 
769
      TEMP_FAILURE_RETRY(close(plugindir_fd));
834
770
    }
835
771
  }
836
772
  
837
773
  /* Lower permissions */
838
 
  ret = setgid(gid);
 
774
  setgid(gid);
839
775
  if(ret == -1){
840
776
    error(0, errno, "setgid");
841
777
  }
846
782
  
847
783
  /* Open plugin directory with close_on_exec flag */
848
784
  {
849
 
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
850
 
#ifdef O_CLOEXEC
851
 
                  O_CLOEXEC
852
 
#else  /* not O_CLOEXEC */
853
 
                  0
854
 
#endif  /* not O_CLOEXEC */
855
 
                  );
 
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
    }
856
803
    if(dir_fd == -1){
857
804
      error(0, errno, "Could not open plugin dir");
858
805
      exitstatus = EX_UNAVAILABLE;
864
811
    ret = set_cloexec_flag(dir_fd);
865
812
    if(ret < 0){
866
813
      error(0, errno, "set_cloexec_flag");
 
814
      TEMP_FAILURE_RETRY(close(dir_fd));
867
815
      exitstatus = EX_OSERR;
868
816
      goto fallback;
869
817
    }
870
818
#endif  /* O_CLOEXEC */
871
 
  }
872
 
  
873
 
  int good_name(const struct dirent * const dirent){
874
 
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
875
 
                                      "*.dpkg-old", "*.dpkg-bak",
876
 
                                      "*.dpkg-divert", NULL };
877
 
#ifdef __GNUC__
878
 
#pragma GCC diagnostic push
879
 
#pragma GCC diagnostic ignored "-Wcast-qual"
880
 
#endif
881
 
    for(const char **pat = (const char **)patterns;
882
 
        *pat != NULL; pat++){
883
 
#ifdef __GNUC__
884
 
#pragma GCC diagnostic pop
885
 
#endif
886
 
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
887
 
         != FNM_NOMATCH){
888
 
        if(debug){
889
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
890
 
                    " matching pattern %s\n", dirent->d_name, *pat);
891
 
        }
892
 
        return 0;
893
 
      }
 
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;
894
826
    }
895
 
    return 1;
896
 
  }
897
 
  
898
 
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
899
 
                             alphasort);
900
 
  if(numplugins == -1){
901
 
    error(0, errno, "Could not scan plugin dir");
902
 
    direntries = NULL;
903
 
    exitstatus = EX_OSERR;
904
 
    goto fallback;
905
827
  }
906
828
  
907
829
  FD_ZERO(&rfds_all);
908
830
  
909
831
  /* Read and execute any executable in the plugin directory*/
910
 
  for(int i = 0; i < numplugins; i++){
911
 
    
912
 
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
913
 
    if(plugin_fd == -1){
914
 
      error(0, errno, "Could not open plugin");
915
 
      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");
916
904
      continue;
917
905
    }
918
 
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
 
906
    
 
907
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
919
908
    if(ret == -1){
920
909
      error(0, errno, "stat");
921
 
      close(plugin_fd);
922
 
      free(direntries[i]);
 
910
      free(filename);
923
911
      continue;
924
912
    }
925
913
    
926
914
    /* Ignore non-executable files */
927
915
    if(not S_ISREG(st.st_mode)
928
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
929
 
                                        X_OK, 0)) != 0)){
 
916
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
930
917
      if(debug){
931
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
932
 
                " with bad type or mode\n",
933
 
                plugindir != NULL ? plugindir : PDIR,
934
 
                direntries[i]->d_name);
 
918
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
919
                " with bad type or mode\n", filename);
935
920
      }
936
 
      close(plugin_fd);
937
 
      free(direntries[i]);
 
921
      free(filename);
938
922
      continue;
939
923
    }
940
924
    
941
 
    plugin *p = getplugin(direntries[i]->d_name);
 
925
    plugin *p = getplugin(dirst->d_name);
942
926
    if(p == NULL){
943
927
      error(0, errno, "getplugin");
944
 
      close(plugin_fd);
945
 
      free(direntries[i]);
 
928
      free(filename);
946
929
      continue;
947
930
    }
948
931
    if(p->disabled){
949
932
      if(debug){
950
933
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
951
 
                direntries[i]->d_name);
 
934
                dirst->d_name);
952
935
      }
953
 
      close(plugin_fd);
954
 
      free(direntries[i]);
 
936
      free(filename);
955
937
      continue;
956
938
    }
957
939
    {
971
953
        }
972
954
      }
973
955
    }
974
 
    /* If this plugin has any environment variables, we need to
975
 
       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. */
976
959
    if(p->environ[0] != NULL){
977
960
      for(char **e = environ; *e != NULL; e++){
978
961
        if(not add_environment(p, *e, false)){
982
965
    }
983
966
    
984
967
    int pipefd[2];
985
 
#ifndef O_CLOEXEC
986
968
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
987
 
#else  /* O_CLOEXEC */
988
 
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
989
 
#endif  /* O_CLOEXEC */
990
969
    if(ret == -1){
991
970
      error(0, errno, "pipe");
992
971
      exitstatus = EX_OSERR;
993
 
      free(direntries[i]);
994
 
      goto fallback;
995
 
    }
996
 
    if(pipefd[0] >= FD_SETSIZE){
997
 
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
998
 
              FD_SETSIZE);
999
 
      close(pipefd[0]);
1000
 
      close(pipefd[1]);
1001
 
      exitstatus = EX_OSERR;
1002
 
      free(direntries[i]);
1003
 
      goto fallback;
1004
 
    }
1005
 
#ifndef O_CLOEXEC
 
972
      goto fallback;
 
973
    }
1006
974
    /* Ask OS to automatic close the pipe on exec */
1007
975
    ret = set_cloexec_flag(pipefd[0]);
1008
976
    if(ret < 0){
1009
977
      error(0, errno, "set_cloexec_flag");
1010
 
      close(pipefd[0]);
1011
 
      close(pipefd[1]);
1012
978
      exitstatus = EX_OSERR;
1013
 
      free(direntries[i]);
1014
979
      goto fallback;
1015
980
    }
1016
981
    ret = set_cloexec_flag(pipefd[1]);
1017
982
    if(ret < 0){
1018
983
      error(0, errno, "set_cloexec_flag");
1019
 
      close(pipefd[0]);
1020
 
      close(pipefd[1]);
1021
984
      exitstatus = EX_OSERR;
1022
 
      free(direntries[i]);
1023
985
      goto fallback;
1024
986
    }
1025
 
#endif  /* not O_CLOEXEC */
1026
987
    /* Block SIGCHLD until process is safely in process list */
1027
988
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1028
989
                                              &sigchld_action.sa_mask,
1030
991
    if(ret < 0){
1031
992
      error(0, errno, "sigprocmask");
1032
993
      exitstatus = EX_OSERR;
1033
 
      free(direntries[i]);
1034
994
      goto fallback;
1035
995
    }
1036
996
    /* Starting a new process to be watched */
1040
1000
    } while(pid == -1 and errno == EINTR);
1041
1001
    if(pid == -1){
1042
1002
      error(0, errno, "fork");
1043
 
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1044
 
                                     &sigchld_action.sa_mask, NULL));
1045
 
      close(pipefd[0]);
1046
 
      close(pipefd[1]);
1047
1003
      exitstatus = EX_OSERR;
1048
 
      free(direntries[i]);
1049
1004
      goto fallback;
1050
1005
    }
1051
1006
    if(pid == 0){
1067
1022
        _exit(EX_OSERR);
1068
1023
      }
1069
1024
      
1070
 
      if(fexecve(plugin_fd, p->argv,
1071
 
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1072
 
        error(0, errno, "fexecve for %s/%s",
1073
 
              plugindir != NULL ? plugindir : PDIR,
1074
 
              direntries[i]->d_name);
1075
 
        _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
        }
1076
1040
      }
1077
1041
      /* no return */
1078
1042
    }
1079
1043
    /* Parent process */
1080
 
    close(pipefd[1]);           /* Close unused write end of pipe */
1081
 
    close(plugin_fd);
1082
 
    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);
1083
1048
    if(new_plugin == NULL){
1084
1049
      error(0, errno, "getplugin");
1085
1050
      ret = (int)(TEMP_FAILURE_RETRY
1089
1054
        error(0, errno, "sigprocmask");
1090
1055
      }
1091
1056
      exitstatus = EX_OSERR;
1092
 
      free(direntries[i]);
1093
1057
      goto fallback;
1094
1058
    }
1095
 
    free(direntries[i]);
1096
1059
    
1097
1060
    new_plugin->pid = pid;
1098
1061
    new_plugin->fd = pipefd[0];
1099
 
 
1100
 
    if(debug){
1101
 
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
1102
 
              new_plugin->name, (intmax_t) (new_plugin->pid));
1103
 
    }
1104
 
 
 
1062
    
1105
1063
    /* Unblock SIGCHLD so signal handler can be run if this process
1106
1064
       has already completed */
1107
1065
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1113
1071
      goto fallback;
1114
1072
    }
1115
1073
    
1116
 
    FD_SET(new_plugin->fd, &rfds_all);
 
1074
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
 
1075
                                          -Wconversion */
1117
1076
    
1118
1077
    if(maxfd < new_plugin->fd){
1119
1078
      maxfd = new_plugin->fd;
1120
1079
    }
1121
1080
  }
1122
1081
  
1123
 
  free(direntries);
1124
 
  direntries = NULL;
1125
 
  close(dir_fd);
1126
 
  dir_fd = -1;
 
1082
  TEMP_FAILURE_RETRY(closedir(dir));
 
1083
  dir = NULL;
1127
1084
  free_plugin(getplugin(NULL));
1128
1085
  
1129
1086
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1168
1125
                      (intmax_t) (proc->pid),
1169
1126
                      WTERMSIG(proc->status),
1170
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));
1171
1131
            }
1172
1132
          }
1173
1133
          
1174
1134
          /* Remove the plugin */
1175
 
          FD_CLR(proc->fd, &rfds_all);
 
1135
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
 
1136
                                          -Wconversion */
1176
1137
          
1177
1138
          /* Block signal while modifying process_list */
1178
1139
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1218
1179
      }
1219
1180
      
1220
1181
      /* This process has not completed.  Does it have any output? */
1221
 
      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 */
1222
1185
        /* This process had nothing to say at this time */
1223
1186
        proc = proc->next;
1224
1187
        continue;
1225
1188
      }
1226
1189
      /* Before reading, make the process' data buffer large enough */
1227
1190
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1228
 
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
1229
 
                                   + (size_t) BUFFER_SIZE);
1230
 
        if(new_buffer == NULL){
 
1191
        proc->buffer = realloc(proc->buffer, proc->buffer_size
 
1192
                               + (size_t) BUFFER_SIZE);
 
1193
        if(proc->buffer == NULL){
1231
1194
          error(0, errno, "malloc");
1232
1195
          exitstatus = EX_OSERR;
1233
1196
          goto fallback;
1234
1197
        }
1235
 
        proc->buffer = new_buffer;
1236
1198
        proc->buffer_size += BUFFER_SIZE;
1237
1199
      }
1238
1200
      /* Read from the process */
1291
1253
    free(custom_argv);
1292
1254
  }
1293
1255
  
1294
 
  free(direntries);
1295
 
  
1296
 
  if(dir_fd != -1){
1297
 
    close(dir_fd);
 
1256
  if(dir != NULL){
 
1257
    closedir(dir);
1298
1258
  }
1299
1259
  
1300
1260
  /* Kill the processes */
1320
1280
  free_plugin_list();
1321
1281
  
1322
1282
  free(plugindir);
1323
 
  free(pluginhelperdir);
1324
1283
  free(argfile);
1325
1284
  
1326
1285
  return exitstatus;