diff --git a/checkin_notes b/checkin_notes index 743fda4bdb..05356f0994 100755 --- a/checkin_notes +++ b/checkin_notes @@ -2023,3 +2023,50 @@ David Sept 28 2002 server_types.C,h tools/ add.C + +David Oct 3 2002 + - Changed the PHP framework for testing to facilitate + multi-project, multi-host tests (all on one machine). + See doc/test.html for details. + - NOTE: the BOINC-related environment variables have changed. + There are no longer any project-specific variables + (e.g. database name, shmem key) + - NOTE: the server programs now expect a configuration file + in their directory. + - TODO: enhance the testing framework to allow projects with + multiple scheduling servers or data servers + - TODO: enhance the testing framework to allow specification + of failure and recovery of servers + + - changed things so a NULL in the DB won't crash db_mysql.C + - parse_int() can now handle hex + - "add" now takes args for DB name/passwd + - "create_work" now takes args for DB name/passwd, + upload/download URL, download dir + + db/ + db_mysql.C + mysql_util.C + doc/ + data_server_setup.html + sched_server_setup.html + test.html + web_site.html + html_user/ + index.html + lib/ + md5_file.C + parse.C + sched/ + Makefile.in + config.C,h (new) + feeder.C + file_upload_handler.C + main.C + validate.C + validate_test.C + tools/ + add.C + backend_lib.C,h + create_work.C + process_result_template.C diff --git a/db/db_mysql.C b/db/db_mysql.C index 86442038f4..f506a4a8f9 100644 --- a/db/db_mysql.C +++ b/db/db_mysql.C @@ -25,15 +25,15 @@ #include "db.h" -#define TYPE_PROJECT 1 -#define TYPE_PLATFORM 2 +#define TYPE_PROJECT 1 +#define TYPE_PLATFORM 2 #define TYPE_APP 3 #define TYPE_APP_VERSION 4 -#define TYPE_USER 5 -#define TYPE_TEAM 6 -#define TYPE_HOST 7 -#define TYPE_WORKUNIT 8 -#define TYPE_RESULT 9 +#define TYPE_USER 5 +#define TYPE_TEAM 6 +#define TYPE_HOST 7 +#define TYPE_WORKUNIT 8 +#define TYPE_RESULT 9 char* table_name[] = { "", @@ -230,6 +230,11 @@ void struct_to_str(void* vp, char* q, int type) { } } +static void strcpy2(char* dest, char* src) { + if (!src) *dest = 0; + else strcpy(dest, src); +} + void row_to_struct(MYSQL_ROW& r, void* vp, int type) { PROJECT* prp; PLATFORM* pp; @@ -248,23 +253,23 @@ void row_to_struct(MYSQL_ROW& r, void* vp, int type) { prp = (PROJECT*)vp; memset(prp, 0, sizeof(PROJECT)); prp->id = atoi(r[i++]); - strcpy(prp->name, r[i++]); + strcpy2(prp->name, r[i++]); break; case TYPE_PLATFORM: pp = (PLATFORM*)vp; memset(pp, 0, sizeof(PLATFORM)); pp->id = atoi(r[i++]); pp->create_time = atoi(r[i++]); - strcpy(pp->name, r[i++]); + strcpy2(pp->name, r[i++]); break; case TYPE_APP: app = (APP*)vp; memset(app, 0, sizeof(APP)); app->id = atoi(r[i++]); app->create_time = atoi(r[i++]); - strcpy(app->name, r[i++]); + strcpy2(app->name, r[i++]); app->min_version = atoi(r[i++]); - strcpy(app->result_xml_template, r[i++]); + strcpy2(app->result_xml_template, r[i++]); break; case TYPE_APP_VERSION: avp = (APP_VERSION*)vp; @@ -274,10 +279,10 @@ void row_to_struct(MYSQL_ROW& r, void* vp, int type) { avp->appid = atoi(r[i++]); avp->version_num = atoi(r[i++]); avp->platformid = atoi(r[i++]); - strcpy(avp->xml_doc, r[i++]); + strcpy2(avp->xml_doc, r[i++]); avp->min_core_version = atoi(r[i++]); avp->max_core_version = atoi(r[i++]); - strcpy(avp->message, r[i++]); + strcpy2(avp->message, r[i++]); avp->deprecated = atoi(r[i++]); break; case TYPE_USER: @@ -285,17 +290,17 @@ void row_to_struct(MYSQL_ROW& r, void* vp, int type) { memset(up, 0, sizeof(USER)); up->id = atoi(r[i++]); up->create_time = atoi(r[i++]); - strcpy(up->email_addr, r[i++]); - strcpy(up->name, r[i++]); - strcpy(up->web_password, r[i++]); - strcpy(up->authenticator, r[i++]); - strcpy(up->country, r[i++]); - strcpy(up->postal_code, r[i++]); + strcpy2(up->email_addr, r[i++]); + strcpy2(up->name, r[i++]); + strcpy2(up->web_password, r[i++]); + strcpy2(up->authenticator, r[i++]); + strcpy2(up->country, r[i++]); + strcpy2(up->postal_code, r[i++]); up->total_credit = atof(r[i++]); up->expavg_credit = atof(r[i++]); up->expavg_time = atof(r[i++]); - strcpy(up->global_prefs, r[i++]); - strcpy(up->project_prefs, r[i++]); + strcpy2(up->global_prefs, r[i++]); + strcpy2(up->project_prefs, r[i++]); up->teamid = atoi(r[i++]); break; case TYPE_TEAM: @@ -303,11 +308,11 @@ void row_to_struct(MYSQL_ROW& r, void* vp, int type) { memset(tp, 0, sizeof(TEAM)); tp->id = atoi(r[i++]); tp->userid = atoi(r[i++]); - strcpy(tp->name, r[i++]); - strcpy(tp->name_lc, r[i++]); - strcpy(tp->url, r[i++]); - strcpy(tp->name_html, r[i++]); - strcpy(tp->description, r[i++]); + strcpy2(tp->name, r[i++]); + strcpy2(tp->name_lc, r[i++]); + strcpy2(tp->url, r[i++]); + strcpy2(tp->name_html, r[i++]); + strcpy2(tp->description, r[i++]); tp->nusers = atoi(r[i++]); break; case TYPE_HOST: @@ -322,21 +327,21 @@ void row_to_struct(MYSQL_ROW& r, void* vp, int type) { hp->expavg_credit = atof(r[i++]); hp->expavg_time = atof(r[i++]); hp->timezone = atoi(r[i++]); - strcpy(hp->domain_name, r[i++]); - strcpy(hp->serialnum, r[i++]); - strcpy(hp->last_ip_addr, r[i++]); + strcpy2(hp->domain_name, r[i++]); + strcpy2(hp->serialnum, r[i++]); + strcpy2(hp->last_ip_addr, r[i++]); hp->nsame_ip_addr = atoi(r[i++]); hp->on_frac = atof(r[i++]); hp->connected_frac = atof(r[i++]); hp->active_frac = atof(r[i++]); hp->p_ncpus = atoi(r[i++]); - strcpy(hp->p_vendor, r[i++]); - strcpy(hp->p_model, r[i++]); + strcpy2(hp->p_vendor, r[i++]); + strcpy2(hp->p_model, r[i++]); hp->p_fpops = atof(r[i++]); hp->p_iops = atof(r[i++]); hp->p_membw = atof(r[i++]); - strcpy(hp->os_name, r[i++]); - strcpy(hp->os_version, r[i++]); + strcpy2(hp->os_name, r[i++]); + strcpy2(hp->os_version, r[i++]); hp->m_nbytes = atof(r[i++]); hp->m_cache = atof(r[i++]); hp->m_swap = atof(r[i++]); @@ -354,8 +359,8 @@ void row_to_struct(MYSQL_ROW& r, void* vp, int type) { wup->appid = atoi(r[i++]); wup->previous_wuid = atoi(r[i++]); wup->has_successor = (atoi(r[i++])!=0); - strcpy(wup->name, r[i++]); - strcpy(wup->xml_doc, r[i++]); + strcpy2(wup->name, r[i++]); + strcpy2(wup->xml_doc, r[i++]); wup->batch = atoi(r[i++]); wup->rsc_fpops = atof(r[i++]); wup->rsc_iops = atof(r[i++]); @@ -382,12 +387,12 @@ void row_to_struct(MYSQL_ROW& r, void* vp, int type) { rp->report_deadline = atoi(r[i++]); rp->sent_time = atoi(r[i++]); rp->received_time = atoi(r[i++]); - strcpy(rp->name, r[i++]); + strcpy2(rp->name, r[i++]); rp->exit_status = atoi(r[i++]); rp->cpu_time = atof(r[i++]); - strcpy(rp->xml_doc_in, r[i++]); - strcpy(rp->xml_doc_out, r[i++]); - strcpy(rp->stderr_out, r[i++]); + strcpy2(rp->xml_doc_in, r[i++]); + strcpy2(rp->xml_doc_out, r[i++]); + strcpy2(rp->stderr_out, r[i++]); rp->batch = atoi(r[i++]); rp->project_state = atoi(r[i++]); rp->validate_state = atoi(r[i++]); diff --git a/db/mysql_util.C b/db/mysql_util.C index 4d73915964..9f6ebeb0b0 100644 --- a/db/mysql_util.C +++ b/db/mysql_util.C @@ -54,6 +54,7 @@ int db_close() { void db_print_error(char* p) { if (mp) { printf("
%s: Database error: %s\n", p, mysql_error(mp)); + fprintf(stderr, "%s: Database error: %s\n", p, mysql_error(mp)); } } diff --git a/doc/data_server_setup.html b/doc/data_server_setup.html index 5d4e0ce3c7..48042d1573 100644 --- a/doc/data_server_setup.html +++ b/doc/data_server_setup.html @@ -2,28 +2,20 @@

