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_name | The name of the BOINC database |
+db_password | The 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:
@@ -43,12 +56,3 @@ These scripts use functions defined in the PHP include file
You can use these functions to easily write test
scripts for your own applications.
-
-setenv BOINC_DOWNLOAD_DIR ~/download
-setenv BOINC_UPLOAD_URL http://maggie.ssl.berkeley.edu/barry/cgi/file_upload_handler
-setenv BOINC_DOWNLOAD_URL http://maggie.ssl.berkeley.edu/barry/download
-setenv BOINC_PLATFORM sparc-sun-solaris2.7
-setenv BOINC_EMAIL barry@ssl.berkeley.edu
-setenv BOINC_USER barry
-setenv BOINC_MASTER_URL http:\\\\\/\\\\/maggie.ssl.berkeley.edu\\\\/~barry
-
\ No newline at end of file
diff --git a/doc/web_site.html b/doc/web_site.html
index 5a3da1c7f3..49fe5367b0 100644
--- a/doc/web_site.html
+++ b/doc/web_site.html
@@ -1,7 +1,9 @@
Setting up a web site
-
Setting up a web site
+
+
+
The master URL
Each project is publicly identified by a master URL.
diff --git a/html/user/index.html b/html/user/index.html
index 7b3c70081d..cb4c15f00b 100644
--- a/html/user/index.html
+++ b/html/user/index.html
@@ -13,6 +13,6 @@ create an account, then download the BOINC client.
Teams
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