From 3cd542a6e198f4fed682f4bf1af9ef898ac8e0b6 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 29 Jul 2002 00:39:45 +0000 Subject: [PATCH] new versioning semantics svn path=/trunk/boinc/; revision=250 --- TODO | 11 ++- checkin_notes | 63 +++++++++++++++ client/client_state.C | 152 +++++++----------------------------- client/client_state.h | 1 + client/client_types.C | 25 +----- client/client_types.h | 5 +- client/cs_scheduler.C | 5 +- client/main.C | 30 +++---- db/db.h | 4 +- db/db_mysql.C | 10 +-- db/schema.sql | 4 +- doc/app.html | 14 ++++ doc/index.html | 1 - doc/intro.html | 28 ++++--- doc/project.html | 15 +++- doc/tools_other.html | 24 +++++- html/user/db.inc | 8 +- sched/handle_request.C | 22 +++--- sched/sched_shmem.C | 20 +++-- sched/server_types.C | 3 +- test/init.inc | 14 +++- test/master.html | 8 +- tools/Makefile.in | 4 +- tools/add.C | 173 +++++++++++++++++++++++++---------------- tools/backend_lib.C | 8 +- 25 files changed, 343 insertions(+), 309 deletions(-) diff --git a/TODO b/TODO index 82af3a71a6..715430e256 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/checkin_notes b/checkin_notes index 8faa07eb28..c06da8b522 100755 --- a/checkin_notes +++ b/checkin_notes @@ -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 diff --git a/client/client_state.C b/client/client_state.C index 74ce708229..5276be8319 100644 --- a/client/client_state.C +++ b/client/client_state.C @@ -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; imaster_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; iproject == 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; iproject == 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; iproject == 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; iapp == 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; iproject == 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; iapp_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")) return 0; else if (parse_str(buf, "", name)) continue; - else if (parse_int(buf, "", 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, "\n" " %s\n" - " %d\n" "\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) { diff --git a/client/client_types.h b/client/client_types.h index 8e06aaa8d4..842e7f0178 100644 --- a/client/client_types.h +++ b/client/client_types.h @@ -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 input_files; diff --git a/client/cs_scheduler.C b/client/cs_scheduler.C index 9cd1d58cac..ad430504ab 100644 --- a/client/cs_scheduler.C +++ b/client/cs_scheduler.C @@ -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; iparse_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; } diff --git a/db/db.h b/db/db.h index 34192eac3a..0b1419518f 100644 --- a/db/db.h +++ b/db/db.h @@ -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 diff --git a/db/db_mysql.C b/db/db_mysql.C index a3561e8f1d..0bd160bad1 100644 --- a/db/db_mysql.C +++ b/db/db_mysql.C @@ -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: diff --git a/db/schema.sql b/db/schema.sql index 84f8272fd7..3b75063e78 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -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) ); diff --git a/doc/app.html b/doc/app.html index 74d5271f25..c42a27d8d2 100644 --- a/doc/app.html +++ b/doc/app.html @@ -20,6 +20,20 @@ is called an application version. An application version can consist of multiple files: for example, a controller script, pre- and post-processing programs, and a primary. + +

+Each application version has an integer version number. +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. + +

+Each application has a minimum version. +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. +

