1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17
18 #include "cpp.h"
19
20 #ifdef _WIN32
21 #include "boinc_win.h"
22 #else
23 #include "config.h"
24 #include <cmath>
25 #include <cstdlib>
26 #include <cstdio>
27 #include <ctime>
28 #endif
29
30 #ifdef _MSC_VER
31 #define snprintf _snprintf
32 #endif
33
34 #include "error_numbers.h"
35 #include "filesys.h"
36 #include "parse.h"
37 #include "str_util.h"
38 #include "str_replace.h"
39 #include "util.h"
40
41 #include "client_state.h"
42 #include "client_types.h"
43 #include "client_msgs.h"
44 #include "file_names.h"
45 #include "log_flags.h"
46 #include "main.h"
47 #include "project.h"
48 #include "result.h"
49 #include "scheduler_op.h"
50
51 using std::vector;
52
SCHEDULER_OP(HTTP_OP_SET * h)53 SCHEDULER_OP::SCHEDULER_OP(HTTP_OP_SET* h) {
54 scheduler_op_retval = 0;
55 http_op.http_op_state = HTTP_STATE_IDLE;
56 http_ops = h;
57 safe_strcpy(scheduler_url,"");
58 url_index = 0;
59 cur_proj = NULL;
60 state = SCHEDULER_OP_STATE_IDLE;
61 reason = 0;
62 url_random = 0.0;
63 }
64
65 // See if there's a pending master file fetch.
66 // If so, start it and return true.
67 //
check_master_fetch_start()68 bool SCHEDULER_OP::check_master_fetch_start() {
69 int retval;
70
71 PROJECT* p = gstate.next_project_master_pending();
72 if (!p) return false;
73 retval = init_master_fetch(p);
74 if (retval) {
75 msg_printf(p, MSG_INTERNAL_ERROR,
76 "Couldn't start download of scheduler list: %s", boincerror(retval)
77 );
78 p->master_fetch_failures++;
79 project_rpc_backoff(p, "scheduler list fetch failed\n");
80 return false;
81 }
82 msg_printf(p, MSG_INFO, "Fetching scheduler list");
83 return true;
84 }
85
86 #ifndef SIM
87
88 // try to initiate an RPC to the given project.
89 // If there are multiple schedulers, start with a random one.
90 // User messages and project RPC backoff is done at this level.
91 //
init_op_project(PROJECT * p,int r)92 int SCHEDULER_OP::init_op_project(PROJECT* p, int r) {
93 int retval;
94 char err_msg[256];
95
96 reason = r;
97 if (log_flags.sched_op_debug) {
98 msg_printf(p, MSG_INFO,
99 "[sched_op] Starting scheduler request"
100 );
101 }
102
103 // if project has no schedulers,
104 // skip everything else and just get its master file.
105 //
106 if (p->scheduler_urls.size() == 0) {
107 retval = init_master_fetch(p);
108 if (retval) {
109 snprintf(err_msg, sizeof(err_msg),
110 "Scheduler list fetch initialization failed: %d\n", retval
111 );
112 project_rpc_backoff(p, err_msg);
113 }
114 return retval;
115 }
116
117 if (reason == RPC_REASON_INIT) {
118 work_fetch.set_initial_work_request(p);
119 if (!gstate.cpu_benchmarks_done()) {
120 gstate.cpu_benchmarks_set_defaults();
121 }
122 }
123
124 url_index = 0;
125 retval = gstate.make_scheduler_request(p);
126 if (!retval) {
127 retval = start_rpc(p);
128 }
129 if (retval) {
130 snprintf(err_msg, sizeof(err_msg),
131 "scheduler request to %s failed: %s\n",
132 p->get_scheduler_url(url_index, url_random), boincerror(retval)
133 );
134 project_rpc_backoff(p, err_msg);
135 } else {
136 // RPC started OK, so we must have network connectivity.
137 // Now's a good time to check for new BOINC versions
138 // and project list
139 //
140 if (!cc_config.no_info_fetch) {
141 gstate.new_version_check();
142 gstate.all_projects_list_check();
143 }
144 }
145 return retval;
146 }
147
148 #endif
149
150 // One of the following errors occurred:
151 // - connection failure in fetching master file
152 // - connection failure in scheduler RPC
153 // - got master file, but it didn't have any <scheduler> elements
154 // - tried all schedulers, none responded
155 // - sent nonzero work request, got a reply with no work
156 //
157 // Back off contacting this project's schedulers,
158 // and output an error msg if needed
159 //
project_rpc_backoff(PROJECT * p,const char * reason_msg)160 void SCHEDULER_OP::project_rpc_backoff(PROJECT* p, const char *reason_msg) {
161 char buf[1024];
162
163 if (gstate.in_abort_sequence) {
164 return;
165 }
166
167 if (p->master_fetch_failures >= gstate.master_fetch_retry_cap) {
168 snprintf(buf, sizeof(buf),
169 "%d consecutive failures fetching scheduler list",
170 p->master_fetch_failures
171 );
172 p->master_url_fetch_pending = true;
173 p->set_min_rpc_time(gstate.now + gstate.master_fetch_interval, buf);
174 return;
175 }
176
177 // if nrpc failures is master_fetch_period,
178 // then set master_url_fetch_pending and initialize again
179 //
180 if (p->nrpc_failures == gstate.master_fetch_period) {
181 p->master_url_fetch_pending = true;
182 p->min_rpc_time = 0;
183 p->nrpc_failures = 0;
184 p->master_fetch_failures++;
185 }
186
187 // if network is down, don't count it as RPC failure
188 //
189 if (!net_status.need_physical_connection) {
190 p->nrpc_failures++;
191 }
192 //msg_printf(p, MSG_INFO, "nrpc_failures %d need_conn %d", p->nrpc_failures, gstate.need_physical_connection);
193
194 int n = p->nrpc_failures;
195 if (n > gstate.retry_cap) n = gstate.retry_cap;
196 double exp_backoff = calculate_exponential_backoff(
197 n, gstate.sched_retry_delay_min, gstate.sched_retry_delay_max
198 );
199 //msg_printf(p, MSG_INFO, "simulating backoff of %f", exp_backoff);
200 p->set_min_rpc_time(gstate.now + exp_backoff, reason_msg);
201 }
202
203
204 // RPC failed, either on startup or later.
205 // If RPC was requested by project or acct mgr, or init,
206 // keep trying (subject to backoff); otherwise give up.
207 // The other cases (results_dur, need_work, project req, and trickle_up)
208 // will be retriggered automatically
209 //
rpc_failed(const char * msg)210 void SCHEDULER_OP::rpc_failed(const char* msg) {
211 project_rpc_backoff(cur_proj, msg);
212 switch (cur_proj->sched_rpc_pending) {
213 case RPC_REASON_INIT:
214 case RPC_REASON_ACCT_MGR_REQ:
215 break;
216 default:
217 cur_proj->sched_rpc_pending = 0;
218 }
219 cur_proj = 0;
220 }
221
request_string(char * buf,int len)222 static void request_string(char* buf, int len) {
223 bool first = true;
224 strlcpy(buf, "", len);
225 for (int i=0; i<coprocs.n_rsc; i++) {
226 if (rsc_work_fetch[i].req_secs) {
227 if (!first) strlcat(buf, " and ", len);
228 strlcat(buf, rsc_name_long(i), len);
229 first = false;
230 }
231 }
232 }
233
234 // low-level routine to initiate an RPC
235 // If successful, creates an HTTP_OP that must be polled
236 // PRECONDITION: the request file has been created
237 //
start_rpc(PROJECT * p)238 int SCHEDULER_OP::start_rpc(PROJECT* p) {
239 int retval;
240 char request_file[1024], reply_file[1024], buf[1024];
241
242 safe_strcpy(scheduler_url, p->get_scheduler_url(url_index, url_random));
243 if (log_flags.sched_ops) {
244 msg_printf(p, MSG_INFO,
245 "Sending scheduler request: %s.", rpc_reason_string(reason)
246 );
247 if (p->trickle_up_pending && reason != RPC_REASON_TRICKLE_UP) {
248 msg_printf(p, MSG_INFO, "Sending trickle-up message");
249 }
250 if (p->nresults_returned) {
251 msg_printf(p, MSG_INFO,
252 "Reporting %d completed tasks", p->nresults_returned
253 );
254 }
255 request_string(buf, sizeof(buf));
256 if (strlen(buf)) {
257 msg_printf(p, MSG_INFO, "Requesting new tasks for %s", buf);
258 } else {
259 if (p->pwf.project_reason) {
260 msg_printf(p, MSG_INFO,
261 "Not requesting tasks: %s", project_reason_string(p, buf, sizeof(buf))
262 );
263 } else {
264 msg_printf(p, MSG_INFO, "Not requesting tasks");
265 }
266 }
267 }
268 if (log_flags.sched_op_debug) {
269 for (int i=0; i<coprocs.n_rsc; i++) {
270 msg_printf(p, MSG_INFO,
271 "[sched_op] %s work request: %.2f seconds; %.2f devices",
272 rsc_name_long(i),
273 rsc_work_fetch[i].req_secs,
274 rsc_work_fetch[i].req_instances
275 );
276 }
277 }
278
279 get_sched_request_filename(*p, request_file, sizeof(request_file));
280 get_sched_reply_filename(*p, reply_file, sizeof(reply_file));
281
282 cur_proj = p;
283 retval = http_op.init_post(p, scheduler_url, request_file, reply_file);
284 if (retval) {
285 if (log_flags.sched_ops) {
286 msg_printf(p, MSG_INFO,
287 "Scheduler request initialization failed: %s", boincerror(retval)
288 );
289 }
290 rpc_failed("Scheduler request initialization failed");
291 return retval;
292 }
293 http_ops->insert(&http_op);
294 p->rpc_seqno++;
295 state = SCHEDULER_OP_STATE_RPC;
296 return 0;
297 }
298
299 // initiate a fetch of a project's master URL file
300 //
init_master_fetch(PROJECT * p)301 int SCHEDULER_OP::init_master_fetch(PROJECT* p) {
302 int retval;
303 char master_filename[256];
304
305 get_master_filename(*p, master_filename, sizeof(master_filename));
306
307 if (log_flags.sched_op_debug) {
308 msg_printf(p, MSG_INFO, "[sched_op] Fetching master file");
309 }
310 cur_proj = p;
311 retval = http_op.init_get(p, p->master_url, master_filename, true, 0, 0);
312 if (retval) {
313 if (log_flags.sched_ops) {
314 msg_printf(p, MSG_INFO,
315 "Master file fetch failed: %s", boincerror(retval)
316 );
317 }
318 rpc_failed("Master file fetch initialization failed");
319 return retval;
320 }
321 http_ops->insert(&http_op);
322 state = SCHEDULER_OP_STATE_GET_MASTER;
323 return 0;
324 }
325
326 // parse a master file.
327 //
parse_master_file(PROJECT * p,vector<string> & urls)328 int SCHEDULER_OP::parse_master_file(PROJECT* p, vector<string> &urls) {
329 char buf[256], buf2[256];
330 char master_filename[256];
331 string str;
332 FILE* f;
333 int n;
334
335 get_master_filename(*p, master_filename, sizeof(master_filename));
336 f = boinc_fopen(master_filename, "r");
337 if (!f) {
338 msg_printf(p, MSG_INTERNAL_ERROR, "Can't open scheduler list file");
339 return ERR_FOPEN;
340 }
341 p->scheduler_urls.clear();
342 while (fgets(buf, 256, f)) {
343
344 // allow for the possibility of > 1 tag per line here
345 // (UMTS may collapse lines)
346 //
347 char* q = buf;
348 while (q && parse_str(q, "<scheduler>", str)) {
349 push_unique(str, urls);
350 q = strstr(q, "</scheduler>");
351 if (q) q += strlen("</scheduler>");
352 }
353
354 // check for new syntax: <link ...>
355 //
356 q = buf;
357 while (q) {
358 n = sscanf(q, "<link rel=\"boinc_scheduler\" href=\"%s", buf2);
359 if (n == 1) {
360 char* q2 = strchr(buf2, '"');
361 if (q2) *q2 = 0;
362 strip_whitespace(buf2);
363 str = string(buf2);
364 push_unique(str, urls);
365 }
366 q = strchr(q, '>');
367 if (q) q = strchr(q, '<');
368 }
369 }
370 fclose(f);
371 if (log_flags.sched_op_debug) {
372 msg_printf(p, MSG_INFO,
373 "[sched_op] Found %d scheduler URLs in master file\n",
374 (int)urls.size()
375 );
376 }
377
378 // couldn't find any scheduler URLs in the master file?
379 //
380 if ((int) urls.size() == 0) {
381 msg_printf(p, MSG_INTERNAL_ERROR,
382 "No scheduler URLs found in master file\n"
383 );
384 p->sched_rpc_pending = 0;
385 return ERR_XML_PARSE;
386 }
387
388 return 0;
389 }
390
391 // A master file has just been read.
392 // transfer scheduler URLs to project.
393 // Return true if any of them is new
394 //
update_urls(PROJECT * p,vector<string> & urls)395 bool SCHEDULER_OP::update_urls(PROJECT* p, vector<string> &urls) {
396 unsigned int i, j;
397 bool found, any_new;
398
399 any_new = false;
400 for (i=0; i<urls.size(); i++) {
401 found = false;
402 for (j=0; j<p->scheduler_urls.size(); j++) {
403 if (urls[i] == p->scheduler_urls[j]) {
404 found = true;
405 break;
406 }
407 }
408 if (!found) any_new = true;
409 }
410
411 p->scheduler_urls.clear();
412 for (i=0; i<urls.size(); i++) {
413 p->scheduler_urls.push_back(urls[i]);
414 }
415
416 return any_new;
417 }
418
419 #ifndef SIM
420
421 // poll routine. If an operation is in progress, check for completion
422 //
poll()423 bool SCHEDULER_OP::poll() {
424 int retval;
425 vector<string> urls;
426 bool changed;
427
428 switch(state) {
429 case SCHEDULER_OP_STATE_GET_MASTER:
430 // here we're fetching the master file for a project
431 //
432 if (http_op.http_op_state == HTTP_STATE_DONE) {
433 state = SCHEDULER_OP_STATE_IDLE;
434 cur_proj->master_url_fetch_pending = false;
435 http_ops->remove(&http_op);
436 if (http_op.http_op_retval == 0) {
437 if (log_flags.sched_op_debug) {
438 msg_printf(cur_proj, MSG_INFO,
439 "[sched_op] Got master file; parsing"
440 );
441 }
442 retval = parse_master_file(cur_proj, urls);
443 if (retval || (urls.size()==0)) {
444 // master file parse failed.
445 //
446 cur_proj->master_fetch_failures++;
447 rpc_failed("Couldn't parse scheduler list");
448 } else {
449 // parse succeeded
450 //
451 msg_printf(cur_proj, MSG_INFO, "Master file download succeeded");
452 cur_proj->master_fetch_failures = 0;
453 changed = update_urls(cur_proj, urls);
454
455 // reenable scheduler RPCs if have new URLs
456 //
457 if (changed) {
458 cur_proj->min_rpc_time = 0;
459 cur_proj->nrpc_failures = 0;
460 }
461 }
462 } else {
463 // master file fetch failed.
464 //
465 char buf[256];
466 snprintf(buf, sizeof(buf), "Scheduler list fetch failed: %s",
467 boincerror(http_op.http_op_retval)
468 );
469 cur_proj->master_fetch_failures++;
470 rpc_failed("Master file request failed");
471 }
472 gstate.set_client_state_dirty("Master fetch complete");
473 gstate.request_work_fetch("Master fetch complete");
474 cur_proj = NULL;
475 return true;
476 }
477 break;
478 case SCHEDULER_OP_STATE_RPC:
479
480 // here we're doing a scheduler RPC
481 //
482 if (http_op.http_op_state == HTTP_STATE_DONE) {
483 state = SCHEDULER_OP_STATE_IDLE;
484 http_ops->remove(&http_op);
485 if (http_op.http_op_retval) {
486 if (log_flags.sched_ops) {
487 msg_printf(cur_proj, MSG_INFO,
488 "Scheduler request failed: %s", http_op.error_msg
489 );
490 }
491
492 // scheduler RPC failed. Try another scheduler if one exists
493 //
494 while (1) {
495 url_index++;
496 if (url_index == (int)cur_proj->scheduler_urls.size()) {
497 break;
498 }
499 retval = start_rpc(cur_proj);
500 if (!retval) return true;
501 }
502 if (url_index == (int) cur_proj->scheduler_urls.size()) {
503 rpc_failed("Scheduler request failed");
504 }
505 } else {
506 retval = gstate.handle_scheduler_reply(cur_proj, scheduler_url);
507 switch (retval) {
508 case 0:
509 break;
510 case ERR_PROJECT_DOWN:
511 project_rpc_backoff(cur_proj, "project is down");
512 break;
513 default:
514 project_rpc_backoff(cur_proj, "can't parse scheduler reply");
515 break;
516 }
517 cur_proj->sched_rpc_pending = 0;
518 // do this after handle_scheduler_reply()
519 }
520 cur_proj = NULL;
521 gstate.set_client_state_dirty("RPC complete");
522 gstate.request_work_fetch("RPC complete");
523 return true;
524 }
525 }
526 return false;
527 }
528
529 #endif
530
abort(PROJECT * p)531 void SCHEDULER_OP::abort(PROJECT* p) {
532 if (state != SCHEDULER_OP_STATE_IDLE && cur_proj == p) {
533 gstate.http_ops->remove(&http_op);
534 state = SCHEDULER_OP_STATE_IDLE;
535 cur_proj = NULL;
536 }
537 }
538
clear()539 void SCHEDULER_REPLY::clear() {
540 hostid = 0;
541 request_delay = 0;
542 next_rpc_delay = 0;
543 messages.clear();
544 global_prefs_xml = 0;
545 project_prefs_xml = 0;
546 safe_strcpy(master_url, "");
547 safe_strcpy(host_venue, "");
548 user_create_time = 0;
549 code_sign_key = 0;
550 code_sign_key_signature = 0;
551 message_ack = false;
552 project_is_down = false;
553 send_file_list = false;
554 send_full_workload = false;
555 dont_use_dcf = false;
556 send_time_stats_log = 0;
557 send_job_log = 0;
558 scheduler_version = 0;
559 got_rss_feeds = false;
560 }
561
SCHEDULER_REPLY()562 SCHEDULER_REPLY::SCHEDULER_REPLY() {
563 clear();
564 }
565
~SCHEDULER_REPLY()566 SCHEDULER_REPLY::~SCHEDULER_REPLY() {
567 if (global_prefs_xml) free(global_prefs_xml);
568 if (project_prefs_xml) free(project_prefs_xml);
569 if (code_sign_key) free(code_sign_key);
570 if (code_sign_key_signature) free(code_sign_key_signature);
571 }
572
573 #ifndef SIM
574
handle_no_rsc_apps(const char * name,PROJECT * p,bool value)575 static void handle_no_rsc_apps(const char* name, PROJECT* p, bool value) {
576 int i = rsc_index(name);
577 if (i < 0) return;
578 p->no_rsc_apps[i] = value;
579 }
580
581 // parse a scheduler reply.
582 // Some of the items go into the SCHEDULER_REPLY object.
583 // Others are copied straight to the PROJECT
584 //
parse(FILE * in,PROJECT * project)585 int SCHEDULER_REPLY::parse(FILE* in, PROJECT* project) {
586 char buf[256], msg_buf[1024], pri_buf[256], attr_buf[256];
587 int retval;
588 MIOFILE mf;
589 XML_PARSER xp(&mf);
590 string delete_file_name;
591 bool verify_files_on_app_start = false;
592 bool non_cpu_intensive = false;
593 bool ended = false;
594
595 mf.init_file(in);
596 bool found_start_tag = false, btemp;
597 double cpid_time = 0;
598
599 clear();
600 safe_strcpy(host_venue, project->host_venue);
601 // the project won't send us a venue if it's doing maintenance
602 // or doesn't check the DB because no work.
603 // Don't overwrite the host venue in that case.
604 sr_feeds.clear();
605 trickle_up_urls.clear();
606
607 if (!project->anonymous_platform) {
608 for (int i=0; i<MAX_RSC; i++) {
609 project->no_rsc_apps[i] = false;
610 }
611 }
612
613 // First line should either be tag (HTTP 1.0) or
614 // hex length of response (HTTP 1.1)
615 //
616 while (!xp.get_tag(attr_buf, sizeof(attr_buf))) {
617 if (!found_start_tag) {
618 if (xp.match_tag("scheduler_reply")) {
619 found_start_tag = true;
620 }
621 continue;
622 }
623 if (xp.match_tag("/scheduler_reply")) {
624
625 // update statistics after parsing the scheduler reply
626 // add new record if vector is empty or we have a new day
627 //
628 if (project->statistics.empty() || project->statistics.back().day!=dday()) {
629 project->trim_statistics();
630 DAILY_STATS nds;
631 project->statistics.push_back(nds);
632 }
633 DAILY_STATS& ds = project->statistics.back();
634 ds.day=dday();
635 ds.user_total_credit=project->user_total_credit;
636 ds.user_expavg_credit=project->user_expavg_credit;
637 ds.host_total_credit=project->host_total_credit;
638 ds.host_expavg_credit=project->host_expavg_credit;
639
640 project->write_statistics_file();
641
642 if (cpid_time) {
643 project->cpid_time = cpid_time;
644 } else {
645 project->cpid_time = project->user_create_time;
646 }
647 if (project->dont_use_dcf) {
648 project->duration_correction_factor = 1;
649 }
650
651 // boolean project attributes.
652 // If the scheduler reply didn't specify them, they're not set.
653 //
654 project->verify_files_on_app_start = verify_files_on_app_start;
655 project->non_cpu_intensive = non_cpu_intensive;
656 project->ended = ended;
657 return 0;
658 }
659 else if (xp.parse_str("project_name", project->project_name, sizeof(project->project_name))) {
660 continue;
661 }
662 else if (xp.parse_str("master_url", master_url, sizeof(master_url))) {
663 continue;
664 }
665 else if (xp.parse_str("symstore", project->symstore, sizeof(project->symstore))) continue;
666 else if (xp.parse_str("user_name", project->user_name, sizeof(project->user_name))) continue;
667 else if (xp.parse_double("user_total_credit", project->user_total_credit)) continue;
668 else if (xp.parse_double("user_expavg_credit", project->user_expavg_credit)) continue;
669 else if (xp.parse_double("user_create_time", project->user_create_time)) continue;
670 else if (xp.parse_double("cpid_time", cpid_time)) continue;
671 else if (xp.parse_str("team_name", project->team_name, sizeof(project->team_name))) continue;
672 else if (xp.parse_int("hostid", hostid)) continue;
673 else if (xp.parse_double("host_total_credit", project->host_total_credit)) continue;
674 else if (xp.parse_double("host_expavg_credit", project->host_expavg_credit)) continue;
675 else if (xp.parse_str("host_venue", host_venue, sizeof(host_venue))) continue;
676 else if (xp.parse_double("host_create_time", project->host_create_time)) continue;
677 else if (xp.parse_double("request_delay", request_delay)) continue;
678 else if (xp.parse_double("next_rpc_delay", next_rpc_delay)) continue;
679 else if (xp.match_tag("global_preferences")) {
680 retval = dup_element_contents(
681 xp.f->f,
682 "</global_preferences>",
683 &global_prefs_xml
684 );
685 if (retval) {
686 msg_printf(project, MSG_INTERNAL_ERROR,
687 "Can't parse global prefs in scheduler reply: %s",
688 boincerror(retval)
689 );
690 return retval;
691 }
692 } else if (xp.match_tag("project_preferences")) {
693 retval = dup_element_contents(
694 xp.f->f,
695 "</project_preferences>",
696 &project_prefs_xml
697 );
698 if (retval) {
699 msg_printf(project, MSG_INTERNAL_ERROR,
700 "Can't parse project prefs in scheduler reply: %s",
701 boincerror(retval)
702 );
703 return retval;
704 }
705 } else if (xp.match_tag("gui_urls")) {
706 string foo;
707 retval = copy_element_contents(xp.f->f, "</gui_urls>", foo);
708 if (retval) {
709 msg_printf(project, MSG_INTERNAL_ERROR,
710 "Can't parse GUI URLs in scheduler reply: %s",
711 boincerror(retval)
712 );
713 return retval;
714 }
715 project->gui_urls = "<gui_urls>\n"+foo+"</gui_urls>\n";
716 } else if (xp.match_tag("code_sign_key")) {
717 retval = dup_element_contents(
718 xp.f->f,
719 "</code_sign_key>",
720 &code_sign_key
721 );
722 if (retval) {
723 msg_printf(project, MSG_INTERNAL_ERROR,
724 "Can't parse code sign key in scheduler reply: %s",
725 boincerror(retval)
726 );
727 return ERR_XML_PARSE;
728 }
729 strip_whitespace(code_sign_key);
730 } else if (xp.match_tag("code_sign_key_signature")) {
731 retval = dup_element_contents(
732 xp.f->f,
733 "</code_sign_key_signature>",
734 &code_sign_key_signature
735 );
736 if (retval) {
737 msg_printf(project, MSG_INTERNAL_ERROR,
738 "Can't parse code sign key signature in scheduler reply: %s",
739 boincerror(retval)
740 );
741 return ERR_XML_PARSE;
742 }
743 } else if (xp.match_tag("app")) {
744 APP app;
745 retval = app.parse(xp);
746 if (retval) {
747 msg_printf(project, MSG_INTERNAL_ERROR,
748 "Can't parse application in scheduler reply: %s",
749 boincerror(retval)
750 );
751 } else {
752 apps.push_back(app);
753 }
754 } else if (xp.match_tag("file_info")) {
755 FILE_INFO file_info;
756 retval = file_info.parse(xp);
757 if (retval) {
758 msg_printf(project, MSG_INTERNAL_ERROR,
759 "Can't parse file info in scheduler reply: %s",
760 boincerror(retval)
761 );
762 } else {
763 file_infos.push_back(file_info);
764 }
765 } else if (xp.match_tag("app_version")) {
766 APP_VERSION av;
767 retval = av.parse(xp);
768 if (retval) {
769 msg_printf(project, MSG_INTERNAL_ERROR,
770 "Can't parse application version in scheduler reply: %s",
771 boincerror(retval)
772 );
773 } else {
774 app_versions.push_back(av);
775 }
776 } else if (xp.match_tag("workunit")) {
777 WORKUNIT wu;
778 retval = wu.parse(xp);
779 if (retval) {
780 msg_printf(project, MSG_INTERNAL_ERROR,
781 "Can't parse workunit in scheduler reply: %s",
782 boincerror(retval)
783 );
784 } else {
785 workunits.push_back(wu);
786 }
787 } else if (xp.match_tag("result")) {
788 RESULT result; // make sure this is here so constructor
789 // gets called each time
790 retval = result.parse_server(xp);
791 if (retval) {
792 msg_printf(project, MSG_INTERNAL_ERROR,
793 "Can't parse task in scheduler reply: %s",
794 boincerror(retval)
795 );
796 } else {
797 results.push_back(result);
798 }
799 } else if (xp.match_tag("result_ack")) {
800 RESULT result;
801 retval = result.parse_name(xp, "/result_ack");
802 if (retval) {
803 msg_printf(project, MSG_INTERNAL_ERROR,
804 "Can't parse ack in scheduler reply: %s",
805 boincerror(retval)
806 );
807 } else {
808 result_acks.push_back(result);
809 }
810 } else if (xp.match_tag("result_abort")) {
811 RESULT result;
812 retval = result.parse_name(xp, "/result_abort");
813 if (retval) {
814 msg_printf(project, MSG_INTERNAL_ERROR,
815 "Can't parse result abort in scheduler reply: %s",
816 boincerror(retval)
817 );
818 } else {
819 result_abort.push_back(result);
820 }
821 } else if (xp.match_tag("result_abort_if_not_started")) {
822 RESULT result;
823 retval = result.parse_name(xp, "/result_abort_if_not_started");
824 if (retval) {
825 msg_printf(project, MSG_INTERNAL_ERROR,
826 "Can't parse result abort-if-not-started in scheduler reply: %s",
827 boincerror(retval)
828 );
829 } else {
830 result_abort_if_not_started.push_back(result);
831 }
832 } else if (xp.parse_string("delete_file_info", delete_file_name)) {
833 file_deletes.push_back(delete_file_name);
834 } else if (xp.parse_str("message", msg_buf, sizeof(msg_buf))) {
835 parse_attr(attr_buf, "priority", pri_buf, sizeof(pri_buf));
836 USER_MESSAGE um(msg_buf, pri_buf);
837 messages.push_back(um);
838 continue;
839 } else if (xp.parse_bool("message_ack", message_ack)) {
840 continue;
841 } else if (xp.parse_bool("project_is_down", project_is_down)) {
842 continue;
843 } else if (xp.parse_str("email_hash", project->email_hash, sizeof(project->email_hash))) {
844 continue;
845 } else if (xp.parse_str("cross_project_id", project->cross_project_id, sizeof(project->cross_project_id))) {
846 continue;
847 } else if (xp.parse_str("external_cpid", project->external_cpid, sizeof(project->external_cpid))) {
848 continue;
849 } else if (xp.match_tag("trickle_down")) {
850 retval = gstate.handle_trickle_down(project, in);
851 if (retval) {
852 msg_printf(project, MSG_INTERNAL_ERROR,
853 "handle_trickle_down failed: %s", boincerror(retval)
854 );
855 }
856 continue;
857 } else if (xp.parse_bool("non_cpu_intensive", non_cpu_intensive)) {
858 continue;
859 } else if (xp.parse_bool("ended", ended)) {
860 continue;
861 } else if (xp.parse_bool("no_cpu_apps", btemp)) {
862 if (!project->anonymous_platform) {
863 handle_no_rsc_apps("CPU", project, btemp);
864 }
865 continue;
866
867 // deprecated syntax
868 } else if (xp.parse_bool("no_cuda_apps", btemp)) {
869 if (!project->anonymous_platform) {
870 handle_no_rsc_apps(GPU_TYPE_NVIDIA, project, btemp);
871 }
872 continue;
873 } else if (xp.parse_bool("no_ati_apps", btemp)) {
874 if (!project->anonymous_platform) {
875 handle_no_rsc_apps(GPU_TYPE_ATI, project, btemp);
876 }
877 continue;
878
879 } else if (xp.parse_str("no_rsc_apps", buf, sizeof(buf))) {
880 if (!project->anonymous_platform) {
881 handle_no_rsc_apps(buf, project, true);
882 }
883 continue;
884 } else if (xp.parse_bool("verify_files_on_app_start", verify_files_on_app_start)) {
885 continue;
886 } else if (xp.parse_bool("send_full_workload", send_full_workload)) {
887 continue;
888 } else if (xp.parse_bool("dont_use_dcf", dont_use_dcf)) {
889 continue;
890 } else if (xp.parse_int("send_time_stats_log", send_time_stats_log)){
891 continue;
892 } else if (xp.parse_int("send_job_log", send_job_log)) {
893 continue;
894 } else if (xp.parse_int("scheduler_version", scheduler_version)) {
895 continue;
896 } else if (xp.match_tag("project_files")) {
897 retval = parse_project_files(xp, project_files);
898 #ifdef ENABLE_AUTO_UPDATE
899 } else if (xp.match_tag("auto_update")) {
900 retval = auto_update.parse(xp);
901 if (!retval) auto_update.present = true;
902 #endif
903 } else if (xp.match_tag("rss_feeds")) {
904 got_rss_feeds = true;
905 parse_rss_feed_descs(xp, sr_feeds);
906 continue;
907 } else if (xp.match_tag("trickle_up_urls")) {
908 parse_trickle_up_urls(xp, trickle_up_urls);
909 continue;
910 } else if (xp.parse_int("userid", project->userid)) {
911 continue;
912 } else if (xp.parse_int("teamid", project->teamid)) {
913 continue;
914 } else if (xp.parse_double("desired_disk_usage", project->desired_disk_usage)) {
915 continue;
916 } else {
917 if (log_flags.unparsed_xml) {
918 msg_printf(project, MSG_INFO,
919 "[unparsed_xml] SCHEDULER_REPLY::parse(): unrecognized %s\n",
920 xp.parsed_tag
921 );
922 }
923 }
924 }
925 if (found_start_tag) {
926 msg_printf(project, MSG_INTERNAL_ERROR, "No close tag in scheduler reply");
927 } else {
928 msg_printf(project, MSG_INTERNAL_ERROR, "No start tag in scheduler reply");
929 }
930
931 return ERR_XML_PARSE;
932 }
933 #endif
934
USER_MESSAGE(char * m,char * p)935 USER_MESSAGE::USER_MESSAGE(char* m, char* p) {
936 message = m;
937 priority = p;
938 }
939
940