/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,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
 
5
 * Copyright © 2008-2011 Teddy Hogeborn
 
6
 * Copyright © 2008-2011 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
19
19
 * along with this program.  If not, see
20
20
 * <http://www.gnu.org/licenses/>.
21
21
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
22
 * Contact the authors at <mandos@recompile.se>.
23
23
 */
24
24
 
25
25
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
26
                                   asprintf(), O_CLOEXEC */
27
27
#include <stddef.h>             /* size_t, NULL */
28
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
29
 
                                   EXIT_SUCCESS, realloc() */
 
28
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
 
29
                                   realloc() */
30
30
#include <stdbool.h>            /* bool, true, false */
31
 
#include <stdio.h>              /* perror, fileno(), fprintf(),
 
31
#include <stdio.h>              /* fileno(), fprintf(),
32
32
                                   stderr, STDOUT_FILENO */
33
33
#include <sys/types.h>          /* DIR, fdopendir(), stat(), struct
34
34
                                   stat, waitpid(), WIFEXITED(),
54
54
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
55
                                   FD_CLOEXEC */
56
56
#include <string.h>             /* strsep, strlen(), asprintf(),
57
 
                                   strsignal() */
 
57
                                   strsignal(), strcmp(), strncmp() */
58
58
#include <errno.h>              /* errno */
59
59
#include <argp.h>               /* struct argp_option, struct
60
60
                                   argp_state, struct argp,
68
68
                                */
69
69
#include <errno.h>              /* errno, EBADF */
70
70
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
71
 
#include <sysexits.h>           /* EX_OSERR, EX_USAGE */
 
71
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_IOERR,
 
72
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
 
73
#include <errno.h>              /* errno */
 
74
#include <error.h>              /* error() */
72
75
 
73
76
#define BUFFER_SIZE 256
74
77
 
76
79
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
77
80
 
78
81
const char *argp_program_version = "plugin-runner " VERSION;
79
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
82
const char *argp_program_bug_address = "<mandos@recompile.se>";
80
83
 
81
84
typedef struct plugin{
82
85
  char *name;                   /* can be NULL or any plugin name */
168
171
}
169
172
 
170
173
/* Helper function for add_argument and add_environment */
 
174
__attribute__((nonnull))
171
175
static bool add_to_char_array(const char *new, char ***array,
172
176
                              int *len){
173
177
  /* Resize the pointed-to array to hold one more pointer */
196
200
}
197
201
 
198
202
/* Add to a plugin's argument vector */
 
203
__attribute__((nonnull(2)))
199
204
static bool add_argument(plugin *p, const char *arg){
200
205
  if(p == NULL){
201
206
    return false;
204
209
}
205
210
 
206
211
/* Add to a plugin's environment */
 
212
__attribute__((nonnull(2)))
207
213
static bool add_environment(plugin *p, const char *def, bool replace){
208
214
  if(p == NULL){
209
215
    return false;
265
271
        /* No child processes */
266
272
        break;
267
273
      }
268
 
      perror("waitpid");
 
274
      error(0, errno, "waitpid");
269
275
    }
270
276
    
271
277
    /* A child exited, find it in process_list */
283
289
}
284
290
 
285
291
/* Prints out a password to stdout */
 
