// Berkeley Open Infrastructure for Network Computing // http://boinc.berkeley.edu // Copyright (C) 2005 University of California // // This is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; // either version 2.1 of the License, or (at your option) any later version. // // This software is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU Lesser General Public License for more details. // // To view the GNU Lesser General Public License visit // http://www.gnu.org/copyleft/lesser.html // or write to the Free Software Foundation, Inc., // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // wrapper.C // wrapper program - lets you use non-BOINC apps with BOINC // // Handles: // - suspend/resume/quit/abort // - reporting CPU time // - loss of heartbeat from core client // // Does NOT handle: // - checkpointing // If your app does checkpointing, // and there's some way to figure out when it's done it, // this program could be modified to report to the core client. // // See http://boinc.berkeley.edu/wrapper.php for details // Contributor: Andrew J. Younge (ajy4490@umiacs.umd.edu) #include #include #include #ifdef _WIN32 #include "boinc_win.h" #else #include #include #include "procinfo.h" #endif #include "boinc_api.h" #include "diagnostics.h" #include "filesys.h" #include "parse.h" #include "str_util.h" #include "util.h" #include "error_numbers.h" #define JOB_FILENAME "job.xml" #define CHECKPOINT_FILENAME "checkpoint.txt" using std::vector; using std::string; struct TASK { string application; string stdin_filename; string stdout_filename; string stderr_filename; string command_line; double final_cpu_time; double starting_cpu; #ifdef _WIN32 HANDLE pid_handle; HANDLE thread_handle; #else int pid; #endif int parse(XML_PARSER&); bool poll(int& status); int run(int argc, char** argv); void kill(); void stop(); void resume(); double cpu_time(); }; vector tasks; bool app_suspended = false; int TASK::parse(XML_PARSER& xp) { char tag[1024]; bool is_tag; final_cpu_time = 0; while (!xp.get(tag, sizeof(tag), is_tag)) { if (!is_tag) { fprintf(stderr, "SCHED_CONFIG::parse(): unexpected text %s\n", tag); continue; } if (!strcmp(tag, "/task")) { return 0; } else if (xp.parse_string(tag, "application", application)) continue; else if (xp.parse_string(tag, "stdin_filename", stdin_filename)) continue; else if (xp.parse_string(tag, "stdout_filename", stdout_filename)) continue; else if (xp.parse_string(tag, "stderr_filename", stderr_filename)) continue; else if (xp.parse_string(tag, "command_line", command_line)) continue; } return ERR_XML_PARSE; } int parse_job_file() { MIOFILE mf; char tag[1024], buf[256]; bool is_tag; boinc_resolve_filename(JOB_FILENAME, buf, 1024); FILE* f = boinc_fopen(buf, "r"); if (!f) { fprintf(stderr, "can't open job file %s\n", buf); return ERR_FOPEN; } mf.init_file(f); XML_PARSER xp(&mf); if (!xp.parse_start("job_desc")) return ERR_XML_PARSE; while (!xp.get(tag, sizeof(tag), is_tag)) { if (!is_tag) { fprintf(stderr, "SCHED_CONFIG::parse(): unexpected text %s\n", tag); continue; } if (!strcmp(tag, "/job_desc")) { return 0; } if (!strcmp(tag, "task")) { TASK task; int retval = task.parse(xp); if (!retval) { tasks.push_back(task); } } } return ERR_XML_PARSE; } #ifdef _WIN32 // CreateProcess() takes HANDLEs for the stdin/stdout. // We need to use CreateFile() to get them. Ugh. // HANDLE win_fopen(const char* path, const char* mode) { SECURITY_ATTRIBUTES sa; memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; if (!strcmp(mode, "r")) { return CreateFile( path, GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING, 0, 0 ); } else if (!strcmp(mode, "w")) { return CreateFile( path, GENERIC_WRITE, FILE_SHARE_WRITE, &sa, OPEN_ALWAYS, 0, 0 ); } else if (!strcmp(mode, "a")) { return CreateFile( path, GENERIC_WRITE, FILE_SHARE_WRITE, &sa, OPEN_ALWAYS, 0, 0 ); } else { return 0; } } #endif // the "state file" might tell us which app we're in the middle of, // what the starting CPU time is, etc. // Not implemented yet. // void parse_state_file() { } int TASK::run(int argct, char** argvt) { string app_path, stdout_path, stdin_path, stderr_path; boinc_resolve_filename_s(application.c_str(), app_path); // Append wrapper's command-line arguments to those in the job file. // for (int i=1; i tasks.size()) { fprintf(stderr, "Checkpoint file: ntasks %d too large\n", ntasks); boinc_finish(1); } for (unsigned int i=ntasks; i