cs_scheduler.cpp 35.9 KB
Newer Older
1
// This file is part of BOINC.
David Anderson's avatar
David Anderson committed
2
// http://boinc.berkeley.edu
3
// Copyright (C) 2008 University of California
Karl Chen's avatar
Karl Chen committed
4
//
5 6 7 8
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
Karl Chen's avatar
Karl Chen committed
9
//
10
// BOINC is distributed in the hope that it will be useful,
David Anderson's avatar
David Anderson committed
11 12 13
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
David Anderson's avatar
David Anderson committed
14
//
15 16
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
David Anderson's avatar
David Anderson committed
17

David Anderson's avatar
various  
David Anderson committed
18
// High-level logic for communicating with scheduling servers,
19
// and for merging the result of a scheduler RPC into the client state
David Anderson's avatar
David Anderson committed
20

David Anderson's avatar
David Anderson committed
21
// The scheduler RPC mechanism is in scheduler_op.C
David Anderson's avatar
David Anderson committed
22

David Anderson's avatar
David Anderson committed
23
#include "cpp.h"
Rom Walton's avatar
Rom Walton committed
24 25

#ifdef _WIN32
David Anderson's avatar
David Anderson committed
26
#include "boinc_win.h"
Rom Walton's avatar
Rom Walton committed
27 28 29
#endif

#ifndef _WIN32
30
#include "config.h"
Eric J. Korpela's avatar
 
Eric J. Korpela committed
31 32 33 34
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
David Anderson's avatar
David Anderson committed
35 36
#include <map>
#include <set>
David Anderson's avatar
David Anderson committed
37
#endif
David Anderson's avatar
David Anderson committed
38

David Anderson's avatar
David Anderson committed
39
#include "crypt.h"
David Anderson's avatar
David Anderson committed
40 41
#include "error_numbers.h"
#include "file_names.h"
David Anderson's avatar
David Anderson committed
42
#include "filesys.h"
David Anderson's avatar
David Anderson committed
43
#include "parse.h"
David Anderson's avatar
David Anderson committed
44
#include "str_util.h"
45
#include "str_replace.h"
46
#include "url.h"
David Anderson's avatar
various  
David Anderson committed
47
#include "util.h"
David Anderson's avatar
David Anderson committed
48

David Anderson's avatar
David Anderson committed
49
#include "client_msgs.h"
David Anderson's avatar
David Anderson committed
50
#include "scheduler_op.h"
51
#include "sandbox.h"
David Anderson's avatar
David Anderson committed
52 53 54

#include "client_state.h"

Daniel Hsu's avatar
Daniel Hsu committed
55
using std::max;
Eric J. Korpela's avatar
Eric J. Korpela committed
56 57 58
using std::vector;
using std::string;

David Anderson's avatar
David Anderson committed
59
// quantities like avg CPU time decay by a factor of e every week
60
//
David Anderson's avatar
various  
David Anderson committed
61
#define EXP_DECAY_RATE  (1./(SECONDS_PER_DAY*7))
David Anderson's avatar
David Anderson committed
62

David Anderson's avatar
David Anderson committed
63
// try to report results this much before their deadline
David Anderson's avatar
David Anderson committed
64
//
David Anderson's avatar
David Anderson committed
65
#define REPORT_DEADLINE_CUSHION ((double)SECONDS_PER_DAY)
Karl Chen's avatar
Karl Chen committed
66

David Anderson's avatar
David Anderson committed
67
#ifndef SIM
David Anderson's avatar
David Anderson committed
68

