// $Header$ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cctk.h" #include "cctk_Parameters.h" #include "cctk_Version.h" #include "util_String.h" #include "util_Network.h" #include "rdf.hh" // number of space chars for indentation #define NUM_INDENT_SPACES 2 namespace Formaline { using namespace std; static bool is_clean_for_shell (char const * str); #if 0 static list parse (char const * const key, string& node); #endif rdf:: rdf (char const * const id, enum state const st) : storage (st) { // // This code was copied over from function Formaline_AnnounceInitial() // in announce.cc and modified here to create a valid RDF/XML (rather then // an unstructured plain XML document). // DECLARE_CCTK_PARAMETERS; if (verbose) CCTK_INFO ("Announcing initial RDF metadata information"); // // RDF/XML document header with some namespace definitions // msgbuf << "" << endl << "" << endl // << "\t" << endl // << "\t" << endl << "\t" << endl << "\t" << endl // << "\t" << endl << "\t" << endl // << "\t" << endl << "]>" << endl << "" << endl << endl; // // general metadata with inlined attribute values // const string jobID = clean (string (id)); char hostbuf[512] = ""; Util_GetHostName (hostbuf, sizeof (hostbuf)); const string host = clean (hostbuf); const cGH* const cctkGH = NULL; const int nprocs = CCTK_nProcs (cctkGH); #if 0 const string user = clean (CCTK_RunUser()); #else const string user = clean (getenv ("USER")); #endif char** argv; CCTK_CommandLine (&argv); const string executable = clean (argv[0]); char parfilebuf[512] = ""; CCTK_ParameterFilename (sizeof (parfilebuf), parfilebuf); const string parfile = clean (parfilebuf); const string version = clean (CCTK_FullVersion ()); ostringstream compiled_at_buf; compiled_at_buf << CCTK_CompileDate () << " " << CCTK_CompileTime (); const string compiled_at (clean (compiled_at_buf.str())); char rundatebuf[32] = ""; char runtimebuf[32] = ""; Util_CurrentDate (sizeof (rundatebuf), rundatebuf); Util_CurrentTime (sizeof (runtimebuf), runtimebuf); ostringstream started_at_buf; started_at_buf << rundatebuf << " " << runtimebuf; const string started_at (clean (started_at_buf.str())); char cwdbuf[512]; getcwd (cwdbuf, sizeof (cwdbuf)); const string cwd (clean (cwdbuf)); msgbuf << "" << endl << "\t" << jobID << "" << endl << "\t" << host << "" << endl << "\t" << nprocs << "" << endl << "\t" << user << "" << endl << "\t" << executable << "" << endl << "\t" << parfile << "" << endl << "\t" << version << "" << endl << "\t" << compiled_at << "" << endl << "\t" << started_at << "" << endl << "\t" << cwd << "" << endl << "" << endl << endl; // // metadata as references to other nodes // msgbuf << "" << endl << "\t" << endl << "\t" << endl << "" << endl << endl; // store thorn list msgbuf << "" << endl; const int numthorns = CCTK_NumCompiledThorns (); for (int thorn = 0; thorn < numthorns; ++ thorn) { const char* const thornname = CCTK_CompiledThorn (thorn); msgbuf << "\t" << endl; } msgbuf << "" << endl << endl; // store parameter file contents msgbuf << "" << endl << "\t"; ifstream file (parfile.c_str()); char c; ostringstream filebuf; while (filebuf and file.get (c)) filebuf.put (c); file.close(); msgbuf << clean (filebuf.str()); filebuf.clear(); msgbuf << "" << endl << "" << endl << endl; // store all parameters which have been set in the parfile msgbuf << "" << endl << "" << endl << "" << endl; ostringstream parambuf; parambuf << "" << endl << "" << endl << "" << endl; const bool list_all_parameters = CCTK_Equals (out_save_parameters, "all"); for (int thorn = 0; thorn < numthorns; ++ thorn) { const char* const thornname = CCTK_CompiledThorn (thorn); msgbuf << "" << endl; msgbuf << "\t" << thornname << "" << endl; // skip parameters that belong to inactive thorns const bool is_active = CCTK_IsThornActive (thornname); msgbuf << "\t" << (is_active ? "true" : "false") << "" << endl; // loop over all parameters of this thorn (if it is active) if (is_active) { for (int first = 1; ; first = 0) { char* fullname = NULL; const cParamData* pdata = NULL; // get the first/next parameter const int ierr = CCTK_ParameterWalk (first, thornname,&fullname,&pdata); assert (ierr >= 0); if (ierr > 0) break; msgbuf << "\tthorn << "/" << pdata->name << "\"/>" << endl; // get its value const void* const pvalue = CCTK_ParameterGet (pdata->name, pdata->thorn, NULL); assert (pvalue); if (pdata->n_set or list_all_parameters) { const char* paramtype; ostringstream paramvaluebuf; switch (pdata->type) { case PARAMETER_BOOLEAN: { paramtype = "BooleanParameter"; const CCTK_INT v = *static_cast (pvalue); paramvaluebuf << (v ? "true" : "false"); } break; case PARAMETER_INT: { paramtype = "IntegerParameter"; const CCTK_INT v = *static_cast (pvalue); paramvaluebuf << v; } break; case PARAMETER_REAL: { paramtype = "RealParameter"; CCTK_REAL const v = *static_cast (pvalue); paramvaluebuf << v; } break; case PARAMETER_KEYWORD: { paramtype = "KeywordParameter"; const char* const v = *static_cast (pvalue); paramvaluebuf << clean (v); } break; case PARAMETER_STRING: { paramtype = "StringParameter"; const char* const v = *static_cast (pvalue); paramvaluebuf << clean (v); } break; default: assert (0 and "invalid parameter type"); } // switch (pdata->type) parambuf << "thorn << "/" << pdata->name << "\">" << endl << "\t" << fullname << "" << endl << "\t" << paramvaluebuf.str() << "" << endl << "" << endl; } // if (pdata->n_set or list_all_parameters) free (fullname); } // loop over all parameters of this thorn } // if (is_active) msgbuf << "" << endl; } // loop over all thorns // FIXME: is there some better method for concatenation ?? msgbuf << endl << parambuf.str(); // // close the RDF/XML document // msgbuf << endl << "" << endl; } rdf:: ~ rdf () { DECLARE_CCTK_PARAMETERS; string const socket_script = "socket-client.pl"; string const socket_data = "socket-data"; // Write the data string const msgstr = msgbuf.str(); ostringstream databuf; databuf << "POST HTTP/1.0 200\r\n" << "Content-Type: text/xml\r\n" << "Content-Length: " << msgstr.length() << "\r\n" << "\r\n" << msgstr << "\r\n" << "\r\n"; string const datastr = databuf.str(); ostringstream datafilenamebuf; datafilenamebuf << out_dir << "/" << socket_data; string const datafilenamestr = datafilenamebuf.str(); char const * const datafilename = datafilenamestr.c_str(); ofstream datafile; datafile.open (datafilename, ios::out); datafile << datastr; datafile.close (); // Write the script ostringstream scriptbuf; scriptbuf << "#! /usr/bin/perl -w" << endl << endl << "use strict;" << endl << "use Socket;" << endl << endl << "my $input = '" << datafilename << "';" << endl << "my @hostlist = ("; // NUM_RDF_ENTRIES must match the size of the // Formaline::rdf_hostname and Formaline::rdf_port parameter arrays #define NUM_RDF_ENTRIES 5 // add all array parameters which have been set for (int i = 0; i < NUM_RDF_ENTRIES; i++) { if (*rdf_hostname[i]) { if (i) scriptbuf << "," << endl << " "; scriptbuf << "'" << rdf_hostname[i] << ":" << rdf_port[i] << "'"; } } scriptbuf << ");" << endl << endl << "foreach my $entry (@hostlist) {" << endl << " next if ($entry !~ /^(.+):(\\d+)$/);" << endl << endl << " my $host = $1;" << endl << " my $port = $2;" << endl << endl << " my $SH;" << endl << endl << " # try to use IO::Socket::INET if the module exists;" << endl << " # it accepts a timeout for its internal connect call" << endl << " eval 'use IO::Socket::INET;" << endl << endl << " $SH = IO::Socket::INET->new (PeerAddr => $host," << endl << " PeerPort => $port," << endl << " Proto => \\'tcp\\'," << endl << " Type => SOCK_STREAM," << endl << " Timeout => 0.2);';" << endl << " # if that failed, fall back to making the standard socket/connect calls" << endl << " # (with their built-in fixed timeout)" << endl << " if ($@) {" << endl << " my $iaddr = inet_aton ($host);" << endl << " next if (not $iaddr);" << endl << "" << endl << " socket ($SH, PF_INET, SOCK_STREAM, getprotobyname ('tcp'));" << endl << " my $sin = sockaddr_in ($port, $iaddr);" << endl << " connect ($SH, $sin) || next;" << endl << " }" << endl << endl << " # send off the data" << endl << " if (defined $SH) {" << endl << " open (my $FH, '<' . $input);" << endl << " print $SH $_ while (<$FH>);" << endl << " close $FH;" << endl << " close $SH;" << endl << " }" << endl << "}" << endl << endl; string const scriptstr = scriptbuf.str(); ostringstream scriptfilenamebuf; scriptfilenamebuf << out_dir << "/" << socket_script; string const scriptfilenamestr = scriptfilenamebuf.str(); char const * const scriptfilename = scriptfilenamestr.c_str(); ofstream scriptfile; scriptfile.open (scriptfilename, ios::out); scriptfile << scriptstr; scriptfile.close (); // Check that the file name is sane if (! is_clean_for_shell (scriptfilename)) { static bool did_complain = false; if (! did_complain) { did_complain = true; CCTK_WARN (1, "Strange character in file name -- not calling system()"); return; } } // Make the script executable ostringstream chmodbuf; chmodbuf << "chmod a+x " << scriptfilenamestr << " < /dev/null > /dev/null 2> /dev/null"; string const chmodstr = chmodbuf.str(); char const * const chmod = chmodstr.c_str(); system (chmod); bool my_use_relay_host = use_relay_host; char const * my_relay_host = 0; if (my_use_relay_host) { my_relay_host = relay_host; if (strcmp (my_relay_host, "") == 0) { // Determine a good relay host char run_host [1000]; Util_GetHostName (run_host, sizeof run_host); if (strncmp (run_host, "ic", 2) == 0 && strlen (run_host) == 6) { // Peyote or Lagavulin int const node = atoi (run_host + 2); if (node < 192) { // Peyote my_relay_host = "peyote"; } else { // Lagavulin my_relay_host = "lagavulin"; } } else if (strncmp (run_host, "mike", 4) == 0 && strlen (run_host) == 7) { // Supermike my_use_relay_host = false; } else { // Don't know a good relay host; try without my_use_relay_host = false; } if (verbose) { if (my_use_relay_host) { CCTK_VInfo (CCTK_THORNSTRING, "Using \"%s\" as relay host", my_relay_host); } else { CCTK_INFO ("Announcing without relay host"); } } } } if (my_use_relay_host) { // Check that the relay host name is sane if (! is_clean_for_shell (my_relay_host)) { static bool did_complain = false; if (! did_complain) { did_complain = true; CCTK_WARN (1, "Strange character in relay host name -- not calling system()"); return; } } } char cwd[10000]; if (my_use_relay_host) { // Get the current directory char * const cwderr = getcwd (cwd, sizeof cwd); if (cwderr == NULL) { static bool did_complain = false; if (! did_complain) { did_complain = true; CCTK_WARN (1, "Cannot determine current working directory"); return; } } // Check that the current directory name is sane if (! is_clean_for_shell (cwd)) { static bool did_complain = false; if (! did_complain) { did_complain = true; CCTK_WARN (1, "Strange character in current directory -- not calling system()"); return; } } } else { cwd[0] = '\0'; } // Send the data ostringstream cmdbuf; if (my_use_relay_host) { cmdbuf << "ssh " << my_relay_host << " '" << "cd " << cwd << " && "; } cmdbuf << scriptfilenamestr << " < /dev/null > /dev/null 2> /dev/null"; if (my_use_relay_host) { cmdbuf << "'"; } string const cmdstr = cmdbuf.str(); char const * const cmd = cmdstr.c_str(); int const ierr = system (cmd); if (ierr != 0) { static bool did_complain = false; if (! did_complain) { did_complain = true; CCTK_WARN (1, "Failed to send data to the rdf"); } } // remove (datafilename); remove (scriptfilename); } void rdf:: store (char const * const key, bool const value) { #if 0 ostringstream valuebuf; valuebuf << (value ? "true" : "false"); string node; list const keys = parse (key, node); string indent_string (NUM_INDENT_SPACES, ' '); for (list::const_iterator lsi = keys.begin(); lsi != keys.end(); ++ lsi) { msgbuf << indent_string << "" << endl; indent_string.append (NUM_INDENT_SPACES, ' '); } msgbuf << indent_string << "" << clean (valuebuf.str()) << "" << endl; for (list::const_reverse_iterator lsi = keys.rbegin(); lsi != keys.rend(); ++ lsi) { indent_string.erase(0, NUM_INDENT_SPACES); msgbuf << indent_string << "" << endl; } msgbuf << endl; #endif } void rdf:: store (char const * const key, CCTK_INT const value) { #if 0 ostringstream valuebuf; valuebuf << value; string node; list const keys = parse (key, node); string indent_string (NUM_INDENT_SPACES, ' '); for (list::const_iterator lsi = keys.begin(); lsi != keys.end(); ++ lsi) { msgbuf << indent_string << "" << endl; indent_string.append (NUM_INDENT_SPACES, ' '); } msgbuf << indent_string << "" << clean (valuebuf.str()) << "" << endl; for (list::const_reverse_iterator lsi = keys.rbegin(); lsi != keys.rend(); ++ lsi) { indent_string.erase(0, NUM_INDENT_SPACES); msgbuf << indent_string << "" << endl; } msgbuf << endl; #endif } void rdf:: store (char const * const key, CCTK_REAL const value) { #if 0 int const prec = numeric_limits::digits10; ostringstream valuebuf; valuebuf << setprecision(prec) << value; string node; list const keys = parse (key, node); string indent_string (NUM_INDENT_SPACES, ' '); for (list::const_iterator lsi = keys.begin(); lsi != keys.end(); ++ lsi) { msgbuf << indent_string << "" << endl; indent_string.append (NUM_INDENT_SPACES, ' '); } msgbuf << indent_string << "" << clean (valuebuf.str()) << "" << endl; for (list::const_reverse_iterator lsi = keys.rbegin(); lsi != keys.rend(); ++ lsi) { indent_string.erase(0, NUM_INDENT_SPACES); msgbuf << indent_string << "" << endl; } msgbuf << endl; #endif } void rdf:: store (char const * const key, char const * const value) { #if 0 // don't store keys with empty string values if (not *value) return; ostringstream valuebuf; valuebuf << value; string node; list const keys = parse (key, node); string indent_string (NUM_INDENT_SPACES, ' '); for (list::const_iterator lsi = keys.begin(); lsi != keys.end(); ++ lsi) { msgbuf << indent_string << "" << endl; indent_string.append (NUM_INDENT_SPACES, ' '); } msgbuf << indent_string // FIXME: is the default datatype for RDF objects ?? << "" // " rdf:datatype=\"&xsd;string\">" << clean (valuebuf.str()) << "" << endl; for (list::const_reverse_iterator lsi = keys.rbegin(); lsi != keys.rend(); ++ lsi) { indent_string.erase(0, NUM_INDENT_SPACES); msgbuf << indent_string << "" << endl; } msgbuf << endl; #endif } string rdf:: clean (string const & txt) const { ostringstream buf; for (string::const_iterator p = txt.begin(); p != txt.end(); ++ p) { switch (* p) { case '<': buf << "<"; break; case '&': buf << "&"; break; default: buf << * p; } } return buf.str(); } static bool is_clean_for_shell (char const * const str) { for (char const * p = str; * p; ++ p) { if (! isalnum (* p)) { // Allow only certain characters switch (* p) { case '+': case ',': case '-': case '.': case '/': case ':': case '_': case '~': break; default: // We don't like this character return false; } } } return true; } #if 0 static list parse (char const * const key, string& node) { assert (key); string str(key); list strs; size_t p = 0; for (;;) { size_t const s = str.find ("/", p); if (s == string::npos) break; strs.push_back (str.substr (p, s - p)); p = s + 1; } node = str.substr (p); return strs; } #endif } // namespace Formaline