Setting up a data server

-The BOINC data server is implemented as an Apache -or similar web server. -Its upload function runs as a CGI program. -It has been tested under Linux and Solaris. +The BOINC data server is implemented using Apache +or a similar web server. +It is used both to upload and to download files. +File download is handled by the web server, using GET operations. +File upload is done by a CGI program, file_upload_handler. The host need not have access to the BOINC database.

You must copy the file upload authentication key to each data server. -

Compiling the file upload handler

-The upload handler, file_upload_handler, -is compiled by the makefile in sched/. -The following environment variables must be defined first: -

-setenv BOINC_UPLOAD_DIR     /users/barry/upload
-setenv BOINC_KEY_DIR        /users/barry/keys
-
-

-BOINC_UPLOAD_DIR is that path of the upload directory. -BOINC_KEY_DIR is a directory containing the -file upload authentication public key. +The file upload handler uses a configuration of +the same format as used by the +scheduling server.

Web server configuration

diff --git a/doc/sched_server_setup.html b/doc/sched_server_setup.html index e0c02a8184..c897026ae7 100644 --- a/doc/sched_server_setup.html +++ b/doc/sched_server_setup.html @@ -4,40 +4,48 @@

The BOINC scheduling server runs as a CGI or fast CGI program under Apache or similar web server. -It has been tested on Linux and Solaris. -The host must have access to the BOINC database. +The host must have access to the project's BOINC database.