David Anderson's avatar
David Anderson committed
69 70
// Write a scheduler request to a disk file,
// to be sent to a scheduling server
Eric Heien's avatar
Eric Heien committed
71
//
David Anderson's avatar
David Anderson committed
72
int CLIENT_STATE::make_scheduler_request(PROJECT* p) {
David Anderson's avatar
David Anderson committed
73
    char buf[1024];
David Anderson's avatar
David Anderson committed
74
    MIOFILE mf;
David Anderson's avatar
David Anderson committed
75 76
    unsigned int i;
    RESULT* rp;
77
    int retval;
David Anderson's avatar
David Anderson committed
78 79
    double disk_total, disk_project;

David Anderson's avatar
David Anderson committed
80
    get_sched_request_filename(*p, buf, sizeof(buf));
David Anderson's avatar
David Anderson committed
81
    FILE* f = boinc_fopen(buf, "wb");
82
    if (!f) return ERR_FOPEN;
83

David Anderson's avatar
David Anderson committed
84 85
    double trs = total_resource_share();
    double rrs = runnable_resource_share();
David Anderson's avatar
David Anderson committed
86
    double prrs = potentially_runnable_resource_share();
David Anderson's avatar
David Anderson committed
87 88 89 90 91 92 93 94 95 96 97
    double resource_share_fraction, rrs_fraction, prrs_fraction;
    if (trs) {
        resource_share_fraction = p->resource_share / trs;
    } else {
        resource_share_fraction = 1;
    }
    if (rrs) {
        rrs_fraction = p->resource_share / rrs;
    } else {
        rrs_fraction = 1;
    }
David Anderson's avatar
David Anderson committed
98
    if (prrs) {
David Anderson's avatar
David Anderson committed
99
        prrs_fraction = p->resource_share / prrs;
David Anderson's avatar
David Anderson committed
100
    } else {
David Anderson's avatar
David Anderson committed
101
        prrs_fraction = 1;
David Anderson's avatar
David Anderson committed
102
    }
Daniel Hsu's avatar
Daniel Hsu committed
103

104 105 106 107 108 109
    // if hostid is zero, rpc_seqno better be also
    //
    if (!p->hostid) {
        p->rpc_seqno = 0;
    }

David Anderson's avatar
David Anderson committed
110
    mf.init_file(f);
David Anderson's avatar
David Anderson committed
111 112 113 114 115
    fprintf(f,
        "<scheduler_request>\n"
        "    <authenticator>%s</authenticator>\n"
        "    <hostid>%d</hostid>\n"
        "    <rpc_seqno>%d</rpc_seqno>\n"
David Anderson's avatar
David Anderson committed
116 117
        "    <core_client_major_version>%d</core_client_major_version>\n"
        "    <core_client_minor_version>%d</core_client_minor_version>\n"
118
        "    <core_client_release>%d</core_client_release>\n"
Daniel Hsu's avatar
Daniel Hsu committed
119
        "    <resource_share_fraction>%f</resource_share_fraction>\n"
David Anderson's avatar
David Anderson committed
120 121
        "    <rrs_fraction>%f</rrs_fraction>\n"
        "    <prrs_fraction>%f</prrs_fraction>\n"
122 123
        "    <duration_correction_factor>%f</duration_correction_factor>\n"
        "    <sandbox>%d</sandbox>\n",
David Anderson's avatar
David Anderson committed
124 125 126
        p->authenticator,
        p->hostid,
        p->rpc_seqno,
David Anderson's avatar
David Anderson committed
127 128 129
        core_client_version.major,
        core_client_version.minor,
        core_client_version.release,
David Anderson's avatar
David Anderson committed
130
        resource_share_fraction,
David Anderson's avatar
David Anderson committed
131 132
        rrs_fraction,
        prrs_fraction,
133 134
        p->duration_correction_factor,
        g_use_sandbox?1:0
David Anderson's avatar
David Anderson committed
135
    );
136
    work_fetch.write_request(f, p);
Rom Walton's avatar
Rom Walton committed
137

David Anderson's avatar
David Anderson committed
138 139 140
    // write client capabilities
    //
    fprintf(f,
141
        "    <client_cap_plan_class>1</client_cap_plan_class>\n"
David Anderson's avatar
David Anderson committed
142 143
    );

144
    write_platforms(p, mf);
Rom Walton's avatar
Rom Walton committed
145

146 147
    // send supported app_versions for anonymous platform clients
    //
David Anderson's avatar
David Anderson committed
148 149 150 151 152
    if (p->anonymous_platform) {
        fprintf(f, "    <app_versions>\n");
        for (i=0; i<app_versions.size(); i++) {
            APP_VERSION* avp = app_versions[i];
            if (avp->project != p) continue;
153
            avp->write(mf, false);
David Anderson's avatar
David Anderson committed
154 155 156
        }
        fprintf(f, "    </app_versions>\n");
    }
Eric Heien's avatar
Eric Heien committed
157
    if (strlen(p->code_sign_key)) {
Brian Boshes's avatar
Brian Boshes committed
158
        fprintf(f, "    <code_sign_key>\n%s</code_sign_key>\n", p->code_sign_key);
David Anderson's avatar
David Anderson committed
159
    }
David Anderson's avatar
David Anderson committed
160

David Anderson's avatar
David Anderson committed
161 162 163 164 165 166 167
	// send working prefs
	//
	fprintf(f, "<working_global_preferences>\n");
	global_prefs.write(mf);
	fprintf(f, "</working_global_preferences>\n");

    // send master global preferences if present and not host-specific
David Anderson's avatar
David Anderson committed
168
    //
David Anderson's avatar
David Anderson committed
169
    if (!global_prefs.host_specific && boinc_file_exists(GLOBAL_PREFS_FILE_NAME)) {
Rom Walton's avatar
Rom Walton committed
170 171 172 173 174
        FILE* fprefs = fopen(GLOBAL_PREFS_FILE_NAME, "r");
        if (fprefs) {
            copy_stream(fprefs, f);
            fclose(fprefs);
        }
David Anderson's avatar
David Anderson committed
175
        PROJECT* pp = lookup_project(global_prefs.source_project);
David Anderson's avatar
David Anderson committed
176 177 178 179 180 181
        if (pp && strlen(pp->email_hash)) {
            fprintf(f,
                "<global_prefs_source_email_hash>%s</global_prefs_source_email_hash>\n",
                pp->email_hash
            );
        }
David Anderson's avatar
David Anderson committed
182
    }
David Anderson's avatar
David Anderson committed
183

David Anderson's avatar
David Anderson committed
184 185 186
    // Of the projects with same email hash as this one,
    // send the oldest cross-project ID.
    // Use project URL as tie-breaker.
David Anderson's avatar
David Anderson committed
187
    //
David Anderson's avatar
David Anderson committed
188
    PROJECT* winner = p;
David Anderson's avatar
David Anderson committed
189 190 191 192
    for (i=0; i<projects.size(); i++ ) {
        PROJECT* project = projects[i];
        if (project == p) continue;
        if (strcmp(project->email_hash, p->email_hash)) continue;
193
        if (project->cpid_time < winner->cpid_time) {
David Anderson's avatar
David Anderson committed
194
            winner = project;
195
        } else if (project->cpid_time == winner->cpid_time) {
David Anderson's avatar
David Anderson committed
196 197 198
            if (strcmp(project->master_url, winner->master_url) < 0) {
                winner = project;
            }
David Anderson's avatar
David Anderson committed
199 200
        }
    }
David Anderson's avatar
David Anderson committed
201 202 203 204
    fprintf(f,
        "<cross_project_id>%s</cross_project_id>\n",
        winner->cross_project_id
    );
David Anderson's avatar
David Anderson committed
205

David Anderson's avatar
David Anderson committed
206
    retval = time_stats.write(mf, true);
207 208
    //if (retval) return retval;
    // can't return without closing file
David Anderson's avatar
David Anderson committed
209
    retval = net_stats.write(mf);
210
    //if (retval) return retval;
David Anderson's avatar
David Anderson committed
211

David Anderson's avatar
David Anderson committed
212
    // update hardware info, and write host info
David Anderson's avatar
David Anderson committed
213
    //
David Anderson's avatar
David Anderson committed
214
    host_info.get_host_info();
215
    set_ncpus();
David Anderson's avatar
David Anderson committed
216
    retval = host_info.write(mf, config.suppress_net_info);
217
    //if (retval) return retval;
David Anderson's avatar
David Anderson committed
218 219 220 221 222 223 224 225 226 227 228 229 230

    // get and write disk usage
    //
    total_disk_usage(disk_total);
    project_disk_usage(p, disk_project);
    fprintf(f,
        "    <disk_usage>\n"
        "        <d_boinc_used_total>%f</d_boinc_used_total>\n"
        "        <d_boinc_used_project>%f</d_boinc_used_project>\n"
        "    </disk_usage>\n",
        disk_total, disk_project
    );

231 232 233 234 235
    // copy request values from RSC_WORK_FETCH to COPROC
    //
    if (coproc_cuda) {
        coproc_cuda->req_secs = cuda_work_fetch.req_secs;
        coproc_cuda->req_instances = cuda_work_fetch.req_instances;
236
        coproc_cuda->estimated_delay = cuda_work_fetch.req_secs?cuda_work_fetch.busy_time_estimator.get_busy_time():0;
237
    }
238 239 240
    if (coproc_ati) {
        coproc_ati->req_secs = ati_work_fetch.req_secs;
        coproc_ati->req_instances = ati_work_fetch.req_instances;
241
        coproc_ati->estimated_delay = ati_work_fetch.req_secs?ati_work_fetch.busy_time_estimator.get_busy_time():0;
242
    }
243

244 245 246
    if (coprocs.coprocs.size()) {
        fprintf(f, "    <coprocs>\n");
        for (i=0; i<coprocs.coprocs.size(); i++) {
247
            COPROC* c = coprocs.coprocs[i];
248
            c->write_xml(mf);
249 250 251 252
        }
        fprintf(f, "    </coprocs>\n");
    }

David Anderson's avatar
David Anderson committed
253 254
    // report results
    //
David Anderson's avatar
David Anderson committed
255
    p->nresults_returned = 0;
David Anderson's avatar
David Anderson committed
256
    for (i=0; i<results.size(); i++) {
Eric Heien's avatar
Eric Heien committed
257
        rp = results[i];
David Anderson's avatar
David Anderson committed
258
        if (rp->project == p && rp->ready_to_report) {
David Anderson's avatar
David Anderson committed
259
            p->nresults_returned++;
David Anderson's avatar
David Anderson committed
260
            rp->write(mf, true);
David Anderson's avatar
David Anderson committed
261
        }
David Anderson's avatar
David Anderson committed
262
    }
Brian Boshes's avatar
Brian Boshes committed
263

David Anderson's avatar
David Anderson committed
264
    read_trickle_files(p, f);
David Anderson's avatar
David Anderson committed
265 266 267 268 269 270 271

    // report sticky files as needed
    //
    for (i=0; i<file_infos.size(); i++) {
        FILE_INFO* fip = file_infos[i];
        if (fip->project != p) continue;
        if (!fip->report_on_rpc) continue;
David Anderson's avatar
David Anderson committed
272
        if (fip->marked_for_delete) continue;
David Anderson's avatar
David Anderson committed
273 274 275
        fprintf(f,
            "    <file_info>\n"
            "        <name>%s</name>\n"
David Anderson's avatar
David Anderson committed
276 277
            "        <nbytes>%f</nbytes>\n"
            "        <status>%d</status>\n"
David Anderson's avatar
David Anderson committed
278
            "        <report_on_rpc/>\n"
David Anderson's avatar
David Anderson committed
279
            "    </file_info>\n",
David Anderson's avatar
David Anderson committed
280
            fip->name, fip->nbytes, fip->status
David Anderson's avatar
David Anderson committed
281 282
        );
    }
David Anderson's avatar
David Anderson committed
283

David Anderson's avatar
David Anderson committed
284 285 286 287
    // NOTE: there's also a send_file_list flag, not currently used

    if (p->send_time_stats_log) {
        fprintf(f, "<time_stats_log>\n");
288
        time_stats.get_log_after(p->send_time_stats_log, mf);
David Anderson's avatar
David Anderson committed
289 290 291 292 293 294 295 296 297
        fprintf(f, "</time_stats_log>\n");
    }
    if (p->send_job_log) {
        fprintf(f, "<job_log>\n");
        job_log_filename(*p, buf, sizeof(buf));
        send_log_after(buf, p->send_job_log, mf);
        fprintf(f, "</job_log>\n");
    }

David Anderson's avatar
David Anderson committed
298 299 300 301 302 303 304 305 306
    // send names of results in progress for this project
    //
    fprintf(f, "<other_results>\n");
    for (i=0; i<results.size(); i++) {
        rp = results[i];
        if (rp->project == p && !rp->ready_to_report) {
            fprintf(f,
                "    <other_result>\n"
                "        <name>%s</name>\n"
307
                "        <plan_class>%s</plan_class>\n"
David Anderson's avatar
David Anderson committed
308
                "    </other_result>\n",
309 310
                rp->name,
                rp->plan_class
David Anderson's avatar
David Anderson committed
311 312 313 314 315 316 317 318 319 320 321
            );
        }
    }
    fprintf(f, "</other_results>\n");

    // send summary of in-progress results
    // to give scheduler info on our CPU commitment
    //
    fprintf(f, "<in_progress_results>\n");
    for (i=0; i<results.size(); i++) {
        rp = results[i];
322
        double x = rp->estimated_time_remaining(false);
David Anderson's avatar
David Anderson committed
323 324 325
        if (x == 0) continue;
        fprintf(f,
            "    <ip_result>\n"
David Anderson's avatar
 
David Anderson committed
326
            "        <name>%s</name>\n"
327 328
            "        <report_deadline>%.0f</report_deadline>\n"
            "        <cpu_time_remaining>%.2f</cpu_time_remaining>\n"
David Anderson's avatar
David Anderson committed
329
            "    </ip_result>\n",
David Anderson's avatar
 
David Anderson committed
330
            rp->name,
David Anderson's avatar
David Anderson committed
331 332 333 334 335
            rp->report_deadline,
            x
        );
    }
    fprintf(f, "</in_progress_results>\n");
David Anderson's avatar
David Anderson committed
336
    fprintf(f, "</scheduler_request>\n");
Brian Boshes's avatar
Brian Boshes committed
337

338
    fclose(f);
David Anderson's avatar
David Anderson committed
339 340 341
    return 0;
}

