// $Header$ #include #include #include #include #include #include #include #include #include #include #include #include #include "cctk.h" #include "cctk_Parameters.h" #include "util_Network.h" #include "portal.hh" namespace Formaline { using namespace std; static bool is_clean_for_shell (char const * string); portal:: portal (char const * const id, enum state const st) : storage (st) { DECLARE_CCTK_PARAMETERS; msgbuf << "" << ""; switch (get_state()) { case initial: msgbuf << "cactus.registerApplication"; break; case update: msgbuf << "cactus.updateApplication"; break; case final: msgbuf << "cactus.deregisterApplication"; break; default: assert (0); } msgbuf << "" << "" << "" << "jobid" << "" << clean (id) << "" << ""; } portal:: ~ portal () { DECLARE_CCTK_PARAMETERS; string const socket_script = "socket-client.pl"; string const socket_data = "socket-data"; // Write the data msgbuf << ""; msgbuf << ""; 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 $host = '" << portal_hostname << "';" << endl << "my $port = '" << portal_port << "';" << endl << endl << "open (my $FH, '<' . $input);" << endl << endl << "socket (my $SH, PF_INET, SOCK_STREAM, getprotobyname ('tcp'));" << endl << "my $sin = sockaddr_in ($port, inet_aton ($host));" << endl << "connect ($SH, $sin) || exit -1;" << endl << endl << "while (my $line = <$FH>)" << endl << "{" << endl << " print $SH $line;" << endl << "}" << endl << endl << "close $SH;" << 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 portal"); } } remove (datafilename); remove (scriptfilename); } void portal:: store (char const * const key, bool const value) { assert (key); ostringstream keybuf; keybuf << key; ostringstream valuebuf; valuebuf << (value ? "true" : "false"); msgbuf << "" << "" << clean (keybuf.str()) << "" << "" << clean (valuebuf.str()) << "" << ""; } void portal:: store (char const * const key, int const value) { assert (key); ostringstream keybuf; keybuf << key; ostringstream valuebuf; valuebuf << value; msgbuf << "" << "" << clean (keybuf.str()) << "" << "" << clean (valuebuf.str()) << "" << ""; } void portal:: store (char const * const key, double const value) { assert (key); ostringstream keybuf; keybuf << key; ostringstream valuebuf; valuebuf << setprecision(15) << value; msgbuf << "" << "" << clean (keybuf.str()) << "" << "" << clean (valuebuf.str()) << "" << ""; } void portal:: store (char const * const key, char const * const value) { assert (key); ostringstream keybuf; keybuf << key; ostringstream valuebuf; valuebuf << value; msgbuf << "" << "" << clean (keybuf.str()) << "" << "" << clean (valuebuf.str()) << "" << ""; } string portal:: 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 string) { for (char const * p = string; * 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; } } // namespace Formaline