/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: 2015-05-23 20:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 756.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

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-2015 Teddy Hogeborn
6
 
 * Copyright © 2008-2015 Björn Påhlsson
 
5
 * Copyright © 2008-2014 Teddy Hogeborn
 
6
 * Copyright © 2008-2014 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
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;
348
347
 
349
348
int main(int argc, char *argv[]){
350
349
  char *plugindir = NULL;
351
 
  char *pluginhelperdir = NULL;
352
350
  char *argfile = NULL;
353
351
  FILE *conffp;
354
352
  struct dirent **direntries = NULL;
416
414
      .doc = "Group ID the plugins will run as", .group = 3 },
417
415
    { .name = "debug", .key = 132,
418
416
      .doc = "Debug mode", .group = 4 },
419
 
    { .name = "plugin-helper-dir", .key = 133,
420
 
      .arg = "DIRECTORY",
421
 
      .doc = "Specify a different plugin helper directory",
422
 
      .group = 2 },
423
417
    /*
424
418
     * These reproduce what we would get without ARGP_NO_HELP
425
419
     */
551
545
    case 132:                   /* --debug */
552
546
      debug = true;
553
547
      break;
554
 
    case 133:                   /* --plugin-helper-dir */
555
 
      free(pluginhelperdir);
556
 
      pluginhelperdir = strdup(arg);
557
 
      if(pluginhelperdir != NULL){
558
 
        errno = 0;
559
 
      }
560
 
      break;
561
548
      /*
562
549
       * These reproduce what we would get without ARGP_NO_HELP
563
550
       */
614
601
    case 130:                   /* --userid */
615
602
    case 131:                   /* --groupid */
616
603
    case 132:                   /* --debug */
617
 
    case 133:                   /* --plugin-helper-dir */
618
604
    case '?':                   /* --help */
619
605
    case -3:                    /* --usage */
620
606
    case 'V':                   /* --version */
775
761
    goto fallback;
776
762
  }
777
763
  
778
 
  {
779
 
    char *pluginhelperenv;
780
 
    bool bret = true;
781
 
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
782
 
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
783
 
    if(ret != -1){
784
 
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
785
 
    }
786
 
    if(ret == -1 or not bret){
787
 
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
788
 
            " environment variable to \"%s\" for all plugins\n",
789
 
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
790
 
    }
791
 
    if(ret != -1){
792
 
      free(pluginhelperenv);
793
 
    }
794
 
  }
795
 
  
796
764
  if(debug){
797
765
    for(plugin *p = plugin_list; p != NULL; p=p->next){
798
766
      fprintf(stderr, "Plugin: %s has %d arguments\n",
827
795
          }
828
796
        }
829
797
      }
830
 
      close(plugindir_fd);
 
798
      TEMP_FAILURE_RETRY(close(plugindir_fd));
831
799
    }
832
800
  }
833
801
  
925
893
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
926
894
    if(ret == -1){
927
895
      error(0, errno, "stat");
928
 
      close(plugin_fd);
 
896
      TEMP_FAILURE_RETRY(close(plugin_fd));
929
897
      free(direntries[i]);
930
898
      continue;
931
899
    }
940
908
                plugindir != NULL ? plugindir : PDIR,
941
909
                direntries[i]->d_name);
942
910
      }
943
 
      close(plugin_fd);
 
911
      TEMP_FAILURE_RETRY(close(plugin_fd));
944
912
      free(direntries[i]);
945
913
      continue;
946
914
    }
948
916
    plugin *p = getplugin(direntries[i]->d_name);
949
917
    if(p == NULL){
950
918
      error(0, errno, "getplugin");
951
 
      close(plugin_fd);
 
919
      TEMP_FAILURE_RETRY(close(plugin_fd));
952
920
      free(direntries[i]);
953
921
      continue;
954
922
    }
957
925
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
958
926
                direntries[i]->d_name);
959
927
      }
960
 
      close(plugin_fd);
 
928
      TEMP_FAILURE_RETRY(close(plugin_fd));
961
929
      free(direntries[i]);
962
930
      continue;
963
931
    }
1003
971
    if(pipefd[0] >= FD_SETSIZE){
1004
972
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
1005
973
              FD_SETSIZE);
1006
 
      close(pipefd[0]);
1007
 
      close(pipefd[1]);
 
974
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
975
      TEMP_FAILURE_RETRY(close(pipefd[1]));
1008
976
      exitstatus = EX_OSERR;
1009
977
      free(direntries[i]);
1010
978
      goto fallback;
1014
982
    ret = set_cloexec_flag(pipefd[0]);
1015
983
    if(ret < 0){
1016
984
      error(0, errno, "set_cloexec_flag");
1017
 
      close(pipefd[0]);
1018
 
      close(pipefd[1]);
 
985
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
986
      TEMP_FAILURE_RETRY(close(pipefd[1]));
1019
987
      exitstatus = EX_OSERR;
1020
988
      free(direntries[i]);
1021
989
      goto fallback;
1023
991
    ret = set_cloexec_flag(pipefd[1]);
1024
992
    if(ret < 0){
1025
993
      error(0, errno, "set_cloexec_flag");
1026
 
      close(pipefd[0]);
1027
 
      close(pipefd[1]);
 
994
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
995
      TEMP_FAILURE_RETRY(close(pipefd[1]));
1028
996
      exitstatus = EX_OSERR;
1029
997
      free(direntries[i]);
1030
998
      goto fallback;
1049
1017
      error(0, errno, "fork");
1050
1018
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1051
1019
                                     &sigchld_action.sa_mask, NULL));
1052
 
      close(pipefd[0]);
1053
 
      close(pipefd[1]);
 
1020
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
1021
      TEMP_FAILURE_RETRY(close(pipefd[1]));
1054
1022
      exitstatus = EX_OSERR;
1055
1023
      free(direntries[i]);
1056
1024
      goto fallback;
1084
1052
      /* no return */
1085
1053
    }
1086
1054
    /* Parent process */
1087
 
    close(pipefd[1]);           /* Close unused write end of pipe */
1088
 
    close(plugin_fd);
 
1055
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
 
1056
                                             pipe */
 
1057
    TEMP_FAILURE_RETRY(close(plugin_fd));
1089
1058
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1090
1059
    if(new_plugin == NULL){
1091
1060
      error(0, errno, "getplugin");
1137
1106
  
1138
1107
  free(direntries);
1139
1108
  direntries = NULL;
1140
 
  close(dir_fd);
 
1109
  TEMP_FAILURE_RETRY(close(dir_fd));
1141
1110
  dir_fd = -1;
1142
1111
  free_plugin(getplugin(NULL));
1143
1112
  
1341
1310
  free(direntries);
1342
1311
  
1343
1312
  if(dir_fd != -1){
1344
 
    close(dir_fd);
 
1313
    TEMP_FAILURE_RETRY(close(dir_fd));
1345
1314
  }
1346
1315
  
1347
1316
  /* Kill the processes */
1367
1336
  free_plugin_list();
1368
1337
  
1369
1338
  free(plugindir);
1370
 
  free(pluginhelperdir);
1371
1339
  free(argfile);
1372
1340
  
1373
1341
  return exitstatus;