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 #ifdef _WIN32
19 #include "boinc_win.h"
20 #include "win_util.h"
21 #else
22 #include "config.h"
23 #include <cstring>
24 #include <errno.h>
25 #endif
26
27 #ifdef __APPLE__
28 #include "mac_spawn.h"
29 #endif
30
31 #ifdef _MSC_VER
32 #define snprintf _snprintf
33 #endif
34
35 #include "error_numbers.h"
36 #include "filesys.h"
37 #include "md5_file.h"
38 #include "parse.h"
39 #include "str_replace.h"
40 #include "str_util.h"
41 #include "util.h"
42
43 #include "client_msgs.h"
44 #include "client_state.h"
45 #include "cs_proxy.h"
46 #include "file_names.h"
47 #include "project.h"
48 #include "result.h"
49
50 #define MAX_STATE_FILE_WRITE_ATTEMPTS 2
51
set_client_state_dirty(const char * source)52 void CLIENT_STATE::set_client_state_dirty(const char* source) {
53 if (log_flags.statefile_debug) {
54 msg_printf(0, MSG_INFO, "[statefile] set dirty: %s\n", source);
55 }
56 client_state_dirty = true;
57 }
58
valid_state_file(const char * fname)59 static bool valid_state_file(const char* fname) {
60 char buf[256];
61 FILE* f = boinc_fopen(fname, "r");
62 if (!f) return false;
63 if (!fgets(buf, 256, f)) {
64 fclose(f);
65 return false;
66 }
67 if (!match_tag(buf, "<client_state>")) {
68 fclose(f);
69 return false;
70 }
71 while (fgets(buf, 256, f)) {
72 if (match_tag(buf, "</client_state>")) {
73 fclose(f);
74 return true;
75 }
76 }
77 fclose(f);
78 return false;
79 }
80
81 // return true if r0 arrived before r1,
82 // with tie-break based on name hash.
83 // used to sort result list
84 //
arrived_first(RESULT * r0,RESULT * r1)85 static inline bool arrived_first(RESULT* r0, RESULT* r1) {
86 if (r0->received_time < r1->received_time) {
87 return true;
88 }
89 if (r0->received_time > r1->received_time) {
90 return false;
91 }
92 return (r0->name_md5 < r1->name_md5);
93 }
94
95 // Parse the client_state.xml file
96 //
parse_state_file()97 int CLIENT_STATE::parse_state_file() {
98 const char *fname;
99
100 // Look for a valid state file:
101 // First "next", then regular, then "prev"
102 //
103 if (valid_state_file(STATE_FILE_NEXT)) {
104 fname = STATE_FILE_NEXT;
105 msg_printf(0, MSG_INFO, "Using state file %s", STATE_FILE_NEXT);
106 } else if (valid_state_file(STATE_FILE_NAME)) {
107 fname = STATE_FILE_NAME;
108 } else if (valid_state_file(STATE_FILE_PREV)) {
109 msg_printf(0, MSG_INFO, "Using state file %s", STATE_FILE_PREV);
110 fname = STATE_FILE_PREV;
111 } else {
112 msg_printf(0, MSG_INFO, "Creating new client state file");
113
114 // avoid warning messages about version
115 //
116 old_major_version = BOINC_MAJOR_VERSION;
117 old_minor_version = BOINC_MINOR_VERSION;
118 old_release = BOINC_RELEASE;
119 return ERR_FOPEN;
120 }
121 return parse_state_file_aux(fname);
122 }
123
parse_state_file_aux(const char * fname)124 int CLIENT_STATE::parse_state_file_aux(const char* fname) {
125 PROJECT *project=NULL;
126 int retval=0;
127 string stemp;
128
129 FILE* f = fopen(fname, "r");
130 if (!f) return ERR_FOPEN;
131 MIOFILE mf;
132 XML_PARSER xp(&mf);
133 mf.init_file(f);
134 while (!xp.get_tag()) {
135 if (xp.match_tag("/client_state")) {
136 break;
137 }
138 if (xp.match_tag("client_state")) {
139 continue;
140 }
141 if (xp.match_tag("project")) {
142 PROJECT temp_project;
143 retval = temp_project.parse_state(xp);
144 if (retval) {
145 msg_printf(NULL, MSG_INTERNAL_ERROR, "Can't parse project in state file");
146 } else {
147 #ifdef SIM
148 project = new PROJECT;
149 *project = temp_project;
150 projects.push_back(project);
151 #else
152 project = lookup_project(temp_project.master_url);
153 if (project) {
154 project->copy_state_fields(temp_project);
155 } else {
156 msg_printf(&temp_project, MSG_INTERNAL_ERROR,
157 "Project %s is in state file but no account file found",
158 temp_project.get_project_name()
159 );
160 }
161 #endif
162 }
163 continue;
164 }
165 if (xp.match_tag("app")) {
166 APP* app = new APP;
167 retval = app->parse(xp);
168 if (!project) {
169 msg_printf(NULL, MSG_INTERNAL_ERROR,
170 "Application %s outside project in state file",
171 app->name
172 );
173 delete app;
174 continue;
175 }
176 if (project->anonymous_platform) {
177 delete app;
178 continue;
179 }
180 if (retval) {
181 msg_printf(NULL, MSG_INTERNAL_ERROR,
182 "Can't parse application in state file"
183 );
184 delete app;
185 continue;
186 }
187 retval = link_app(project, app);
188 if (retval) {
189 msg_printf(project, MSG_INTERNAL_ERROR,
190 "Can't handle application %s in state file",
191 app->name
192 );
193 delete app;
194 continue;
195 }
196 apps.push_back(app);
197 continue;
198 }
199 if (xp.match_tag("file_info") || xp.match_tag("file")) {
200 FILE_INFO* fip = new FILE_INFO;
201 retval = fip->parse(xp);
202 if (!project) {
203 msg_printf(NULL, MSG_INTERNAL_ERROR,
204 "File info outside project in state file"
205 );
206 delete fip;
207 continue;
208 }
209 if (retval) {
210 msg_printf(NULL, MSG_INTERNAL_ERROR,
211 "Can't handle file info in state file"
212 );
213 delete fip;
214 continue;
215 }
216 retval = link_file_info(project, fip);
217 if (project->anonymous_platform && retval == ERR_NOT_UNIQUE) {
218 delete fip;
219 continue;
220 }
221 if (retval) {
222 msg_printf(project, MSG_INTERNAL_ERROR,
223 "Can't handle file info %s in state file",
224 fip->name
225 );
226 delete fip;
227 continue;
228 }
229 file_infos.push_back(fip);
230 #ifndef SIM
231 // If the file had a failure before,
232 // don't start another file transfer
233 //
234 int failnum;
235 if (fip->had_failure(failnum)) {
236 if (fip->pers_file_xfer) {
237 delete fip->pers_file_xfer;
238 fip->pers_file_xfer = NULL;
239 }
240 }
241 if (fip->pers_file_xfer) {
242 retval = fip->pers_file_xfer->init(fip, fip->pers_file_xfer->is_upload);
243 if (retval) {
244 msg_printf(project, MSG_INTERNAL_ERROR,
245 "Can't initialize file transfer for %s",
246 fip->name
247 );
248 }
249 retval = pers_file_xfers->insert(fip->pers_file_xfer);
250 if (retval) {
251 msg_printf(project, MSG_INTERNAL_ERROR,
252 "Can't start persistent file transfer for %s",
253 fip->name
254 );
255 }
256 }
257 #endif
258 continue;
259 }
260 if (xp.match_tag("app_version")) {
261 APP_VERSION* avp = new APP_VERSION;
262 retval = avp->parse(xp);
263 if (!project) {
264 msg_printf(NULL, MSG_INTERNAL_ERROR,
265 "Application version outside project in state file"
266 );
267 delete avp;
268 continue;
269 }
270 if (project->anonymous_platform) {
271 delete avp;
272 continue;
273 }
274 if (retval) {
275 msg_printf(NULL, MSG_INTERNAL_ERROR,
276 "Can't parse application version in state file"
277 );
278 delete avp;
279 continue;
280 }
281 if (strlen(avp->platform) == 0) {
282 safe_strcpy(avp->platform, get_primary_platform());
283 } else {
284 if (!is_supported_platform(avp->platform)) {
285 // if it's a platform we haven't heard of,
286 // must be that the user tried out a 64 bit client
287 // and then reverted to a 32-bit client.
288 // Let's not throw away the app version and its WUs
289 //
290 #ifndef SIM
291 msg_printf(project, MSG_INTERNAL_ERROR,
292 "App version has unsupported platform %s; changing to %s",
293 avp->platform, get_primary_platform()
294 );
295 #endif
296 safe_strcpy(avp->platform, get_primary_platform());
297 }
298 }
299 if (avp->missing_coproc) {
300 msg_printf(project, MSG_INFO,
301 "Application uses missing %s GPU",
302 avp->missing_coproc_name
303 );
304 }
305 retval = link_app_version(project, avp);
306 if (retval) {
307 delete avp;
308 continue;
309 }
310 app_versions.push_back(avp);
311 continue;
312 }
313 if (xp.match_tag("workunit")) {
314 WORKUNIT* wup = new WORKUNIT;
315 retval = wup->parse(xp);
316 if (!project) {
317 msg_printf(NULL, MSG_INTERNAL_ERROR,
318 "Workunit outside project in state file"
319 );
320 delete wup;
321 continue;
322 }
323 if (retval) {
324 msg_printf(NULL, MSG_INTERNAL_ERROR,
325 "Can't parse workunit in state file"
326 );
327 delete wup;
328 continue;
329 }
330 retval = link_workunit(project, wup);
331 if (retval) {
332 msg_printf(project, MSG_INTERNAL_ERROR,
333 "Can't handle workunit in state file"
334 );
335 delete wup;
336 continue;
337 }
338 workunits.push_back(wup);
339 continue;
340 }
341 if (xp.match_tag("result")) {
342 RESULT* rp = new RESULT;
343 retval = rp->parse_state(xp);
344 if (!project) {
345 msg_printf(NULL, MSG_INTERNAL_ERROR,
346 "Task %s outside project in state file",
347 rp->name
348 );
349 delete rp;
350 continue;
351 }
352 if (retval) {
353 msg_printf(NULL, MSG_INTERNAL_ERROR,
354 "Can't parse task in state file"
355 );
356 delete rp;
357 continue;
358 }
359 retval = link_result(project, rp);
360 if (retval) {
361 msg_printf(project, MSG_INTERNAL_ERROR,
362 "Can't link task %s in state file",
363 rp->name
364 );
365 delete rp;
366 continue;
367 }
368 // handle transition from old clients which didn't store result.platform;
369 // skip for anon platform
370 if (!project->anonymous_platform) {
371 if (!strlen(rp->platform) || !is_supported_platform(rp->platform)) {
372 safe_strcpy(rp->platform, get_primary_platform());
373 rp->version_num = latest_version(rp->wup->app, rp->platform);
374 }
375 }
376 rp->avp = lookup_app_version(
377 rp->wup->app, rp->platform, rp->version_num, rp->plan_class
378 );
379 if (!rp->avp) {
380 msg_printf(project, MSG_INTERNAL_ERROR,
381 "No application found for task: %s %d %s; discarding",
382 rp->platform, rp->version_num, rp->plan_class
383 );
384 delete rp;
385 continue;
386 }
387 if (rp->avp->missing_coproc) {
388 msg_printf(project, MSG_INFO,
389 "Missing coprocessor for task %s", rp->name
390 );
391 rp->coproc_missing = true;
392 }
393 rp->wup->version_num = rp->version_num;
394 results.push_back(rp);
395 continue;
396 }
397 if (xp.match_tag("project_files")) {
398 if (!project) {
399 msg_printf(NULL, MSG_INTERNAL_ERROR,
400 "Project files outside project in state file"
401 );
402 xp.skip_unexpected();
403 continue;
404 }
405 parse_project_files(xp, project->project_files);
406 project->link_project_files();
407 continue;
408 }
409 if (xp.match_tag("host_info")) {
410 #ifdef SIM
411 retval = host_info.parse(xp, false);
412 coprocs = host_info.coprocs;
413 coprocs.bound_counts();
414 #else
415 retval = host_info.parse(xp, true);
416 #endif
417 if (retval) {
418 msg_printf(NULL, MSG_INTERNAL_ERROR,
419 "Can't parse host info in state file"
420 );
421 }
422 continue;
423 }
424 if (xp.match_tag("time_stats")) {
425 retval = time_stats.parse(xp);
426 if (retval) {
427 msg_printf(NULL, MSG_INTERNAL_ERROR,
428 "Can't parse time stats in state file"
429 );
430 }
431 continue;
432 }
433 if (xp.match_tag("net_stats")) {
434 retval = net_stats.parse(xp);
435 if (retval) {
436 msg_printf(NULL, MSG_INTERNAL_ERROR,
437 "Can't parse network stats in state file"
438 );
439 }
440 continue;
441 }
442 if (xp.match_tag("active_task_set")) {
443 retval = active_tasks.parse(xp);
444 if (retval) {
445 msg_printf(NULL, MSG_INTERNAL_ERROR,
446 "Can't parse active tasks in state file"
447 );
448 }
449 continue;
450 }
451 if (xp.parse_string("platform_name", statefile_platform_name)) {
452 continue;
453 }
454 if (xp.parse_string("alt_platform", stemp)) {
455 continue;
456 }
457 if (xp.parse_int("user_run_request", retval)) {
458 cpu_run_mode.set(retval, 0);
459 continue;
460 }
461 if (xp.parse_int("user_run_prev_request", retval)) {
462 cpu_run_mode.set_prev(retval);
463 continue;
464 }
465 if (xp.parse_int("user_gpu_request", retval)) {
466 gpu_run_mode.set(retval, 0);
467 continue;
468 }
469 if (xp.parse_int("user_gpu_prev_request", retval)) {
470 gpu_run_mode.set_prev(retval);
471 continue;
472 }
473 if (xp.parse_int("user_network_request", retval)) {
474 network_run_mode.set(retval, 0);
475 continue;
476 }
477 if (xp.parse_int("core_client_major_version", old_major_version)) {
478 continue;
479 }
480 if (xp.parse_int("core_client_minor_version", old_minor_version)) {
481 continue;
482 }
483 if (xp.parse_int("core_client_release", old_release)) {
484 continue;
485 }
486 if (xp.parse_str("language", language, sizeof(language))) {
487 continue;
488 }
489 if (xp.match_tag("proxy_info")) {
490 retval = gui_proxy_info.parse(xp);
491 if (retval) {
492 msg_printf(NULL, MSG_INTERNAL_ERROR,
493 "Can't parse proxy info in state file"
494 );
495 }
496 continue;
497 }
498 if (xp.parse_str("host_venue", main_host_venue, sizeof(main_host_venue))) {
499 continue;
500 }
501 if (xp.parse_double("new_version_check_time", new_version_check_time)) {
502 continue;
503 }
504 if (xp.parse_double("all_projects_list_check_time", all_projects_list_check_time)) {
505 continue;
506 }
507 if (xp.parse_string("newer_version", newer_version)) {
508 continue;
509 }
510 #ifdef ENABLE_AUTO_UPDATE
511 if (xp.match_tag("auto_update")) {
512 if (!project) {
513 msg_printf(NULL, MSG_INTERNAL_ERROR,
514 "auto update outside project in state file"
515 );
516 xp.skip_unexpected();
517 continue;
518 }
519 if (!auto_update.parse(xp) && !auto_update.validate_and_link(project)) {
520 auto_update.present = true;
521 }
522 continue;
523 }
524 #endif
525 if (log_flags.unparsed_xml) {
526 msg_printf(0, MSG_INFO,
527 "[unparsed_xml] state_file: unrecognized: %s",
528 xp.parsed_tag
529 );
530 }
531 xp.skip_unexpected();
532 }
533 sort_results();
534 fclose(f);
535
536 // if total resource share is zero, set all shares to 1
537 //
538 if (projects.size()) {
539 unsigned int i;
540 double x=0;
541 for (i=0; i<projects.size(); i++) {
542 x += projects[i]->resource_share;
543 }
544 if (!x) {
545 msg_printf(NULL, MSG_INFO,
546 "All projects have zero resource share; setting to 100"
547 );
548 for (i=0; i<projects.size(); i++) {
549 projects[i]->resource_share = 100;
550 }
551 }
552 }
553 return 0;
554 }
555
556 // this is called whenever new results are added,
557 // namely at startup and after a scheduler RPC.
558 // Sort results based on (arrival time, name),
559 // then set result.index to their position in this order.
560 // This determines the order in which results are run.
561 //
sort_results()562 void CLIENT_STATE::sort_results() {
563 unsigned int i;
564 for (i=0; i<results.size(); i++) {
565 RESULT* rp = results[i];
566 rp->name_md5 = md5_string(string(rp->name));
567 }
568 std::sort(
569 results.begin(),
570 results.end(),
571 arrived_first
572 );
573 for (i=0; i<results.size(); i++) {
574 RESULT* rp = results[i];
575 rp->index = i;
576 }
577 }
578
project_name_compare(PROJECT * p0,PROJECT * p1)579 static inline bool project_name_compare(PROJECT* p0, PROJECT* p1) {
580 return strcasecmp(p0->project_name, p1->project_name) < 0;
581 }
582
sort_projects_by_name()583 void CLIENT_STATE::sort_projects_by_name() {
584 std::sort(
585 projects.begin(),
586 projects.end(),
587 project_name_compare
588 );
589 }
590
591 #ifndef SIM
592
593 // Write the client_state.xml file
594 //
write_state_file()595 int CLIENT_STATE::write_state_file() {
596 MFILE mf;
597 int retval, ret1, ret2, attempt;
598 #ifdef _WIN32
599 char win_error_msg[4096];
600 #endif
601
602 for (attempt=1; attempt<=MAX_STATE_FILE_WRITE_ATTEMPTS; attempt++) {
603 if (attempt > 1) boinc_sleep(1.0);
604
605 if (log_flags.statefile_debug) {
606 msg_printf(0, MSG_INFO,
607 "[statefile] Writing state file"
608 );
609 }
610 #ifdef _WIN32
611 retval = mf.open(STATE_FILE_NEXT, "wc");
612 #else
613 retval = mf.open(STATE_FILE_NEXT, "w");
614 #endif
615 if (retval) {
616 if ((attempt == MAX_STATE_FILE_WRITE_ATTEMPTS) || log_flags.statefile_debug) {
617 msg_printf(0, MSG_INTERNAL_ERROR,
618 "Can't open %s: %s",
619 STATE_FILE_NEXT, boincerror(retval)
620 );
621 }
622 if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
623 return ERR_FOPEN;
624 }
625 MIOFILE miof;
626 miof.init_mfile(&mf);
627 ret1 = write_state(miof);
628 ret2 = mf.close();
629 if (ret1) {
630 if ((attempt == MAX_STATE_FILE_WRITE_ATTEMPTS) || log_flags.statefile_debug) {
631 msg_printf(NULL, MSG_INTERNAL_ERROR,
632 "Couldn't write state file: %s", boincerror(retval)
633 );
634 }
635 if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
636 return ret1;
637 }
638 if (ret2) {
639 if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
640 return ret2;
641 }
642
643 // only attempt to rename the current state file if it exists.
644 //
645 if (boinc_file_exists(STATE_FILE_NAME)) {
646 if (boinc_file_exists(STATE_FILE_PREV)) {
647 retval = boinc_delete_file(STATE_FILE_PREV);
648 if (retval) {
649 if ((attempt == MAX_STATE_FILE_WRITE_ATTEMPTS) || log_flags.statefile_debug) {
650 #ifdef _WIN32
651 msg_printf(0, MSG_INFO,
652 "Can't delete previous state file; %s",
653 windows_format_error_string(GetLastError(), win_error_msg, sizeof(win_error_msg))
654 );
655 #else
656 msg_printf(0, MSG_INFO,
657 "Can't delete previous state file: %s",
658 strerror(errno)
659 );
660 #endif
661 }
662 if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
663 }
664 }
665
666 retval = boinc_rename(STATE_FILE_NAME, STATE_FILE_PREV);
667 if (retval) {
668 if ((attempt == MAX_STATE_FILE_WRITE_ATTEMPTS) || log_flags.statefile_debug) {
669 #ifdef _WIN32
670 msg_printf(0, MSG_INFO,
671 "Can't rename current state file to previous state file; %s",
672 windows_format_error_string(GetLastError(), win_error_msg, sizeof(win_error_msg))
673 );
674 #else
675 msg_printf(0, MSG_INFO,
676 "Can't rename current state file to previous state file: %s",
677 strerror(errno)
678 );
679 #endif
680 }
681 if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
682 }
683 }
684
685 retval = boinc_rename(STATE_FILE_NEXT, STATE_FILE_NAME);
686 if (log_flags.statefile_debug) {
687 msg_printf(0, MSG_INFO,
688 "[statefile] Done writing state file"
689 );
690 }
691 if (!retval) break; // Success!
692
693 if ((attempt == MAX_STATE_FILE_WRITE_ATTEMPTS) || log_flags.statefile_debug) {
694 #ifdef _WIN32
695 msg_printf(0, MSG_INFO,
696 "rename error: %s",
697 windows_format_error_string(GetLastError(), win_error_msg, sizeof(win_error_msg))
698 );
699 #elif defined (__APPLE__)
700 if (log_flags.statefile_debug) {
701 // system() is deprecated in Mac OS 10.10.
702 // Apple says to call posix_spawn instead.
703 callPosixSpawn("ls -al /Library/Application\\ Support/BOINC\\ Data/client*.*");
704 }
705 #endif
706 }
707 if (attempt < MAX_STATE_FILE_WRITE_ATTEMPTS) continue;
708 return ERR_RENAME;
709 }
710 return 0;
711 }
712
write_state(MIOFILE & f)713 int CLIENT_STATE::write_state(MIOFILE& f) {
714 unsigned int i, j;
715 int retval;
716
717 #ifdef SIM
718 fprintf(stderr, "simulator shouldn't write state file\n");
719 exit(1);
720 #endif
721 f.printf("<client_state>\n");
722 retval = host_info.write(f, true, true);
723 if (retval) return retval;
724 retval = time_stats.write(f, false);
725 if (retval) return retval;
726 retval = net_stats.write(f);
727 if (retval) return retval;
728 for (j=0; j<projects.size(); j++) {
729 PROJECT* p = projects[j];
730 retval = p->write_state(f);
731 if (retval) return retval;
732 for (i=0; i<apps.size(); i++) {
733 if (apps[i]->project == p) {
734 retval = apps[i]->write(f);
735 if (retval) return retval;
736 }
737 }
738 for (i=0; i<file_infos.size(); i++) {
739 if (file_infos[i]->project != p) continue;
740 FILE_INFO* fip = file_infos[i];
741 // don't write file infos for anonymous platform app files
742 //
743 if (fip->anonymous_platform_file) continue;
744 retval = fip->write(f, false);
745 if (retval) return retval;
746 }
747 for (i=0; i<app_versions.size(); i++) {
748 if (app_versions[i]->project == p) {
749 app_versions[i]->write(f);
750 }
751 }
752 for (i=0; i<workunits.size(); i++) {
753 if (workunits[i]->project == p) workunits[i]->write(f);
754 }
755 for (i=0; i<results.size(); i++) {
756 if (results[i]->project == p) results[i]->write(f, false);
757 }
758 p->write_project_files(f);
759 #ifdef ENABLE_AUTO_UPDATE
760 if (auto_update.present && auto_update.project==p) {
761 auto_update.write(f);
762 }
763 #endif
764 }
765 active_tasks.write(f);
766 f.printf(
767 "<platform_name>%s</platform_name>\n"
768 "<core_client_major_version>%d</core_client_major_version>\n"
769 "<core_client_minor_version>%d</core_client_minor_version>\n"
770 "<core_client_release>%d</core_client_release>\n"
771 "<user_run_request>%d</user_run_request>\n"
772 "<user_run_prev_request>%d</user_run_prev_request>\n"
773 "<user_gpu_request>%d</user_gpu_request>\n"
774 "<user_gpu_prev_request>%d</user_gpu_prev_request>\n"
775 "<user_network_request>%d</user_network_request>\n"
776 "<new_version_check_time>%f</new_version_check_time>\n"
777 "<all_projects_list_check_time>%f</all_projects_list_check_time>\n",
778 get_primary_platform(),
779 core_client_version.major,
780 core_client_version.minor,
781 core_client_version.release,
782 cpu_run_mode.get_perm(),
783 cpu_run_mode.get_prev(),
784 gpu_run_mode.get_perm(),
785 gpu_run_mode.get_prev(),
786 network_run_mode.get_perm(),
787 new_version_check_time,
788 all_projects_list_check_time
789 );
790 if (strlen(language)) {
791 f.printf("<language>%s</language>\n", language);
792 }
793 if (newer_version.size()) {
794 f.printf("<newer_version>%s</newer_version>\n", newer_version.c_str());
795 }
796 for (i=1; i<platforms.size(); i++) {
797 f.printf("<alt_platform>%s</alt_platform>\n", platforms[i].name.c_str());
798 }
799 if (gui_proxy_info.present) {
800 gui_proxy_info.write(f);
801 }
802 if (strlen(main_host_venue)) {
803 f.printf("<host_venue>%s</host_venue>\n", main_host_venue);
804 }
805 f.printf("</client_state>\n");
806 return 0;
807 }
808
809 // Write the client_state.xml file if necessary
810 // TODO: write no more often than X seconds
811 //
write_state_file_if_needed()812 int CLIENT_STATE::write_state_file_if_needed() {
813 int retval;
814 if (client_state_dirty) {
815 client_state_dirty = false;
816 retval = write_state_file();
817 if (retval) return retval;
818 }
819 return 0;
820 }
821
822 #endif // ifndef SIM
823
824 // look for app_versions.xml file in project dir.
825 // If find, get app versions from there,
826 // and use "anonymous platform" mechanism for this project
827 //
check_anonymous()828 void CLIENT_STATE::check_anonymous() {
829 unsigned int i;
830 char path[MAXPATHLEN];
831 FILE* f;
832 int retval;
833
834 for (i=0; i<projects.size(); i++) {
835 PROJECT* p = projects[i];
836 snprintf(path, sizeof(path), "%s/%s", p->project_dir(), APP_INFO_FILE_NAME);
837 f = fopen(path, "r");
838 if (!f) continue;
839 msg_printf(p, MSG_INFO,
840 "Found %s; using anonymous platform", APP_INFO_FILE_NAME
841 );
842
843 p->anonymous_platform = true;
844 // flag as anonymous even if can't parse file
845 retval = parse_app_info(p, f);
846 if (retval) {
847 msg_printf_notice(p, false,
848 "http://boinc.berkeley.edu/manager_links.php?target=notice&controlid=app_info",
849 "%s",
850 _("Syntax error in app_info.xml")
851 );
852 }
853 fclose(f);
854 }
855 }
856
857 // parse a project's app_info.xml (anonymous platform) file
858 //
parse_app_info(PROJECT * p,FILE * in)859 int CLIENT_STATE::parse_app_info(PROJECT* p, FILE* in) {
860 char buf[256], path[MAXPATHLEN];
861 MIOFILE mf;
862 mf.init_file(in);
863 XML_PARSER xp(&mf);
864
865 while (!xp.get_tag()) {
866 if (xp.match_tag("app_info")) continue;
867 if (xp.match_tag("/app_info")) {
868 notices.remove_notices(p, REMOVE_APP_INFO_MSG);
869 return 0;
870 }
871 if (xp.match_tag("file_info") || xp.match_tag("file")) {
872 FILE_INFO* fip = new FILE_INFO;
873 if (fip->parse(xp)) {
874 delete fip;
875 continue;
876 }
877 if (!fip->download_urls.empty() || !fip->upload_urls.empty()) {
878 msg_printf(p, MSG_INFO,
879 "Can't specify URLs in app_info.xml"
880 );
881 delete fip;
882 continue;
883 }
884 if (link_file_info(p, fip)) {
885 delete fip;
886 continue;
887 }
888 // check that the file is actually there
889 //
890 get_pathname(fip, path, sizeof(path));
891 double size;
892 if (file_size(path, size)) {
893 safe_strcpy(buf,
894 _("File referenced in app_info.xml does not exist: ")
895 );
896 safe_strcat(buf, fip->name);
897 msg_printf(p, MSG_USER_ALERT, "%s", buf);
898 delete fip;
899 continue;
900 }
901 fip->nbytes = size;
902 fip->status = FILE_PRESENT;
903 fip->anonymous_platform_file = true;
904 file_infos.push_back(fip);
905 continue;
906 }
907 if (xp.match_tag("app")) {
908 APP* app = new APP;
909 if (app->parse(xp)) {
910 delete app;
911 continue;
912 }
913 if (lookup_app(p, app->name)) {
914 delete app;
915 continue;
916 }
917 link_app(p, app);
918 apps.push_back(app);
919 continue;
920 }
921 if (xp.match_tag("app_version")) {
922 APP_VERSION* avp = new APP_VERSION;
923 if (avp->parse(xp)) {
924 delete avp;
925 continue;
926 }
927 if (cc_config.dont_use_vbox && strstr(avp->plan_class, "vbox")) {
928 msg_printf(p, MSG_INFO,
929 "skipping vbox app in app_info.xml; vbox disabled in cc_config.xml"
930 );
931 delete avp;
932 continue;
933 }
934 if (strlen(avp->platform) == 0) {
935 safe_strcpy(avp->platform, get_primary_platform());
936 }
937 if (link_app_version(p, avp)) {
938 delete avp;
939 continue;
940 }
941 app_versions.push_back(avp);
942 continue;
943 }
944 if (log_flags.unparsed_xml) {
945 msg_printf(p, MSG_INFO,
946 "Unparsed line in app_info.xml: %s",
947 xp.parsed_tag
948 );
949 }
950 }
951 return ERR_XML_PARSE;
952 }
953
954 #ifndef SIM
955
write_state_gui(MIOFILE & f)956 int CLIENT_STATE::write_state_gui(MIOFILE& f) {
957 unsigned int i, j;
958 int retval;
959
960 f.printf("<client_state>\n");
961
962 retval = host_info.write(f, true, true);
963 if (retval) return retval;
964
965 // the following are for compatibility with old managers
966 //
967 if (coprocs.have_nvidia()) {
968 f.printf("<have_cuda/>\n");
969 }
970 if (coprocs.have_ati()) {
971 f.printf("<have_ati/>\n");
972 }
973
974 #if 1
975 // NOTE: the following is not in CC_STATE.
976 // However, BoincView (which does its own parsing) expects it
977 // to be in the get_state() reply, so leave it in for now
978 //
979 retval = net_stats.write(f);
980 if (retval) return retval;
981 #endif
982
983 retval = time_stats.write(f, true);
984 if (retval) return retval;
985
986 for (j=0; j<projects.size(); j++) {
987 PROJECT* p = projects[j];
988 retval = p->write_state(f, true);
989 if (retval) return retval;
990 for (i=0; i<apps.size(); i++) {
991 if (apps[i]->project == p) {
992 retval = apps[i]->write(f);
993 if (retval) return retval;
994 }
995 }
996 for (i=0; i<app_versions.size(); i++) {
997 if (app_versions[i]->project == p) app_versions[i]->write(f);
998 }
999 for (i=0; i<workunits.size(); i++) {
1000 if (workunits[i]->project == p) workunits[i]->write(f);
1001 }
1002 for (i=0; i<results.size(); i++) {
1003 if (results[i]->project == p) results[i]->write_gui(f);
1004 }
1005 }
1006 f.printf(
1007 "<platform_name>%s</platform_name>\n"
1008 "<core_client_major_version>%d</core_client_major_version>\n"
1009 "<core_client_minor_version>%d</core_client_minor_version>\n"
1010 "<core_client_release>%d</core_client_release>\n"
1011 "<executing_as_daemon>%d</executing_as_daemon>\n",
1012 get_primary_platform(),
1013 core_client_version.major,
1014 core_client_version.minor,
1015 core_client_version.release,
1016 executing_as_daemon?1:0
1017 );
1018 for (i=0; i<platforms.size(); i++) {
1019 f.printf(
1020 "<platform>%s</platform>\n", platforms[i].name.c_str()
1021 );
1022 }
1023
1024 global_prefs.write(f);
1025
1026 // the following used by BoincView - don't remove
1027 //
1028 if (strlen(main_host_venue)) {
1029 f.printf("<host_venue>%s</host_venue>\n", main_host_venue);
1030 }
1031
1032 f.printf("</client_state>\n");
1033 return 0;
1034 }
1035
write_tasks_gui(MIOFILE & f,bool active_only)1036 int CLIENT_STATE::write_tasks_gui(MIOFILE& f, bool active_only) {
1037 unsigned int i;
1038
1039 if (active_only) {
1040 for (i=0; i<active_tasks.active_tasks.size(); i++) {
1041 RESULT* rp = active_tasks.active_tasks[i]->result;
1042 rp->write_gui(f);
1043 }
1044 } else {
1045 for (i=0; i<results.size(); i++) {
1046 RESULT* rp = results[i];
1047 rp->write_gui(f);
1048 }
1049 }
1050 return 0;
1051 }
1052
write_file_transfers_gui(MIOFILE & f)1053 int CLIENT_STATE::write_file_transfers_gui(MIOFILE& f) {
1054 unsigned int i;
1055
1056 f.printf("<file_transfers>\n");
1057 for (i=0; i<file_infos.size(); i++) {
1058 FILE_INFO* fip = file_infos[i];
1059 if (fip->pers_file_xfer) {
1060 fip->write_gui(f);
1061 }
1062 }
1063 f.printf("</file_transfers>\n");
1064
1065 return 0;
1066 }
1067
1068 #endif
1069