From cbf997aa71a07703b7990dd877cf1bc4a4ff6278 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 29 Sep 2002 00:32:11 +0000 Subject: [PATCH] new preferences scheme svn path=/trunk/boinc/; revision=444 --- checkin_notes | 31 ++++++++++++++++++ client/client_state.C | 26 ++++++---------- client/client_state.h | 2 +- client/client_types.C | 1 + client/client_types.h | 2 +- client/cs_scheduler.C | 71 ++++++++++++++++++++++++++++-------------- client/file_names.C | 7 ----- client/file_names.h | 5 ++- client/prefs.C | 35 +++++---------------- client/prefs.h | 7 ++--- client/scheduler_op.C | 19 ++++++----- client/scheduler_op.h | 4 +-- db/db.h | 6 ++-- db/db_mysql.C | 4 +-- db/schema.sql | 1 - doc/boinc_dev.html | 5 ++- doc/prefs_impl.html | 52 +++++++++++++++++++++++++++++++ doc/prefs_mod.html | 4 --- html/user/prefs.inc | 6 ++-- sched/handle_request.C | 40 +++++++++++++++++------- sched/server_types.C | 18 +++++------ sched/server_types.h | 1 - tools/add.C | 1 - 23 files changed, 215 insertions(+), 133 deletions(-) create mode 100644 doc/prefs_impl.html delete mode 100644 doc/prefs_mod.html diff --git a/checkin_notes b/checkin_notes index 9a438fc938..743fda4bdb 100755 --- a/checkin_notes +++ b/checkin_notes @@ -1992,3 +1992,34 @@ David Sept 26 2002 test_uc.php tools/ add.C + +David Sept 28 2002 + - Clarified the way that preferences (global and project) + are stored in the database and in the core client, + and the protocol (part of scheduler RPC) for maintaining them. + See doc/prefs_impl.html + - Implemented the above: lots of small changes to client, server + - Changed names from "prefs" to "global_prefs" where relevant + + client/ + client_state.C,h + client_types.C,h + cs_scheduler.C + file_names.C,h + prefs.C,h + scheduler_op.C,h + db/ + db.h + db_mysql.C + schema.sql + doc/ + boinc_dev.html + prefs_impl.html (new) + prefs_mod.html (removed) + html_user/ + prefs.inc + sched/ + handle_request.C + server_types.C,h + tools/ + add.C diff --git a/client/client_state.C b/client/client_state.C index 61a31cc6da..29601c4a09 100644 --- a/client/client_state.C +++ b/client/client_state.C @@ -64,20 +64,11 @@ int CLIENT_STATE::init() { // nslots = 1; - // Read the user preferences file, if it exists. + // Read the global preferences file, if it exists. // - retval = prefs.parse_file(); + retval = global_prefs.parse_file(); if (retval) { - retval = write_initial_prefs(); - if (retval) { - printf("can't initialize prefs.xml\n"); - return retval; - } - retval = prefs.parse_file(); - if (retval) { - printf("can't initialize prefs.xml\n"); - return retval; - } + printf("No global preferences file; will use defaults.\n"); } // parse account files. @@ -192,11 +183,14 @@ double CLIENT_STATE::allowed_disk_usage() { double percent_space, min_val; // Calculate allowed disk usage based on % pref - percent_space = host_info.d_total*prefs.disk_max_used_pct/100.0; + // + percent_space = host_info.d_total*global_prefs.disk_max_used_pct/100.0; + + min_val = host_info.d_free - global_prefs.disk_min_free_gb*1e9; - min_val = host_info.d_free - prefs.disk_min_free_gb*1e9; // Return the minimum of the three - return min(min(prefs.disk_max_used_gb*1e9, percent_space), min_val); + // + return min(min(global_prefs.disk_max_used_gb*1e9, percent_space), min_val); } // See if (on the basis of user prefs) we should suspend activities. @@ -204,7 +198,7 @@ double CLIENT_STATE::allowed_disk_usage() { // int CLIENT_STATE::check_suspend_activities() { bool should_suspend = false; - if (prefs.dont_run_on_batteries && host_is_running_on_batteries()) { + if (global_prefs.dont_run_on_batteries && host_is_running_on_batteries()) { should_suspend = true; } diff --git a/client/client_state.h b/client/client_state.h index b0ce47676a..0292816c78 100644 --- a/client/client_state.h +++ b/client/client_state.h @@ -53,7 +53,7 @@ public: FILE_XFER_SET* file_xfers; ACTIVE_TASK_SET active_tasks; HOST_INFO host_info; - PREFS prefs; + GLOBAL_PREFS global_prefs; NET_STATS net_stats; CLIENT_STATE(); diff --git a/client/client_types.C b/client/client_types.C index c91c592ba6..accb63e312 100644 --- a/client/client_types.C +++ b/client/client_types.C @@ -64,6 +64,7 @@ int PROJECT::parse_account(FILE* in) { strcpy(master_url, ""); strcpy(authenticator, ""); + if (project_specific_prefs) free(project_specific_prefs); while (fgets(buf, 256, in)) { if (match_tag(buf, "")) continue; if (match_tag(buf, "")) return 0; diff --git a/client/client_types.h b/client/client_types.h index 85939a3409..39f1221b82 100644 --- a/client/client_types.h +++ b/client/client_types.h @@ -51,7 +51,7 @@ public: char master_url[256]; // url of site that contains scheduler tags // for this project char authenticator[256]; // user's authenticator on this project - char* project_specific_prefs; + char* project_specific_prefs; // without enclosing tags double resource_share; // project's resource share // relative to other projects. Arbitrary scale. diff --git a/client/cs_scheduler.C b/client/cs_scheduler.C index f4828ee286..3b5083e711 100644 --- a/client/cs_scheduler.C +++ b/client/cs_scheduler.C @@ -64,8 +64,8 @@ double CLIENT_STATE::current_water_days() { // double CLIENT_STATE::work_needed_secs() { double x = current_water_days(); - if (x > prefs.high_water_days) return 0; - return (prefs.high_water_days - x)*86400; + if (x > global_prefs.high_water_days) return 0; + return (global_prefs.high_water_days - x)*86400; } // update exponentially-averaged CPU times of all projects @@ -185,10 +185,13 @@ int CLIENT_STATE::make_scheduler_request(PROJECT* p, double work_req) { fprintf(f, "\n%s\n", p->code_sign_key); } - FILE* fprefs = fopen(PREFS_FILE_NAME, "r"); - if (!fprefs) return ERR_FOPEN; - copy_stream(fprefs, f); - fclose(fprefs); + // insert global preferences if present + // + FILE* fprefs = fopen(GLOBAL_PREFS_FILE_NAME, "r"); + if (fprefs) { + copy_stream(fprefs, f); + fclose(fprefs); + } time_stats.write(f, true); net_stats.write(f, true); @@ -247,7 +250,7 @@ bool CLIENT_STATE::scheduler_rpc_poll() { switch(scheduler_op->state) { case SCHEDULER_OP_STATE_IDLE: - below_low_water = (current_water_days() <= prefs.low_water_days); + below_low_water = (current_water_days() <= global_prefs.low_water_days); if (below_low_water && some_project_rpc_ok()) { compute_resource_debts(); scheduler_op->init_get_work(); @@ -276,7 +279,7 @@ bool CLIENT_STATE::scheduler_rpc_poll() { return action; } -// Parse the reply from a scheduler +// Handle the reply from a scheduler // void CLIENT_STATE::handle_scheduler_reply( PROJECT* project, char* scheduler_url @@ -315,32 +318,52 @@ void CLIENT_STATE::handle_scheduler_reply( if (sr.request_delay) { project->min_rpc_time = time(0) + sr.request_delay; } + if (sr.hostid) { project->hostid = sr.hostid; project->rpc_seqno = 0; } - // if the scheduler reply includes preferences - // that are newer than what we have on disk, write them to disk + // if the scheduler reply includes global preferences, + // insert extra elements, write to disk, and parse // - - if (sr.prefs_mod_time > prefs.mod_time) { - f = fopen(PREFS_FILE_NAME, "w"); + if (sr.global_prefs_xml) { + f = fopen(GLOBAL_PREFS_FILE_NAME, "w"); fprintf(f, - "\n" - " %d\n" - " %s\n" - " %s\n", - sr.prefs_mod_time, + "\n" + " %s\n" + " %s\n" + "%s" + "\n", project->master_url, - scheduler_url - ); - fputs(sr.prefs_xml, f); - fprintf(f, - "\n" + scheduler_url, + sr.global_prefs_xml ); fclose(f); - prefs.parse_file(); + global_prefs.parse_file(); + } + + // deal with project preferences (should always be there) + // + if (sr.project_prefs_xml) { + char path[256]; + f = fopen(TEMP_FILE_NAME, "w"); + fprintf(f, + "\n" + " %s\n" + " %s\n" + "%s" + "\n", + project->master_url, + project->authenticator, + sr.project_prefs_xml + ); + fclose(f); + get_account_filename(project->master_url, path); + retval = boinc_rename(TEMP_FILE_NAME, path); + f = fopen(path, "r"); + project->parse_account(f); + fclose(f); } // if the scheduler reply includes a code-signing key, diff --git a/client/file_names.C b/client/file_names.C index 2509634b8c..0925c55de0 100644 --- a/client/file_names.C +++ b/client/file_names.C @@ -189,13 +189,6 @@ int make_slot_dir(int slot) { #endif -// Returns a filename used for prefs backup -// -int make_prefs_backup_name(PREFS& prefs, char* name) { - sprintf(name, "prefs_backup_%d", prefs.mod_time); - return 0; -} - void get_account_filename(char* master_url, char* path) { char buf[256]; escape_project_url(master_url, buf); diff --git a/client/file_names.h b/client/file_names.h index 865e44202c..eb89055697 100644 --- a/client/file_names.h +++ b/client/file_names.h @@ -31,7 +31,6 @@ extern void get_slot_dir(int slot, char* path); extern int make_project_dir(PROJECT&); extern int make_slot_dir(int); -extern int make_prefs_backup_name(PREFS&, char*); extern void get_account_filename(char* master_url, char* path); extern bool is_account_file(char*); @@ -39,9 +38,9 @@ extern bool is_account_file(char*); #define SLOTS_DIR "slots" #define STATE_FILE_TEMP "state_file_temp.xml" #define STATE_FILE_NAME "client_state.xml" -#define PREFS_FILE_NAME "prefs.xml" +#define GLOBAL_PREFS_FILE_NAME "global_prefs.xml" #define MASTER_FILE_NAME "master.html" -#define PREFS_TEMP_FILE_NAME "prefs_temp.xml" #define SCHED_OP_REQUEST_FILE "sched_request.xml" #define SCHED_OP_RESULT_FILE "sched_reply.xml" #define LOG_FLAGS_FILE "log_flags.xml" +#define TEMP_FILE_NAME "temp.xml" diff --git a/client/prefs.C b/client/prefs.C index 244677fa7e..40bdaf215f 100644 --- a/client/prefs.C +++ b/client/prefs.C @@ -31,10 +31,9 @@ #include "prefs.h" // the following values determine how the client behaves -// if the user didn't set preferences via web +// if there are no global prefs yet // -PREFS::PREFS() { - mod_time = 0; +GLOBAL_PREFS::GLOBAL_PREFS() { dont_run_on_batteries = false; dont_run_if_user_active = false; confirm_before_connecting = false; @@ -45,14 +44,13 @@ PREFS::PREFS() { disk_min_free_gb = 0.1; }; -// Parse XML based prefs, usually from prefs.xml +// Parse XML global prefs // -int PREFS::parse(FILE* in) { +int GLOBAL_PREFS::parse(FILE* in) { char buf[256]; - PROJECT* project; while (fgets(buf, 256, in)) { - if (match_tag(buf, "")) { + if (match_tag(buf, "")) { return 0; } else if (match_tag(buf, "")) { dont_run_on_batteries = true; @@ -78,32 +76,15 @@ int PREFS::parse(FILE* in) { return ERR_XML_PARSE; } -// Parse prefs.xml for user preferences +// Parse global prefs file // -int PREFS::parse_file() { +int GLOBAL_PREFS::parse_file() { FILE* f; int retval; - f = fopen(PREFS_FILE_NAME, "r"); + f = fopen(GLOBAL_PREFS_FILE_NAME, "r"); if (!f) return ERR_FOPEN; retval = parse(f); fclose(f); return retval; } - -// Write the default preferences -// TODO: should mod_time really be 1? -// -int write_initial_prefs() { - FILE* f = fopen(PREFS_FILE_NAME, "w"); - if (!f) return ERR_FOPEN; - fprintf(f, - "\n" - " 1\n" - " 2\n" - " 1\n" - "\n" - ); - fclose(f); - return 0; -} diff --git a/client/prefs.h b/client/prefs.h index 9a6179e0d6..37436853fc 100644 --- a/client/prefs.h +++ b/client/prefs.h @@ -33,8 +33,7 @@ // The following structure is a parsed version of the prefs file // -struct PREFS { - int mod_time; +struct GLOBAL_PREFS { bool dont_run_on_batteries; bool dont_run_if_user_active; bool confirm_before_connecting; @@ -44,11 +43,9 @@ struct PREFS { double disk_max_used_pct; double disk_min_free_gb; - PREFS(); + GLOBAL_PREFS(); int parse(FILE*); int parse_file(); }; -extern int write_initial_prefs(); - #endif diff --git a/client/scheduler_op.C b/client/scheduler_op.C index 36c3095e62..357df77ea8 100644 --- a/client/scheduler_op.C +++ b/client/scheduler_op.C @@ -340,13 +340,15 @@ bool SCHEDULER_OP::poll() { } SCHEDULER_REPLY::SCHEDULER_REPLY() { - prefs_xml = 0; + global_prefs_xml = 0; + project_prefs_xml = 0; code_sign_key = 0; code_sign_key_signature = 0; } SCHEDULER_REPLY::~SCHEDULER_REPLY() { - if (prefs_xml) free(prefs_xml); + if (global_prefs_xml) free(global_prefs_xml); + if (project_prefs_xml) free(project_prefs_xml); if (code_sign_key) free(code_sign_key); if (code_sign_key_signature) free(code_sign_key_signature); } @@ -359,8 +361,8 @@ int SCHEDULER_REPLY::parse(FILE* in) { strcpy(message_priority, ""); request_delay = 0; hostid = 0; - prefs_mod_time = 0; - prefs_xml = 0; + global_prefs_xml = 0; + project_prefs_xml = 0; code_sign_key = 0; code_sign_key_signature = 0; @@ -388,10 +390,11 @@ int SCHEDULER_REPLY::parse(FILE* in) { continue; } else if (parse_int(buf, "", request_delay)) { continue; - } else if (parse_int(buf, "", prefs_mod_time)) { - continue; - } else if (match_tag(buf, "")) { - retval = dup_element_contents(in, "", &prefs_xml); + } else if (match_tag(buf, "")) { + retval = dup_element_contents(in, "", &global_prefs_xml); + if (retval) return ERR_XML_PARSE; + } else if (match_tag(buf, "")) { + retval = dup_element_contents(in, "", &project_prefs_xml); if (retval) return ERR_XML_PARSE; } else if (match_tag(buf, "")) { retval = dup_element_contents(in, "", &code_sign_key); diff --git a/client/scheduler_op.h b/client/scheduler_op.h index 9019739786..3f03beeb37 100644 --- a/client/scheduler_op.h +++ b/client/scheduler_op.h @@ -76,8 +76,8 @@ struct SCHEDULER_REPLY { char message[1024]; char message_priority[256]; char project_name[256]; - int prefs_mod_time; - char* prefs_xml; + char* global_prefs_xml; // not including tags + char* project_prefs_xml; // not including tags char user_name[256]; double total_credit; double expavg_credit; diff --git a/db/db.h b/db/db.h index 4fa5e824e1..62dcd1cbbb 100644 --- a/db/db.h +++ b/db/db.h @@ -116,10 +116,10 @@ struct USER { double expavg_credit; // credit per second, recent average double expavg_time; // when the above was computed char global_prefs[MAX_BLOB_SIZE]; // global preferences - unsigned int global_prefs_mod_time; // When global prefs were last updated - // zero if they're not defined + // within tag char project_prefs[MAX_BLOB_SIZE]; - int teamid; // if the user is part of a team + // within tag + int teamid; // if user is part of a team }; #define TEAM_TYPE_COMPANY_SMALL 1 diff --git a/db/db_mysql.C b/db/db_mysql.C index 0a60742fbd..86442038f4 100644 --- a/db/db_mysql.C +++ b/db/db_mysql.C @@ -120,7 +120,7 @@ void struct_to_str(void* vp, char* q, int type) { "web_password='%s', authenticator='%s', " "country='%s', postal_code='%s', " "total_credit=%f, expavg_credit=%f, expavg_time=%f, " - "global_prefs='%s', global_prefs_mod_time=%d, project_prefs='%s', " + "global_prefs='%s', project_prefs='%s', " "teamid=%d", up->id, up->create_time, @@ -134,7 +134,6 @@ void struct_to_str(void* vp, char* q, int type) { up->expavg_credit, up->expavg_time, up->global_prefs, - up->global_prefs_mod_time, up->project_prefs, up->teamid ); @@ -296,7 +295,6 @@ void row_to_struct(MYSQL_ROW& r, void* vp, int type) { up->expavg_credit = atof(r[i++]); up->expavg_time = atof(r[i++]); strcpy(up->global_prefs, r[i++]); - up->global_prefs_mod_time = atoi(r[i++]); strcpy(up->project_prefs, r[i++]); up->teamid = atoi(r[i++]); break; diff --git a/db/schema.sql b/db/schema.sql index b995c92dd0..442b4fdb07 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -49,7 +49,6 @@ create table user ( expavg_credit float not null, expavg_time float not null, global_prefs blob, - global_prefs_mod_time integer not null, project_prefs blob, teamid integer not null, primary key (id) diff --git a/doc/boinc_dev.html b/doc/boinc_dev.html index adca6ffa8e..6194064bdb 100644 --- a/doc/boinc_dev.html +++ b/doc/boinc_dev.html @@ -37,8 +37,11 @@ before getting into the source code. +
  • Miscellaneous + diff --git a/doc/prefs_impl.html b/doc/prefs_impl.html new file mode 100644 index 0000000000..ae4ce02882 --- /dev/null +++ b/doc/prefs_impl.html @@ -0,0 +1,52 @@ +
    +In database:
    +    user.global_prefs: XML, within  tags
    +        may be empty; nonempty only if user has actually seen
    +        always includes  element
    +        includes ,  elements if
    +            prefs came from another project
    +    user.project_prefs: XML, within  tags
    +        always present.
    +        contains at least  element
    +
    +In client:
    +    global_prefs.xml (present ONLY if have obtained from a server)
    +        same as user.global_prefs,
    +        but the following is inserted at start:
    +            
    +            
    +        stored in memory:
    +            in parsed form (as struct)
    +    account_XXX.xml
    +        same as user.project_prefs, but with the following added:
    +            
    +            
    +        stored in memory:
    +            in PROJECT struct
    +                master_url, authenticator, resource share parsed;
    +                project_specific_prefs raw XML (with enclosing tags)
    +
    +
    +RPC request:
    +    includes global_prefs.xml if it's there
    +
    +RPC handling and reply:
    +    always send project prefs
    +    if request message includes global prefs
    +        if missing in DB, or request copy is newer:
    +            install in DB
    +        else if DB copy is newer
    +            include DB copy in reply
    +    else
    +        if present in DB, include in reply
    +
    +handling of RPC reply
    +    if includes global prefs
    +        insert ,  elements if missing
    +        write to global_prefs.xml
    +        parse into memory
    +    project prefs
    +        insert ,  elements,
    +        write to account_XXX.xml file
    +        parse; update resource share, project_specific_prefs in PROJECT struct
    +
    diff --git a/doc/prefs_mod.html b/doc/prefs_mod.html deleted file mode 100644 index 927942558a..0000000000 --- a/doc/prefs_mod.html +++ /dev/null @@ -1,4 +0,0 @@ -Modifying Preferences - -

    Modifying Preferences

    -

    diff --git a/html/user/prefs.inc b/html/user/prefs.inc index 5433a45f6e..4d21258479 100644 --- a/html/user/prefs.inc +++ b/html/user/prefs.inc @@ -287,6 +287,8 @@ function prefs_project_parse_form(&$prefs) { // function global_prefs_make_xml($prefs) { $xml = "\n"; + $now = time(); + $xml = $xml."$now\n"; if ($prefs->dont_run_on_batteries) { $xml = $xml."\n"; } @@ -322,14 +324,12 @@ function project_prefs_make_xml($prefs) { // function global_prefs_update($user, $prefs) { $prefs_xml = global_prefs_make_xml($prefs); - $now = time(); - mysql_query("update user set global_prefs='$prefs_xml', global_prefs_mod_time=$now where id=$user->id"); + mysql_query("update user set global_prefs='$prefs_xml' where id=$user->id"); $user->global_prefs = $prefs_xml; } function project_prefs_update($user, $prefs) { $prefs_xml = project_prefs_make_xml($prefs); - $now = time(); mysql_query("update user set project_prefs='$prefs_xml' where id=$user->id"); $user->project_prefs = $prefs_xml; } diff --git a/sched/handle_request.C b/sched/handle_request.C index a3a22225e8..8c5e628039 100644 --- a/sched/handle_request.C +++ b/sched/handle_request.C @@ -224,23 +224,39 @@ int update_host_record(SCHEDULER_REQUEST& sreq, HOST& host) { return 0; } +// Deal with global preferences. // If the client sent global prefs, and they're more recent than ours, // update user record in DB. -// If we our DB has more recent global prefs than client's, send them. +// If DB has more recent global prefs than client's, send them. // int handle_global_prefs(SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply) { - if (sreq.global_prefs_mod_time > reply.user.global_prefs_mod_time - && strlen(sreq.global_prefs_xml) - ) { - strncpy(reply.user.global_prefs, sreq.global_prefs_xml, sizeof(reply.user.global_prefs)); - reply.user.global_prefs_mod_time = sreq.global_prefs_mod_time; - if (reply.user.global_prefs_mod_time > (unsigned)time(0)) { - reply.user.global_prefs_mod_time = (unsigned)time(0); + unsigned int req_mod_time, db_mod_time; + bool need_update; + reply.send_global_prefs = false; + if (sreq.global_prefs_xml) { + need_update = false; + parse_int(sreq.global_prefs_xml, "", (int)req_mod_time); + if (strlen(reply.user.global_prefs)) { + parse_int(reply.user.global_prefs, "", (int)db_mod_time); + if (req_mod_time > db_mod_time) { + need_update = true; + } else if (req_mod_time < db_mod_time) { + reply.send_global_prefs = true; + } + } else { + need_update = true; + } + if (need_update) { + strncpy( + reply.user.global_prefs, sreq.global_prefs_xml, + sizeof(reply.user.global_prefs) + ); + db_user_update(reply.user); + } + } else { + if (strlen(reply.user.global_prefs)) { + reply.send_global_prefs = true; } - db_user_update(reply.user); - } - if (reply.user.global_prefs_mod_time > sreq.global_prefs_mod_time) { - reply.send_global_prefs = true; } return 0; } diff --git a/sched/server_types.C b/sched/server_types.C index dbac35f0c4..588d159047 100644 --- a/sched/server_types.C +++ b/sched/server_types.C @@ -46,7 +46,6 @@ int SCHEDULER_REQUEST::parse(FILE* fin) { strcpy(authenticator, ""); hostid = 0; work_req_seconds = 0; - global_prefs_mod_time = 0; global_prefs_xml = strdup(""); fgets(buf, 256, fin); @@ -59,14 +58,13 @@ int SCHEDULER_REQUEST::parse(FILE* fin) { else if (parse_str(buf, "", platform_name, sizeof(platform_name))) continue; else if (parse_int(buf, "", core_client_version)) continue; else if (parse_int(buf, "", work_req_seconds)) continue; - else if (parse_int(buf, "", (int)global_prefs_mod_time)) { - continue; - } - else if (match_tag(buf, "")) { + else if (match_tag(buf, "")) { + global_prefs_xml = strdup("\n"); while (fgets(buf, 256, fin)) { - if (strstr(buf, "")) break; + if (strstr(buf, "")) break; strcatdup(global_prefs_xml, buf); } + strcatdup(global_prefs_xml, "\n"); } else if (match_tag(buf, "")) { host.parse(fin); @@ -145,13 +143,13 @@ int SCHEDULER_REPLY::write(FILE* fout) { } if (send_global_prefs) { - fprintf(fout, - "%d\n", - user.global_prefs_mod_time - ); fputs(user.global_prefs, fout); } + // always send project prefs + // + fputs(user.project_prefs, fout); + // acknowledge results // for (i=0; i