Commit 25eaeae8 authored by David Anderson's avatar David Anderson

master URL

svn path=/trunk/boinc/; revision=127
parent 155adafa
......@@ -522,3 +522,55 @@ Eric H June 20, 2002
win_main.cpp (changed from win_main.C)
windows_cpp.h
David A June 20 2002
- Replaced the "accounts.xml" file with the user preferences ("prefs.xml").
All non-host-specific project info is stored in this file;
all host-specific project info is in client_state.xml.
The PROJECT class is a union of the two.
The logic for dealing with inconsistencies between
prefs.xml and client_state.xml, and with updating in-memory
and on-disk project lists in response to an update from a server,
are a little tricky and are described in the code.
- The prefs file can be overwritten by <preferences> in a scheduling
server reply. To prevent buggy servers from zeroing out
users' project lists, the client makes sure there's at least
one project, and backs up the old prefs.xml into a timestamped file.
- The command-line client, if prefs.xml is absent,
prompts the user for a project URL and authenticator,
and creates an initial prefs.xml.
- Each project now has a "master URL", with is its home page
and also contains <scheduler> elements giving the URLs of
its scheduling servers.
- Added a class SCHEDULER_OP which encapsulates fetching and
parsing a project's master page (if necessary),
then making an RPC to one of its scheduling servers.
TODO: add retry and failure logic.
- A project can have more than one scheduling server.
TODO: use all of them.
- Project directories are stored in URL-encoded form.
This allows project master URLs to have slashes, which is a necessity.
client/
Makefile.in
accounts.C,h (deleted)
app.C,h
client_state.C,h
client_types.C,h
cs_apps.C
cs_scheduler.C
file_names.C,h
main.C
prefs.C,h
scheduler_op.C,h (new)
scheduler_reply.C,h (deleted)
doc/
project.html
lib/
parse.C,h
sched/
server_types.C
test/
account1.xml, account2.xml (deleted)
init.inc
prefs1.xml, prefs2.xml (new)
test_*.php
......@@ -19,7 +19,6 @@ PROGS = $(CLIENT_PROG) test_net_xfer test_http test_file_xfer
all: $(PROGS)
OBJS = \
accounts.o \
app.o \
client_state.o \
client_types.o \
......@@ -36,7 +35,7 @@ OBJS = \
net_stats.o \
net_xfer.o \
prefs.o \
scheduler_reply.o \
scheduler_op.o \
speed_stats.o \
time_stats.o \
util.o \
......
// The contents of this file are subject to the Mozilla Public License
// Version 1.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is the Berkeley Open Infrastructure for Network Computing.
//
// The Initial Developer of the Original Code is the SETI@home project.
// Portions created by the SETI@home project are Copyright (C) 2002
// University of California at Berkeley. All Rights Reserved.
//
// Contributor(s):
//
#include "windows_cpp.h"
#include <stdio.h>
#include "error_numbers.h"
#include "file_names.h"
#include "client_types.h"
#include "parse.h"
#include "accounts.h"
ACCOUNTS::ACCOUNTS() {
}
int ACCOUNTS::parse_file() {
char buf[256];
PROJECT* project;
FILE* f = fopen(ACCOUNT_FILE_NAME, "r");
if (!f) {
fprintf(stderr, "Can't open accounts file: %s\n", ACCOUNT_FILE_NAME);
return ERR_FOPEN;
}
fgets(buf, 256, f);
if (!match_tag(buf, "<accounts>")) return ERR_XML_PARSE;
while (fgets(buf, 256, f)) {
if (match_tag(buf, "</accounts>")) {
return 0;
} else if (match_tag(buf, "<project>")) {
project = new PROJECT;
project->parse(f);
projects.push_back(project);
} else {
fprintf(stderr, "ACCOUNTS::parse_file(): unrecognized: %s\n", buf);
}
}
return ERR_XML_PARSE;
}
// The contents of this file are subject to the Mozilla Public License
// Version 1.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is the Berkeley Open Infrastructure for Network Computing.
//
// The Initial Developer of the Original Code is the SETI@home project.
// Portions created by the SETI@home project are Copyright (C) 2002
// University of California at Berkeley. All Rights Reserved.
//
// Contributor(s):
//
#ifndef _LOGINS_
#define _LOGINS_
#include <vector>
#include "client_types.h"
class ACCOUNTS {
public:
vector<PROJECT*> projects;
ACCOUNTS();
int parse_file();
};
#endif
......@@ -75,7 +75,7 @@ void parse_command_line(char* p, char** argv) {
static void print_argv(char** argv) {
int i;
for (i=0; argv[i]; i++) {
printf("argv[%d]: %s\n", i, argv[i]);
fprintf(stderr, "argv[%d]: %s\n", i, argv[i]);
}
}
......@@ -495,13 +495,13 @@ int ACTIVE_TASK_SET::restart_tasks() {
int ACTIVE_TASK::write(FILE* fout) {
fprintf(fout,
"<active_task>\n"
" <project_domain>%s</project_domain>\n"
" <project_master_url>%s</project_master_url>\n"
" <result_name>%s</result_name>\n"
" <app_version_num>%d</app_version_num>\n"
" <slot>%d</slot>\n"
" <cpu_time>%f</cpu_time>\n"
"</active_task>\n",
result->project->domain,
result->project->master_url,
result->name,
app_version->version_num,
slot,
......@@ -511,20 +511,20 @@ int ACTIVE_TASK::write(FILE* fout) {
}
int ACTIVE_TASK::parse(FILE* fin, CLIENT_STATE* cs) {
char buf[256], result_name[256], project_domain[256];
char buf[256], result_name[256], project_master_url[256];
int app_version_num=0;
PROJECT* project;
strcpy(result_name, "");
strcpy(project_domain, "");
strcpy(project_master_url, "");
cpu_time = 0;
while (fgets(buf, 256, fin)) {
if (match_tag(buf, "</active_task>")) {
project = cs->lookup_project(project_domain);
project = cs->lookup_project(project_master_url);
if (!project) {
fprintf(stderr,
"ACTIVE_TASK::parse(): project not found: %s\n",
project_domain
project_master_url
);
return -1;
}
......@@ -544,7 +544,7 @@ int ACTIVE_TASK::parse(FILE* fin, CLIENT_STATE* cs) {
return 0;
}
else if (parse_str(buf, "<result_name>", result_name)) continue;
else if (parse_str(buf, "<project_domain>", project_domain)) continue;
else if (parse_str(buf, "<project_master_url>", project_master_url)) continue;
else if (parse_int(buf, "<app_version_num>", app_version_num)) continue;
else if (parse_int(buf, "<slot>", slot)) continue;
else if (parse_double(buf, "<cpu_time>", cpu_time)) continue;
......
......@@ -40,6 +40,8 @@
#include <stdio.h>
#include <vector>
#include "client_types.h"
class CLIENT_STATE;
// The following classes provide an interface for task execution
......
......@@ -35,6 +35,7 @@ CLIENT_STATE::CLIENT_STATE() {
net_xfers = new NET_XFER_SET;
http_ops = new HTTP_OP_SET(net_xfers);
file_xfers = new FILE_XFER_SET(http_ops);
scheduler_op = new SCHEDULER_OP(http_ops);
client_state_dirty = false;
exit_when_idle = false;
contacted_sched_server = false;
......@@ -43,41 +44,27 @@ CLIENT_STATE::CLIENT_STATE() {
platform_name = HOST;
}
int CLIENT_STATE::init(ACCOUNTS& accounts) {
int CLIENT_STATE::init(PREFS* p) {
nslots = 1;
PROJECT* p1, *p2;
unsigned int i;
parse_state_file();
prefs = p;
// copy projects from the login file to the state.
// copy all PROJECTs from the prefs to the client state.
//
for (i=0; i<accounts.projects.size(); i++) {
p1 = accounts.projects[i];
p2 = lookup_project(p1->domain);
if (p2) {
if (strcmp(p1->email_addr, p2->email_addr)
|| strcmp(p1->authenticator, p2->authenticator)
) {
fprintf(stderr,
"Your account file doesn't match your client state file.\n"
"You should probably delete the client state file.\n"
);
}
} else {
p2 = new PROJECT;
*p2 = *p1;
projects.push_back(p2);
}
for (i=0; i<p->projects.size(); i++) {
projects.push_back(p->projects[i]);
}
// Error out if there are any projects in the state file
// not in the project file.
// Then parse the client state file,
// ignoring any <project> tags (and associated stuff)
// for projects not in the prefs
//
// TODO
parse_state_file();
print_counts();
if (log_flags.state_debug) {
print_counts();
}
make_project_dirs();
make_slot_dirs();
......@@ -92,7 +79,7 @@ int CLIENT_STATE::init(ACCOUNTS& accounts) {
//
int CLIENT_STATE::check_suspend_activities() {
bool should_suspend = false;
if (prefs.dont_run_on_batteries && host_is_running_on_batteries()) {
if (prefs->dont_run_on_batteries && host_is_running_on_batteries()) {
should_suspend = true;
}
......@@ -138,7 +125,7 @@ bool CLIENT_STATE::do_something() {
int CLIENT_STATE::parse_state_file() {
char buf[256];
FILE* f = fopen(STATE_FILE_NAME, "r");
PROJECT* project;
PROJECT* project=0, *p2;
int retval;
if (!f) {
......@@ -154,37 +141,72 @@ int CLIENT_STATE::parse_state_file() {
return 0;
} else if (match_tag(buf, "<project>")) {
project = new PROJECT;
project->parse(f);
projects.push_back(project);
project->parse_state(f);
p2 = lookup_project(project->master_url);
if (p2) {
p2->copy_state_fields(*project);
p2->scheduler_urls = project->scheduler_urls;
p2->project_name = project->project_name;
p2->user_name = project->user_name;
p2->rpc_seqno = project->rpc_seqno;
p2->hostid = project->hostid;
p2->next_request_time = project->next_request_time;
p2->exp_avg_cpu = project->exp_avg_cpu;
p2->exp_avg_mod_time = project->exp_avg_mod_time;
} else {
fprintf(stderr,
"Project %s found in state file but not prefs.\n",
project->master_url
);
project = 0;
}
} else if (match_tag(buf, "<app>")) {
APP* app = new APP;
app->parse(f);
retval = link_app(project, app);
if (!retval) apps.push_back(app);
if (project) {
retval = link_app(project, app);
if (!retval) apps.push_back(app);
} else {
delete app;
}
} else if (match_tag(buf, "<file_info>")) {
FILE_INFO* fip = new FILE_INFO;
fip->parse(f);
retval = link_file_info(project, fip);
if (!retval) file_infos.push_back(fip);
if (project) {
retval = link_file_info(project, fip);
if (!retval) file_infos.push_back(fip);
} else {
delete fip;
}
} else if (match_tag(buf, "<app_version>")) {
APP_VERSION* avp = new APP_VERSION;
avp->parse(f);
retval = link_app_version(project, avp);
if (!retval) app_versions.push_back(avp);
if (project) {
retval = link_app_version(project, avp);
if (!retval) app_versions.push_back(avp);
} else {
delete avp;
}
} else if (match_tag(buf, "<workunit>")) {
WORKUNIT* wup = new WORKUNIT;
wup->parse(f);
retval = link_workunit(project, wup);
if (!retval) workunits.push_back(wup);
if (project) {
retval = link_workunit(project, wup);
if (!retval) workunits.push_back(wup);
} else {
delete wup;
}
} else if (match_tag(buf, "<result>")) {
RESULT* rp = new RESULT;
rp->parse(f, "</result>");
retval = link_result(project, rp);
if (!retval) results.push_back(rp);
if (project) {
retval = link_result(project, rp);
if (!retval) results.push_back(rp);
} else {
delete rp;
}
} else if (match_tag(buf, "<host_info>")) {
host_info.parse(f);
} else if (match_tag(buf, "<prefs>")) {
prefs.parse(f);
} else if (match_tag(buf, "<time_stats>")) {
time_stats.parse(f);
} else if (match_tag(buf, "<net_stats>")) {
......@@ -233,12 +255,11 @@ int CLIENT_STATE::write_state_file() {
}
fprintf(f, "<client_state>\n");
host_info.write(f);
prefs.write(f);
time_stats.write(f, false);
net_stats.write(f, false);
for (j=0; j<projects.size(); j++) {
PROJECT* p = projects[j];
p->write(f);
p->write_state(f);
for (i=0; i<apps.size(); i++) {
if (apps[i]->project == p) apps[i]->write(f);
}
......@@ -274,9 +295,9 @@ int CLIENT_STATE::write_state_file() {
return 0;
}
PROJECT* CLIENT_STATE::lookup_project(char* domain) {
PROJECT* CLIENT_STATE::lookup_project(char* master_url) {
for (unsigned int i=0; i<projects.size(); i++) {
if (!strcmp(domain, projects[i]->domain)) {
if (!strcmp(master_url, projects[i]->master_url)) {
return projects[i];
}
}
......
......@@ -19,21 +19,21 @@
#include <vector>
#include "client_types.h"
#include "app.h"
#include "net_xfer.h"
#include "http.h"
#include "client_types.h"
#include "file_xfer.h"
#include "hostinfo.h"
#include "http.h"
#include "net_stats.h"
#include "net_xfer.h"
#include "prefs.h"
#include "scheduler_op.h"
#include "time_stats.h"
#include "net_stats.h"
#include "accounts.h"
class CLIENT_STATE {
public:
CLIENT_STATE();
int init(ACCOUNTS&);
int init(PREFS*);
int restart_tasks();
bool do_something();
void parse_cmdline(int argc, char** argv);
......@@ -49,14 +49,13 @@ private:
int version;
char* platform_name;
HTTP_OP scheduler_op;
PROJECT* scheduler_op_project;
NET_XFER_SET* net_xfers;
HTTP_OP_SET* http_ops;
FILE_XFER_SET* file_xfers;
SCHEDULER_OP* scheduler_op;
ACTIVE_TASK_SET active_tasks;
HOST_INFO host_info;
PREFS prefs;
PREFS* prefs;
TIME_STATS time_stats;
NET_STATS net_stats;
unsigned int nslots;
......@@ -89,7 +88,7 @@ private:
void print_counts();
bool garbage_collect();
int make_scheduler_request(PROJECT*, int);
void handle_scheduler_reply(PROJECT*);
void handle_scheduler_reply(SCHEDULER_OP&);
double estimate_duration(WORKUNIT*);
double current_water_days();
......
......@@ -26,67 +26,122 @@
#include "client_types.h"
int PROJECT::parse(FILE* in) {
char buf[256];
PROJECT::PROJECT() {
}
strcpy(name, "");
strcpy(domain, "");
strcpy(scheduler_url, "");
strcpy(url_source, "");
strcpy(email_addr, "");
PROJECT::~PROJECT() {
if (project_specific_prefs) {
free(project_specific_prefs);
}
}
// parse project fields from prefs.xml
//
int PROJECT::parse_prefs(FILE* in) {
char buf[256], *p;
int retval;
strcpy(master_url, "");
strcpy(authenticator, "");
while (fgets(buf, 256, in)) {
if (match_tag(buf, "</project>")) return 0;
else if (parse_str(buf, "<master_url>", master_url)) continue;
else if (parse_str(buf, "<authenticator>", authenticator)) continue;
else if (parse_double(buf, "<resource_share>", resource_share)) continue;
else if (match_tag(buf, "<project_specific>")) {
retval = dup_element_contents(in, "</project_specific>", &p);
if (retval) return ERR_XML_PARSE;
project_specific_prefs = p;
continue;
}
else fprintf(stderr, "PROJECT::parse_prefs(): unrecognized: %s\n", buf);
}
return ERR_XML_PARSE;
}
// parse project fields from client_state.xml
//
int PROJECT::parse_state(FILE* in) {
char buf[256];
STRING256 string;
strcpy(project_name, "");
strcpy(user_name, "");
next_request_time = 0;
resource_share = 1;
exp_avg_cpu = 0;
exp_avg_mod_time = 0;
while (fgets(buf, 256, in)) {
if (match_tag(buf, "</project>")) return 0;
else if (parse_str(buf, "<name>", name)) continue;
else if (parse_str(buf, "<domain>", domain)) continue;
else if (parse_str(buf, "<scheduler_url>", scheduler_url)) continue;
else if (parse_str(buf, "<url_source>", url_source)) continue;
else if (parse_str(buf, "<email_addr>", email_addr)) continue;
else if (parse_str(buf, "<authenticator>", authenticator)) continue;
else if (parse_str(buf, "<scheduler_url>", string.text)) {
scheduler_urls.push_back(string);
continue;
}
else if (parse_str(buf, "<project_name>", project_name)) continue;
else if (parse_str(buf, "<user_name>", user_name)) continue;
else if (parse_int(buf, "<rpc_seqno>", rpc_seqno)) continue;
else if (parse_int(buf, "<hostid>", hostid)) continue;
else if (parse_int(buf, "<next_request_time>", next_request_time)) continue;
else if (parse_double(buf, "<resource_share>", resource_share)) continue;
else if (parse_double(buf, "<exp_avg_cpu>", exp_avg_cpu)) continue;
else if (parse_int(buf, "<exp_avg_mod_time>", exp_avg_mod_time)) continue;
else fprintf(stderr, "PROJECT::parse(): unrecognized: %s\n", buf);
else fprintf(stderr, "PROJECT::parse_state(): unrecognized: %s\n", buf);
}
return ERR_XML_PARSE;
}
int PROJECT::write(FILE* out) {
int PROJECT::write_state(FILE* out) {
unsigned int i;
fprintf(out,
"<project>\n"
" <name>%s</name>\n"
" <domain>%s</domain>\n"
" <scheduler_url>%s</scheduler_url>\n"
" <url_source>%s</url_source>\n"
" <email_addr>%s</email_addr>\n"
" <authenticator>%s</authenticator>\n",
name, domain, scheduler_url, url_source, email_addr, authenticator
);
for (i=0; i<scheduler_urls.size(); i++) {
fprintf(out,
" <scheduler_url>%s</scheduler_url>\n",
scheduler_urls[i].text
);
}
fprintf(out,
" <master_url>%s</master_url>\n"
" <project_name>%s</project_name>\n"
" <user_name>%s</user_name>\n"
" <rpc_seqno>%d</rpc_seqno>\n"
" <hostid>%d</hostid>\n"
" <next_request_time>%d</next_request_time>\n"
" <resource_share>%f</resource_share>\n"
" <exp_avg_cpu>%f</exp_avg_cpu>\n"
" <exp_avg_mod_time>%d</exp_avg_mod_time>\n"
"</project>\n",
master_url,
project_name,
user_name,
rpc_seqno,
hostid,
next_request_time,
resource_share,
exp_avg_cpu,
exp_avg_mod_time
);
return 0;
}
void PROJECT::copy_state_fields(PROJECT& p) {
scheduler_urls = p.scheduler_urls;
project_name = p.project_name;
user_name = p.user_name;
rpc_seqno = p.rpc_seqno;
hostid = p.hostid;
next_request_time = p.next_request_time;
exp_avg_cpu = p.exp_avg_cpu;
exp_avg_mod_time = p.exp_avg_mod_time;
}
void PROJECT::copy_prefs_fields(PROJECT& p) {
authenticator = p.authenticator;
if (p.project_specific_prefs) {
project_specific_prefs = strdup(p.project_specific_prefs);
}
resource_share = p.resource_share;
}
int APP::parse(FILE* in) {
char buf[256];
......
......@@ -24,8 +24,8 @@
#include "windows_cpp.h"
#ifndef _TYPES_
#define _TYPES_
#ifndef _CLIENT_TYPES_
#define _CLIENT_TYPES_
#include <vector>
#include <stdio.h>
......@@ -42,22 +42,34 @@ struct STRING256 {
class PROJECT {
public:
char name[256]; // descriptive. not unique
char domain[256]; // can't have any slashes
char scheduler_url[256]; // POST here
char url_source[256]; // GET here to find latest scheduler_url
char email_addr[256]; // user's account on this project
// the following items come from prefs.xml
// They are a function only of the user and the project
//
char master_url[256];
char authenticator[256]; // user's authenticator on this project
char* project_specific_prefs;
double resource_share; // project's resource share
// relative to other projects. Arbitrary scale.
// the following items come from client_state.xml
// They may depend on the host as well as user and project
//
vector<STRING256> scheduler_urls; // where to find scheduling servers
char project_name[256]; // descriptive. not unique
char user_name[256];
int rpc_seqno;
int hostid;
int next_request_time; // don't contact server until this time
double resource_share; // determines project's resource share
// relative to other projects. Arbitrary scale.
double exp_avg_cpu; // exponentially weighted cpu time
int exp_avg_mod_time; // last time average was changed
int parse(FILE*);
int write(FILE*);
PROJECT();
~PROJECT();
void copy_state_fields(PROJECT&);
void copy_prefs_fields(PROJECT&);
int parse_prefs(FILE*);
int parse_state(FILE*);
int write_state(FILE*);
};
struct APP {
......
......@@ -108,7 +108,12 @@ bool CLIENT_STATE::start_apps() {
bool action = false;
for (i=0; i<results.size(); i++) {
if (active_tasks.active_tasks.size() == nslots) return 0;
if (active_tasks.active_tasks.size() == nslots) {
if (log_flags.task_debug) {
printf("start_apps(): all slots full\n");
}
return 0;
}
rp = results[i];
if (!rp->is_compute_done && !rp->is_active && input_files_available(rp)) {
if (log_flags.task_debug) {
......
......@@ -17,6 +17,15 @@
// Contributor(s):
//
// This file contains high-level logic for communicating with
// scheduling servers:
// - what project to ask for work
// - how much work to ask for
// - merging the result of a scheduler RPC into the client state
// Note: code for actually doing a scheduler RPC is elsewhere,
// namely scheduler_op.C
#include <stdio.h>
#include <math.h>
#include <time.h>
......@@ -30,7 +39,7 @@