292
__attribute__((nonnull))
286
293
static bool print_out_password(const char *buffer, size_t length){
287
294
  ssize_t ret;
288
295
  for(size_t written = 0; written < length; written += (size_t)ret){
296
303
}
297
304
 
298
305
/* Removes and free a plugin from the plugin list */
 
306
__attribute__((nonnull))
299
307
static void free_plugin(plugin *plugin_node){
300
308
  
301
309
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
356
364
  sigemptyset(&sigchld_action.sa_mask);
357
365
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
358
366
  if(ret == -1){
359
 
    perror("sigaddset");
360
 
    exitstatus = EXIT_FAILURE;
 
367
    error(0, errno, "sigaddset");
 
368
    exitstatus = EX_OSERR;
361
369
    goto fallback;
362
370
  }
363
371
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
364
372
  if(ret == -1){
365
 
    perror("sigaction");
366
 
    exitstatus = EXIT_FAILURE;
 
373
    error(0, errno, "sigaction");
 
374
    exitstatus = EX_OSERR;
367
375
    goto fallback;
368
376
  }
369
377
  
413
421
    { .name = NULL }
414
422
  };
415
423
  
 
424
  __attribute__((nonnull(3)))
416
425
  error_t parse_opt(int key, char *arg, struct argp_state *state){
417
426
    errno = 0;
418
427
    switch(key){
419
428
      char *tmp;
420
 
      intmax_t tmpmax;
 
429
      intmax_t tmp_id;
421
430
    case 'g':                   /* --global-options */
422
431
      {
423
432
        char *plugin_option;
496
505
      /* This is already done by parse_opt_config_file() */
497
506
      break;
498
507
    case 130:                   /* --userid */
499
 
      tmpmax = strtoimax(arg, &tmp, 10);
 
508
      tmp_id = strtoimax(arg, &tmp, 10);
500
509
      if(errno != 0 or tmp == arg or *tmp != '\0'
501
 
         or tmpmax != (uid_t)tmpmax){
 
510
         or tmp_id != (uid_t)tmp_id){
502
511
        argp_error(state, "Bad user ID number: \"%s\", using %"
503
512
                   PRIdMAX, arg, (intmax_t)uid);
504
513
        break;
505
514
      }
506
 
      uid = (uid_t)tmpmax;
 
515
      uid = (uid_t)tmp_id;
507
516
      break;
508
517
    case 131:                   /* --groupid */
509
 
      tmpmax = strtoimax(arg, &tmp, 10);
 
518
      tmp_id = strtoimax(arg, &tmp, 10);
510
519
      if(errno != 0 or tmp == arg or *tmp != '\0'
511
 
         or tmpmax != (gid_t)tmpmax){
 
520
         or tmp_id != (gid_t)tmp_id){
512
521
        argp_error(state, "Bad group ID number: \"%s\", using %"
513
522
                   PRIdMAX, arg, (intmax_t)gid);
514
523
        break;
515
524
      }
516
 
      gid = (gid_t)tmpmax;
 
525
      gid = (gid_t)tmp_id;
517
526
      break;
518
527
    case 132:                   /* --debug */
519
528
      debug = true;
598
607
  case ENOMEM:
599
608
  default:
600
609
    errno = ret;
601
 
    perror("argp_parse");
 
610
    error(0, errno, "argp_parse");
602
611
    exitstatus = EX_OSERR;
603
612
    goto fallback;
604
613
  case EINVAL:
625
634
    custom_argc = 1;
626
635
    custom_argv = malloc(sizeof(char*) * 2);
627
636
    if(custom_argv == NULL){
628
 
      perror("malloc");
629
 
      exitstatus = EXIT_FAILURE;
 
637
      error(0, errno, "malloc");
 
638
      exitstatus = EX_OSERR;
630
639
      goto fallback;
631
640
    }
632
641
    custom_argv[0] = argv[0];
648
657
        }
649
658
        new_arg = strdup(p);
650
659
        if(new_arg == NULL){
651
 
          perror("strdup");
652
 
          exitstatus = EXIT_FAILURE;
 
660
          error(0, errno, "strdup");
 
661
          exitstatus = EX_OSERR;
653
662
          free(org_line);
654
663
          goto fallback;
655
664
        }
658
667
        custom_argv = realloc(custom_argv, sizeof(char *)
659
668
                              * ((unsigned int) custom_argc + 1));
660
669
        if(custom_argv == NULL){
661
 
          perror("realloc");
662
 
          exitstatus = EXIT_FAILURE;
 
670
          error(0, errno, "realloc");
 
671
          exitstatus = EX_OSERR;
663
672
          free(org_line);
664
673
          goto fallback;
665
674
        }
671
680
      ret = fclose(conffp);
672
681
    } while(ret == EOF and errno == EINTR);
673
682
    if(ret == EOF){
674
 
      perror("fclose");
675
 
      exitstatus = EXIT_FAILURE;
 
683
      error(0, errno, "fclose");
 
684
      exitstatus = EX_IOERR;
676
685
      goto fallback;
677
686
    }
678
687
    free(org_line);
680
689
    /* Check for harmful errors and go to fallback. Other errors might
681
690
       not affect opening plugins */
682
691
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
683
 
      perror("fopen");
684
 
      exitstatus = EXIT_FAILURE;
 
692
      error(0, errno, "fopen");
 
693
      exitstatus = EX_OSERR;
685
694
      goto fallback;
686
695
    }
687
696
  }
697
706
    case ENOMEM:
698
707
    default:
699
708
      errno = ret;
700
 
      perror("argp_parse");
 
709
      error(0, errno, "argp_parse");
701
710
      exitstatus = EX_OSERR;
702
711
      goto fallback;
703
712
    case EINVAL:
717
726
  case ENOMEM:
718
727
  default:
719
728
    errno = ret;
720
 
    perror("argp_parse");
 
729
    error(0, errno, "argp_parse");
