new versioning semantics

svn path=/trunk/boinc/; revision=250
This commit is contained in:
David Anderson 2002-07-29 00:39:45 +00:00
parent 9bce75fdf1
commit 3cd542a6e1
25 changed files with 343 additions and 309 deletions

11
TODO
View File

@ -1,24 +1,27 @@
HIGH-PRIORITY (must be done to support SETI@home)
- Code-signing
In progress - David
Done - David
- Upload authentication (David)
Each result contains a "certificate", signed with project key, giving
- list of: file name, max size
- min, max times to xfer
modify put program to decrypt certificate, enforce name/size/time limits
In progress - David
Done - David
- Network retry policies (Eric?)
- File retry policies (Eric?)
can't download file: when to give up? how to retry?
exponential backoff
can't upload file: when to give up/retry?
- scheduler RPC retry
can't connect to sched server
error return from sched server
Done - David
- make scheduling server use fast CGI
In progress - Michael
Done - Michael
- proxy support
HTTP, Socks

View File

@ -1271,4 +1271,67 @@ Michael Gary 7/24/2002
uc_cpu.C (added)
Makefile.in
David July 28 2002
- Changed the "add" utility so that, when adding an app version,
you can give it the signature files (computed offline, presumably)
- Changed the "add" utility so that an app version can consist
of multiple files
- Removed the notion of alpha/beta/production versions of an app.
The same effect can be achieved by making separate projects
for alpha and beta testing.
- Apps now have a "minimum version number" on the server side -
Don't send a WU unless there's an app version of that number or greater.
Send the latest available version for the platform.
- Clarified app version semantics:
- Workunits don't have a version# on the server
- When a client gets a workunit, it associates it with
the most recent version of the application that it knows about
(possibly one it received in the same reply message).
It continues to use this version for this WU,
even if it receives a later version while the WU is in progress.
- On the client, no version #s are associated with apps
PROGRAMMERS NOTE:
- Removed checking of args in client_state.C
This gunks up the code too much. Let's do checking at higher level.
- Comments should be in the imperative mood.
"Write the state file", not "Writes the state file"
- Comments should not be vague, e.g.
// See if the application (name) associated with project p is
// around here
... what does "around here" mean?
- Leave a space between "if" and "("
- When asserting that a pointer is non-NULL, just say "assert(p)".
Saying "assert(p!=NULL)" is like saying if (flag==true)
- Linux doesn't have -lsocket and -lnsl.
Don't put them in Makefile.in.
TODO
client/
client_state.C,h
client_types.C,h
cs_scheduler.C
main.C
db/
db.h
db_mysql.C
schema.sql
doc/
app.html
index.html
intro.html
project.html
tools_other.html
html_user/
db.inc
sched/
handle_request.C
sched_shmem.C
server_types.C
test/
init.inc
master.html
tools/
Makefile.in
add.C
backend_lib.C

View File