342 343 344
// called from the client's polling loop.
// initiate scheduler RPC activity if needed and possible
//
David Anderson's avatar
David Anderson committed
345
bool CLIENT_STATE::scheduler_rpc_poll() {
Daniel Hsu's avatar
Daniel Hsu committed
346 347
    PROJECT *p;
    bool action=false;
David Anderson's avatar
David Anderson committed
348
    static double last_time=0;
349
    static double last_work_fetch_time = 0;
350
    double elapsed_time;
David Anderson's avatar
David Anderson committed
351

352
	// check only every 5 sec
David Anderson's avatar
David Anderson committed
353
	//
354
    if (now - last_time < SCHEDULER_RPC_POLL_PERIOD) return false;
355
    last_time = now;
356 357 358

    switch(scheduler_op->state) {
    case SCHEDULER_OP_STATE_IDLE:
David Anderson's avatar
David Anderson committed
359 360 361 362 363
        if (scheduler_op->check_master_fetch_start()) {
            action = true;
            break;
        }

364 365 366 367 368
        // If we haven't run benchmarks yet, don't do a scheduler RPC.
        // We need to know CPU speed to handle app versions
        //
        if (!host_info.p_calculated) return false;

369 370 371 372
        // check for various reasons to contact particular projects.
        // If we need to contact a project,
        // see if we should ask it for work as well.
        //
David Anderson's avatar
David Anderson committed
373 374
        p = next_project_sched_rpc_pending();
        if (p) {
375 376
			// if the user requested the RPC,
            // clear backoffs to allow work requests
377 378
			//
			if (p->sched_rpc_pending == RPC_REASON_USER_REQ) {
379 380
				p->cpu_pwf.clear_backoff();
				p->cuda_pwf.clear_backoff();
381
				p->ati_pwf.clear_backoff();
382
			}
383
            work_fetch.compute_work_request(p);
David Anderson's avatar
David Anderson committed
384
			scheduler_op->init_op_project(p, p->sched_rpc_pending);
David Anderson's avatar
David Anderson committed
385 386 387
            action = true;
            break;
        }
David Anderson's avatar
David Anderson committed
388
        if (network_suspended) break;
389 390
        p = next_project_trickle_up_pending();
        if (p) {
391
            work_fetch.compute_work_request(p);
David Anderson's avatar
David Anderson committed
392
            scheduler_op->init_op_project(p, RPC_REASON_TRICKLE_UP);
393 394 395
            action = true;
            break;
        }
Daniel Hsu's avatar
Daniel Hsu committed
396
        
David Anderson's avatar
David Anderson committed
397
        // report overdue results
Daniel Hsu's avatar
Daniel Hsu committed
398 399 400
        //
        p = find_project_with_overdue_results();
        if (p) {
401
            work_fetch.compute_work_request(p);
David Anderson's avatar
David Anderson committed
402
            scheduler_op->init_op_project(p, RPC_REASON_RESULTS_DUE);
Daniel Hsu's avatar
Daniel Hsu committed
403
            action = true;
David Anderson's avatar
David Anderson committed
404 405
            break;
        }
406

407
        // should we check work fetch?  Do this at most once/minute
408 409 410 411 412 413 414

        if (exit_when_idle && contacted_sched_server) break;
        if (tasks_suspended) break;

        if (must_check_work_fetch) {
            last_work_fetch_time = 0;
        }
415 416
        elapsed_time = now - last_work_fetch_time;
        if (elapsed_time < WORK_FETCH_PERIOD) return false;
417
        must_check_work_fetch = false;
418 419 420 421 422 423 424
        last_work_fetch_time = now;

        p = work_fetch.choose_project();
        if (p) {
            scheduler_op->init_op_project(p, RPC_REASON_NEED_WORK);
            action = true;
            break;
David Anderson's avatar
David Anderson committed
425
        }
426 427 428 429 430 431 432 433 434 435 436
        break;
    default:
        scheduler_op->poll();
        if (scheduler_op->state == SCHEDULER_OP_STATE_IDLE) {
            action = true;
        }
        break;
    }
    return action;
}