Application version are maintained in the app_version table in the BOINC DB, diff --git a/doc/index.html b/doc/index.html index 6d0feef40d..0a80194079 100644 --- a/doc/index.html +++ b/doc/index.html @@ -18,7 +18,6 @@

  • Compute model: remote file access
  • Accounting and result validation
  • Back end examples -
  • Versioning
  • The BOINC application library
  • Client graphics
  • Application development diff --git a/doc/intro.html b/doc/intro.html index e0b256b043..53b80aa90e 100644 --- a/doc/intro.html +++ b/doc/intro.html @@ -55,40 +55,44 @@ are encouraged to do so.
  • Support for Large Data Computations
    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.

  • Extreme Resource Requirement Support
    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.

  • Application Program Interface
    An Application Program Interface (API) is provided to application developers -to ease integration with BOINC. See the API page for -details. +to ease integration with BOINC. +See the API page for details.

  • Support for Applications in any Language
    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.

  • Database and Server Architecture
    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).

    diff --git a/doc/project.html b/doc/project.html index c93dd5199b..6d1223c886 100644 --- a/doc/project.html +++ b/doc/project.html @@ -1,9 +1,20 @@

    Projects and applications

    -A project is an organization that uses BOINC. +A project 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. +

    +Each is identified by a master URL, +which refers to an XHTML document describing the project. +

    +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. +

    The components of a project are shown below.
    diff --git a/doc/tools_other.html b/doc/tools_other.html index 26576fcd84..2022872945 100644 --- a/doc/tools_other.html +++ b/doc/tools_other.html @@ -26,12 +26,30 @@ add platform name -platform_name name

    Create a platform record (just creates a DB record);
    -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 +
    -app_name x +
    -platform_name y +
    -version a +
    -download_dir d +
    -url_base e +
    -exec_dir b +
    [ -message x ] +
    [ -message_priority y ] +
    [ -code_sign_keyfile x -exec_files file1 file2 ... ] +
    [ -signed_exec_files file1 sign1 file2 sign2 ... ]
    +
    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.
    add user -email_addr x -name y -web_password z -authenticator a
    diff --git a/html/user/db.inc b/html/user/db.inc index 12c6448b5a..99edf72a4d 100644 --- a/html/user/db.inc +++ b/html/user/db.inc @@ -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", "
    ".htmlspecialchars($app->result_xml_template)."
    "); echo ""; } diff --git a/sched/handle_request.C b/sched/handle_request.C index 4591558880..8ceb0054b3 100644 --- a/sched/handle_request.C +++ b/sched/handle_request.C @@ -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; i0; i++) { diff --git a/sched/sched_shmem.C b/sched/sched_shmem.C index 2d20ca753e..e90edace5f 100644 --- a/sched/sched_shmem.C +++ b/sched/sched_shmem.C @@ -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; iappid == 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; } diff --git a/sched/server_types.C b/sched/server_types.C index 54cde2cc54..69d53feb82 100644 --- a/sched/server_types.C +++ b/sched/server_types.C @@ -210,9 +210,8 @@ int APP::write(FILE* fout) { fprintf(fout, "\n" " %s\n" - " %d\n" "\n", - name, prod_vers // TODO: handle other phases + name ); return 0; } diff --git a/test/init.inc b/test/init.inc index 9e1bd72d54..8ab73e30a4 100644 --- a/test/init.inc +++ b/test/init.inc @@ -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"); } diff --git a/test/master.html b/test/master.html index 1696f6c70b..60044e2e7a 100644 --- a/test/master.html +++ b/test/master.html @@ -1,7 +1,3 @@ - - - - +

    Test BOINC Project

    + http://localhost/boinc-cgi/cgi - - diff --git a/tools/Makefile.in b/tools/Makefile.in index 0d743b43e8..3ece4a806f 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -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 \ diff --git a/tools/add.C b/tools/add.C index ffbc63de43..1ad2fed424 100644 --- a/tools/add.C +++ b/tools/add.C @@ -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\n" + " %s\n" + " %s/%s\n" + " \n" + " \n%s" + " \n" + " %f\n" + "\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, - "\n" - " %s\n" - " %s/%s\n" - " \n" - " \n%s" - " \n" - " %f\n" - "\n" + sprintf(longbuf, "\n" " %s\n" - " %d\n" - " \n" - " %s\n" - " \n" - " \n" - "\n", - exec_file, - url_base, exec_file, - signature_text, - nbytes, + " %d\n", app_name, - version, - exec_file + version ); + strcat(app_version.xml_doc, longbuf); + for (i=0; i\n" + " %s\n" + "%s" + " \n", + exec_files[i], + i?"":" \n" + ); + strcat(app_version.xml_doc, longbuf); + } + strcat(app_version.xml_doc, "\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]; diff --git a/tools/backend_lib.C b/tools/backend_lib.C index 4db281794c..7d5b57bf87 100644 --- a/tools/backend_lib.C +++ b/tools/backend_lib.C @@ -37,8 +37,8 @@ #define DOWNLOAD_URL_MACRO "" 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);