1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2012 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 #ifdef _WIN32
18 #include <boinc_win.h>
19 #endif
20 
21 #ifdef _MSC_VER
22 #define snprintf _snprintf
23 #endif
24 
25 #include <string.h>
26 
27 #include "str_replace.h"
28 #include "url.h"
29 
30 #include "client_msgs.h"
31 #include "client_state.h"
32 #include "log_flags.h"
33 #include "result.h"
34 #include "sandbox.h"
35 
36 #include "project.h"
37 
PROJECT()38 PROJECT::PROJECT() {
39     init();
40 }
41 
init()42 void PROJECT::init() {
43     safe_strcpy(master_url, "");
44     safe_strcpy(authenticator, "");
45     safe_strcpy(_project_dir, "");
46     safe_strcpy(_project_dir_absolute, "");
47     project_specific_prefs = "";
48     gui_urls = "";
49     resource_share = 100;
50     desired_disk_usage = 0;
51     for (int i=0; i<MAX_RSC; i++) {
52         no_rsc_pref[i] = false;
53         no_rsc_config[i] = false;
54         no_rsc_apps[i] = false;
55         no_rsc_ams[i] = false;
56     }
57     safe_strcpy(host_venue, "");
58     using_venue_specific_prefs = false;
59     scheduler_urls.clear();
60     safe_strcpy(project_name, "");
61     safe_strcpy(symstore, "");
62     safe_strcpy(user_name, "");
63     safe_strcpy(team_name, "");
64     safe_strcpy(email_hash, "");
65     safe_strcpy(cross_project_id, "");
66     safe_strcpy(external_cpid, "");
67     cpid_time = 0;
68     user_total_credit = 0;
69     user_expavg_credit = 0;
70     user_create_time = 0;
71     ams_resource_share = -1;
72     rpc_seqno = 0;
73     userid = 0;
74     teamid = 0;
75     hostid = 0;
76     host_total_credit = 0;
77     host_expavg_credit = 0;
78     host_create_time = 0;
79     nrpc_failures = 0;
80     master_fetch_failures = 0;
81     min_rpc_time = 0;
82     possibly_backed_off = true;
83     master_url_fetch_pending = false;
84     sched_rpc_pending = 0;
85     next_rpc_time = 0;
86     last_rpc_time = 0;
87     trickle_up_pending = false;
88     anonymous_platform = false;
89     non_cpu_intensive = false;
90     verify_files_on_app_start = false;
91     report_results_immediately = false;
92     pwf.reset(this);
93     send_time_stats_log = 0;
94     send_job_log = 0;
95     send_full_workload = false;
96     dont_use_dcf = false;
97     suspended_via_gui = false;
98     dont_request_more_work = false;
99     detach_when_done = false;
100     attached_via_acct_mgr = false;
101     ended = false;
102     safe_strcpy(code_sign_key, "");
103     user_files.clear();
104     project_files.clear();
105     next_runnable_result = NULL;
106     duration_correction_factor = 1;
107     project_files_downloaded_time = 0;
108     use_symlinks = false;
109     possibly_backed_off = false;
110     nuploading_results = 0;
111     too_many_uploading_results = false;
112     njobs_success = 0;
113     njobs_error = 0;
114     elapsed_time = 0;
115     cpu_ec = 0;
116     cpu_time = 0;
117     gpu_ec = 0;
118     gpu_time = 0;
119     app_configs.clear();
120 
121 #ifdef SIM
122     idle_time = 0;
123     idle_time_sumsq = 0;
124     completed_task_count = 0;
125     completions_ratio_mean = 0.0;
126     completions_ratio_s = 0.0;
127     completions_ratio_stdev = 0.1;  // for the first couple of completions - guess.
128     completions_required_stdevs = 3.0;
129     result_index = 0;
130     ignore = false;
131 #endif
132 }
133 
handle_no_rsc_ams(PROJECT * p,const char * name)134 static void handle_no_rsc_ams(PROJECT* p, const char* name) {
135     int i = rsc_index(name);
136     if (i < 0) return;
137     p->no_rsc_ams[i] = true;
138 }
139 
handle_no_rsc_pref(PROJECT * p,const char * name)140 static void handle_no_rsc_pref(PROJECT* p, const char* name) {
141     int i = rsc_index(name);
142     if (i<0) return;
143     p->no_rsc_pref[i] = true;
144 }
145 
handle_no_rsc_apps(PROJECT * p,const char * name)146 static void handle_no_rsc_apps(PROJECT* p, const char* name) {
147     int i = rsc_index(name);
148     if (i < 0) return;
149     p->no_rsc_apps[i] = true;
150 }
151 
parse_rsc_param(XML_PARSER & xp,const char * end_tag,int & rsc_type,double & value)152 static bool parse_rsc_param(XML_PARSER& xp, const char* end_tag, int& rsc_type, double& value) {
153     char name[256];
154     bool val_found = false;
155 
156     rsc_type = -1;
157     while (!xp.get_tag()) {
158         if (xp.match_tag(end_tag)) {
159             return (rsc_type > 0 && val_found);
160         }
161         if (xp.parse_str("name", name, sizeof(name))) {
162             rsc_type = rsc_index(name);
163             continue;
164         }
165         if (xp.parse_double("rsc_type", value)) {
166             val_found = true;
167         }
168     }
169     return false;
170 }
171 // parse project fields from client_state.xml
172 //
parse_state(XML_PARSER & xp)173 int PROJECT::parse_state(XML_PARSER& xp) {
174     char buf[256];
175     string sched_url, stemp;
176     string str1, str2;
177     int retval, rt;
178     double x;
179     bool btemp;
180 
181     init();
182     while (!xp.get_tag()) {
183         if (xp.match_tag("/project")) {
184             if (cpid_time == 0) {
185                 cpid_time = user_create_time;
186             }
187             if (dont_use_dcf) {
188                 duration_correction_factor = 1;
189             }
190             return 0;
191         }
192         if (xp.parse_string("scheduler_url", sched_url)) {
193             scheduler_urls.push_back(sched_url);
194             continue;
195         }
196         if (xp.parse_str("master_url", master_url, sizeof(master_url))) continue;
197         if (xp.parse_str("project_name", project_name, sizeof(project_name))) continue;
198         if (xp.parse_str("symstore", symstore, sizeof(symstore))) continue;
199         if (xp.parse_str("user_name", user_name, sizeof(user_name))) continue;
200         if (xp.parse_str("team_name", team_name, sizeof(team_name))) continue;
201         if (xp.parse_str("host_venue", host_venue, sizeof(host_venue))) continue;
202         if (xp.parse_str("email_hash", email_hash, sizeof(email_hash))) continue;
203         if (xp.parse_str("cross_project_id", cross_project_id, sizeof(cross_project_id))) continue;
204         if (xp.parse_str("external_cpid", external_cpid, sizeof(external_cpid))) continue;
205         if (xp.parse_double("cpid_time", cpid_time)) continue;
206         if (xp.parse_double("user_total_credit", user_total_credit)) continue;
207         if (xp.parse_double("user_expavg_credit", user_expavg_credit)) continue;
208         if (xp.parse_double("user_create_time", user_create_time)) continue;
209         if (xp.parse_int("rpc_seqno", rpc_seqno)) continue;
210         if (xp.parse_int("userid", userid)) continue;
211         if (xp.parse_int("teamid", teamid)) continue;
212         if (xp.parse_int("hostid", hostid)) continue;
213         if (xp.parse_double("host_total_credit", host_total_credit)) continue;
214         if (xp.parse_double("host_expavg_credit", host_expavg_credit)) continue;
215         if (xp.parse_double("host_create_time", host_create_time)) continue;
216         if (xp.match_tag("code_sign_key")) {
217             retval = copy_element_contents(
218                 xp.f->f,
219                 "</code_sign_key>",
220                 code_sign_key,
221                 sizeof(code_sign_key)
222             );
223             if (retval) return retval;
224             strip_whitespace(code_sign_key);
225             continue;
226         }
227         if (xp.parse_int("nrpc_failures", nrpc_failures)) continue;
228         if (xp.parse_int("master_fetch_failures", master_fetch_failures)) continue;
229         if (xp.parse_double("min_rpc_time", x)) continue;
230         if (xp.parse_bool("master_url_fetch_pending", master_url_fetch_pending)) continue;
231         if (xp.parse_int("sched_rpc_pending", sched_rpc_pending)) continue;
232         if (xp.parse_double("next_rpc_time", next_rpc_time)) continue;
233         if (xp.parse_bool("trickle_up_pending", trickle_up_pending)) continue;
234         if (xp.parse_int("send_time_stats_log", send_time_stats_log)) continue;
235         if (xp.parse_int("send_job_log", send_job_log)) continue;
236         if (xp.parse_bool("send_full_workload", send_full_workload)) continue;
237         if (xp.parse_bool("dont_use_dcf", dont_use_dcf)) continue;
238         if (xp.parse_bool("non_cpu_intensive", non_cpu_intensive)) continue;
239         if (xp.parse_bool("verify_files_on_app_start", verify_files_on_app_start)) continue;
240         if (xp.parse_bool("suspended_via_gui", suspended_via_gui)) continue;
241         if (xp.parse_bool("dont_request_more_work", dont_request_more_work)) continue;
242         if (xp.parse_bool("detach_when_done", detach_when_done)) continue;
243         if (xp.parse_bool("ended", ended)) continue;
244         if (xp.parse_double("rec", pwf.rec)) continue;
245         if (xp.parse_double("rec_time", pwf.rec_time)) continue;
246         if (xp.parse_double("cpu_backoff_interval", rsc_pwf[0].backoff_interval)) continue;
247         if (xp.parse_double("cpu_backoff_time", rsc_pwf[0].backoff_time)) {
248             if (rsc_pwf[0].backoff_time > gstate.now + 28*SECONDS_PER_DAY) {
249                 rsc_pwf[0].backoff_time = gstate.now + 28*SECONDS_PER_DAY;
250             }
251             continue;
252         }
253         if (xp.match_tag("rsc_backoff_interval")) {
254             if (parse_rsc_param(xp, "/rsc_backoff_interval", rt, x)) {
255                 rsc_pwf[rt].backoff_interval = x;
256             }
257             continue;
258         }
259         if (xp.match_tag("rsc_backoff_time")) {
260             if (parse_rsc_param(xp, "/rsc_backoff_time", rt, x)) {
261                 rsc_pwf[rt].backoff_time = x;
262             }
263             continue;
264         }
265         if (xp.parse_double("resource_share", resource_share)) continue;
266             // not authoritative
267         if (xp.parse_double("duration_correction_factor", duration_correction_factor)) continue;
268         if (xp.parse_bool("attached_via_acct_mgr", attached_via_acct_mgr)) continue;
269         if (xp.parse_bool("no_cpu_apps", btemp)) {
270             if (btemp) handle_no_rsc_apps(this, "CPU");
271             continue;
272         }
273 
274         // deprecated
275         if (xp.parse_bool("no_cuda_apps", btemp)) {
276             if (btemp) handle_no_rsc_apps(this, GPU_TYPE_NVIDIA);
277             continue;
278         }
279         if (xp.parse_bool("no_ati_apps", btemp)) {
280             if (btemp) handle_no_rsc_apps(this, GPU_TYPE_ATI);
281             continue;
282         }
283 
284         if (xp.parse_str("no_rsc_apps", buf, sizeof(buf))) {
285             handle_no_rsc_apps(this, buf);
286             continue;
287         }
288         if (xp.parse_bool("no_cpu_ams", btemp)) {
289             if (btemp) handle_no_rsc_ams(this, "CPU");
290             continue;
291         }
292         if (xp.parse_bool("no_cuda_ams", btemp)) {
293             if (btemp) handle_no_rsc_ams(this, GPU_TYPE_NVIDIA);
294             continue;
295         }
296         if (xp.parse_bool("no_ati_ams", btemp)) {
297             if (btemp) handle_no_rsc_ams(this, GPU_TYPE_ATI);
298             continue;
299         }
300         if (xp.parse_bool("no_intel_gpu_ams", btemp)) {
301             if (btemp) handle_no_rsc_ams(this, GPU_TYPE_INTEL);
302             continue;
303         }
304         if (xp.parse_str("no_rsc_ams", buf, sizeof(buf))) {
305             handle_no_rsc_ams(this, buf);
306             continue;
307         }
308         if (xp.parse_str("no_rsc_pref", buf, sizeof(buf))) {
309             handle_no_rsc_pref(this, buf);
310             continue;
311         }
312 
313             // backwards compat - old state files had ams_resource_share = 0
314         if (xp.parse_double("ams_resource_share_new", ams_resource_share)) continue;
315         if (xp.parse_double("ams_resource_share", x)) {
316             if (x > 0) ams_resource_share = x;
317             continue;
318         }
319         if (xp.parse_bool("scheduler_rpc_in_progress", btemp)) continue;
320         if (xp.parse_bool("use_symlinks", use_symlinks)) continue;
321         if (xp.parse_bool("anonymous_platform", btemp)) continue;
322         if (xp.parse_string("trickle_up_url", stemp)) {
323             trickle_up_ops.push_back(new TRICKLE_UP_OP(stemp));
324             continue;
325         }
326         if (xp.parse_double("desired_disk_usage", desired_disk_usage)) continue;
327         if (xp.parse_int("njobs_success", njobs_success)) continue;
328         if (xp.parse_int("njobs_error", njobs_error)) continue;
329         if (xp.parse_double("elapsed_time", elapsed_time)) continue;
330         if (xp.parse_double("last_rpc_time", last_rpc_time)) continue;
331         if (xp.parse_double("cpu_ec", cpu_ec)) continue;
332         if (xp.parse_double("cpu_time", cpu_time)) continue;
333         if (xp.parse_double("gpu_ec", gpu_ec)) continue;
334         if (xp.parse_double("gpu_time", gpu_time)) continue;
335         if (xp.parse_double("disk_usage", disk_usage)) continue;
336         if (xp.parse_double("disk_share", disk_share)) continue;
337 #ifdef SIM
338         if (xp.match_tag("available")) {
339             available.parse(xp, "/available");
340             continue;
341         }
342 #endif
343         if (log_flags.unparsed_xml) {
344             msg_printf(0, MSG_INFO,
345                 "[unparsed_xml] PROJECT::parse_state(): unrecognized: %s",
346                 xp.parsed_tag
347             );
348         }
349         xp.skip_unexpected();
350     }
351     return ERR_XML_PARSE;
352 }
353 
354 // Write project information to client state file or GUI RPC reply
355 //
write_state(MIOFILE & out,bool gui_rpc)356 int PROJECT::write_state(MIOFILE& out, bool gui_rpc) {
357     unsigned int i;
358     char un[2048], tn[2048];
359 
360     out.printf(
361         "<project>\n"
362     );
363 
364     xml_escape(user_name, un, sizeof(un));
365     xml_escape(team_name, tn, sizeof(tn));
366     out.printf(
367         "    <master_url>%s</master_url>\n"
368         "    <project_name>%s</project_name>\n"
369         "    <symstore>%s</symstore>\n"
370         "    <user_name>%s</user_name>\n"
371         "    <team_name>%s</team_name>\n"
372         "    <host_venue>%s</host_venue>\n"
373         "    <email_hash>%s</email_hash>\n"
374         "    <cross_project_id>%s</cross_project_id>\n"
375         "    <external_cpid>%s</external_cpid>\n"
376         "    <cpid_time>%f</cpid_time>\n"
377         "    <user_total_credit>%f</user_total_credit>\n"
378         "    <user_expavg_credit>%f</user_expavg_credit>\n"
379         "    <user_create_time>%f</user_create_time>\n"
380         "    <rpc_seqno>%d</rpc_seqno>\n"
381         "    <userid>%d</userid>\n"
382         "    <teamid>%d</teamid>\n"
383         "    <hostid>%d</hostid>\n"
384         "    <host_total_credit>%f</host_total_credit>\n"
385         "    <host_expavg_credit>%f</host_expavg_credit>\n"
386         "    <host_create_time>%f</host_create_time>\n"
387         "    <nrpc_failures>%d</nrpc_failures>\n"
388         "    <master_fetch_failures>%d</master_fetch_failures>\n"
389         "    <min_rpc_time>%f</min_rpc_time>\n"
390         "    <next_rpc_time>%f</next_rpc_time>\n"
391         "    <rec>%f</rec>\n"
392         "    <rec_time>%f</rec_time>\n"
393         "    <resource_share>%f</resource_share>\n"
394         "    <desired_disk_usage>%f</desired_disk_usage>\n"
395         "    <duration_correction_factor>%f</duration_correction_factor>\n"
396         "    <sched_rpc_pending>%d</sched_rpc_pending>\n"
397         "    <send_time_stats_log>%d</send_time_stats_log>\n"
398         "    <send_job_log>%d</send_job_log>\n"
399         "    <njobs_success>%d</njobs_success>\n"
400         "    <njobs_error>%d</njobs_error>\n"
401         "    <elapsed_time>%f</elapsed_time>\n"
402         "    <last_rpc_time>%f</last_rpc_time>\n"
403         "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
404         master_url,
405         project_name,
406         symstore,
407         un,
408         tn,
409         host_venue,
410         email_hash,
411         cross_project_id,
412         external_cpid,
413         cpid_time,
414         user_total_credit,
415         user_expavg_credit,
416         user_create_time,
417         rpc_seqno,
418         userid,
419         teamid,
420         hostid,
421         host_total_credit,
422         host_expavg_credit,
423         host_create_time,
424         nrpc_failures,
425         master_fetch_failures,
426         min_rpc_time,
427         next_rpc_time,
428         pwf.rec,
429         pwf.rec_time,
430         resource_share,
431         desired_disk_usage,
432         duration_correction_factor,
433         sched_rpc_pending,
434         send_time_stats_log,
435         send_job_log,
436         njobs_success,
437         njobs_error,
438         elapsed_time,
439         last_rpc_time,
440         anonymous_platform?"    <anonymous_platform/>\n":"",
441         master_url_fetch_pending?"    <master_url_fetch_pending/>\n":"",
442         trickle_up_pending?"    <trickle_up_pending/>\n":"",
443         send_full_workload?"    <send_full_workload/>\n":"",
444         dont_use_dcf?"    <dont_use_dcf/>\n":"",
445         non_cpu_intensive?"    <non_cpu_intensive/>\n":"",
446         verify_files_on_app_start?"    <verify_files_on_app_start/>\n":"",
447         suspended_via_gui?"    <suspended_via_gui/>\n":"",
448         dont_request_more_work?"    <dont_request_more_work/>\n":"",
449         detach_when_done?"    <detach_when_done/>\n":"",
450         ended?"    <ended/>\n":"",
451         attached_via_acct_mgr?"    <attached_via_acct_mgr/>\n":"",
452         (this == gstate.scheduler_op->cur_proj)?"   <scheduler_rpc_in_progress/>\n":"",
453         use_symlinks?"    <use_symlinks/>\n":""
454     );
455     for (int j=0; j<coprocs.n_rsc; j++) {
456         out.printf(
457             "    <rsc_backoff_time>\n"
458             "        <name>%s</name>\n"
459             "        <value>%f</value>\n"
460             "    </rsc_backoff_time>\n"
461             "    <rsc_backoff_interval>\n"
462             "        <name>%s</name>\n"
463             "        <value>%f</value>\n"
464             "    </rsc_backoff_interval>\n",
465             rsc_name(j), rsc_pwf[j].backoff_time,
466             rsc_name(j), rsc_pwf[j].backoff_interval
467         );
468         if (no_rsc_ams[j]) {
469             out.printf("    <no_rsc_ams>%s</no_rsc_ams>\n", rsc_name(j));
470         }
471         if (no_rsc_apps[j]) {
472             out.printf("    <no_rsc_apps>%s</no_rsc_apps>\n", rsc_name(j));
473         }
474         if (no_rsc_pref[j]) {
475             out.printf("    <no_rsc_pref>%s</no_rsc_pref>\n", rsc_name(j));
476         }
477         if (j>0 && gui_rpc && (rsc_pwf[j].ncoprocs_excluded == rsc_work_fetch[j].ninstances)) {
478             out.printf("    <no_rsc_config>%s</no_rsc_config>\n", rsc_name(j));
479         }
480     }
481     if (ams_resource_share >= 0) {
482         out.printf("    <ams_resource_share_new>%f</ams_resource_share_new>\n",
483             ams_resource_share
484         );
485     }
486     if (gui_rpc) {
487         out.printf(
488             "%s"
489             "    <sched_priority>%f</sched_priority>\n"
490             "    <project_files_downloaded_time>%f</project_files_downloaded_time>\n",
491             gui_urls.c_str(),
492             sched_priority,
493             project_files_downloaded_time
494         );
495         if (download_backoff.next_xfer_time > gstate.now) {
496             out.printf(
497                 "    <download_backoff>%f</download_backoff>\n",
498                 download_backoff.next_xfer_time - gstate.now
499             );
500         }
501         if (upload_backoff.next_xfer_time > gstate.now) {
502             out.printf(
503                 "    <upload_backoff>%f</upload_backoff>\n",
504                 upload_backoff.next_xfer_time - gstate.now
505             );
506         }
507         if (strlen(host_venue)) {
508             out.printf("    <venue>%s</venue>\n", host_venue);
509         }
510         out.printf("    <project_dir>%s</project_dir>\n", project_dir_absolute());
511     } else {
512         for (i=0; i<scheduler_urls.size(); i++) {
513             out.printf(
514                 "    <scheduler_url>%s</scheduler_url>\n",
515                 scheduler_urls[i].c_str()
516             );
517         }
518         if (strlen(code_sign_key)) {
519             out.printf(
520                 "    <code_sign_key>\n%s\n</code_sign_key>\n", code_sign_key
521             );
522         }
523         for (i=0; i<trickle_up_ops.size(); i++) {
524             TRICKLE_UP_OP* t = trickle_up_ops[i];
525             out.printf(
526                 "    <trickle_up_url>%s</trickle_up_url>\n",
527                 t->url.c_str()
528             );
529         }
530         out.printf(
531             "    <cpu_ec>%f</cpu_ec>\n"
532             "    <cpu_time>%f</cpu_time>\n"
533             "    <gpu_ec>%f</gpu_ec>\n"
534             "    <gpu_time>%f</gpu_time>\n"
535             "    <disk_usage>%f</disk_usage>\n"
536             "    <disk_share>%f</disk_share>\n",
537             cpu_ec, cpu_time, gpu_ec, gpu_time, disk_usage, disk_share
538         );
539     }
540     out.printf(
541         "</project>\n"
542     );
543     return 0;
544 }
545 
546 // Some project data is stored in account file, other in client_state.xml
547 // Copy fields that are stored in client_state.xml from "p" into "this"
548 //
copy_state_fields(PROJECT & p)549 void PROJECT::copy_state_fields(PROJECT& p) {
550     scheduler_urls = p.scheduler_urls;
551     safe_strcpy(project_name, p.project_name);
552     safe_strcpy(user_name, p.user_name);
553     safe_strcpy(team_name, p.team_name);
554     safe_strcpy(host_venue, p.host_venue);
555     safe_strcpy(email_hash, p.email_hash);
556     safe_strcpy(cross_project_id, p.cross_project_id);
557     safe_strcpy(external_cpid, p.external_cpid);
558     user_total_credit = p.user_total_credit;
559     user_expavg_credit = p.user_expavg_credit;
560     user_create_time = p.user_create_time;
561     cpid_time = p.cpid_time;
562     rpc_seqno = p.rpc_seqno;
563     userid = p.userid;
564     teamid = p.teamid;
565     hostid = p.hostid;
566     host_total_credit = p.host_total_credit;
567     host_expavg_credit = p.host_expavg_credit;
568     host_create_time = p.host_create_time;
569     nrpc_failures = p.nrpc_failures;
570     master_fetch_failures = p.master_fetch_failures;
571     min_rpc_time = p.min_rpc_time;
572     next_rpc_time = p.next_rpc_time;
573     master_url_fetch_pending = p.master_url_fetch_pending;
574     sched_rpc_pending = p.sched_rpc_pending;
575     trickle_up_pending = p.trickle_up_pending;
576     safe_strcpy(code_sign_key, p.code_sign_key);
577     for (int i=0; i<MAX_RSC; i++) {
578         rsc_pwf[i] = p.rsc_pwf[i];
579         no_rsc_pref[i] = p.no_rsc_pref[i];
580         no_rsc_apps[i] = p.no_rsc_apps[i];
581         no_rsc_ams[i] = p.no_rsc_ams[i];
582     }
583     pwf = p.pwf;
584     send_full_workload = p.send_full_workload;
585     dont_use_dcf = p.dont_use_dcf;
586     send_time_stats_log = p.send_time_stats_log;
587     send_job_log = p.send_job_log;
588     non_cpu_intensive = p.non_cpu_intensive;
589     verify_files_on_app_start = p.verify_files_on_app_start;
590     suspended_via_gui = p.suspended_via_gui;
591     dont_request_more_work = p.dont_request_more_work;
592     detach_when_done = p.detach_when_done;
593     attached_via_acct_mgr = p.attached_via_acct_mgr;
594     ended = p.ended;
595     duration_correction_factor = p.duration_correction_factor;
596     ams_resource_share = p.ams_resource_share;
597     if (ams_resource_share >= 0) {
598         resource_share = ams_resource_share;
599     }
600     desired_disk_usage = p.desired_disk_usage;
601     use_symlinks = p.use_symlinks;
602     njobs_success = p.njobs_success;
603     njobs_error = p.njobs_error;
604     elapsed_time = p.elapsed_time;
605     last_rpc_time = p.last_rpc_time;
606     cpu_ec = p.cpu_ec;
607     cpu_time = p.cpu_time;
608     gpu_ec = p.gpu_ec;
609     gpu_time = p.gpu_time;
610 }
611 
612 // Write project statistic to GUI RPC reply
613 //
write_statistics(MIOFILE & out)614 int PROJECT::write_statistics(MIOFILE& out) {
615     trim_statistics();
616     out.printf(
617         "<project_statistics>\n"
618         "    <master_url>%s</master_url>\n",
619         master_url
620     );
621 
622     for (vector<DAILY_STATS>::iterator i=statistics.begin();
623         i!=statistics.end(); ++i
624     ) {
625         out.printf(
626             "    <daily_statistics>\n"
627             "        <day>%f</day>\n"
628             "        <user_total_credit>%f</user_total_credit>\n"
629             "        <user_expavg_credit>%f</user_expavg_credit>\n"
630             "        <host_total_credit>%f</host_total_credit>\n"
631             "        <host_expavg_credit>%f</host_expavg_credit>\n"
632             "    </daily_statistics>\n",
633             i->day,
634             i->user_total_credit,
635             i->user_expavg_credit,
636             i->host_total_credit,
637             i->host_expavg_credit
638         );
639     }
640     out.printf(
641         "</project_statistics>\n"
642     );
643     return 0;
644 }
645 
suspend()646 void PROJECT::suspend() {
647     suspended_via_gui = true;
648     gstate.request_schedule_cpus("project suspended");
649     gstate.request_work_fetch("project suspended");
650 }
resume()651 void PROJECT::resume() {
652     suspended_via_gui = false;
653     gstate.request_schedule_cpus("project resumed");
654     gstate.request_work_fetch("project resumed");
655 }
656 
abort_not_started()657 void PROJECT::abort_not_started() {
658     for (unsigned int i=0; i<gstate.results.size(); i++) {
659         RESULT* rp = gstate.results[i];
660         if (rp->project != this) continue;
661         if (rp->is_not_started()) {
662             rp->abort_inactive(EXIT_ABORTED_VIA_GUI);
663         }
664     }
665 }
666 
get_task_durs(double & not_started_dur,double & in_progress_dur)667 void PROJECT::get_task_durs(double& not_started_dur, double& in_progress_dur) {
668     not_started_dur = 0;
669     in_progress_dur = 0;
670     for (unsigned int i=0; i<gstate.results.size(); i++) {
671         RESULT* rp = gstate.results[i];
672         if (rp->project != this) continue;
673         double d = rp->estimated_runtime_remaining();
674         d /= gstate.time_stats.availability_frac(rp->avp->gpu_usage.rsc_type);
675         if (rp->is_not_started()) {
676             not_started_dur += d;
677         } else {
678             in_progress_dur += d;
679         }
680     }
681 }
682 
get_scheduler_url(int index,double r)683 const char* PROJECT::get_scheduler_url(int index, double r) {
684     int n = (int) scheduler_urls.size();
685     int ir = (int)(r*n);
686     int i = (index + ir)%n;
687     return scheduler_urls[i].c_str();
688 }
689 
690 // delete current sym links.
691 // This is done when parsing scheduler reply,
692 // to ensure that we get rid of sym links for
693 // project files no longer in use
694 //
delete_project_file_symlinks()695 void PROJECT::delete_project_file_symlinks() {
696     unsigned int i;
697     char path[MAXPATHLEN];
698 
699     for (i=0; i<project_files.size(); i++) {
700         FILE_REF& fref = project_files[i];
701         snprintf(path, sizeof(path), "%s/%s", project_dir(), fref.open_name);
702         delete_project_owned_file(path, false);
703     }
704 }
705 
706 // install pointers from FILE_REFs to FILE_INFOs for project files,
707 // and flag FILE_INFOs as being project files.
708 //
link_project_files()709 void PROJECT::link_project_files() {
710     FILE_INFO* fip;
711     vector<FILE_REF>::iterator fref_iter;
712     fref_iter = project_files.begin();
713     while (fref_iter != project_files.end()) {
714         FILE_REF& fref = *fref_iter;
715         fip = gstate.lookup_file_info(this, fref.file_name);
716         if (!fip) {
717             msg_printf(this, MSG_INTERNAL_ERROR,
718                 "project file refers to non-existent %s", fref.file_name
719             );
720             fref_iter = project_files.erase(fref_iter);
721             continue;
722         }
723         fref.file_info = fip;
724         fip->is_project_file = true;
725         ++fref_iter;
726     }
727 }
728 
create_project_file_symlinks()729 void PROJECT::create_project_file_symlinks() {
730     for (unsigned i=0; i<gstate.file_infos.size(); i++) {
731         FILE_INFO* fip = gstate.file_infos[i];
732         if (fip->project == this && fip->is_project_file && fip->status == FILE_PRESENT) {
733             write_symlink_for_project_file(fip);
734         }
735     }
736 }
737 
write_project_files(MIOFILE & f)738 void PROJECT::write_project_files(MIOFILE& f) {
739     unsigned int i;
740 
741     if (!project_files.size()) return;
742     f.printf("<project_files>\n");
743     for (i=0; i<project_files.size(); i++) {
744         FILE_REF& fref = project_files[i];
745         fref.write(f);
746     }
747     f.printf("</project_files>\n");
748 }
749 
750 // write symlinks for project files.
751 // Note: it's conceivable that one physical file
752 // has several logical names, so try them all
753 //
write_symlink_for_project_file(FILE_INFO * fip)754 int PROJECT::write_symlink_for_project_file(FILE_INFO* fip) {
755     char link_path[MAXPATHLEN], file_path[MAXPATHLEN];
756     unsigned int i;
757 
758     for (i=0; i<project_files.size(); i++) {
759         FILE_REF& fref = project_files[i];
760         if (fref.file_info != fip) continue;
761         snprintf(link_path, sizeof(link_path), "%s/%s", project_dir(), fref.open_name);
762         snprintf(file_path, sizeof(file_path), "%s/%s", project_dir(), fip->name);
763         make_soft_link(this, link_path, file_path);
764     }
765     return 0;
766 }
767 
768 // a project file download just finished.
769 // If it's the last one, update project_files_downloaded_time
770 //
update_project_files_downloaded_time()771 void PROJECT::update_project_files_downloaded_time() {
772     unsigned int i;
773     for (i=0; i<project_files.size(); i++) {
774         FILE_REF& fref = project_files[i];
775         FILE_INFO* fip = fref.file_info;
776         if (fip->status != FILE_PRESENT) continue;
777     }
778     project_files_downloaded_time = gstate.now;
779 }
780 
some_download_stalled()781 bool PROJECT::some_download_stalled() {
782 #ifndef SIM
783     unsigned int i;
784 
785     if (!download_backoff.ok_to_transfer()) return true;
786 
787     for (i=0; i<gstate.pers_file_xfers->pers_file_xfers.size(); i++) {
788         PERS_FILE_XFER* pfx = gstate.pers_file_xfers->pers_file_xfers[i];
789         if (pfx->fip->project != this) continue;
790         if (pfx->is_upload) continue;
791         if (pfx->next_request_time > gstate.now) return true;
792     }
793 #endif
794     return false;
795 }
796 
runnable(int rsc_type)797 bool PROJECT::runnable(int rsc_type) {
798     if (suspended_via_gui) return false;
799     for (unsigned int i=0; i<gstate.results.size(); i++) {
800         RESULT* rp = gstate.results[i];
801         if (rp->project != this) continue;
802         if (rsc_type != RSC_TYPE_ANY) {
803             if (rp->avp->gpu_usage.rsc_type != rsc_type) {
804                 continue;
805             }
806         }
807         if (rp->runnable()) return true;
808     }
809     return false;
810 }
811 
uploading()812 bool PROJECT::uploading() {
813     for (unsigned int i=0; i<gstate.file_xfers->file_xfers.size(); i++) {
814         FILE_XFER& fx = *gstate.file_xfers->file_xfers[i];
815         if (fx.fip->project == this && fx.is_upload) {
816             return true;
817         }
818     }
819     return false;
820 }
821 
downloading()822 bool PROJECT::downloading() {
823     if (suspended_via_gui) return false;
824     for (unsigned int i=0; i<gstate.results.size(); i++) {
825         RESULT* rp = gstate.results[i];
826         if (rp->project != this) continue;
827         if (rp->downloading()) return true;
828     }
829     return false;
830 }
831 
has_results()832 bool PROJECT::has_results() {
833     for (unsigned i=0; i<gstate.results.size(); i++) {
834         RESULT *rp = gstate.results[i];
835         if (rp->project == this) return true;
836     }
837     return false;
838 }
839 
some_result_suspended()840 bool PROJECT::some_result_suspended() {
841     unsigned int i;
842     for (i=0; i<gstate.results.size(); i++) {
843         RESULT *rp = gstate.results[i];
844         if (rp->project != this) continue;
845         if (rp->suspended_via_gui) return true;
846     }
847     return false;
848 }
849 
can_request_work()850 bool PROJECT::can_request_work() {
851     if (suspended_via_gui) return false;
852     if (master_url_fetch_pending) return false;
853     if (min_rpc_time > gstate.now) return false;
854     if (dont_request_more_work) return false;
855     if (gstate.in_abort_sequence) return false;
856     return true;
857 }
858 
potentially_runnable()859 bool PROJECT::potentially_runnable() {
860     if (runnable(RSC_TYPE_ANY)) return true;
861     if (can_request_work()) return true;
862     if (downloading()) return true;
863     return false;
864 }
865 
nearly_runnable()866 bool PROJECT::nearly_runnable() {
867     if (runnable(RSC_TYPE_ANY)) return true;
868     if (downloading()) return true;
869     return false;
870 }
871 
set_min_rpc_time(double future_time,const char * reason)872 void PROJECT::set_min_rpc_time(double future_time, const char* reason) {
873     if (future_time <= min_rpc_time) return;
874     min_rpc_time = future_time;
875     possibly_backed_off = true;
876     if (log_flags.sched_op_debug) {
877         msg_printf(this, MSG_INFO,
878             "[sched_op] Deferring communication for %s",
879             timediff_format(min_rpc_time - gstate.now).c_str()
880         );
881         msg_printf(this, MSG_INFO, "[sched_op] Reason: %s\n", reason);
882     }
883 }
884 
885 // Return true if we should not contact the project yet.
886 //
waiting_until_min_rpc_time()887 bool PROJECT::waiting_until_min_rpc_time() {
888     return (min_rpc_time > gstate.now);
889 }
890 
trim_statistics()891 void PROJECT::trim_statistics() {
892     double cutoff = dday() - cc_config.save_stats_days*86400;
893     // delete old stats; fill in the gaps if some days missing
894     //
895     while (!statistics.empty()) {
896         DAILY_STATS& ds = statistics[0];
897         if (ds.day >= cutoff) {
898             break;
899         }
900         if (statistics.size() > 1) {
901             DAILY_STATS& ds2 = statistics[1];
902             if (ds2.day <= cutoff) {
903                 statistics.erase(statistics.begin());
904             } else {
905                 ds.day = cutoff;
906                 break;
907             }
908         } else {
909             ds.day = cutoff;
910             break;
911         }
912     }
913 }
914 
project_dir()915 const char* PROJECT::project_dir() {
916     if (_project_dir[0] == 0) {
917         char buf[1024];
918         escape_project_url(master_url, buf);
919         snprintf(_project_dir, sizeof(_project_dir), "%s/%s", PROJECTS_DIR, buf);
920     }
921     return _project_dir;
922 }
923 
project_dir_absolute()924 const char* PROJECT::project_dir_absolute() {
925     if (_project_dir_absolute[0] == 0) {
926         relative_to_absolute(project_dir(), _project_dir_absolute);
927     }
928     return _project_dir_absolute;
929 }
930 
931 // If no_rsc_apps flags are set for all resource types, something's wrong;
932 // clear them, and fall back to per-resource backoff.
933 // Otherwise we might never contact the project again
934 //
check_no_rsc_apps()935 void PROJECT::check_no_rsc_apps() {
936     for (int i=0; i<coprocs.n_rsc; i++) {
937         if (!no_rsc_apps[i]) return;
938     }
939     msg_printf(this, MSG_INFO,
940         "Warning: no_rsc_apps flag set for all resources.  Clearing flags."
941     );
942     for (int i=0; i<coprocs.n_rsc; i++) {
943         no_rsc_apps[i] = false;
944     }
945 }
946 
947 // set no_rsc_apps[] for anonymous platform project
948 //
check_no_apps()949 void PROJECT::check_no_apps() {
950     for (int i=0; i<coprocs.n_rsc; i++) {
951         no_rsc_apps[i] = true;
952     }
953 
954     for (unsigned int i=0; i<gstate.app_versions.size(); i++) {
955         APP_VERSION* avp = gstate.app_versions[i];
956         if (avp->project != this) continue;
957         no_rsc_apps[avp->gpu_usage.rsc_type] = false;
958     }
959 }
960 
961 // show a notice if we can't possibly get work from this project,
962 // and there's something the user could do about it
963 //
show_no_work_notice()964 void PROJECT::show_no_work_notice() {
965     bool show_ams = false, show_prefs=false, show_config = false;
966     bool user_action_possible = false;
967     for (int i=0; i<coprocs.n_rsc; i++) {
968         if (no_rsc_apps[i]) continue;
969         bool banned_by_user = no_rsc_pref[i] || no_rsc_config[i] || no_rsc_ams[i];
970         if (!banned_by_user) {
971             // work for this resource is possible; return
972             notices.remove_notices(this, REMOVE_NO_WORK_MSG);
973             return;
974         }
975         if (no_rsc_pref[i]) show_prefs = true;
976         if (no_rsc_config[i]) show_config = true;
977         if (no_rsc_ams[i]) show_ams = true;
978         user_action_possible = true;
979     }
980     if (!user_action_possible) {
981         // no work is possible because project has no apps for any resource
982         //
983         notices.remove_notices(this, REMOVE_NO_WORK_MSG);
984         return;
985     }
986 
987     bool first = true;
988     string x;
989     x = NO_WORK_MSG;
990     x += "  ";
991     x += _("To fix this, you can ");
992     if (show_prefs) {
993         first = false;
994         x += _("change Project Preferences on the project's web site");
995     }
996     if (show_config) {
997         if (!first) {
998             x += ", or ";
999         }
1000         x += _("remove GPU exclusions in your cc_config.xml file");
1001         first = false;
1002     }
1003     if (show_ams) {
1004         if (!first) {
1005             x += ", or ";
1006         }
1007         x += _("change your settings at your account manager web site");
1008     }
1009     x += ".";
1010     msg_printf(this, MSG_USER_ALERT, "%s", x.c_str());
1011 }
1012