-The scheduling server uses an auxiliary program -called "feeder"; the two programs communicate -through a shared-memory segment. +The scheduling server uses an auxiliary program called "feeder"; +the two programs communicate through a shared-memory segment.

-The scheduling server and feeder programs are compiled -by typing "configure" and "make" in the sched/ directory. -Prior to doing this, you must define -the following environment variables: +Both programs read a configuration file config.xml +with the following form:

-setenv BOINC_DB_NAME        boinc_barry
-setenv BOINC_DB_PASSWD
-setenv BOINC_SHMEM_KEY      0xdabacafe
-setenv BOINC_KEY_DIR        /users/david/boinc_server/keys
+<config>
+    <db_name>david_test</db_name>
+    <db_passwd></db_passwd>
+    <shmem_key>0xbeefacafe</shmem_key>
+    <key_dir>/home/david/boinc_keys</key_dir>
+    <upload_dir>/home/david/boinc_projects/test/upload</upload_dir>
+    <user_name>david</user_name>
+</config>
 
-BOINC_DB_NAME and BOINC_DB_PASSWORD are the -name and password of the BOINC database. +The elements are as follows:

-BOINC_SHMEM_KEY is the identifier of the -shared-memory segment; + + + + + + + +
db_nameThe name of the BOINC database
db_passwordThe password of the BOINC database
shmem_key +The identifier of the shared-memory segment; it is an arbitrary 32-bit quantity, but must be unique among different BOINC servers sharing a single host. -

-BOINC_KEY_DIR is the directory containing +

key_dir +The directory containing the file upload authentication private key. - -

-If any of these variables changes, -you must do a "make clean; make" in the sched directory. - +

upload_dir +The directory where uploaded files are stored +(this is used by the data server). +
user_name +This name is prepended to web log error messages +to distinguish between multiple servers on a single host. +

You must modify your Apache config file to allow execution of the scheduling server. @@ -52,4 +60,4 @@ ScriptAlias /boinc-cgi/ "/users/barry/cgi/" Order allow,deny Allow from all </Directory> - \ No newline at end of file + diff --git a/doc/test.html b/doc/test.html index df9bc8b103..879a8fc930 100644 --- a/doc/test.html +++ b/doc/test.html @@ -1,6 +1,19 @@ Test applications and scripts

Test applications and scripts

+ +
+setenv BOINC_PROJECTS_DIR   /home/david/boinc_projects
+setenv BOINC_HOSTS_DIR      /home/david/boinc_hosts
+setenv BOINC_USER_NAME      david
+setenv BOINC_SRC_DIR        /home/david/boinc_cvs/boinc
+setenv BOINC_CGI_DIR        /home/david/cgi-bin
+setenv BOINC_CGI_URL        http://localhost/cgi-bin
+setenv BOINC_HTML_DIR       /home/david/html
+setenv BOINC_HTML_URL       http://localhost
+setenv BOINC_KEY_DIR        /home/david/boinc_keys
+setenv BOINC_PLATFORM       i686-pc-linux-gnu
+

The apps directory contains the following test applications:

