diff --git a/checkin_notes b/checkin_notes index 5ad7af6a33..4e933d2dff 100644 --- a/checkin_notes +++ b/checkin_notes @@ -1321,3 +1321,17 @@ David Feb 12 2008 client/ work_fetch.C + +David Feb 12 2008 + - client code: my next-to-last checkin was flawed + + client/ + app_start.C + client_state.C,h + sandbox.C + clientgui/ + BOINCClientManager.cpp + MainDocument.cpp + lib/ + common_defs.h + util.C,h diff --git a/client/app_start.C b/client/app_start.C index a572e4f708..7b273c600d 100644 --- a/client/app_start.C +++ b/client/app_start.C @@ -496,23 +496,11 @@ int ACTIVE_TASK::start(bool first_time) { cmd_line = exec_path + std::string(" ") + wup->command_line; relative_to_absolute(slot_dir, slotdirpath); bool success = false; + get_sandbox_account_token(); for (i=0; i<5; i++) { - if (strlen(gstate.sandbox_account_name)) { - HANDLE hToken; - std::string username = gstate.sandbox_account_name; - std::string password = r_base64_decode(gstate.sandbox_account_password); - if (!LogonUser( username.c_str(), NULL, password.c_str(), - LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT, - &hToken) - ) { - windows_error_string(error_msg, sizeof(error_msg)); - msg_printf(wup->project, MSG_INTERNAL_ERROR, - "LogonUser failed: %s", error_msg - ); - } - + if (sandbox_account_token != NULL) { if (CreateProcessAsUser( - hToken, + sandbox_account_token, exec_path, (LPSTR)cmd_line.c_str(), NULL, diff --git a/client/client_state.C b/client/client_state.C index 06b6e84404..dd3d6d274f 100644 --- a/client/client_state.C +++ b/client/client_state.C @@ -211,7 +211,6 @@ int CLIENT_STATE::init() { "BOINC is running as a service and as a non-system user." ); } - read_client_auth_file(); #endif #ifdef __APPLE__ diff --git a/client/client_state.h b/client/client_state.h index 7844c8967d..b8efff383b 100644 --- a/client/client_state.h +++ b/client/client_state.h @@ -111,11 +111,7 @@ public: char attach_project_auth[256]; bool exit_before_upload; // exit when about to upload a file -#ifdef _WIN32 - char sandbox_account_name[256]; - char sandbox_account_password[256]; - void read_client_auth_file(); -#else +#ifndef _WIN32 gid_t boinc_project_gid; #endif diff --git a/client/sandbox.C b/client/sandbox.C index 52c86971e2..7310670eaf 100644 --- a/client/sandbox.C +++ b/client/sandbox.C @@ -38,22 +38,7 @@ bool g_use_sandbox = false; -#ifdef _WIN32 - -void CLIENT_STATE::read_client_auth_file() { - FILE* f; - char buf[256]; - - f = fopen(CLIENT_AUTH_FILENAME, "r"); - if (!f) return; - while (fgets(buf, 256, f)) { - if (parse_str(buf, "", sandbox_account_name, sizeof(sandbox_account_name))) continue; - if (parse_str(buf, "", sandbox_account_password, sizeof(sandbox_account_name))) continue; - } - fclose(f); -} - -#else +#ifndef _WIN32 #ifndef _DEBUG static int lookup_group(char* name, gid_t& gid) { struct group* gp = getgrnam(name); diff --git a/clientgui/BOINCClientManager.cpp b/clientgui/BOINCClientManager.cpp index e689ae6e20..3d61c1a72b 100644 --- a/clientgui/BOINCClientManager.cpp +++ b/clientgui/BOINCClientManager.cpp @@ -239,11 +239,15 @@ bool CBOINCClientManager::StartupBOINCCore() { argv[4] = NULL; } #endif - // Under wxMac-2.8.0, wxExecute starts a separate thread to wait for child's termination. - // That wxProcessTerminationThread uses a huge amount of processor time (about 11% of one - // CPU on 2GHz Intel Dual-Core Mac). + // Under wxMac-2.8.0, wxExecute starts a separate thread + // to wait for child's termination. + // That wxProcessTerminationThread uses a huge amount of processor + // time (about 11% of one CPU on 2GHz Intel Dual-Core Mac). // m_lBOINCCoreProcessId = ::wxExecute(argv); - run_program("/Library/Application Support/BOINC Data", buf, argv[3] ? 4 : 3, argv, 0.0, m_lBOINCCoreProcessId); + run_program( + "/Library/Application Support/BOINC Data", + buf, argv[3] ? 4 : 3, argv, 0.0, m_lBOINCCoreProcessId + ); #endif } else { buf[0] = '\0'; diff --git a/clientgui/MainDocument.cpp b/clientgui/MainDocument.cpp index 32048f6d33..5227b4c7be 100644 --- a/clientgui/MainDocument.cpp +++ b/clientgui/MainDocument.cpp @@ -1181,7 +1181,9 @@ int CMainDocument::WorkShowGraphics(RESULT* result) char* argv[5]; if (previous_gfx_app) { - // If this graphics app is already running, just bring it to the front + // If this graphics app is already running, + // just bring it to the front + // if (! GetProcessForPID(previous_gfx_app->pid, &gfx_app_psn)) { SetFrontProcess(&gfx_app_psn); } @@ -1190,6 +1192,7 @@ int CMainDocument::WorkShowGraphics(RESULT* result) // For unknown reasons, the graphics application exits with // "RegisterProcess failed (error = -50)" unless we pass its // full path twice in the argument list to execv. + // argv[0] = "switcher"; argv[1] = (char *)result->graphics_exec_path.c_str(); argv[2] = (char *)result->graphics_exec_path.c_str(); @@ -1219,6 +1222,7 @@ int CMainDocument::WorkShowGraphics(RESULT* result) char* argv[2]; // If graphics app is already running, don't launch a second instance + // if (previous_gfx_app) return 0; argv[0] = "--graphics"; argv[1] = 0; diff --git a/db/bossa_constraints.sql b/db/bossa_constraints.sql index fa73f16e4d..f307b8ecc1 100644 --- a/db/bossa_constraints.sql +++ b/db/bossa_constraints.sql @@ -1,9 +1,10 @@ alter table bossa_app - add unique(name); + add unique(name), + add unique(short_name); alter table bossa_job add unique(name), - add index bj_more_needed(app_id, more_needed); + add index bj_conf_needed(app_id, conf_needed); alter table bossa_job_inst add index bji_job(job_id), diff --git a/db/bossa_schema.sql b/db/bossa_schema.sql index afd0abf44a..333a9dd127 100644 --- a/db/bossa_schema.sql +++ b/db/bossa_schema.sql @@ -2,6 +2,7 @@ create table bossa_app ( id integer not null auto_increment, create_time integer not null, name varchar(255) not null, + short_name varchar(255) not null, description varchar(255) not null, long_jobs tinyint not null, display_script varchar(255) not null, @@ -25,7 +26,8 @@ create table bossa_job ( time_estimate integer not null, time_limit integer not null, transition_time double not null, - nneeded integer not null, + conf_needed double not null, + canonical_inst_id integer not null, primary key(id) ) engine=InnoDB; @@ -35,12 +37,15 @@ create table bossa_job_inst ( job_id integer not null, user_id integer not null, finish_time integer not null, + validate_state integer not null, + -- 0 init, 1 valid, 2 invalid info text, primary key(id) ) engine=InnoDB; -create table bossa_app_user ( - app_id integer not null, +create table bossa_user ( user_id integer not null, info text + -- Info about skill. + -- May depend on app; may be scalar or something else ); diff --git a/doc/boinc_news.php b/doc/boinc_news.php index 08f7e7c7cb..6b6b0cd37d 100644 --- a/doc/boinc_news.php +++ b/doc/boinc_news.php @@ -1,6 +1,10 @@ BOINC on JXTA." +), array("Feb 11, 2008", "Bulgarian BOINC users: find forums and information in your language at diff --git a/html/inc/bossa_example.inc b/html/inc/bossa_example.inc index 2f22a839b2..05c4385639 100644 --- a/html/inc/bossa_example.inc +++ b/html/inc/bossa_example.inc @@ -3,7 +3,7 @@ // two results are compatible if neither found an ellipse, // or they both did and centers are within 20 pixels // -function compare($r1, $r2) { +function ellipse_compare($r1, $r2) { if ($r1->have_ellipse) { if ($r2->have_ellipse) { $dx = ($r1->cx - $r2->cx); @@ -18,7 +18,7 @@ function compare($r1, $r2) { // handle a completed job with the given consensus set // -function handle_consensus($bj, $c) { +function ellipse_handle($bj, $c) { $res = $c[0]; if ($res->have_ellipse) { $res->cx = 0; diff --git a/html/ops/bossa_ops.php b/html/ops/bossa_ops.php index 9a976ff6af..b992f3c635 100644 --- a/html/ops/bossa_ops.php +++ b/html/ops/bossa_ops.php @@ -5,9 +5,11 @@ require_once("../inc/util_ops.inc"); function show_bapp($app) { echo " - Name: $app->name
Description: $app->description
Created: ".date_str($app->create_time)." - $app->display_script - $app->backend_script + Name: $app->name
+ Short name: $app->short_name
+ Description: $app->description
+ Created: ".date_str($app->create_time)." + "; if ($app->hidden) { @@ -35,12 +37,11 @@ function add_app_form() { start_table(); row1("Add app"); row2("Name
Visible to users
", ""); + row2("Short name
Used in file and function names - no spaces or special characters
", ""); row2("Description
Visible to users
", ""); - row2("Display script filename", ""); - row2("Backend script filename", ""); - row2("Min confidence sum for consensus", ""); - row2("Min confidence fraction for consensus", ""); - row2("Max instances", ""); + row2("Min confidence sum for consensus", ""); + row2("Min confidence fraction for consensus", ""); + row2("Max job instances", ""); row2("", ""); end_table(); echo ""; diff --git a/html/ops/bossa_transitioner.php b/html/ops/bossa_transitioner.php index b4116b2dfb..58dcbebad0 100644 --- a/html/ops/bossa_transitioner.php +++ b/html/ops/bossa_transitioner.php @@ -12,6 +12,61 @@ function lookup_app($id) { return null; } +// return the confidence of the instances that agree with the given one +// +function agree_conf($instances, $inst) { + $sum = $inst->conf; + foreach ($instances as $i) { + if ($i->id == $inst->id) continue; + if (compatible($i, $inst)) { + $sum += $i->conf; + } + } + return $sum; +} + +// return the total confidence +// +function total_confidence($instances) { + $sum = 0; + foreach ($instances as $i) { + if (!$i->finished_time) continue; + $sum += $i->conf; + } +} + +// See if there's a canonical instance. +// If there's not, return the confidence of the largest compatible set +// +function find_canonical($instances, $total_conf, &$max_conf) { + $best = null; + $best_conf = 0; + foreach ($instances as $inst) { + $ac = agree_conf($instances, $inst); + if ($ac > $best_conf) { + $best_conf = $ac; + $best = $inst; + } + } + if (!$best) return; + $max_conf = $best_conf; + if ($best_conf < $app->min_conf_sum) return; + if ($best_conf/$total_conf < $app->min_conf_frac) return; + return $best; +} + +function get_confidence(&$instances) { + foreach ($instances as $inst) { + $user = BoincUser::lookup_id($inst->user_id); + BossaUser::lookup($user); + $inst->conf = 1; + } +} + +// this gets invoked when +// 1) an instance has been completed +// 2) an instance has timed out +// function handle_job($job) { $app = lookup_app($job->app_id); if (!$app) { @@ -19,6 +74,32 @@ function handle_job($job) { return; } $instances = BossaJobInst::enum("job_id=$job->id"); + if ($job->canonical_inst_id) { + // Already have a canonical instance. + // Are there new instances to validate? + // + $canonical_inst = find_canonical($job, $instances); + foreach ($instances as $inst) { + switch ($inst->validate_state) { + case VALIDATE_STATE_INIT: + if (compatible($inst, $canonical_inst)) { + $inst->validate_state = VALIDATE_STATE_VALID; + } else { + $inst->validate_state = VALIDATE_STATE_INVALID; + } + $inst->update("validate_state=$inst->validate_state"); + } + } + } else { + // No canonical instance yet. + // If we have enough total confidence, check for consensus + // + get_confidences($instances); + $total_conf = total_confidence(instances); + if ($total_conf >= $app->min_conf_sum) { + $inst = find_canonical($instances, $total_conf, $max_conf); + } + } } function do_pass() { @@ -32,6 +113,10 @@ function do_pass() { function main() { global $apps; $apps = BossaApp::enum(); + foreach ($apps as $app) { + $bs = "../lib/".$app->short_name."_backend.inc"; + require_once($bs); + } while (1) { do_pass(); } diff --git a/lib/common_defs.h b/lib/common_defs.h index eeec6e2a6d..c89701333f 100644 --- a/lib/common_defs.h +++ b/lib/common_defs.h @@ -187,6 +187,7 @@ struct VERSION_INFO { #define REG_BLANK_NAME "Blank" #define REG_BLANK_TIME "Blank Time" #define REG_STARTUP_NAME "BOINC" +#define CLIENT_AUTH_FILENAME "client_auth.xml" #else #define LOCK_FILE_NAME "lockfile" #endif diff --git a/lib/util.C b/lib/util.C index 4d5eb30cdf..98ff4bea09 100644 --- a/lib/util.C +++ b/lib/util.C @@ -279,13 +279,41 @@ void update_average( avg_time = now; } +#ifdef _WIN32 +void get_sandbox_account_token() { + FILE* f; + char buf[256], username[256], password[256]; + static bool first=true; + + if (!first) return; + first = true; + f = fopen(CLIENT_AUTH_FILENAME, "r"); + if (!f) return; + while (fgets(buf, 256, f)) { + if (parse_str(buf, "", username, sizeof(username))) continue; + if (parse_str(buf, "", password, sizeof(password))) continue; + } + fclose(f); + std::string password_str = r_base64_decode(ca.boinc_project.password); + retval = LogonUser( + username, + NULL, + password_str.c_str(), + LOGON32_LOGON_SERVICE, + LOGON32_PROVIDER_DEFAULT, + &sandbox_account_token + ); + if (!retval) sandbox_account_token = NULL; +} +#endif + // chdir into the given directory, and run a program there. // If nsecs is nonzero, make sure it's still running after that many seconds. // // argv is set up Unix-style, i.e. argv[0] is the program name // -#ifdef _WIN32 +#ifdef _WIN32 int run_program( const char* dir, const char* file, int argc, char *const argv[], double nsecs, HANDLE& id ) { @@ -307,18 +335,35 @@ int run_program( } } - retval = CreateProcess( - file, - cmdline, - NULL, - NULL, - FALSE, - 0, - NULL, - dir, - &startup_info, - &process_info - ); + get_sandbox_account_token(); + if (sandbox_account_token != NULL) { + retval = CreateProcessAsUser( + sandbox_account_token, + file, + cmdline, + NULL, + NULL, + FALSE, + 0, + NULL, + dir, + &startup_info, + &process_info + ); + } else { + retval = CreateProcess( + file, + cmdline, + NULL, + NULL, + FALSE, + 0, + NULL, + dir, + &startup_info, + &process_info + ); + } if (!retval) { return -1; // CreateProcess returns 1 if successful, false if it failed. diff --git a/lib/util.h b/lib/util.h index 00167f8ed8..47a1388ab8 100644 --- a/lib/util.h +++ b/lib/util.h @@ -72,6 +72,7 @@ extern int boinc_calling_thread_cpu_time(double&); extern void mysql_timestamp(double, char*); #ifdef _WIN32 +extern HANDLE sandbox_account_token = NULL; extern int run_program( const char* path, const char* cdir, int argc, char *const argv[], double, HANDLE& );