721
730
    exitstatus = EX_OSERR;
722
731
    goto fallback;
723
732
  case EINVAL:
739
748
    }
740
749
  }
741
750
  
742
 
  /* Strip permissions down to nobody */
 
751
  if(getuid() == 0){
 
752
    /* Work around Debian bug #633582:
 
753
       <http://bugs.debian.org/633582> */
 
754
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
 
755
    if(plugindir_fd == -1){
 
756
      error(0, errno, "open");
 
757
    } else {
 
758
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
 
759
      if(ret == -1){
 
760
        error(0, errno, "fstat");
 
761
      } else {
 
762
        if(S_ISDIR(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
 
763
          ret = fchown(plugindir_fd, uid, gid);
 
764
          if(ret == -1){
 
765
            error(0, errno, "fchown");
 
766
          }
 
767
        }
 
768
      }
 
769
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
770
    }
 
771
  }
 
772
  
 
773
  /* Lower permissions */
743
774
  setgid(gid);
744
775
  if(ret == -1){
745
 
    perror("setgid");
 
776
    error(0, errno, "setgid");
746
777
  }
747
778
  ret = setuid(uid);
748
779
  if(ret == -1){
749
 
    perror("setuid");
 
780
    error(0, errno, "setuid");
750
781
  }
751
782
  
752
783
  /* Open plugin directory with close_on_exec flag */
770
801
                    );
771
802
    }
772
803
    if(dir_fd == -1){
773
 
      perror("Could not open plugin dir");
774
 
      exitstatus = EXIT_FAILURE;
 
804
      error(0, errno, "Could not open plugin dir");
 
805
      exitstatus = EX_UNAVAILABLE;
775
806
      goto fallback;
776
807
    }
777
808
    
779
810
  /* Set the FD_CLOEXEC flag on the directory */
780
811
    ret = set_cloexec_flag(dir_fd);
781
812
    if(ret < 0){
782
 
      perror("set_cloexec_flag");
 
813
      error(0, errno, "set_cloexec_flag");
783
814
      TEMP_FAILURE_RETRY(close(dir_fd));
784
 
      exitstatus = EXIT_FAILURE;
 
815
      exitstatus = EX_OSERR;
785
816
      goto fallback;
786
817
    }
787
818
#endif  /* O_CLOEXEC */
788
819
    
789
820
    dir = fdopendir(dir_fd);
790
821
    if(dir == NULL){
791
 
      perror("Could not open plugin dir");
 
822
      error(0, errno, "Could not open plugin dir");
792
823
      TEMP_FAILURE_RETRY(close(dir_fd));
793
 
      exitstatus = EXIT_FAILURE;
 
824
      exitstatus = EX_OSERR;
794
825
      goto fallback;
795
826
    }
796
827
  }
806
837
    /* All directory entries have been processed */
807
838
    if(dirst == NULL){
808
839
      if(errno == EBADF){
809
 
        perror("readdir");
810
 
        exitstatus = EXIT_FAILURE;
 
840
        error(0, errno, "readdir");
 
841
        exitstatus = EX_IOERR;
811
842
        goto fallback;
812
843
      }
813
844
      break;
843
874
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
844
875
        size_t suf_len = strlen(*suf);
845
876
        if((d_name_len >= suf_len)
846
 
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
 
877
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
847
878
                == 0)){
848
879
          if(debug){
849
880
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
869
900
                                             dirst->d_name));
870
901
    }
871
902
    if(ret < 0){
872
 
      perror("asprintf");
 
903
      error(0, errno, "asprintf");
873
904
      continue;
874
905
    }
875
906
    
876
907
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
877
908
    if(ret == -1){
878
 
      perror("stat");
 
909
      error(0, errno, "stat");
879
910
      free(filename);
880
911
      continue;
881
912
    }
893
924
    
894
925
    plugin *p = getplugin(dirst->d_name);
895
926
    if(p == NULL){
896
 
      perror("getplugin");
 
927
      error(0, errno, "getplugin");
897
928
      free(filename);
898
929
      continue;
899
930
    }
911
942
      if(g != NULL){
912
943
        for(char **a = g->argv + 1; *a != NULL; a++){
913
944
          if(not add_argument(p, *a)){
914
 
            perror("add_argument");
 
945
            error(0, errno, "add_argument");
915
946
          }
916
947
        }
917
948
        /* Add global environment variables */
918
949
        for(char **e = g->environ; *e != NULL; e++){
919
950
          if(not add_environment(p, *e, false)){
920
 
            perror("add_environment");
 
951
            error(0, errno, "add_environment");
921
952
          }
922
953
        }
923
954
      }