diff --git a/lib/md5_file.C b/lib/md5_file.C index 59145d3f76..698b4c3af9 100644 --- a/lib/md5_file.C +++ b/lib/md5_file.C @@ -33,7 +33,7 @@ int md5_file(char* path, char* output, double& nbytes) { nbytes = 0; f = fopen(path, "rb"); if (!f) { - fprintf(stdout, "md5_file: can't open %s\n", path); + fprintf(stderr, "md5_file: can't open %s\n", path); perror("md5_file"); return -1; } diff --git a/lib/parse.C b/lib/parse.C index 9547237950..e3b4102701 100644 --- a/lib/parse.C +++ b/lib/parse.C @@ -48,7 +48,7 @@ bool match_tag(char* buf, char* tag) { bool parse_int(char* buf, char* tag, int& x) { char* p = strstr(buf, tag); if (!p) return false; - x = atoi(p+strlen(tag)); + x = strtol(p+strlen(tag), 0, 0); // this parses 0xabcd correctly return true; } diff --git a/sched/Makefile.in b/sched/Makefile.in index 8cb5c6dc64..75b9898840 100644 --- a/sched/Makefile.in +++ b/sched/Makefile.in @@ -9,12 +9,6 @@ CFLAGS = -g -Wall @DEFS@ \ -I@top_srcdir@/db \ -I@top_srcdir@/lib \ -I@top_srcdir@/tools \ - -DBOINC_DB_NAME=\"$(BOINC_DB_NAME)\" \ - -DBOINC_DB_PASSWD=\"$(BOINC_DB_PASSWD)\" \ - -DBOINC_SHMEM_KEY=$(BOINC_SHMEM_KEY) \ - -DBOINC_KEY_DIR=\"$(BOINC_KEY_DIR)\" \ - -DBOINC_UPLOAD_DIR=\"$(BOINC_UPLOAD_DIR)\" \ - -DBOINC_USER=\"$(BOINC_USER)\" \ -I/usr/local/mysql/include \ -I@top_srcdir@/RSAEuro/source \ @@ -22,7 +16,7 @@ CC = g++ $(CFLAGS) CLIBS = @LIBS@ -PROGS = cgi feeder show_shmem file_upload_handler fcgi validate_test +PROGS = cgi feeder show_shmem file_upload_handler validate_test all: $(PROGS) @@ -31,6 +25,7 @@ CGI_OBJS = \ main.o \ sched_shmem.o \ server_types.o \ + config.o \ ../db/db_mysql.o \ ../db/mysql_util.o \ ../lib/shmem.o \ @@ -39,8 +34,10 @@ CGI_OBJS = \ FEEDER_OBJS = \ feeder.o \ sched_shmem.o \ + config.o \ ../db/db_mysql.o \ ../db/mysql_util.o \ + ../lib/parse.o \ ../lib/shmem.o SHOW_SHMEM_OBJS = \ @@ -52,6 +49,7 @@ SHOW_SHMEM_OBJS = \ FILE_UPLOAD_OBJS = \ file_upload_handler.o \ + config.o \ ../lib/crypt.o \ ../lib/parse.o \ ../lib/md5.o \ @@ -61,6 +59,7 @@ FILE_UPLOAD_OBJS = \ VALIDATE_OBJS = \ validate.o \ validate_test.o \ + config.o \ ../db/db_mysql.o \ ../db/mysql_util.o \ ../lib/parse.o diff --git a/sched/config.C b/sched/config.C new file mode 100644 index 0000000000..9bc2de0110 --- /dev/null +++ b/sched/config.C @@ -0,0 +1,31 @@ +#include +#include + +#include "parse.h" +#include "error_numbers.h" + +#include "config.h" + +int CONFIG::parse(FILE* in) { + char buf[256]; + + memset(this, 0, sizeof(CONFIG)); + while (fgets(buf, 256, in)) { + if (match_tag(buf, "")) return 0; + else if (parse_str(buf, "", db_name, sizeof(db_name))) continue; + else if (parse_str(buf, "", db_passwd, sizeof(db_passwd))) continue; + else if (parse_int(buf, "", shmem_key)) continue; + else if (parse_str(buf, "", key_dir, sizeof(key_dir))) continue; + else if (parse_str(buf, "", upload_dir, sizeof(upload_dir))) continue; + else if (parse_str(buf, "", user_name, sizeof(user_name))) continue; + } + return ERR_XML_PARSE; +} + +int CONFIG::parse_file() { + FILE* f; + + f = fopen("config.xml", "r"); + if (!f) return ERR_FOPEN; + return parse(f); +} diff --git a/sched/config.h b/sched/config.h new file mode 100644 index 0000000000..c6d5611fd4 --- /dev/null +++ b/sched/config.h @@ -0,0 +1,14 @@ +// info needed by server-side software +// +class CONFIG { +public: + char db_name[256]; + char db_passwd[256]; + int shmem_key; + char key_dir[256]; + char upload_dir[256]; + char user_name[256]; + + int parse(FILE*); + int parse_file(); +}; diff --git a/sched/feeder.C b/sched/feeder.C index 56728837c6..56d0528264 100644 --- a/sched/feeder.C +++ b/sched/feeder.C @@ -51,11 +51,14 @@ #include "db.h" #include "shmem.h" +#include "config.h" #include "sched_shmem.h" #define RESULTS_PER_ENUM 100 #define TRIGGER_FILENAME "feeder_trigger" +CONFIG config; + int check_trigger(SCHED_SHMEM* ssp) { FILE* f; char buf[256]; @@ -66,7 +69,7 @@ int check_trigger(SCHED_SHMEM* ssp) { fclose(f); if (!strcmp(buf, "\n")) { detach_shmem((void*)ssp); - destroy_shmem(BOINC_SHMEM_KEY); + destroy_shmem(config.shmem_key); unlink(TRIGGER_FILENAME); exit(0); } else if (!strcmp(buf, "\n")) { @@ -175,25 +178,31 @@ int main(int argc, char** argv) { bool asynch = false; void* p; + retval = config.parse_file(); + if (retval) { + fprintf(stderr, "feeder: can't parse config file\n"); + exit(1); + } + for (i=1; iinit(); - retval = db_open(getenv("BOINC_DB_NAME"), getenv("BOINC_DB_PASSWD")); + retval = db_open(config.db_name, config.db_passwd); if (retval) { fprintf(stderr, "feeder: db_open: %d\n", retval); exit(1); diff --git a/sched/file_upload_handler.C b/sched/file_upload_handler.C index 4a170b152d..46c7e72dd3 100644 --- a/sched/file_upload_handler.C +++ b/sched/file_upload_handler.C @@ -58,8 +58,11 @@ #include #include "parse.h" +#include "config.h" #include "crypt.h" +CONFIG config; + #define MAX_FILES 32 struct FILE_INFO { @@ -86,7 +89,7 @@ int FILE_INFO::parse(FILE* in) { strcatdup(signed_xml, buf); if (parse_str(buf, "", name, sizeof(name))) continue; if (parse_double(buf, "", max_nbytes)) continue; - //fprintf(stderr, "file_upload_handler (%s): FILE_INFO::parse: unrecognized: %s \n", BOINC_USER, buf); + //fprintf(stderr, "file_upload_handler (%s): FILE_INFO::parse: unrecognized: %s \n", config.user, buf); } return 1; } @@ -97,7 +100,7 @@ int print_status(int status, char* message) { printf("%s\n", message); fprintf(stderr, "file_upload_handler (%s): status %d: %s>\n", - BOINC_USER, status, message + config.user_name, status, message ); } return 0; @@ -164,7 +167,7 @@ int handle_request(FILE* in, R_RSA_PUBLIC_KEY& key) { if (retval) { fprintf(stderr, "file_upload_handler (%s): FILE_INFO.parse\n", - BOINC_USER + config.user_name ); return retval; } @@ -182,7 +185,7 @@ int handle_request(FILE* in, R_RSA_PUBLIC_KEY& key) { struct stat sbuf; // TODO: check to ensure path doesn't point somewhere bad // - sprintf( path, "%s/%s", BOINC_UPLOAD_DIR, file_name ); + sprintf(path, "%s/%s", config.upload_dir, file_name ); retval = stat( path, &sbuf ); if (retval && errno != ENOENT) { print_status( -1, "cannot open file" ); @@ -226,12 +229,12 @@ int handle_request(FILE* in, R_RSA_PUBLIC_KEY& key) { return -1; } - sprintf(path, "%s/%s", BOINC_UPLOAD_DIR, file_info.name); + sprintf(path, "%s/%s", config.upload_dir, file_info.name); retval = copy_socket_to_file(in, path, offset, nbytes); if (retval) { fprintf(stderr, "file_upload_handler (%s): copy_socket_to_file %d %s\n", - BOINC_USER, retval, path + config.user_name, retval, path ); } break; @@ -244,7 +247,7 @@ int get_key(R_RSA_PUBLIC_KEY& key) { FILE* f; int retval; char buf[256]; - sprintf(buf, "%s/upload_public", BOINC_KEY_DIR); + sprintf(buf, "%s/upload_public", config.key_dir); f = fopen(buf, "r"); if (!f) return -1; retval = scan_key_hex(f, (KEY*)&key, sizeof(key)); @@ -257,9 +260,14 @@ int main() { int retval; R_RSA_PUBLIC_KEY key; + retval = config.parse_file(); + if (retval) { + print_status(-1, "can't read config file"); + exit(0); + } + retval = get_key(key); if (retval) { - fprintf(stderr, "file_upload_handler: can't read key file\n"); print_status(-1, "can't read key file"); exit(0); } diff --git a/sched/main.C b/sched/main.C index 7da52df99f..4cbbc08e4b 100644 --- a/sched/main.C +++ b/sched/main.C @@ -29,6 +29,8 @@ using namespace std; #include "db.h" #include "parse.h" #include "shmem.h" + +#include "config.h" #include "server_types.h" #include "handle_request.h" #include "main.h" @@ -64,22 +66,29 @@ int main() { unsigned int counter=0; char* code_sign_key; bool found; + CONFIG config; - sprintf(path, "%s/code_sign_public", BOINC_KEY_DIR); + retval = config.parse_file(); + if (retval) { + fprintf(stderr, "Can't parse config file\n"); + exit(1); + } + + sprintf(path, "%s/code_sign_public", config.key_dir); retval = read_file_malloc(path, code_sign_key); if (retval) { fprintf(stderr, "BOINC scheduler (%s): can't read code sign key file (%s)\n", - BOINC_USER, path + config.user_name, path ); exit(1); } - retval = attach_shmem(BOINC_SHMEM_KEY, &p); + retval = attach_shmem(config.shmem_key, &p); if (retval) { fprintf(stderr, "BOINC scheduler (%s): can't attach shmem\n", - BOINC_USER + config.user_name ); exit(1); } @@ -88,7 +97,7 @@ int main() { if (retval) { fprintf(stderr, "BOINC scheduler (%s): shmem has wrong struct sizes - recompile\n", - BOINC_USER + config.user_name ); exit(1); } @@ -97,23 +106,23 @@ int main() { if (ssp->ready) break; fprintf(stderr, "BOINC scheduler (%s): waiting for ready flag\n", - BOINC_USER + config.user_name ); sleep(1); } if (!ssp->ready) { fprintf(stderr, "BOINC scheduler (%s): feeder doesn't seem to be running\n", - BOINC_USER + config.user_name ); exit(1); } //fprintf(stderr, "got ready flag\n"); - retval = db_open(BOINC_DB_NAME, BOINC_DB_PASSWD); + retval = db_open(config.db_name, config.db_passwd); if (retval) { fprintf(stderr, "BOINC scheduler (%s): can't open database\n", - BOINC_USER + config.user_name ); return_error("can't open database"); exit(1); @@ -126,7 +135,7 @@ int main() { if (!found) { fprintf(stderr, "BOINC scheduler (%s): can't find project\n", - BOINC_USER + config.user_name ); exit(1); } @@ -141,24 +150,40 @@ int main() { fprintf(stdout, "Content-type: text/plain\n\n"); fout = fopen(req_path, "w"); if (!fout) { - exit(return_error("Compiled by BOINC_USER: can't write request file")); + fprintf(stderr, + "BOINC scheduler (%s): can't write request file", + config.user_name + ); + exit(1); } copy_stream(stdin, fout); fclose(fout); fin = fopen(req_path, "r"); if (!fin) { - exit(return_error("Compiled by BOINC_USER: can't read request file")); + fprintf(stderr, + "BOINC scheduler (%s): can't read request file", + config.user_name + ); + exit(1); } fout = fopen(reply_path, "w"); if (!fout) { - exit(return_error("Compiled by BOINC_USER: can't write reply file")); + fprintf(stderr, + "BOINC scheduler (%s): can't write reply file", + config.user_name + ); + exit(1); } handle_request(fin, fout, *ssp, code_sign_key); fclose(fin); fclose(fout); fin = fopen(reply_path, "r"); if (!fin) { - exit(return_error("Compiled by BOINC_USER: can't read reply file")); + fprintf(stderr, + "BOINC scheduler (%s): can't read reply file", + config.user_name + ); + exit(1); } copy_stream(fin, stdout); fclose(fin); diff --git a/sched/validate.C b/sched/validate.C index 71c9762b6a..b49a6a75c4 100644 --- a/sched/validate.C +++ b/sched/validate.C @@ -42,10 +42,13 @@ #include #include "db.h" +#include "config.h" extern int check_set(vector, int& canonical, double& credit); extern int check_pair(RESULT&, RESULT&, bool&); +CONFIG config; + #define SECONDS_IN_DAY (3600*24) #define EXP_DECAY_RATE (1./(SECONDS_IN_DAY*7)) @@ -179,7 +182,12 @@ int main(int argc, char** argv) { APP app; bool did_something; - retval = db_open(getenv("BOINC_DB_NAME"), getenv("BOINC_DB_PASSWD")); + retval = config.parse_file(); + if (retval) { + fprintf(stderr, "Can't parse config file\n"); + exit(1); + } + retval = db_open(config.db_name, config.db_passwd); if (retval) { fprintf(stderr, "validate: db_open: %d\n", retval); exit(1); diff --git a/sched/validate_test.C b/sched/validate_test.C index 21ecd32a8d..8d90091cb4 100644 --- a/sched/validate_test.C +++ b/sched/validate_test.C @@ -20,8 +20,11 @@ #include #include "db.h" +#include "config.h" #include "parse.h" +extern CONFIG config; + // get the name of a result's (first) output file // void get_output_file_path(RESULT& result, char* path) { @@ -31,7 +34,7 @@ void get_output_file_path(RESULT& result, char* path) { strcpy(path, ""); retval = parse_str(result.xml_doc_in, "", buf, sizeof(buf)); if (retval) return; - char* upload_dir = getenv("BOINC_UPLOAD_DIR"); + char* upload_dir = config.upload_dir; sprintf(path, "%s/%s", upload_dir, buf); } diff --git a/test/test.inc b/test/test.inc new file mode 100644 index 0000000000..64a6d8da72 --- /dev/null +++ b/test/test.inc @@ -0,0 +1,345 @@ +name = $name; + } +} + +class App_Version { + var $app; + var $version; + var $exec_name; + + function App_Version($app) { + $this->app = $app; + $this->version = 1; + $this->exec_name = $app->name; + } +} + +class Project { + var $name; + var $users; + var $apps; + var $app_versions; + var $project_dir; + var $db_name; + var $generate_keys; + var $shmem_key; + var $key_dir; + var $download_url; + var $scheduler_url; + var $upload_url; + var $user_name; + var $master_url; + + function Project() { + $this->name = "test"; + $this->users = array(); + $this->apps = array(); + $this->app_versions = array(); + $this->generate_keys = false; + $this->shmem_key = "0xbeefacafe"; + } + + function add_user($user) { + array_push($this->users, $user); + } + + function add_app($app) { + array_push($this->apps, $app); + } + + function add_app_version($app_version) { + array_push($this->app_versions, $app_version); + } + + // Set up the database and directory structures for a project + // + function Install() { + $base_dir = get_env_var("BOINC_PROJECTS_DIR"); + $source_dir = get_env_var("BOINC_SRC_DIR"); + $this->download_url = get_env_var("BOINC_HTML_URL")."/".$this->name."/download"; + $this->upload_url = get_env_var("BOINC_CGI_URL")."/".$this->name."/file_upload_handler"; + $this->scheduler_url = get_env_var("BOINC_CGI_URL")."/".$this->name."/cgi"; + $this->project_dir = $base_dir."/".$this->name; + $this->master_url = get_env_var("BOINC_HTML_URL")."/".$this->name."/html_user/index.html"; + PassThru("rm -rf $this->project_dir"); + PassThru("mkdir $this->project_dir"); + PassThru("mkdir $this->project_dir/cgi"); + PassThru("mkdir $this->project_dir/upload; chmod uog+w $this->project_dir/upload"); + PassThru("mkdir $this->project_dir/download"); + PassThru("mkdir $this->project_dir/keys"); + PassThru("mkdir $this->project_dir/html_ops"); + PassThru("mkdir $this->project_dir/html_user"); + + if ($this->generate_keys) { + } else { + $this->key_dir = get_env_var("BOINC_KEY_DIR"); + PassThru("cp $this->key_dir/* $this->project_dir/keys"); + } + + // set up the database + // + $this->user_name = get_env_var("BOINC_USER_NAME"); + $this->db_name = $this->user_name."_".$this->name; + run_db_script("drop.sql", $this->db_name); + run_db_script("schema.sql", $this->db_name); + run_db_script("constraints.sql", $this->db_name); + + db_open($this->db_name); + db_query("insert into project(name) values('$this->name')"); + + $platform = get_env_var("BOINC_PLATFORM"); + db_query("insert into platform(name) values('$platform')"); + + for ($i=0; $iusers); $i++) { + $user = $this->users[$i]; + $now = time(0); + db_query("insert into user values (0, $now, '$user->email_addr', '$user->name', 'foobar', '$user->authenticator', 'Peru', '12345', 0, 0, 0, '', '', 0)"); + } + + echo "adding apps\n"; + for ($i=0; $iapps); $i++) { + $app = $this->apps[$i]; + db_query("insert into app(name, create_time) values ('$app->name', $now)"); + } + + echo "adding app versions\n"; + for ($i=0; $iapp_versions); $i++) { + $app_version = $this->app_versions[$i]; + $app = $app_version->app; + run_tool("add app_version -db_name $this->db_name -app_name $app->name -platform_name $platform -version $app_version->version -download_dir $this->project_dir/download -download_url $this->download_url -code_sign_keyfile $this->key_dir/code_sign_private -exec_dir $source_dir/apps -exec_files $app_version->exec_name"); + } + + // copy the CGI programs and feeder to the project dir, + // and make a config file there + // + PassThru("cp $source_dir/sched/cgi $this->project_dir/cgi/"); + PassThru("cp $source_dir/sched/file_upload_handler $this->project_dir/cgi/"); + PassThru("cp $source_dir/sched/feeder $this->project_dir/cgi/"); + $f = fopen("$this->project_dir/cgi/config.xml", "w"); + fputs($f, "\n"); + fputs($f, " $this->db_name\n"); + fputs($f, " $this->db_passwd\n"); + fputs($f, " $this->shmem_key\n"); + fputs($f, " $this->key_dir\n"); + fputs($f, " $this->project_dir/upload\n"); + fputs($f, " $this->user_name\n"); + fputs($f, "\n"); + fclose($f); + + // copy the user and administrative PHP files to the project dir, + // + PassThru("cp -f $source_dir/html_user/* $this->project_dir/html_user"); + PassThru("cp -f $source_dir/html_ops/* $this->project_dir/html_ops"); + + // put a file with the database name in each directory + // + $f = fopen("$this->project_dir/html_user/db_name", "w"); + fputs($f, "$this->db_name\n"); + fclose($f); + $f = fopen("$this->project_dir/html_ops/db_name", "w"); + fputs($f, "$this->db_name\n"); + fclose($f); + + // edit "index.html" in the user directory to have + // the right scheduler URL + // + $u = str_replace("/", "\\\/", $this->scheduler_url); + $x = "sed -e s/SCHEDULER_URL/$u/ $this->project_dir/html_user/index.html > temp; mv temp $this->project_dir/html_user/index.html"; + echo "$x\n"; + PassThru($x); + + // create symbolic links to the CGI and HTML directories + // + $cgi_dir = get_env_var("BOINC_CGI_DIR"); + $cgi_url = get_env_var("BOINC_CGI_URL"); + $html_dir = get_env_var("BOINC_HTML_DIR"); + $html_url = get_env_var("BOINC_HTML_URL"); + PassThru("rm -f $cgi_dir/$this->name"); + PassThru("ln -s $this->project_dir/cgi $cgi_dir/$this->name"); + PassThru("rm -f $html_dir/$this->name"); + PassThru("ln -s $this->project_dir $html_dir/$this->name"); + } + + function start(){ + PassThru("cd $this->project_dir/cgi; feeder -asynch > feeder_out"); + } + + function stop() { + $f = fopen($this->project_dir."/cgi/feeder_trigger", "w"); + fputs($f, "\n"); + fclose($f); + } + + function check_results_done() { + db_open($this->db_name); + $result = mysql_query("select * from result where state<>4"); + while ($x = mysql_fetch_object($result)) { + echo "result $x->id is not done\n"; + } + } + + function compare_file($result, $correct) { + PassThru("diff $this->project_dir/upload/$result $correct", $retval); + if ($retval) { + echo "File mismatch: $out $correct\n"; + } else { + echo "Files match: $out $correct\n"; + } + } +} + + +class User { + var $name; + var $authenticator; + + function User() { + $this->name = "John"; + $this->email_addr = "john@boinc.org"; + $this->authenticator = "3f7b90793a0175ad0bda68684e8bd136"; + } +} + + +class Host { + var $name; + var $projects; + var $user; + var $host_dir; + + function Host($user) { + $this->user = $user; + $this->name = "test"; + $this->projects = array(); + } + + function add_project($project) { + array_push($this->projects, $project); + } + + function install() { + $base_dir = get_env_var("BOINC_HOSTS_DIR"); + $this->host_dir = $base_dir."/".$this->name; + $user = $this->user; + PassThru("rm -rf $this->host_dir"); + PassThru("mkdir $this->host_dir"); + + // create account files + // + echo "creating account files\n"; + for ($i=0; $iprojects); $i++) { + $project = $this->projects[$i]; + $encoded_name = strtr($project->name, "/", "_"); + echo "writing $this->host_dir/account_$encoded_name.xml\n"; + $f = fopen($this->host_dir."/account_$encoded_name.xml", "w"); + fputs($f, "\n"); + fputs($f, " $project->master_url\n"); + fputs($f, " $user->authenticator\n"); + fputs($f, " 1\n"); + fputs($f, "\n"); + fclose($f); + } + + // copy log_flags.xml + // + PassThru("cp log_flags.xml $this->host_dir"); + } + + function run($args) { + echo "Running core client\n"; + $source_dir = get_env_var("BOINC_SRC_DIR"); + $platform = get_env_var("BOINC_PLATFORM"); + PassThru("cd $this->host_dir; $source_dir/client/boinc_1_$platform $args"); + } +} + +class Work { + var $project; + var $app; + var $wu_template; + var $result_template; + var $nresults; + var $input_files; + + function Work($project, $app) { + $this->project = $project; + $this->app = $app; + $this->input_files = array(); + } + + function install() { + $project = $this->project; + $app = $this->app; + for ($i=0; $iinput_files); $i++) { + $x = $this->input_files[$i]; + PassThru("cp $x $project->project_dir/download"); + } + $cmd = "create_work -db_name $project->db_name -download_dir $project->project_dir/download -upload_url $project->upload_url -download_url $project->download_url/ -keyfile $project->key_dir/upload_private -appname $app->name -rsc_iops 180000000000.0 -rsc_fpops 0.0 -wu_name $this->wu_template -wu_template $this->wu_template -result_template $this->result_template -nresults $this->nresults "; + for ($i=0; $iinput_files); $i++) { + $x = $this->input_files[$i]; + $cmd = $cmd." ".$x; + } + run_tool($cmd); + } +} diff --git a/test/test_uc.php b/test/test_uc.php index 020108869e..4c6d9d37de 100644 --- a/test/test_uc.php +++ b/test/test_uc.php @@ -1,33 +1,35 @@ #! /usr/local/bin/php add_user($user); + $project->add_app($app); + $project->add_app_version($app_version); + $project->install(); // must install projects before adding to hosts + + $host->add_project($project); + $host->install(); + + echo "adding work\n"; + + $work = new Work($project, $app); + $work->wu_template = "uc_wu"; + $work->result_template = "uc_result"; + $work->nresults = 2; + array_push($work->input_files, "input"); + $work->install(); + + $project->start(); + $host->run("-exit_when_idle"); + $project->stop(); + + $project->check_results_done(); + $project->compare_file("uc_wu_0_0", "uc_correct_output"); + $project->compare_file("uc_wu_1_0", "uc_correct_output"); ?> diff --git a/tools/add.C b/tools/add.C index 805a0271c3..3ad7893dd3 100644 --- a/tools/add.C +++ b/tools/add.C @@ -57,11 +57,11 @@ int version, retval, nexec_files; double nbytes; bool signed_exec_files; char buf[256], md5_cksum[64]; -char *app_name=0, *platform_name=0, *project_name=0; +char *db_name=0, *db_passwd=0, *app_name=0, *platform_name=0, *project_name=0; char* exec_dir=0, *exec_files[10], *signature_files[10]; char *email_addr=0, *user_name=0, *web_password=0, *authenticator=0; char *global_prefs_file=0, *download_dir, *download_url; -char* code_sign_keyfile; +char* code_sign_keyfile=0; char *message=0, *message_priority=0; void add_project() { @@ -248,12 +248,12 @@ void add_user() { int main(int argc, char** argv) { int i, retval; - retval = db_open(getenv("BOINC_DB_NAME"), getenv("BOINC_DB_PASSWD")); - if (retval) { - printf("can't open DB %s\n", getenv("BOINC_DB_NAME")); - } for (i=2; i= n) { - fprintf(stderr, "invalid file number\n"); + fprintf(stderr, "process_wu_template: invalid file number\n"); return 1; } strcpy(buf, p+strlen(INFILE_MACRO)+1+2); // assume <= 10 files @@ -93,14 +94,14 @@ static int process_wu_template( if (p) { found = true; strcpy(buf, p+strlen(UPLOAD_URL_MACRO)); - strcpy(p, getenv("BOINC_UPLOAD_URL")); + strcpy(p, upload_url); strcat(p, buf); } p = strstr(out, DOWNLOAD_URL_MACRO); if (p) { found = true; strcpy(buf, p+strlen(DOWNLOAD_URL_MACRO)); - strcpy(p, getenv("BOINC_DOWNLOAD_URL")); + strcpy(p, download_url); strcat(p, buf); } p = strstr(out, MD5_MACRO); @@ -108,11 +109,15 @@ static int process_wu_template( found = true; i = atoi(p+strlen(MD5_MACRO)); if (i >= n) { - fprintf(stderr, "invalid file number\n"); + fprintf(stderr, "process_wu_template: invalid file number\n"); return 1; } sprintf(path, "%s/%s", dirpath, infiles[i]); - md5_file(path, md5, nbytes); + retval = md5_file(path, md5, nbytes); + if (retval) { + fprintf(stderr, "process_wu_template: md5_file %d\n", retval); + return 1; + } strcpy(buf, p+strlen(MD5_MACRO)+1+2); // assume <= 10 files strcpy(p, md5); strcat(p, buf); @@ -130,7 +135,8 @@ static int process_wu_template( } int create_result( - WORKUNIT& wu, char* result_template_filename, int i, R_RSA_PRIVATE_KEY& key + WORKUNIT& wu, char* result_template_filename, int i, R_RSA_PRIVATE_KEY& key, + char* upload_url, char* download_url ) { RESULT r; char base_outfile_name[256]; @@ -155,7 +161,8 @@ int create_result( result_template_file, tempfile, key, - base_outfile_name, wu.name, r.name + base_outfile_name, wu.name, r.name, + upload_url, download_url ); rewind(tempfile); read_file(tempfile, r.xml_doc_in); @@ -176,7 +183,8 @@ int create_work( char* infile_dir, char** infiles, int ninfiles, - R_RSA_PRIVATE_KEY& key + R_RSA_PRIVATE_KEY& key, + char* upload_url, char* download_url ) { int i, retval; assert(wu_template!=NULL); @@ -188,20 +196,30 @@ int create_work( wu.create_time = time(0); retval = process_wu_template( - wu.name, wu_template, wu.xml_doc, infile_dir, infiles, ninfiles + wu.name, wu_template, wu.xml_doc, infile_dir, infiles, ninfiles, + upload_url, download_url ); - if (retval) return retval; + if (retval) { + fprintf(stderr, "process_wu_template: %d\n", retval); + return retval; + } if (wu.dynamic_results) { wu.max_results = nresults; } else { wu.nresults_unsent = nresults; } retval = db_workunit_new(wu); + if (retval) { + fprintf(stderr, "create_work: db_workunit_new %d\n", retval); + return retval; + } wu.id = db_insert_id(); if (!wu.dynamic_results) { for (i=0; i