David Anderson's avatar
David Anderson committed
437
// Handle the reply from a scheduler
Eric Heien's avatar
Eric Heien committed
438
//
439
int CLIENT_STATE::handle_scheduler_reply(PROJECT* project, char* scheduler_url) {
David Anderson's avatar
David Anderson committed
440 441 442 443
    SCHEDULER_REPLY sr;
    FILE* f;
    int retval;
    unsigned int i;
David Anderson's avatar
David Anderson committed
444
    bool signature_valid, update_global_prefs=false, update_project_prefs=false;
David Anderson's avatar
David Anderson committed
445
    char buf[256], filename[256];
David Anderson's avatar
David Anderson committed
446
    std::string old_gui_urls = project->gui_urls;
David Anderson's avatar
David Anderson committed
447
    PROJECT* p2;
448
    vector<RESULT*>new_results;
David Anderson's avatar
David Anderson committed
449 450

    contacted_sched_server = true;
David Anderson's avatar
David Anderson committed
451
    project->last_rpc_time = now;
Karl Chen's avatar
Karl Chen committed
452

David Anderson's avatar
David Anderson committed
453
    get_sched_reply_filename(*project, filename, sizeof(filename));
David Anderson's avatar
David Anderson committed
454 455

    f = fopen(filename, "r");
David Anderson's avatar
David Anderson committed
456
    if (!f) return ERR_FOPEN;
David Anderson's avatar
David Anderson committed
457
    retval = sr.parse(f, project);
David Anderson's avatar
close  
David Anderson committed
458
    fclose(f);
Eric Heien's avatar
Eric Heien committed
459
    if (retval) return retval;
David Anderson's avatar
David Anderson committed
460

David Anderson's avatar
David Anderson committed
461
    if (log_flags.sched_ops) {
462
        if (cpu_work_fetch.req_secs || cuda_work_fetch.req_secs || ati_work_fetch.req_secs) {
463 464 465 466 467
            sprintf(buf, ": got %d new tasks", (int)sr.results.size());
        } else {
            strcpy(buf, "");
        }
        msg_printf(project, MSG_INFO, "Scheduler request completed%s", buf);
468 469
    }
    if (log_flags.sched_op_debug) {
David Anderson's avatar
David Anderson committed
470 471
        if (sr.scheduler_version) {
            msg_printf(project, MSG_INFO,
472
                "[sched_op_debug] Server version %d",
David Anderson's avatar
David Anderson committed
473 474 475
                sr.scheduler_version
            );
        }
David Anderson's avatar
David Anderson committed
476 477
    }

David Anderson's avatar
David Anderson committed
478 479 480 481
    // check that master URL is correct
    //
    if (strlen(sr.master_url)) {
        canonicalize_master_url(sr.master_url);
482 483 484 485 486
        string url1 = sr.master_url;
        string url2 = project->master_url;
        downcase_string(url1);
        downcase_string(url2);
        if (url1 != url2) {
David Anderson's avatar
David Anderson committed
487
            msg_printf(project, MSG_USER_ERROR,
David Anderson's avatar
David Anderson committed
488
                "You used the wrong URL for this project"
David Anderson's avatar
David Anderson committed
489
            );
David Anderson's avatar
David Anderson committed
490
            msg_printf(project, MSG_USER_ERROR,
David Anderson's avatar
David Anderson committed
491 492
                "The correct URL is %s", sr.master_url
            );
493
            p2 = lookup_project(sr.master_url);
David Anderson's avatar
David Anderson committed
494
            if (p2) {
David Anderson's avatar
David Anderson committed
495 496 497 498 499
                msg_printf(project, MSG_INFO,
                    "You seem to be attached to this project twice"
                );
                msg_printf(project, MSG_INFO,
                    "We suggest that you detach projects named %s,",
David Anderson's avatar
David Anderson committed
500 501
                    project->project_name
                );
David Anderson's avatar
David Anderson committed
502 503
                msg_printf(project, MSG_INFO,
                    "then reattach to %s", sr.master_url
David Anderson's avatar
David Anderson committed
504 505
                );
            } else {
David Anderson's avatar
David Anderson committed
506 507 508 509 510
                msg_printf(project, MSG_INFO,
                    "Using the wrong URL can cause problems in some cases."
                );
                msg_printf(project, MSG_INFO,
                    "When convenient, detach this project, then reattach to %s",
David Anderson's avatar
David Anderson committed
511 512 513 514 515 516 517 518
                    sr.master_url
                );
            }
        }
    }

    // make sure we don't already have a project of same name
    //
519 520 521 522 523 524 525
    bool dup_name = false;
    for (i=0; i<projects.size(); i++) {
        p2 = projects[i];
        if (project == p2) continue;
        if (!strcmp(p2->project_name, project->project_name)) {
            dup_name = true;
            break;
David Anderson's avatar
David Anderson committed
526 527
        }
    }
528 529 530 531 532 533 534 535 536
    if (dup_name) {
        msg_printf(project, MSG_USER_ERROR,
            "Already attached to a project named %s (possibly with wrong URL)",
            project->project_name
        );
        msg_printf(project, MSG_USER_ERROR,
            "Consider detaching this project, then trying again"
        );
    }
David Anderson's avatar
David Anderson committed
537

538
    // show messages from server
David Anderson's avatar
David Anderson committed
539
    //
David Anderson's avatar
David Anderson committed
540 541 542
    for (i=0; i<sr.messages.size(); i++) {
        USER_MESSAGE& um = sr.messages[i];
        sprintf(buf, "Message from server: %s", um.message.c_str());
David Anderson's avatar
David Anderson committed
543
        int prio = (!strcmp(um.priority.c_str(), "high"))?MSG_USER_ERROR:MSG_INFO;
David Anderson's avatar
David Anderson committed
544
        show_message(project, buf, prio);
David Anderson's avatar
David Anderson committed
545 546
    }

David Anderson's avatar
David Anderson committed
547 548
    if (log_flags.sched_op_debug && sr.request_delay) {
        msg_printf(project, MSG_INFO,
549
            "Project requested delay of %.0f seconds", sr.request_delay
David Anderson's avatar
David Anderson committed
550 551 552
        );
    }

David Anderson's avatar
David Anderson committed
553 554 555 556
    // if project is down, return error (so that we back off)
    // and don't do anything else
    //
    if (sr.project_is_down) {
Rom Walton's avatar
Rom Walton committed
557
        if (sr.request_delay) {
558
            double x = now + sr.request_delay;
David Anderson's avatar
David Anderson committed
559
			project->set_min_rpc_time(x, "project is down");
Rom Walton's avatar
Rom Walton committed
560
        }
David Anderson's avatar
David Anderson committed
561
        return ERR_PROJECT_DOWN;
David Anderson's avatar
David Anderson committed
562
    }
David Anderson's avatar
David Anderson committed
563 564 565

    // if the scheduler reply includes global preferences,
    // insert extra elements, write to disk, and parse
David Anderson's avatar
David Anderson committed
566
    //
David Anderson's avatar
David Anderson committed
567
    if (sr.global_prefs_xml) {
David Anderson's avatar
David Anderson committed
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
		// skip this if we have host-specific prefs
		// and we're talking to an old scheduler
		//
		if (!global_prefs.host_specific || sr.scheduler_version >= 507) {
			retval = save_global_prefs(
				sr.global_prefs_xml, project->master_url, scheduler_url
			);
			if (retval) {
				return retval;
			}
			update_global_prefs = true;
		} else {
			if (log_flags.sched_op_debug) {
				msg_printf(project, MSG_INFO,
					"ignoring prefs from old server; we have host-specific prefs"
				);
			}
		}
David Anderson's avatar
David Anderson committed
586
    }
Administrator's avatar
Administrator committed
587

David Anderson's avatar
David Anderson committed
588 589 590 591 592 593
    // see if we have a new venue from this project
    // (this must go AFTER the above, since otherwise
    // global_prefs_source_project() is meaningless)
    //
    if (strcmp(project->host_venue, sr.host_venue)) {
        safe_strcpy(project->host_venue, sr.host_venue);
594
        msg_printf(project, MSG_INFO, "New computer location: %s", sr.host_venue);
David Anderson's avatar
David Anderson committed
595 596 597 598 599 600 601
        update_project_prefs = true;
        if (project == global_prefs_source_project()) {
            strcpy(main_host_venue, sr.host_venue);
            update_global_prefs = true;
        }
    }

David Anderson's avatar
David Anderson committed
602
    if (update_global_prefs) {
David Anderson's avatar
David Anderson committed
603
        read_global_prefs();
David Anderson's avatar
David Anderson committed
604 605 606
    }

    // deal with project preferences (should always be there)
David Anderson's avatar
David Anderson committed
607 608
    // If they've changed, write to account file,
    // then parse to get our venue, and pass to running apps
David Anderson's avatar
David Anderson committed
609 610
    //
    if (sr.project_prefs_xml) {
David Anderson's avatar
David Anderson committed
611 612
        if (strcmp(project->project_prefs.c_str(), sr.project_prefs_xml)) {
            project->project_prefs = string(sr.project_prefs_xml);
David Anderson's avatar
David Anderson committed
613
            update_project_prefs = true;
614
        }
David Anderson's avatar
David Anderson committed
615
    }
David Anderson's avatar
David Anderson committed
616

David Anderson's avatar
David Anderson committed
617 618 619
    // the account file has GUI URLs and project prefs.
    // rewrite if either of these has changed
    //
David Anderson's avatar
David Anderson committed
620
    if (project->gui_urls != old_gui_urls || update_project_prefs) {
David Anderson's avatar
David Anderson committed
621 622
        retval = project->write_account_file();
        if (retval) {
David Anderson's avatar
David Anderson committed
623
            msg_printf(project, MSG_INTERNAL_ERROR,
David Anderson's avatar
David Anderson committed
624 625
                "Can't write account file: %s", boincerror(retval)
            );
David Anderson's avatar
David Anderson committed
626 627
            return retval;
        }
David Anderson's avatar
David Anderson committed
628 629 630
    }

    if (update_project_prefs) {
David Anderson's avatar
David Anderson committed
631
        project->parse_account_file();
David Anderson's avatar
David Anderson committed
632 633 634
        if (strlen(project->host_venue)) {
            project->parse_account_file_venue();
        }
David Anderson's avatar
David Anderson committed
635 636 637
        project->parse_preferences_for_user_files();
        active_tasks.request_reread_prefs(project);
    }
David Anderson's avatar
David Anderson committed
638

David Anderson's avatar
David Anderson committed
639 640 641 642 643 644
    // if the scheduler reply includes a code-signing key,
    // accept it if we don't already have one from the project.
    // Otherwise verify its signature, using the key we already have.
    //

    if (sr.code_sign_key) {
David Anderson's avatar
David Anderson committed
645
        if (!strlen(project->code_sign_key)) {
David Anderson's avatar
David Anderson committed
646
            safe_strcpy(project->code_sign_key, sr.code_sign_key);
David Anderson's avatar
David Anderson committed
647
        } else {
David Anderson's avatar
David Anderson committed
648 649 650 651 652 653
            if (sr.code_sign_key_signature) {
                retval = verify_string2(
                    sr.code_sign_key, sr.code_sign_key_signature,
                    project->code_sign_key, signature_valid
                );
                if (!retval && signature_valid) {
David Anderson's avatar
David Anderson committed
654
                    safe_strcpy(project->code_sign_key, sr.code_sign_key);
David Anderson's avatar
David Anderson committed
655
                } else {
David Anderson's avatar
David Anderson committed
656
                    msg_printf(project, MSG_INTERNAL_ERROR,
David Anderson's avatar
David Anderson committed
657 658
                        "New code signing key doesn't validate"
                    );
David Anderson's avatar
David Anderson committed
659 660
                }
            } else {
David Anderson's avatar
David Anderson committed
661
                msg_printf(project, MSG_INTERNAL_ERROR,
David Anderson's avatar
David Anderson committed
662 663
                    "Missing code sign key signature"
                );
David Anderson's avatar
David Anderson committed
664
            }
David Anderson's avatar
David Anderson committed
665 666 667 668 669
        }
    }

    // copy new entities to client state
    //
David Anderson's avatar
David Anderson committed
670 671
    for (i=0; i<sr.apps.size(); i++) {
        APP* app = lookup_app(project, sr.apps[i].name);
David Anderson's avatar
David Anderson committed
672 673 674
        if (app) {
            strcpy(app->user_friendly_name, sr.apps[i].user_friendly_name);
        } else {
David Anderson's avatar
David Anderson committed
675 676 677
            app = new APP;
            *app = sr.apps[i];
            retval = link_app(project, app);
David Anderson's avatar
David Anderson committed
678
            if (retval) {
David Anderson's avatar
David Anderson committed
679
                msg_printf(project, MSG_INTERNAL_ERROR,
David Anderson's avatar
David Anderson committed
680
                    "Can't handle application %s in scheduler reply", app->name
David Anderson's avatar
David Anderson committed
681 682 683 684 685
                );
                delete app;
            } else {
                apps.push_back(app);
            }
David Anderson's avatar
David Anderson committed
686 687
        }
    }
Brian Boshes's avatar
Brian Boshes committed
688
    FILE_INFO* fip;
David Anderson's avatar
David Anderson committed
689
    for (i=0; i<sr.file_infos.size(); i++) {
Brian Boshes's avatar
Brian Boshes committed
690
        fip = lookup_file_info(project, sr.file_infos[i].name);
David Anderson's avatar
David Anderson committed
691
        if (fip) {
692 693
            fip->merge_info(sr.file_infos[i]);
        } else {
Brian Boshes's avatar
Brian Boshes committed
694
            fip = new FILE_INFO;
David Anderson's avatar
David Anderson committed
695
            *fip = sr.file_infos[i];
David Anderson's avatar
David Anderson committed
696
            retval = link_file_info(project, fip);
David Anderson's avatar
David Anderson committed
697
            if (retval) {
David Anderson's avatar
David Anderson committed
698
                msg_printf(project, MSG_INTERNAL_ERROR,
David Anderson's avatar
David Anderson committed
699
                    "Can't handle file %s in scheduler reply", fip->name
David Anderson's avatar
David Anderson committed
700 701 702 703 704
                );
                delete fip;
            } else {
                file_infos.push_back(fip);
            }
David Anderson's avatar
David Anderson committed
705
        }
Brian Boshes's avatar
Brian Boshes committed
706 707
    }
    for (i=0; i<sr.file_deletes.size(); i++) {
David Anderson's avatar
David Anderson committed
708
        fip = lookup_file_info(project, sr.file_deletes[i].c_str());
David Anderson's avatar
David Anderson committed
709
        if (fip) {
David Anderson's avatar
David Anderson committed
710 711 712
            msg_printf(project, MSG_INFO,
                "Got server request to delete file %s", fip->name
            );
David Anderson's avatar
David Anderson committed
713
            fip->marked_for_delete = true;
Brian Boshes's avatar
Brian Boshes committed
714
        }
David Anderson's avatar
David Anderson committed
715 716
    }
    for (i=0; i<sr.app_versions.size(); i++) {
717 718 719 720 721 722
        if (project->anonymous_platform) {
            msg_printf(project, MSG_INTERNAL_ERROR,
                "App version returned from anonymous platform project; ignoring"
            );
            continue;
        }
723
        APP_VERSION& avpp = sr.app_versions[i];
David Anderson's avatar
 
David Anderson committed
724 725 726 727 728 729 730 731 732 733
        if (strlen(avpp.platform) == 0) {
            strcpy(avpp.platform, get_primary_platform());
        } else {
            if (!is_supported_platform(avpp.platform)) {
                msg_printf(project, MSG_INTERNAL_ERROR,
                    "App version has unsupported platform %s", avpp.platform
                );
                continue;
            }
        }
734
        if (avpp.missing_coproc()) {
735
            msg_printf(project, MSG_INTERNAL_ERROR,
736 737
                "App version uses non-existent %s GPU",
                avpp.ncudas?"NVIDIA":"ATI"
738
            );
739
        }
740
        APP* app = lookup_app(project, avpp.app_name);
741 742 743 744 745 746
        if (!app) {
            msg_printf(project, MSG_INTERNAL_ERROR,
                "Missing app %s", avpp.app_name
            );
            continue;
        }
747 748 749
        APP_VERSION* avp = lookup_app_version(
            app, avpp.platform, avpp.version_num, avpp.plan_class
        );
David Anderson's avatar
David Anderson committed
750
        if (avp) {
David Anderson's avatar
David Anderson committed
751 752 753 754 755 756 757 758
            // update performance-related info;
            // generally this shouldn't change,
            // but if it does it's better to use the new stuff
            //
            avp->avg_ncpus = avpp.avg_ncpus;
            avp->max_ncpus = avpp.max_ncpus;
            avp->flops = avpp.flops;
            strcpy(avp->cmdline, avpp.cmdline);
759 760
            avp->ncudas = avpp.ncudas;
            avp->natis = avpp.natis;
761
            strlcpy(avp->api_version, avpp.api_version, sizeof(avp->api_version));
David Anderson's avatar
David Anderson committed
762

David Anderson's avatar
David Anderson committed
763 764 765 766 767
            // if we had download failures, clear them
            //
            avp->clear_errors();
            continue;
        }
David Anderson's avatar
David Anderson committed
768
        avp = new APP_VERSION;
David Anderson's avatar
 
David Anderson committed
769
        *avp = avpp;
David Anderson's avatar
David Anderson committed
770 771 772 773
        retval = link_app_version(project, avp);
        if (retval) {
             delete avp;
             continue;
David Anderson's avatar
David Anderson committed
774
        }
David Anderson's avatar
David Anderson committed
775
        app_versions.push_back(avp);
David Anderson's avatar
David Anderson committed
776 777
    }
    for (i=0; i<sr.workunits.size(); i++) {
David Anderson's avatar
David Anderson committed
778 779 780
        if (lookup_workunit(project, sr.workunits[i].name)) continue;
        WORKUNIT* wup = new WORKUNIT;
        *wup = sr.workunits[i];
David Anderson's avatar
David Anderson committed
781
        wup->project = project;
David Anderson's avatar
David Anderson committed
782 783
        retval = link_workunit(project, wup);
        if (retval) {
David Anderson's avatar
David Anderson committed
784
            msg_printf(project, MSG_INTERNAL_ERROR,
David Anderson's avatar
David Anderson committed
785
                "Can't handle task %s in scheduler reply", wup->name
David Anderson's avatar
David Anderson committed
786 787 788 789
            );
            delete wup;
            continue;
        }
David Anderson's avatar
David Anderson committed
790
        wup->clear_errors();
David Anderson's avatar
David Anderson committed
791
        workunits.push_back(wup);
David Anderson's avatar
David Anderson committed
792
    }
793 794
    double est_cpu_duration = 0;
    double est_cuda_duration = 0;
795
    double est_ati_duration = 0;
David Anderson's avatar
David Anderson committed
796
    for (i=0; i<sr.results.size(); i++) {
David Anderson's avatar
David Anderson committed
797
        if (lookup_result(project, sr.results[i].name)) {
David Anderson's avatar
David Anderson committed
798
            msg_printf(project, MSG_INTERNAL_ERROR,
David Anderson's avatar
David Anderson committed
799
                "Already have task %s\n", sr.results[i].name
David Anderson's avatar
David Anderson committed
800
            );
David Anderson's avatar
David Anderson committed
801 802 803 804 805 806
            continue;
        }
        RESULT* rp = new RESULT;
        *rp = sr.results[i];
        retval = link_result(project, rp);
        if (retval) {
David Anderson's avatar
David Anderson committed
807
            msg_printf(project, MSG_INTERNAL_ERROR,
David Anderson's avatar
David Anderson committed
808
                "Can't handle task %s in scheduler reply", rp->name
David Anderson's avatar
David Anderson committed
809 810 811
            );
            delete rp;
            continue;
David Anderson's avatar
David Anderson committed
812
        }
813 814 815 816
        if (strlen(rp->platform) == 0) {
            strcpy(rp->platform, get_primary_platform());
            rp->version_num = latest_version(rp->wup->app, rp->platform);
        }
817 818 819
        rp->avp = lookup_app_version(
            rp->wup->app, rp->platform, rp->version_num, rp->plan_class
        );
820 821
        if (!rp->avp) {
            msg_printf(project, MSG_INTERNAL_ERROR,
David Anderson's avatar
David Anderson committed
822 823
                "No application found for task: %s %d %s; discarding",
                rp->platform, rp->version_num, rp->plan_class
824 825 826 827
            );
            delete rp;
            continue;
        }
828 829 830 831 832 833 834 835 836 837
        if (rp->avp->missing_coproc()) {
            msg_printf(project, MSG_INTERNAL_ERROR,
                "Missing coprocessor for task %s; aborting", rp->name
            );
            rp->abort_inactive(ERR_MISSING_COPROC);
            continue;
        } else {
            rp->set_state(RESULT_NEW, "handle_scheduler_reply");
            if (rp->avp->ncudas) {
                est_cuda_duration += rp->estimated_duration(false);
838 839
                coproc_cuda->usable = true;
                    // trigger a check of whether GPU is actually usable
840 841
            } else if (rp->avp->natis) {
                est_ati_duration += rp->estimated_duration(false);
842
                coproc_ati->usable = true;
843 844 845 846
            } else {
                est_cpu_duration += rp->estimated_duration(false);
            }
        }
847
        rp->wup->version_num = rp->version_num;
848
        rp->received_time = now;
849
        new_results.push_back(rp);
850
        results.push_back(rp);
851
    }
852 853
    sort_results();

854 855 856
    if (log_flags.sched_op_debug) {
        if (sr.results.size()) {
            msg_printf(project, MSG_INFO,
857 858
                "[sched_op_debug] estimated total CPU job duration: %.0f seconds",
                est_cpu_duration
859
            );
860 861
            if (coproc_cuda) {
                msg_printf(project, MSG_INFO,
862
                    "[sched_op_debug] estimated total NVIDIA CPU job duration: %.0f seconds",
863 864 865
                    est_cuda_duration
                );
            }
866 867 868 869 870 871
            if (coproc_ati) {
                msg_printf(project, MSG_INFO,
                    "[sched_op_debug] estimated total ATI CPU job duration: %.0f seconds",
                    est_ati_duration
                );
            }
872
        }
David Anderson's avatar
David Anderson committed
873 874 875 876 877
    }

    // update records for ack'ed results
    //
    for (i=0; i<sr.result_acks.size(); i++) {
David Anderson's avatar
David Anderson committed
878
        if (log_flags.sched_op_debug) {
879
            msg_printf(project, MSG_INFO,
David Anderson's avatar
David Anderson committed
880
                "[sched_op_debug] handle_scheduler_reply(): got ack for result %s\n",
David Anderson's avatar
David Anderson committed
881 882 883
                sr.result_acks[i].name
            );
        }
David Anderson's avatar
David Anderson committed
884
        RESULT* rp = lookup_result(project, sr.result_acks[i].name);
David Anderson's avatar
David Anderson committed
885
        if (rp) {
David Anderson's avatar
David Anderson committed
886
            rp->got_server_ack = true;
David Anderson's avatar
David Anderson committed
887
        } else {
David Anderson's avatar
David Anderson committed
888
            msg_printf(project, MSG_INTERNAL_ERROR,
David Anderson's avatar
David Anderson committed
889
                "Got ack for task %s, but can't find it", sr.result_acks[i].name
David Anderson's avatar
David Anderson committed
890
            );
David Anderson's avatar