928
959
    if(p->environ[0] != NULL){
929
960
      for(char **e = environ; *e != NULL; e++){
930
961
        if(not add_environment(p, *e, false)){
931
 
          perror("add_environment");
 
962
          error(0, errno, "add_environment");
932
963
        }
933
964
      }
934
965
    }
936
967
    int pipefd[2];
937
968
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
938
969
    if(ret == -1){
939
 
      perror("pipe");
940
 
      exitstatus = EXIT_FAILURE;
 
970
      error(0, errno, "pipe");
 
971
      exitstatus = EX_OSERR;
941
972
      goto fallback;
942
973
    }
943
974
    /* Ask OS to automatic close the pipe on exec */
944
975
    ret = set_cloexec_flag(pipefd[0]);
945
976
    if(ret < 0){
946
 
      perror("set_cloexec_flag");
947
 
      exitstatus = EXIT_FAILURE;
 
977
      error(0, errno, "set_cloexec_flag");
 
978
      exitstatus = EX_OSERR;
948
979
      goto fallback;
949
980
    }
950
981
    ret = set_cloexec_flag(pipefd[1]);
951
982
    if(ret < 0){
952
 
      perror("set_cloexec_flag");
953
 
      exitstatus = EXIT_FAILURE;
 
983
      error(0, errno, "set_cloexec_flag");
 
984
      exitstatus = EX_OSERR;
954
985
      goto fallback;
955
986
    }
956
987
    /* Block SIGCHLD until process is safely in process list */
958
989
                                              &sigchld_action.sa_mask,
959
990
                                              NULL));
960
991
    if(ret < 0){
961
 
      perror("sigprocmask");
962
 
      exitstatus = EXIT_FAILURE;
 
992
      error(0, errno, "sigprocmask");
 
993
      exitstatus = EX_OSERR;
963
994
      goto fallback;
964
995
    }
965
996
    /* Starting a new process to be watched */
968
999
      pid = fork();
969
1000
    } while(pid == -1 and errno == EINTR);
970
1001
    if(pid == -1){
971
 
      perror("fork");
972
 
      exitstatus = EXIT_FAILURE;
 
1002
      error(0, errno, "fork");
 
1003
      exitstatus = EX_OSERR;
973
1004
      goto fallback;
974
1005
    }
975
1006
    if(pid == 0){
976
1007
      /* this is the child process */
977
1008
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
978
1009
      if(ret < 0){
979
 
        perror("sigaction");
980
 
        _exit(EXIT_FAILURE);
 
1010
        error(0, errno, "sigaction");
 
1011
        _exit(EX_OSERR);
981
1012
      }
982
1013
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
983
1014
      if(ret < 0){
984
 
        perror("sigprocmask");
985
 
        _exit(EXIT_FAILURE);
 
1015
        error(0, errno, "sigprocmask");
 
1016
        _exit(EX_OSERR);
986
1017
      }
987
1018
      
988
1019
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
989
1020
      if(ret == -1){
990
 
        perror("dup2");
991
 
        _exit(EXIT_FAILURE);
 
1021
        error(0, errno, "dup2");
 
1022
        _exit(EX_OSERR);
992
1023
      }
993
1024
      
994
1025
      if(dirfd(dir) < 0){
998
1029
      }
999
1030
      if(p->environ[0] == NULL){
1000
1031
        if(execv(filename, p->argv) < 0){
1001
 
          perror("execv");
1002
 
          _exit(EXIT_FAILURE);
 
1032
          error(0, errno, "execv for %s", filename);
 
1033
          _exit(EX_OSERR);
1003
1034
        }
1004
1035
      } else {
1005
1036
        if(execve(filename, p->argv, p->environ) < 0){
1006
 
          perror("execve");
1007
 
          _exit(EXIT_FAILURE);
 
1037
          error(0, errno, "execve for %s", filename);
 
1038
          _exit(EX_OSERR);
1008
1039
        }
1009
1040
      }
1010
1041
      /* no return */
1015
1046
    free(filename);
1016
1047
    plugin *new_plugin = getplugin(dirst->d_name);
1017
1048
    if(new_plugin == NULL){
1018
 
      perror("getplugin");
 
1049
      error(0, errno, "getplugin");
1019
1050
      ret = (int)(TEMP_FAILURE_RETRY
1020
1051
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1021
1052
                               NULL)));
1022
1053
      if(ret < 0){
1023
 
        perror("sigprocmask");
 
1054
        error(0, errno, "sigprocmask");
1024
1055
      }