@ -54,7 +54,7 @@ CLIENT_STATE::CLIENT_STATE() {
int CLIENT_STATE::init(PREFS* p) {
nslots = 1;
unsigned int i;
if(p==NULL) {
if (p==NULL) {
fprintf(stderr, "error: CLIENT_STATE.init: unexpected NULL pointer p\n");
return ERR_NULL;
}
@ -100,7 +100,8 @@ int CLIENT_STATE::time_tests() {
get_host_info(host_info); // this is platform dependent
host_info.p_fpops = run_double_prec_test(4); //these are not
host_info.p_iops = run_int_test(4);
host_info.p_membw = run_mem_bandwidth_test(4);
//host_info.p_membw = run_mem_bandwidth_test(4);
host_info.p_membw = 100000;
host_info.p_calculated = (double)time(0); //set time calculated
//host_info.m_cache = check_cache_size(CACHE_MAX);
return 0;
@ -287,14 +288,14 @@ int CLIENT_STATE::exit_tasks() {
active_tasks.exit_tasks();
active_tasks.poll_time();
retval = write_state_file();
if(retval) {
if (retval) {
fprintf(stderr, "error: CLIENT_STATE.exit_tasks: write_state_file failed\n");
return retval;
}
return 0;
}
// Writes the client_state.xml file
// Write the client_state.xml file
//
int CLIENT_STATE::write_state_file() {
unsigned int i, j;
@ -352,13 +353,8 @@ int CLIENT_STATE::write_state_file() {
// See if the project specified by master_url already exists
// in the client state record.
// TODO: make this smarter (i.e. www.project.com is the same as project.com)
//
PROJECT* CLIENT_STATE::lookup_project(char* master_url) {
if(master_url==NULL) {
fprintf(stderr, "error: CLIENT_STATE.lookup_project: unexpected NULL pointer master_url\n");
return 0;
}
for (unsigned int i=0; i<projects.size(); i++) {
if (!strcmp(master_url, projects[i]->master_url)) {
return projects[i];
@ -367,18 +363,7 @@ PROJECT* CLIENT_STATE::lookup_project(char* master_url) {
return 0;
}
// See if the application (name) associated with project p is
// around here
//
APP* CLIENT_STATE::lookup_app(PROJECT* p, char* name) {
if(p==NULL) {
fprintf(stderr, "error: CLIENT_STATE.lookup_app: unexpected NULL pointer p\n");
return 0;
}
if(name==NULL) {
fprintf(stderr, "error: CLIENT_STATE.lookup_app: unexpected NULL pointer name\n");
return 0;
}
for (unsigned int i=0; i<apps.size(); i++) {
APP* app = apps[i];
if (app->project == p && !strcmp(name, app->name)) return app;
@ -386,18 +371,7 @@ APP* CLIENT_STATE::lookup_app(PROJECT* p, char* name) {
return 0;
}
// See if the result (name) associated with project p is
// around here
//
RESULT* CLIENT_STATE::lookup_result(PROJECT* p, char* name) {
if(p==NULL) {
fprintf(stderr, "error: CLIENT_STATE.lookup_result: unexpected NULL pointer p\n");
return 0;
}
if(name==NULL) {
fprintf(stderr, "error: CLIENT_STATE.lookup_result: unexpected NULL pointer name\n");
return 0;
}
for (unsigned int i=0; i<results.size(); i++) {
RESULT* rp = results[i];
if (rp->project == p && !strcmp(name, rp->name)) return rp;
@ -405,18 +379,7 @@ RESULT* CLIENT_STATE::lookup_result(PROJECT* p, char* name) {
return 0;
}
// See if the workunit (name) associated with project p is
// around here
//
WORKUNIT* CLIENT_STATE::lookup_workunit(PROJECT* p, char* name) {
if(p==NULL) {
fprintf(stderr, "error: CLIENT_STATE.lookup_workunit: unexpected NULL pointer p\n");
return 0;
}
if(name==NULL) {
fprintf(stderr, "error: CLIENT_STATE.lookup_workunit: unexpected NULL pointer name\n");
return 0;
}
for (unsigned int i=0; i<workunits.size(); i++) {
WORKUNIT* wup = workunits[i];
if (wup->project == p && !strcmp(name, wup->name)) return wup;
@ -424,18 +387,7 @@ WORKUNIT* CLIENT_STATE::lookup_workunit(PROJECT* p, char* name) {
return 0;
}
// See if the app_version (name) associated with project p is
// around here
//
APP_VERSION* CLIENT_STATE::lookup_app_version(APP* app, int version_num) {
if(app==NULL) {
fprintf(stderr, "error: CLIENT_STATE.lookup_app_version: unexpected NULL pointer app\n");
return 0;
}
if(version_num<0) {
fprintf(stderr, "error: CLIENT_STATE.lookup_app_version: negative version_num\n");
return 0;
}
for (unsigned int i=0; i<app_versions.size(); i++) {
APP_VERSION* avp = app_versions[i];
if (avp->app == app && version_num==avp->version_num) {
@ -445,18 +397,7 @@ APP_VERSION* CLIENT_STATE::lookup_app_version(APP* app, int version_num) {
return 0;
}
// See if the file info (name) associated with project p is
// around here
//
FILE_INFO* CLIENT_STATE::lookup_file_info(PROJECT* p, char* name) {
if(p==NULL) {
fprintf(stderr, "error: CLIENT_STATE.lookup_file_info: unexpected NULL pointer p\n");
return 0;
}
if(name==NULL) {
fprintf(stderr, "error: CLIENT_STATE.lookup_file_info: unexpected NULL pointer p\n");
return 0;
}
for (unsigned int i=0; i<file_infos.size(); i++) {
FILE_INFO* fip = file_infos[i];
if (fip->project == p && !strcmp(fip->name, name)) {
@ -470,27 +411,11 @@ FILE_INFO* CLIENT_STATE::lookup_file_info(PROJECT* p, char* name) {
// (which, in their XML form, reference one another by name)
//
int CLIENT_STATE::link_app(PROJECT* p, APP* app) {
if(p==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_app: unexpected NULL pointer p\n");
return ERR_NULL;
}
if(app==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_app: unexpected NULL pointer app\n");
return ERR_NULL;
}
app->project = p;
return 0;
}
int CLIENT_STATE::link_file_info(PROJECT* p, FILE_INFO* fip) {
if(p==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_file_info: unexpected NULL pointer p\n");
return ERR_NULL;
}
if(fip==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_file_info: unexpected NULL pointer fip\n");
return ERR_NULL;
}
fip->project = p;
return 0;
}
@ -500,14 +425,7 @@ int CLIENT_STATE::link_app_version(PROJECT* p, APP_VERSION* avp) {
FILE_INFO* fip;
FILE_REF file_ref;
unsigned int i;
if(p==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_app_version: unexpected NULL pointer fip\n");
return ERR_NULL;
}
if(avp==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_app_version: unexpected NULL pointer fip\n");
return ERR_NULL;
}
avp->project = p;
app = lookup_app(p, avp->app_name);
if (!app) {
@ -539,14 +457,7 @@ int CLIENT_STATE::link_app_version(PROJECT* p, APP_VERSION* avp) {
int CLIENT_STATE::link_file_ref(PROJECT* p, FILE_REF* file_refp) {
FILE_INFO* fip;
if(p==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_file_ref: unexpected NULL pointer p\n");
return ERR_NULL;
}
if(file_refp==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_file_ref: unexpected NULL pointer file_refp\n");
return ERR_NULL;
}
fip = lookup_file_info(p, file_refp->file_name);
if (!fip) {
fprintf(stderr,
@ -563,14 +474,7 @@ int CLIENT_STATE::link_workunit(PROJECT* p, WORKUNIT* wup) {
APP_VERSION* avp;
unsigned int i;
int retval;
if(p==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_workunit: unexpected NULL pointer p\n");
return ERR_NULL;
}
if(wup==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_workunit: unexpected NULL pointer wup\n");
return ERR_NULL;
}
app = lookup_app(p, wup->app_name);
if (!app) {
fprintf(stderr,
@ -578,7 +482,7 @@ int CLIENT_STATE::link_workunit(PROJECT* p, WORKUNIT* wup) {
);
return 1;
}
avp = lookup_app_version(app, app->version_num);
avp = lookup_app_version(app, wup->version_num);
if (!avp) {
fprintf(stderr,
"WU refers to nonexistent app_version: %s %d\n",
@ -595,18 +499,12 @@ int CLIENT_STATE::link_workunit(PROJECT* p, WORKUNIT* wup) {
}
return 0;
}
int CLIENT_STATE::link_result(PROJECT* p, RESULT* rp) {
WORKUNIT* wup;
unsigned int i;
int retval;
if(p==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_result: unexpected NULL pointer p\n");
return ERR_NULL;
}
if(rp==NULL) {
fprintf(stderr, "error: CLIENT_STATE.link_result: unexpected NULL pointer rp\n");
return ERR_NULL;
}
wup = lookup_workunit(p, rp->wu_name);
if (!wup) {
fprintf(stderr, "result refers to nonexistent WU: %s\n", rp->wu_name);
@ -622,6 +520,21 @@ int CLIENT_STATE::link_result(PROJECT* p, RESULT* rp) {
return 0;
}
int CLIENT_STATE::latest_version_num(char* app_name) {
unsigned int i;
int best = -1;
APP_VERSION* avp;
for (i=0; i<app_versions.size(); i++) {
avp = app_versions[i];
if (strcmp(avp->app_name, app_name)) continue;
if (avp->version_num < best) continue;
best = avp->version_num;
}
if (best < 0) fprintf(stderr, "CLIENT_STATE::latest_version_num: no version\n");
return best;
}
// Print debugging information about how many projects/files/etc
// are currently in the client state record
//
@ -741,20 +654,11 @@ int CLIENT_STATE::write_state_file_if_needed() {
return 0;
}
// Parse the command line arguments passed to the client for specific flags:
// -exit_when_idle: the client will exit when it has no more work to do
// -no_time_test: the client will skip the speed and host information checks
// -exit_after: the client will exit after X iterations of the do_something loop
// (usually about X seconds)
// Parse the command line arguments passed to the client
//
void CLIENT_STATE::parse_cmdline(int argc, char** argv) {
int i;
if(argc<0) {
fprintf(stderr, "error: CLIENT_STATE.parse_cmdline: negative argc\n");
}
if(argv==NULL) {
fprintf(stderr, "error: CLIENT_STATE.parse_cmdline: unexpected NULL pointer argv\n");
}
for (i=1; i<argc; i++) {
if (!strcmp(argv[i], "-exit_when_idle")) {
exit_when_idle = true;

View File

@ -79,6 +79,7 @@ private:
int link_app_version(PROJECT*, APP_VERSION*);
int link_workunit(PROJECT*, WORKUNIT*);
int link_result(PROJECT*, RESULT*);
int latest_version_num(char*);
int check_suspend_activities();
int make_project_dirs();
int make_slot_dirs();

View File

@ -45,8 +45,6 @@ PROJECT::PROJECT() {
master_url_fetch_pending = 0;
}
// Destroy this project object
//
PROJECT::~PROJECT() {
if (project_specific_prefs) free(project_specific_prefs);
if (code_sign_key) free(code_sign_key);
@ -191,8 +189,6 @@ void PROJECT::copy_prefs_fields(PROJECT& p) {
resource_share = p.resource_share;
}
// Parse application XML information, usually found in client_state.xml
//
int APP::parse(FILE* in) {
char buf[256];
if(in==NULL) {
@ -204,14 +200,11 @@ int APP::parse(FILE* in) {
while (fgets(buf, 256, in)) {
if (match_tag(buf, "</app>")) return 0;
else if (parse_str(buf, "<name>", name)) continue;
else if (parse_int(buf, "<version_num>", version_num)) continue;
else fprintf(stderr, "APP::parse(): unrecognized: %s\n", buf);
}
return ERR_XML_PARSE;
}
// Write application XML information, usually to client_state.xml
//
int APP::write(FILE* out) {
if(out==NULL) {
fprintf(stderr, "error: APP.write: unexpected NULL pointer out\n");
@ -220,10 +213,8 @@ int APP::write(FILE* out) {
fprintf(out,
"<app>\n"
" <name>%s</name>\n"
" <version_num>%d</version_num>\n"
"</app>\n",
name,
version_num
name
);
return 0;
}
@ -231,8 +222,6 @@ int APP::write(FILE* out) {
FILE_INFO::FILE_INFO() {
}
// TODO: Determine what (if anything) needs to be done when destroying FILE_INFO
//
FILE_INFO::~FILE_INFO() {
}
@ -354,8 +343,6 @@ int FILE_INFO::delete_file() {
return file_delete(path);
}
// Parse XML based app_version information, usually from client_state.xml
//
int APP_VERSION::parse(FILE* in) {
char buf[256];
FILE_REF file_ref;
@ -381,8 +368,6 @@ int APP_VERSION::parse(FILE* in) {
return 1;
}
// Write XML based app_version information, usually to client_state.xml
//
int APP_VERSION::write(FILE* out) {
unsigned int i;
if(out==NULL) {
@ -405,8 +390,6 @@ int APP_VERSION::write(FILE* out) {
return 0;
}
// Parse XML based file_ref information, usually from client_state.xml
//
int FILE_REF::parse(FILE* in) {
char buf[256];
if(in==NULL) {
@ -428,8 +411,6 @@ int FILE_REF::parse(FILE* in) {
return 1;
}
// Write XML based file_ref information, usually to client_state.xml
//
int FILE_REF::write(FILE* out) {
if(out==NULL) {
fprintf(stderr, "error: FILE_REF.write: unexpected NULL pointer out\n");
@ -453,8 +434,6 @@ int FILE_REF::write(FILE* out) {
return 0;
}
// Parse XML based workunit information, usually from client_state.xml
//
int WORKUNIT::parse(FILE* in) {
char buf[256];
FILE_REF file_ref;
@ -488,8 +467,6 @@ int WORKUNIT::parse(FILE* in) {
return 1;
}
// Write XML based workunit information, usually to client_state.xml
//
int WORKUNIT::write(FILE* out) {
unsigned int i;
if(out==NULL) {

View File

@ -87,8 +87,6 @@ public:
struct APP {
char name[256];
int version_num;
// use this version number for new results
PROJECT* project;
int parse(FILE*);
@ -157,8 +155,7 @@ struct WORKUNIT {
char app_name[256];
int version_num;
// This isn't sent from the server.
// Instead, the client picks an app version
// (TODO: use alpha/beta/prod scheme)
// Instead, the client picks the latest app version
char command_line[256];
char env_vars[256]; // environment vars in URL format
vector<FILE_REF> input_files;

View File

@ -544,8 +544,11 @@ void CLIENT_STATE::handle_scheduler_reply(
if (!lookup_workunit(project, sr.workunits[i].name)) {
WORKUNIT* wup = new WORKUNIT;
*wup = sr.workunits[i];
wup->version_num = latest_version_num(wup->app_name);
retval = link_workunit(project, wup);
if (!retval) workunits.push_back(wup);
if (!retval) {
workunits.push_back(wup);
}
}
}
for (i=0; i<sr.results.size(); i++) {

View File

@ -34,16 +34,10 @@
// message may be more or less obtrusive
//
void show_message(char* message, char* priority) {
if(message==NULL) {
fprintf(stderr, "error: show_message: unexpected NULL pointer message\n");
}
if(priority==NULL) {
fprintf(stderr, "error: show_message: unexpected NULL pointer priority\n");
}
if (!strcmp(priority, "high")) {
fprintf(stderr, "BOINC core client: %s\n", message);
fprintf(stderr, "BOINC core client: %s (priority: %s)\n", message, priority);
} else {
printf("BOINC core client: %s\n", message);
printf("BOINC core client: %s (priority: %s)\n", message, priority);
}
}
@ -79,9 +73,11 @@ int main(int argc, char** argv) {
// Set the stdout buffer to 0, such that stdout output
// immediately gets written out
//
setbuf(stdout, 0);
// Read any log flags preferences, used mainly for debugging
// Read log flags preferences, used mainly for debugging
//
f = fopen(LOG_FLAGS_FILE, "r");
if (f) {
log_flags.parse(f);
@ -90,6 +86,7 @@ int main(int argc, char** argv) {
// Read the user preferences file, if it exists. If it doesn't,
// prompt user for project URL via initialize_prefs()
//
prefs = new PREFS;
retval = prefs->parse_file();
if (retval) {
@ -106,33 +103,36 @@ int main(int argc, char** argv) {
}
// Initialize the client state with the preferences
//
gstate.init(prefs);
// Parse command line arguments
gstate.parse_cmdline(argc, argv);
// Run the time tests and host information check if needed
// TODO: break time tests and host information check into two
// separate functions?
if(gstate.run_time_tests()) {
if (gstate.run_time_tests()) {
gstate.time_tests();
}
// Restart any tasks that were running when we last quit the client
gstate.restart_tasks();
while (1) {
// do_something is where the meat of the clients work is done
// it will return false if it had nothing to do, in which case
// client will go to sleep for a second
// it will return false if it had nothing to do,
// in which case sleep for a second
//
if (!gstate.do_something()) {
if (log_flags.time_debug) printf("SLEEP 1 SECOND\n");
fflush(stdout);
boinc_sleep(1);
}
// If it's time to exit, break out of the while loop
if (gstate.time_to_exit()) {
printf("time to exit\n");
break;
}
}
// Clean everything up and gracefully exit
gstate.exit_tasks();
return 0;
}

View File

@ -52,9 +52,7 @@ struct APP {
int id;
unsigned int create_time; // UNIX time of record creation
char name[256]; // application name (i.e. "setiathome_4.0"), preferably short
int alpha_vers; // Alpha version number
int beta_vers; // Beta version number
int prod_vers; // Production version number
int min_version; // don't use versions before this
char result_xml_template[MAX_BLOB_SIZE];
// if any workunits have dynamic results,
// XML template for results comes from here

View File

@ -71,14 +71,12 @@ void struct_to_str(void* vp, char* q, int type) {
app = (APP*)vp;
sprintf(q,
"id=%d, create_time=%d, name='%s', "
"alpha_vers=%d, beta_vers=%d, prod_vers=%d, "
"min_version=%d, "
"result_xml_template='%s'",
app->id,
app->create_time,
app->name,
app->alpha_vers,
app->beta_vers,
app->prod_vers,
app->min_version,
app->result_xml_template
);
break;
@ -236,9 +234,7 @@ void row_to_struct(MYSQL_ROW& r, void* vp, int type) {
app->id = atoi(r[i++]);
app->create_time = atoi(r[i++]);
strcpy(app->name, r[i++]);
app->alpha_vers = atoi(r[i++]);
app->beta_vers = atoi(r[i++]);
app->prod_vers = atoi(r[i++]);
app->min_version = atoi(r[i++]);
strcpy(app->result_xml_template, r[i++]);
break;
case TYPE_APP_VERSION:

View File

@ -11,9 +11,7 @@ create table app (
id integer not null auto_increment,
create_time integer not null,
name varchar(254) not null,
alpha_vers integer not null,
beta_vers integer not null,
prod_vers integer not null,
min_version integer not null,
result_xml_template blob,
primary key (id)
);

View File

@ -20,6 +20,20 @@ is called an <b>application version</b>.
An application version can consist of multiple files:
for example, a controller script,
pre- and post-processing programs, and a primary.
<p>
Each application version has an integer <b>version number</b>.
Projects can assign version numbers however they like;
for example, version 304 might represent major version 3 and minor version 4.
Version numbers should be used consistently across platforms;
Windows version 304 should be computationally identical to Mac version 304.
<p>
Each application has a <b>minimum version</b>.
When a client is sent work for an application,
it is also sent the latest application version for its platform.
It is sent work only if this version is the minimum or greater.
<p>
Application version are maintained in the <b>app_version</b>
table in the BOINC DB,

View File

@ -18,7 +18,6 @@
<li><a href=file_access.html>Compute model: remote file access</a>
<li><a href=validation.html>Accounting and result validation</a>
<li><a href=back_end.html>Back end examples</a>
<li><a href=version.html>Versioning</a>
<li><a href=api.html>The BOINC application library</a>
<li><a href=graphics.html>Client graphics</a>
<li><a href=dev.html>Application development</a>

View File

@ -55,40 +55,44 @@ are encouraged to do so.
<li><b>Support for Large Data Computations</b><br>
BOINC supports applications that produce or consume large amounts of data (on
the order of gigabytes). It allows data distribution and collection to be
the order of gigabytes).
It allows data distribution and collection to be
spread across many servers, and it allows participant hosts to do large
data transfers unobtrusively. Users are able to specify maximum storage usage
so that BOINC doesn't fill up their disk. BOINC also handles network
disconnections and allows user specifiable network bandwidth restrictions.
data transfers unobtrusively.
Users are able to specify maximum storage usage
so that BOINC doesn't fill up their disk.
BOINC also handles network
disconnections and allows user-specifiable network bandwidth restrictions.
<p>
</li>
<li><b>Extreme Resource Requirement Support</b><br>
BOINC supports applications with extreme requirements for memory, disk,
cache or other resources. Work is dispatched only to hosts able to handle it.
cache or other resources.
Work is dispatched only to hosts able to handle it.
<p>
</li>
<li><b>Application Program Interface</b><br>
An Application Program Interface (API) is provided to application developers
to ease integration with BOINC. See the <a href=api.html>API page</a> for
details.
to ease integration with BOINC.
See the <a href=api.html>API page</a> for details.
<p>
</li>
<li><b>Support for Applications in any Language</b><br>
BOINC applications can be developed in any language (C++, Fortran, Perl). An
application can consist of several files (e.g. multiple programs and
a coordinating script). New versions of applications can be deployed
without requiring participants to download. Separate alpha, beta, and production versions
are distributed to the appropriate set of hosts.
a coordinating script).
New versions of applications can be deployed
without requiring participants to download.
<p>
</li>
<li><b>Database and Server Architecture</b><br>
BOINC provides a SQL schema, database functions, a simple server architecture,
web based user interfaces and server administration tools. Each project
must provide and maintain its own server systems and backend result
web based user interfaces and server administration tools.
Each project must provide and maintain its own server systems and backend result
processing but these systems can be set up easily and require only
open-source components (MySQL, PHP, Apache, and Linux).
<p>

View File

@ -1,9 +1,20 @@
<h2>Projects and applications</h2>
<p>
A <b>project</b> is an organization that uses BOINC.
A <b>project</b> is a group of one or more distributed applications,
run by a single organization, that use BOINC.
Projects are independent;
each one manages its own applications, databases and servers,
each one has its own applications, databases and servers,
and is not affected by the status of other projects.
<p>
Each is identified by a <a href=master_url.html>master URL</a>,
which refers to an XHTML document describing the project.
<p>
Creating projects is relatively easy.
An organization can create projects to do Alpha and Beta testing
of applications.
Testers can register for these projects,
in addition to or instead of the organization's public project.
<p>
The components of a project are shown below.
<br>
<img vspace=10 src=project.png>

View File

@ -26,12 +26,30 @@ add platform name -platform_name name
<dd>
Create a platform record (just creates a DB record);
<dt>
add app_version -app_name x -platform_name y -version a -exec_dir b -exec_file c -download_dir d -url_base e
add app_version
<dd> -app_name x
<dd> -platform_name y
<dd> -version a
<dd> -download_dir d
<dd> -url_base e
<dd> -exec_dir b
<dd> [ -message x ]
<dd> [ -message_priority y ]
<dd> [ -code_sign_keyfile x -exec_files file1 file2 ... ]
<dd> [ -signed_exec_files file1 sign1 file2 sign2 ... ]
<dd>
<br>
Create an app_version record.
Copy the executable file from the compilation directory
Copy the executable file(s) from the compilation directory
(-exec_dir) to the download directory.
Compute its MD5 checksum, fill in the DB record.
If -exec_files is used, each executable file is signed
using the given private key;
this should be used only for test/debug purposes.
If -signed_exec_files is used, the signatures are passed explicitly;
this should be used for production purposes,
where the signatures are generated on an offline computer.
If -message is used, the version is tagged with the given message
and optional priority.
<dt>
add user -email_addr x -name y -web_password z -authenticator a
<dd>

View File

@ -5,7 +5,9 @@ function db_init() {
if (!$retval) {
exit();
}
mysql_select_db(getenv("BOINC_DB_NAME"));
$name = getenv("BOINC_DB_NAME");
if ($name == NULL) $name = "boinc";
mysql_select_db($name);
}
function lookup_user_auth($auth) {
@ -28,9 +30,7 @@ function show_app($app) {
row("ID", $app->id);
row("created", time_str($app->create_time));
row("name", $app->name);
row("alpha version", $app->alpha_vers);
row("beta version", $app->beta_vers);
row("production version", $app->prod_vers);
row("minimum version", $app->min_version);
row("result template", "<pre>".htmlspecialchars($app->result_xml_template)."</pre>");
echo "</table>";
}

View File

@ -35,7 +35,7 @@
// return true if the WU can be executed on the host
//
bool wu_is_feasible(WORKUNIT& wu, HOST& host) {
return ((wu.rsc_disk < host.d_free) && (wu.rsc_memory < host.m_nbytes));
return ((wu.rsc_disk <= host.d_free) && (wu.rsc_memory <= host.m_nbytes));
}
// estimate the time that a WU will take on a host
@ -44,8 +44,9 @@ double estimate_duration(WORKUNIT& wu, HOST& host) {
return wu.rsc_fpops/host.p_fpops + wu.rsc_iops/host.p_iops;
}
//inserts an xml tag in xml_doc with an estimation of how many seconds
//a workunit will take to complete
// insert an element in xml_doc with an estimation of how many seconds
// a workunit will take to complete
//
int insert_time_tag(WORKUNIT& wu, double seconds) {
assert(seconds>=0);
char *location;
@ -75,7 +76,7 @@ int add_wu_to_reply(
assert(seconds_to_complete>=0);
app = ss.lookup_app(wu.appid);
if (!app) return -1;
app_version = ss.lookup_app_version(app->id, platform.id, app->prod_vers);
app_version = ss.lookup_app_version(app->id, platform.id, app->min_version);
if (!app_version) return -1;
// add the app, app_version, and workunit to the reply,
@ -84,9 +85,10 @@ int add_wu_to_reply(
reply.insert_app_unique(*app);
reply.insert_app_version_unique(*app_version);
retval = insert_time_tag(wu, seconds_to_complete); //add time estimate
//to reply
if (retval) return -1;
// add time estimate to reply
//
retval = insert_time_tag(wu, seconds_to_complete);
if (retval) return retval;
reply.insert_workunit_unique(wu);
return 0;
}
@ -285,10 +287,12 @@ int send_work(
#endif
seconds_to_fill = sreq.work_req_seconds;
if(seconds_to_fill > MAX_SECONDS_TO_SEND)
if (seconds_to_fill > MAX_SECONDS_TO_SEND) {
seconds_to_fill = MAX_SECONDS_TO_SEND;
else if(seconds_to_fill < MIN_SECONDS_TO_SEND)
}
if (seconds_to_fill < MIN_SECONDS_TO_SEND) {
seconds_to_fill = MIN_SECONDS_TO_SEND;
}
for (i=0; i<ss.nwu_results && seconds_to_fill>0; i++) {

View File

@ -55,7 +55,6 @@ int SCHED_SHMEM::scan_tables() {
}
nplatforms = n;
n = 0;
while (!db_app_enum(app)) {
apps[n++] = app;
@ -94,17 +93,22 @@ APP* SCHED_SHMEM::lookup_app(int id) {
return 0;
}
// find the latest version for a given platform
//
APP_VERSION* SCHED_SHMEM::lookup_app_version(
int appid, int platformid, int version
int appid, int platformid, int min_version
) {
int i;
APP_VERSION* avp;
assert(version>=0);
int i, best_version=-1;
APP_VERSION* avp, *best_avp = 0;
assert(min_version>=0);
for (i=0; i<napp_versions; i++) {
avp = &app_versions[i];
if (avp->appid == appid && avp->platformid == platformid && avp->version_num == version) {
return avp;
if (avp->appid == appid && avp->platformid == platformid) {
if (avp->version_num >= min_version && avp->version_num > best_version) {
best_avp = avp;
best_version = avp->version_num;
}
}
}
return 0;
return best_avp;
}

View File

@ -210,9 +210,8 @@ int APP::write(FILE* fout) {
fprintf(fout,
"<app>\n"
" <name>%s</name>\n"
" <version_num>%d</version_num>\n"
"</app>\n",
name, prod_vers // TODO: handle other phases
name
);
return 0;
}

View File

@ -98,7 +98,7 @@ function init_client_dirs($prefs_file) {
PassThru("rm -f client_state.xml");
PassThru("rm -rf ".PROJECTS);
PassThru("rm -rf slots");
PassThru("sed -e s/BOINC_MASTER_URL/\$BOINC_MASTER_URL/ $prefs_file > prefs.xml");
PassThru("sed -e s/BOINC_MASTER_URL/$BOINC_MASTER_URL/ $prefs_file > prefs.xml");
}
function copy_to_download_dir($f) {
@ -109,9 +109,11 @@ function copy_to_download_dir($f) {
function add_user($prefs_file) {
global $BOINC_EMAIL;
PassThru("sed -e s/BOINC_MASTER_URL/\$BOINC_MASTER_URL/ $prefs_file > prefs_temp.xml");
global $BOINC_MASTER_URL;
$cmd = "../tools/add user -email_addr $BOINC_EMAIL -user_name David -web_password foobar -authenticator 3f7b90793a0175ad0bda68684e8bd136 ";
if ($prefs_file) {
PassThru("sed -e s/BOINC_MASTER_URL/$BOINC_MASTER_URL/ $prefs_file > prefs_temp.xml");
$cmd = $cmd." -prefs_file prefs_temp.xml";
}
PassThru($cmd);
@ -145,7 +147,9 @@ function add_core_client_message($message, $priority, $platform) {
$plat = $platform;
}
PassThru("../tools/add app -app_name core_client -version ".VERSION);
PassThru("../tools/add app_version -app_name core_client -platform_name $plat -version ".VERSION." -exec_dir ../client -exec_file ".CORE_CLIENT." -download_dir $BOINC_DOWNLOAD_DIR -url_base $BOINC_URL_BASE -message '$message' -message_priority '$priority' -code_sign_keyfile $BOINC_KEY_DIR/code_sign_private");
$cmd = "../tools/add app_version -app_name core_client -platform_name $plat -version ".VERSION." -download_dir $BOINC_DOWNLOAD_DIR -url_base $BOINC_URL_BASE -message '$message' -message_priority '$priority' -code_sign_keyfile $BOINC_KEY_DIR/code_sign_private -exec_dir ../client -exec_files ".CORE_CLIENT;
//echo "$cmd\n";
PassThru($cmd);
PassThru("cp ../client/".CORE_CLIENT." $BOINC_DOWNLOAD_DIR");
}
@ -173,7 +177,9 @@ function add_app_version($name, $platform, $exec_name) {
$plat = $platform;
}
PassThru("../tools/add app_version -app_name $name -platform_name $plat -version ".VERSION." -exec_dir ../apps -exec_file $exec_name -download_dir $BOINC_DOWNLOAD_DIR -url_base $BOINC_URL_BASE -code_sign_keyfile $BOINC_KEY_DIR/code_sign_private");
$cmd = "../tools/add app_version -app_name $name -platform_name $plat -version ".VERSION." -download_dir $BOINC_DOWNLOAD_DIR -url_base $BOINC_URL_BASE -code_sign_keyfile $BOINC_KEY_DIR/code_sign_private -exec_dir ../apps -exec_files $exec_name";
//echo "$cmd\n";
PassThru($cmd);
PassThru("cp ../apps/$exec_name $BOINC_DOWNLOAD_DIR");
}

View File

@ -1,7 +1,3 @@
<html>
<head>
</head>
<body>
<h3>Test BOINC Project</h3>
<scheduler>http://localhost/boinc-cgi/cgi</scheduler>
</body>
</html>

View File

@ -34,9 +34,7 @@ MYSQL_DIR = /usr/local/mysql/lib
MYSQL_LIBS = \
-L$(MYSQL_DIR) -L/usr/local/lib/mysql \
-L/sw/lib/mysql -lmysqlclient -L/usr/local/lib -lz \
-lm $(NETLIBS)
NETLIBS = -lsocket -lnsl
-lm
LIBS = \
backend_lib.o \

View File

@ -26,8 +26,11 @@
// add platform -platform_name x
// create DB record
// add app_version
// -app_name x -platform_name y -version a -exec_dir b -exec_file c
// -app_name x -platform_name y -version a
// -download_dir d -url_base e
// -exec_dir b
// [ -exec_files file1 file2 ... ]
// [ -signed_exec_files file1 sign1 file2 sign2 ... ]
// create DB record
// copy exec to data directory
// add user -email_addr x -name y -web_password z -authenticator a
@ -46,10 +49,12 @@ APP app;
PLATFORM platform;
APP_VERSION app_version;
USER user;
int version, retval;
int version, retval, nexec_files;
double nbytes;
bool signed_exec_files;
char buf[256], md5_cksum[64];
char *app_name=0, *platform_name=0, *exec_dir=0, *exec_file=0;
char *app_name=0, *platform_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 *prefs_file=0, *download_dir, *url_base;
char* code_sign_keyfile;
@ -61,9 +66,7 @@ void add_app() {
memset(&app, 0, sizeof(app));
strcpy(app.name, app_name);
app.create_time = time(0);
app.alpha_vers = version;
app.beta_vers = version;
app.prod_vers = version;
app.min_version = version;
retval = db_app_new(app);
if (retval) {
db_print_error("db_app_new");
@ -82,11 +85,31 @@ void add_platform() {
}
}
void add_app_version() {
char path[256];
int sign_executable(char* path, char* signature_text) {
DATA_BLOCK signature;
unsigned char signature_buf[SIGNATURE_SIZE_BINARY];
char signature_text[1024];
R_RSA_PRIVATE_KEY code_sign_key;
FILE* fkey = fopen(code_sign_keyfile, "r");
if (!fkey) {
fprintf(stderr, "add: can't open key file (%s)\n", code_sign_keyfile);
exit(1);
}
retval = scan_key_hex(fkey, (KEY*)&code_sign_key, sizeof(code_sign_key));
fclose(fkey);
if (retval) {
fprintf(stderr, "add: can't parse key\n");
exit(1);
}
signature.data = signature_buf;
sign_file(path, code_sign_key, signature);
sprint_hex_data(signature_text, signature);
return 0;
}
void add_app_version() {
char path[256];
char signature_text[1024], longbuf[MAX_BLOB_SIZE];
int i;
memset(&app_version, 0, sizeof(app_version));
@ -110,68 +133,71 @@ void add_app_version() {
if (message) strcpy(app_version.message, message);
if (message_priority) strcpy(app_version.message, message_priority);
// copy executable to download directory
strcpy(app_version.xml_doc, "");
// copy executables to download directory and sign them
//
sprintf(
buf,
"cp %s/%s %s/%s",
exec_dir, exec_file, download_dir, exec_file
);
retval = system(buf);
if (retval) {
printf("failed: %s\n", buf);
return;
for (i=0; i<nexec_files; i++) {
sprintf(
buf,
"cp %s/%s %s/%s",
exec_dir, exec_files[i], download_dir, exec_files[i]
);
retval = system(buf);
if (retval) {
printf("failed: %s\n", buf);
return;
}
if (signed_exec_files) {
read_filename(signature_files[i], signature_text);
} else {
sprintf(path, "%s/%s", exec_dir, exec_files[i]);
sign_executable(path, signature_text);
}
md5_file(path, md5_cksum, nbytes);
// generate the XML doc directly.
// TODO: use a template, as in create_work (??)
//
sprintf(longbuf,
"<file_info>\n"
" <name>%s</name>\n"
" <url>%s/%s</url>\n"
" <executable/>\n"
" <file_signature>\n%s"
" </file_signature>\n"
" <nbytes>%f</nbytes>\n"
"</file_info>\n",
exec_files[i],
url_base, exec_files[i],
signature_text,
nbytes
);
strcat(app_version.xml_doc, longbuf);
}
// sign the executable
//
R_RSA_PRIVATE_KEY code_sign_key;
FILE* fkey = fopen(code_sign_keyfile, "r");
if (!fkey) {
fprintf(stderr, "add: can't open key file (%s)\n", code_sign_keyfile);
exit(1);
}
retval = scan_key_hex(fkey, (KEY*)&code_sign_key, sizeof(code_sign_key));
fclose(fkey);
if (retval) {
fprintf(stderr, "add: can't parse key\n");
exit(1);
}
sprintf(path, "%s/%s", exec_dir, exec_file);
signature.data = signature_buf;
sign_file(path, code_sign_key, signature);
sprint_hex_data(signature_text, signature);
md5_file(path, md5_cksum, nbytes);
// generate the XML doc directly.
// TODO: use a template, as in create_work
//
sprintf(app_version.xml_doc,
"<file_info>\n"
" <name>%s</name>\n"
" <url>%s/%s</url>\n"
" <executable/>\n"
" <file_signature>\n%s"
" </file_signature>\n"
" <nbytes>%f</nbytes>\n"
"</file_info>\n"
sprintf(longbuf,
"<app_version>\n"
" <app_name>%s</app_name>\n"
" <version_num>%d</version_num>\n"
" <file_ref>\n"
" <file_name>%s</file_name>\n"
" <main_program/>\n"
" </file_ref>\n"
"</app_version>\n",
exec_file,
url_base, exec_file,
signature_text,
nbytes,
" <version_num>%d</version_num>\n",
app_name,
version,
exec_file
version
);
strcat(app_version.xml_doc, longbuf);
for (i=0; i<nexec_files; i++) {
sprintf(longbuf,
" <file_ref>\n"
" <file_name>%s</file_name>\n"
"%s"
" </file_ref>\n",
exec_files[i],
i?"":" <main_program/>\n"
);
strcat(app_version.xml_doc, longbuf);
}
strcat(app_version.xml_doc, "</app_version>\n");
app_version.create_time = time(0);
retval = db_app_version_new(app_version);
@ -238,9 +264,24 @@ int main(int argc, char** argv) {
} else if (!strcmp(argv[i], "-exec_dir")) {
i++;
exec_dir = argv[i];
} else if (!strcmp(argv[i], "-exec_file")) {
} else if (!strcmp(argv[i], "-exec_files")) {
signed_exec_files = false;
i++;
exec_file= argv[i];
nexec_files = 0;
while (i < argc) {
exec_files[nexec_files++] = argv[i++];
}
break;
} else if (!strcmp(argv[i], "-signed_exec_files")) {
signed_exec_files = true;
i++;
nexec_files = 0;
while (i < argc) {
exec_files[nexec_files] = argv[i++];
signature_files[nexec_files] = argv[i++];
nexec_files++;
}
break;
} else if (!strcmp(argv[i], "-exec_dir")) {
i++;
exec_dir = argv[i];

View File

@ -37,8 +37,8 @@
#define DOWNLOAD_URL_MACRO "<DOWNLOAD_URL/>"
int read_file(FILE* f, char* buf) {
assert(f!=NULL);
assert(buf!=NULL);
assert(f);
assert(buf);
int n = fread(buf, 1, MAX_BLOB_SIZE, f);
buf[n] = 0;
return 0;
@ -46,8 +46,8 @@ int read_file(FILE* f, char* buf) {
int read_filename(char* path, char* buf) {
int retval;
assert(path!=NULL);
assert(buf!=NULL);
assert(path);
assert(buf);
FILE* f = fopen(path, "r");
if (!f) return -1;
retval = read_file(f, buf);