diff options
-rw-r--r-- | README | 5 | ||||
-rw-r--r-- | interface.ccl | 3 | ||||
-rw-r--r-- | schedule.ccl | 12 | ||||
-rw-r--r-- | src/announce.cc | 257 | ||||
-rw-r--r-- | src/file.cc | 99 | ||||
-rw-r--r-- | src/file.hh | 22 | ||||
-rw-r--r-- | src/multistorage.cc | 14 | ||||
-rw-r--r-- | src/multistorage.hh | 4 | ||||
-rw-r--r-- | src/portal.cc | 250 | ||||
-rw-r--r-- | src/portal.hh | 28 | ||||
-rw-r--r-- | src/storage.cc | 61 | ||||
-rw-r--r-- | src/storage.hh | 32 |
12 files changed, 685 insertions, 102 deletions
@@ -28,3 +28,8 @@ use a configuration script to amend all thorns' make.code.deps files to create the tarballs when the thorns are compiled use perl instead of C for makeblob and makemetablob + +announce: maybe use <array> for parameter arrays + maybe use <bool> for booleans + maybe use <dateTime.iso8601>20100302T00:00:00</dateTime.iso8601> + for dates and times diff --git a/interface.ccl b/interface.ccl index 37a672d..32918c2 100644 --- a/interface.ccl +++ b/interface.ccl @@ -2,3 +2,6 @@ # $Header$ IMPLEMENTS: Formaline + +# for HTTP_Port() +USES INCLUDE HEADER: http_Content.h diff --git a/schedule.ccl b/schedule.ccl index 6e6dd28..3e7f971 100644 --- a/schedule.ccl +++ b/schedule.ccl @@ -11,7 +11,17 @@ if (output_source) -SCHEDULE Formaline_Announce AT wragh +SCHEDULE Formaline_AnnounceInitial AT wragh +{ + LANG: C +} "Put some meta information about the current run into permanent storage" + +SCHEDULE Formaline_AnnounceUpdate AT analysis +{ + LANG: C +} "Put some meta information about the current run into permanent storage" + +SCHEDULE Formaline_AnnounceFinal AT terminate { LANG: C } "Put some meta information about the current run into permanent storage" diff --git a/src/announce.cc b/src/announce.cc index 2525671..6b11184 100644 --- a/src/announce.cc +++ b/src/announce.cc @@ -4,15 +4,22 @@ #include <cstdio> #include <cstdlib> #include <cstring> +#include <ctime> +#include <iomanip> #include <sstream> #include <string> +#include <sys/types.h> +#include <unistd.h> + #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "cctk_Version.h" #include "util_Network.h" +#include "http_Content.h" + #include "file.hh" #include "multistorage.hh" #include "portal.hh" @@ -21,21 +28,69 @@ using namespace std; -extern "C" -void -Formaline_Announce (CCTK_ARGUMENTS) +static char * jobid = 0; + + + + // Create a unique job id +static void +create_jobid (CCTK_ARGUMENTS) { + DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; + ostringstream jobidbuf; + + char run_host [1000]; + Util_GetHostName (run_host, sizeof run_host); + jobidbuf << run_host; + jobidbuf << "-"; + +#if 0 + char const * const run_user = CCTK_RunUser(); +#else + char const * const run_user = getenv ("USER"); +#endif + jobidbuf << run_user; + + jobidbuf << "-"; + + time_t const tim = time (0); + struct tm * const ptm = gmtime (& tim); + jobidbuf << setfill ('0') + << setw(4) << ptm->tm_year + << setw(2) << ptm->tm_mon + << setw(2) << ptm->tm_mday + << "-" + << setw(2) << ptm->tm_hour + << setw(2) << ptm->tm_min + << setw(2) << ptm->tm_sec; + + jobidbuf << "-"; + + pid_t const pid = getpid(); + jobidbuf << pid; + + string const jobidstr = jobidbuf.str(); + jobid = strdup (jobidstr.c_str()); +} + + + +extern "C" +void +Formaline_AnnounceInitial (CCTK_ARGUMENTS) +{ + DECLARE_CCTK_ARGUMENTS; + DECLARE_CCTK_PARAMETERS; // Only store from the root processor if (CCTK_MyProc (cctkGH) != 0) return; - // Create a unique job id - char const * const jobid = "unique ID"; + create_jobid (cctkGH); @@ -43,18 +98,117 @@ Formaline_Announce (CCTK_ARGUMENTS) if (announce_to_portal) { - stores.add_storage (new portal (jobid)); + stores.add_storage (new portal (jobid, storage::initial)); } if (store_into_file) { - stores.add_storage (new file (jobid)); + stores.add_storage (new file (jobid, storage::initial)); } if (stores.num_storages() == 0) return; + // Information in the Portal/Announce format + { + // Don't know what this is for + stores.store ("jobtype", "default"); + } + { + int type; + void const * const ptr + = CCTK_ParameterGet ("cctk_run_title", "Cactus", & type); + assert (type == PARAMETER_STRING); + char const * const run_title = * static_cast<char const * const *> (ptr); + stores.store ("app_title", run_title); + } + { + char run_date [1000]; + Util_CurrentDate (sizeof run_date, run_date); + char run_time [1000]; + Util_CurrentTime (sizeof run_time, run_time); + ostringstream timebuf; + timebuf << run_date << " " << run_time; + string const timestr = timebuf.str(); + stores.store ("start_time", timestr.c_str()); + } + { + // Don't know what this is for + stores.store ("project_name", ""); + } + { + stores.store ("output_files", out_dir); + } + { + char run_host [1000]; + Util_GetHostName (run_host, sizeof run_host); + stores.store ("host", run_host); + } + { + unsigned long http_port; +#ifdef __HTTP_CONTENT_H__ + if (CCTK_IsThornActive ("HTTPD")) + { + // Thorn is compiled in and active, ask it + http_port = HTTP_Port(); + } + else + { + // Thorn is compiled in but not active, ignore it + http_port = 0; + } +#else + { + // Thorn is not compiled in, ignore it + http_port = 0; + } +#endif + stores.store ("port", (int) http_port); + } + { + stores.store ("portal_username", portal_username); + } + { +#if 0 + char const * const run_user = CCTK_RunUser(); +#else + char const * const run_user = getenv ("USER"); +#endif + stores.store ("local_username", run_user); + } + { + char parameter_filename [10000]; + CCTK_ParameterFilename (sizeof parameter_filename, parameter_filename); + stores.store ("parameter_file", parameter_filename); + } + { + char ** argv; + int argc; + int n; + CCTK_CommandLine (& argv); + for (argc = 0; argv [argc]; ++ argc); + stores.store ("executable", argc == 0 ? "" : argv[0]); + } + { + // Don't know what this is for + stores.store ("data_directory", ""); + } + { + // Could also be "private" + stores.store ("app_visibility", "public"); + } + { + // Could apparently be none, register, update, deregister + stores.store ("notification_reports", ""); + } + { + // Could apparently be none, email, im, sms + stores.store ("notification_methods", ""); + } + + + // Cactus { @@ -122,7 +276,7 @@ Formaline_Announce (CCTK_ARGUMENTS) void const * const ptr = CCTK_ParameterGet ("cctk_run_title", "Cactus", & type); assert (type == PARAMETER_STRING); - char const * const run_title = static_cast<char const *> (ptr); + char const * const run_title = * static_cast<char const * const *> (ptr); stores.store ("run title", run_title); } @@ -151,6 +305,8 @@ Formaline_Announce (CCTK_ARGUMENTS) stores.store ("parameter filename", parameter_filename); } +#if 0 + // This is superfluous, and it does not look nice { char parameter_filename [10000]; char parameter_file [1000000]; @@ -164,6 +320,7 @@ Formaline_Announce (CCTK_ARGUMENTS) parameter_file [count] = '\0'; stores.store ("parameter file", parameter_file); } +#endif { int type; @@ -182,6 +339,7 @@ Formaline_Announce (CCTK_ARGUMENTS) +#if 0 // All Cactus thorns { @@ -269,4 +427,87 @@ Formaline_Announce (CCTK_ARGUMENTS) first = 0; } } +#endif + + + + // Simulation state + + { + stores.store ("cctk_iteration", cctk_iteration); + } +} + + + +extern "C" +void +Formaline_AnnounceUpdate (CCTK_ARGUMENTS) +{ + DECLARE_CCTK_ARGUMENTS; + DECLARE_CCTK_PARAMETERS; + + // Only store from the root processor + if (CCTK_MyProc (cctkGH) != 0) return; + + + + multistorage stores; + + if (announce_to_portal) + { + stores.add_storage (new portal (jobid, storage::update)); + } + + if (store_into_file) + { + stores.add_storage (new file (jobid, storage::update)); + } + + if (stores.num_storages() == 0) return; + + + + // Simulation state + + { + stores.store ("cctk_iteration", cctk_iteration); + } +} + + + +extern "C" +void +Formaline_AnnounceFinal (CCTK_ARGUMENTS) +{ + DECLARE_CCTK_ARGUMENTS; + DECLARE_CCTK_PARAMETERS; + + // Only store from the root processor + if (CCTK_MyProc (cctkGH) != 0) return; + + + + multistorage stores; + + if (announce_to_portal) + { + stores.add_storage (new portal (jobid, storage::final)); + } + + if (store_into_file) + { + stores.add_storage (new file (jobid, storage::final)); + } + + if (stores.num_storages() == 0) return; + + + + // Simulation state + + { + stores.store ("cctk_iteration", cctk_iteration); + } } diff --git a/src/file.cc b/src/file.cc index 8982434..2bff9b8 100644 --- a/src/file.cc +++ b/src/file.cc @@ -1,5 +1,7 @@ // $Header$ +#include <iomanip> +#include <ios> #include <sstream> #include "cctk_Parameters.h" @@ -13,14 +15,23 @@ using namespace std; file:: -file (char const * const id) +file (char const * const id, + enum state const st) + : storage (st) { DECLARE_CCTK_PARAMETERS; ostringstream filenamebuf; filenamebuf << out_dir << "/" << storage_filename; string const filenamestring = filenamebuf.str(); - fil.open (filenamestring.c_str()); + + ios::openmode const mode = get_state() == initial ? ios::trunc : ios::app; + fil.open (filenamestring.c_str(), mode); + + if (get_state() == initial) + { + store ("jobid", id); + } } @@ -28,13 +39,97 @@ file (char const * const id) file:: ~ file () { + if (get_state() == final) + { + store ("simulation", "done"); + } fil.close(); } void file:: +store (char const * const key, + int const value) +{ + assert (key); + + ostringstream keybuf; + keybuf << key; + ostringstream valuebuf; + valuebuf << value; + + ostringstream buf; + buf << clean (keybuf.str()) << "=" << clean (valuebuf.str()) << endl; + + write (buf.str()); +} + + + +void file:: +store (char const * const key, + double const value) +{ + assert (key); + + ostringstream keybuf; + keybuf << key; + ostringstream valuebuf; + valuebuf << setprecision(15) << value; + + ostringstream buf; + buf << clean (keybuf.str()) << "=" << clean (valuebuf.str()) << endl; + + write (buf.str()); +} + + + +void file:: +store (char const * const key, + char const * const value) +{ + assert (key); + + ostringstream keybuf; + keybuf << key; + ostringstream valuebuf; + valuebuf << value; + + ostringstream buf; + buf << clean (keybuf.str()) << "=" + << "\"" << clean (valuebuf.str()) << "\"" << endl; + + write (buf.str()); +} + + + +void file:: write (std::string const & msg) { fil << msg; } + + + +string file:: +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; + case '\\': buf << "\\\\"; break; + default: buf << * p; + } + } + + return buf.str(); +} diff --git a/src/file.hh b/src/file.hh index 17cc1ac..3c3c0ac 100644 --- a/src/file.hh +++ b/src/file.hh @@ -17,15 +17,33 @@ class file : public storage ofstream fil; public: - file (char const * id); + + file (char const * id, + enum state st); virtual ~ file (); -protected: + virtual void + store (char const * key, + int value); + + virtual void + store (char const * key, + double value); virtual void + store (char const * key, + char const * value); + +private: + + void write (std::string const & msg); + + std::string + clean (std::string const & txt) + const; }; diff --git a/src/multistorage.cc b/src/multistorage.cc index bf25e33..6521d3c 100644 --- a/src/multistorage.cc +++ b/src/multistorage.cc @@ -58,6 +58,20 @@ store (char const * const key, int const value) void multistorage:: +store (char const * const key, double const value) + const +{ + for (list<storage *>::const_iterator it = stores.begin(); + it != stores.end(); + ++ it) + { + (* it)->store (key, value); + } +} + + + +void multistorage:: store (char const * const key, char const * const value) const { diff --git a/src/multistorage.hh b/src/multistorage.hh index d61e0ef..6affcbb 100644 --- a/src/multistorage.hh +++ b/src/multistorage.hh @@ -38,6 +38,10 @@ public: const; void + store (char const * key, double value) + const; + + void store (char const * key, char const * value) const; }; diff --git a/src/portal.cc b/src/portal.cc index 5d36f5a..01d4a0c 100644 --- a/src/portal.cc +++ b/src/portal.cc @@ -1,46 +1,103 @@ // $Header$ #include <cassert> +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <iomanip> +#include <iostream> #include <string> #include <sstream> +#include "cctk.h" #include "cctk_Parameters.h" #include "portal.hh" -using std::string; -using std::ostringstream; +using namespace std; + + + +extern int h_errno; portal:: -portal (char const * const id) +portal (char const * const id, + enum state const st) + : storage (st), + errorcount (0) { DECLARE_CCTK_PARAMETERS; sock = socket (PF_INET, SOCK_STREAM, 0); -#warning "TODO: handle errors" + if (sock < 0) + { + CCTK_VWarn (1, __LINE__, __FILE__, CCTK_THORNSTRING, + "%s", strerror (errno)); + } struct hostent * hostinfo; hostinfo = gethostbyname (portal_hostname); -#warning "TODO: handle errors" - assert (hostinfo); + if (hostinfo == 0) + { + switch (h_errno) + { + case HOST_NOT_FOUND: + CCTK_WARN (1, "The specified host is unknown."); + break; + case NO_ADDRESS: + // case NO_DATA: + CCTK_WARN (1, "The requested name is valid but does not have an IP address."); + break; + case NO_RECOVERY: + CCTK_WARN (1, "A non-recoverable name server error occurred."); + break; + case TRY_AGAIN: + CCTK_WARN (1, "A temporary error occurred on an authoritative name server. Try again later."); + break; + default: + CCTK_WARN (1, "unknown error"); + } + } struct sockaddr_in addr; addr.sin_family = AF_INET; - addr.sin_port = portal_port; + addr.sin_port = htons (portal_port); addr.sin_addr = * (struct in_addr *) hostinfo->h_addr; int const ierr = connect (sock, (struct sockaddr const *) (& addr), sizeof addr); -#warning "TODO: handle errors" - assert (! ERROR_CHECK (ierr)); + if (ierr < 0) + { + CCTK_VWarn (1, __LINE__, __FILE__, CCTK_THORNSTRING, + "%s", strerror (errno)); + } - ostringstream buf; - buf << "<simulation id=\"" << clean (id) << "\">\n"; - write (buf.str()); + msgbuf << "<?xml version='1.0' ?>" + << "<methodCall><methodName>"; + 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 << "</methodName>" + << "<params><param><value><struct>" + << "<member>" + << "<name>jobid</name>" + << "<value><string>" << clean (id) << "</string></value>" + << "</member>"; } @@ -48,25 +105,174 @@ portal (char const * const id) portal:: ~ portal () { + msgbuf << "</struct></value></param></params>"; + msgbuf << "</methodCall>"; + string const msgstr = msgbuf.str(); + ostringstream buf; - buf << "</simulation>\n"; + buf << "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"; write (buf.str()); CLOSESOCKET (sock); -#warning "TODO: handle errors" +} + + + +void portal:: +store (char const * const key, + int const value) +{ + assert (key); + + ostringstream keybuf; + keybuf << key; + ostringstream valuebuf; + valuebuf << value; + + msgbuf << "<member>" + << "<name>" << clean (keybuf.str()) << "</name>" + << "<value><int>" << clean (valuebuf.str()) << "</int></value>" + << "</member>"; +} + + + +void portal:: +store (char const * const key, + double const value) +{ + assert (key); + + ostringstream keybuf; + keybuf << key; + ostringstream valuebuf; + valuebuf << setprecision(15) << value; + + msgbuf << "<member>" + << "<name>" << clean (keybuf.str()) << "</name>" + << "<value><double>" << clean (valuebuf.str()) << "</double></value>" + << "</member>"; +} + + + +void portal:: +store (char const * const key, + char const * const value) +{ + assert (key); + + ostringstream keybuf; + keybuf << key; + ostringstream valuebuf; + valuebuf << value; + + msgbuf << "<member>" + << "<name>" << clean (keybuf.str()) << "</name>" + << "<value><string>" << clean (valuebuf.str()) << "</string></value>" + << "</member>"; } void portal:: -write (string const & msg) +write (string const & msg0) +{ + // cout << "[" << msg0 << "]" << endl; + string msg = msg0; + for (;;) + { + + char const * const cmsg = msg.c_str(); + size_t const len = msg.length(); + + // TODO: Make sure that we don't wait forever here. Maybe use + // MSG_DONTWAIT. + ssize_t const nelems = send (sock, cmsg, len, MSG_NOSIGNAL); + if (nelems < 0) + { +#ifdef EMSGSIZE + if (nelems == EMSGSIZE) + { + // The socket type requires that message be sent atomically, + // and the size of the message to be sent made this + // impossible. + // Try to send the message as two smaller messages. + if (len > 0) + { + string const msg1 = msg.substr (0, len/2); + string const msg2 = msg.substr (len/2); + write (msg1); + write (msg2); + } + else + { + // There is a limit. In order to not be too inefficient, + // just do nothing if even small messages cannot be sent. + } + } + else +#endif + { + int const maxerrors = 10; + ++ errorcount; + if (errorcount <= maxerrors) + { + CCTK_VWarn (1, __LINE__, __FILE__, CCTK_THORNSTRING, + "%s", strerror (errno)); + if (errorcount == maxerrors) + { + CCTK_WARN (1, "Too many errors. Suppressing further error messages."); + } + } + } + return; + } + + if (nelems == len) return; + + assert (nelems < len); + msg = msg.substr (nelems); + + // Wait until can write + fd_set fds; + FD_ZERO (& fds); + assert (sock >= 0 and sock < FD_SETSIZE); + FD_SET (sock, & fds); + // TODO: Make sure that we don't wait forever here. + int const ierr = select (FD_SETSIZE, 0, & fds, 0, 0); + if (ierr < 0) + { + CCTK_VWarn (1, __LINE__, __FILE__, CCTK_THORNSTRING, + "%s", strerror (errno)); + return; + } + + } +} + + + +string portal:: +clean (string const & txt) + const { - char const * const cmsg = msg.c_str(); + ostringstream buf; + + for (string::const_iterator p = txt.begin(); p != txt.end(); ++ p) + { + switch (* p) + { + case '<': buf << "<"; break; + case '&': buf << "&"; break; + default: buf << * p; + } + } - size_t const len = strlen (cmsg); - ssize_t const nelems = send (sock, cmsg, len, MSG_NOSIGNAL); - assert (! ERROR_CHECK (nelems)); -#warning "TODO: handle errors" -#warning "TODO: handle overflow" - assert (nelems == len); + return buf.str(); } diff --git a/src/portal.hh b/src/portal.hh index c92faa0..0be9074 100644 --- a/src/portal.hh +++ b/src/portal.hh @@ -5,6 +5,7 @@ +#include <sstream> #include <string> #ifdef HAVE_UNISTD_H @@ -55,18 +56,41 @@ class portal : public storage { + SOCKET sock; + std::ostringstream msgbuf; + + int errorcount; + public: - portal (char const * id); + + portal (char const * id, + enum state st); virtual ~ portal (); -protected: + virtual void + store (char const * key, + int value); + + virtual void + store (char const * key, + double value); virtual void + store (char const * key, + char const * value); + +private: + + void write (std::string const & msg); + + std::string + clean (std::string const & txt) + const; }; diff --git a/src/storage.cc b/src/storage.cc index b4f6cd4..dee2659 100644 --- a/src/storage.cc +++ b/src/storage.cc @@ -9,69 +9,24 @@ using namespace std; storage:: -~ storage () +storage (enum state const st) + : m_state (st) { } -void storage:: -store (char const * const key, - int const value) -{ - assert (key); - - ostringstream keybuf; - keybuf << key; - ostringstream valuebuf; - valuebuf << value; - - ostringstream buf; - buf << "<key>" << clean (keybuf.str()) << "</key> " - << "<value>" << clean (valuebuf.str()) << "</value>\n"; - - write (buf.str()); -} - - - -void storage:: -store (char const * const key, - char const * const value) +storage:: +~ storage () { - assert (key); - - ostringstream keybuf; - keybuf << key; - ostringstream valuebuf; - valuebuf << value; - - ostringstream buf; - buf << "<key>" << clean (keybuf.str()) << "</key> " - << "<value>" << clean (valuebuf.str()) << "</value>\n"; - - write (buf.str()); } -string storage:: -clean (string const & txt) +enum storage::state storage:: +get_state () const { - ostringstream buf; - - for (string::const_iterator p = txt.begin(); p != txt.end(); ++ p) - { - switch (* p) - { - case '<': buf << "<langle>"; break; - case '>': buf << "<rangle>"; break; -// case '"': buf << "<quote>"; break; -// case '\\': buf << "<backslash>"; break; - default: buf << * p; - } - } - - return buf.str(); + return m_state; } + diff --git a/src/storage.hh b/src/storage.hh index ce8a6e7..5728404 100644 --- a/src/storage.hh +++ b/src/storage.hh @@ -1,7 +1,5 @@ // $Header$ - - #ifndef STORAGE_HH #define STORAGE_HH @@ -11,27 +9,37 @@ class storage { public: + enum state { initial, update, final }; + +private: + + enum state m_state; + +public: + + storage (enum state); + virtual ~ storage (); + enum state + get_state () + const; + virtual void store (char const * key, - int value); + int value) + = 0; virtual void store (char const * key, - char const * value); - -protected: + double value) + = 0; virtual void - write (std::string const & msg) + store (char const * key, + char const * value) = 0; - - virtual std::string - clean (std::string const & txt) - const; - }; |