1025
 
      exitstatus = EXIT_FAILURE;
 
1056
      exitstatus = EX_OSERR;
1026
1057
      goto fallback;
1027
1058
    }
1028
1059
    
1035
1066
                                              &sigchld_action.sa_mask,
1036
1067
                                              NULL));
1037
1068
    if(ret < 0){
1038
 
      perror("sigprocmask");
1039
 
      exitstatus = EXIT_FAILURE;
 
1069
      error(0, errno, "sigprocmask");
 
1070
      exitstatus = EX_OSERR;
1040
1071
      goto fallback;
1041
1072
    }
1042
1073
    
1068
1099
    fd_set rfds = rfds_all;
1069
1100
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1070
1101
    if(select_ret == -1 and errno != EINTR){
1071
 
      perror("select");
1072
 
      exitstatus = EXIT_FAILURE;
 
1102
      error(0, errno, "select");
 
1103
      exitstatus = EX_OSERR;
1073
1104
      goto fallback;
1074
1105
    }
1075
1106
    /* OK, now either a process completed, or something can be read
1110
1141
                                         &sigchld_action.sa_mask,
1111
1142
                                         NULL));
1112
1143
          if(ret < 0){
1113
 
            perror("sigprocmask");
1114
 
            exitstatus = EXIT_FAILURE;
 
1144
            error(0, errno, "sigprocmask");
 
1145
            exitstatus = EX_OSERR;
1115
1146
            goto fallback;
1116
1147
          }
1117
1148
          
1124
1155
                      (sigprocmask(SIG_UNBLOCK,
1125
1156
                                   &sigchld_action.sa_mask, NULL)));
1126
1157
          if(ret < 0){
1127
 
            perror("sigprocmask");
1128
 
            exitstatus = EXIT_FAILURE;
 
1158
            error(0, errno, "sigprocmask");
 
1159
            exitstatus = EX_OSERR;
1129
1160
            goto fallback;
1130
1161
          }
1131
1162
          
1141
1172
        bool bret = print_out_password(proc->buffer,
1142
1173
                                       proc->buffer_length);
1143
1174
        if(not bret){
1144
 
          perror("print_out_password");
1145
 
          exitstatus = EXIT_FAILURE;
 
1175
          error(0, errno, "print_out_password");
 
1176
          exitstatus = EX_IOERR;
1146
1177
        }
1147
1178
        goto fallback;
1148
1179
      }
1160
1191
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1161
1192
                               + (size_t) BUFFER_SIZE);
1162
1193
        if(proc->buffer == NULL){
1163
 
          perror("malloc");
1164
 
          exitstatus = EXIT_FAILURE;
 
1194
          error(0, errno, "malloc");
 
1195
          exitstatus = EX_OSERR;
1165
1196
          goto fallback;
1166
1197
        }
1167
1198
        proc->buffer_size += BUFFER_SIZE;
1188
1219
  
1189
1220
 fallback:
1190
1221
  
1191
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
 
1222
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
 
1223
                             and exitstatus != EX_OK)){
1192
1224
    /* Fallback if all plugins failed, none are found or an error
1193
1225
       occured */
1194
1226
    bool bret;
1202
1234
    }
1203
1235
    bret = print_out_password(passwordbuffer, len);
1204
1236
    if(not bret){
1205
 
      perror("print_out_password");
1206
 
      exitstatus = EXIT_FAILURE;
 
1237
      error(0, errno, "print_out_password");
 
1238
      exitstatus = EX_IOERR;
1207
1239
    }
1208
1240
  }
1209
1241
  
1210
1242
  /* Restore old signal handler */
1211
1243
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1212
1244
  if(ret == -1){
1213
 
    perror("sigaction");
1214
 
    exitstatus = EXIT_FAILURE;
 
1245
    error(0, errno, "sigaction");
 
1246
    exitstatus = EX_OSERR;
1215
1247
  }
1216
1248
  
1217
1249
  if(custom_argv != NULL){
1232
1264
      ret = kill(p->pid, SIGTERM);
1233
1265
      if(ret == -1 and errno != ESRCH){
1234
1266
        /* Set-uid proccesses might not get closed */
1235
 
        perror("kill");
 
1267
        error(0, errno, "kill");
1236
1268
      }
1237
1269
    }
1238
1270
  }
1242
1274
    ret = wait(NULL);
1243
1275
  } while(ret >= 0);
1244
1276
  if(errno != ECHILD){
1245
 
    perror("wait");
 
1277
    error(0, errno, "wait");
1246
1278
  }
1247
1279
  
1248
1280
  free_plugin_list();