1
// This file is part of BOINC.
2
// http://boinc.berkeley.edu
3
// Copyright (C) 2008 University of California
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.
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.
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/>.
18
// Logic related to general (also known as global) preferences:
19
// when to compute, how much disk to use, etc.
23
#include "boinc_win.h"
32
#include <sys/types.h>
40
#include "file_names.h"
41
#include "cpu_benchmark.h"
42
#include "client_msgs.h"
43
#include "client_state.h"
48
#define MAX_PROJ_PREFS_LEN 65536
49
// max length of project-specific prefs
51
// Return the maximum allowed disk usage as determined by user preferences.
52
// There are three different settings in the prefs;
53
// return the least of the three.
55
double CLIENT_STATE::allowed_disk_usage(double boinc_total) {
56
double limit_pct, limit_min_free, limit_abs;
58
limit_pct = host_info.d_total*global_prefs.disk_max_used_pct/100.0;
59
limit_min_free = boinc_total + host_info.d_free - global_prefs.disk_min_free_gb*GIGA;
60
limit_abs = global_prefs.disk_max_used_gb*(GIGA);
62
double size = min(min(limit_abs, limit_pct), limit_min_free);
63
if (size < 0) size = 0;
67
int CLIENT_STATE::project_disk_usage(PROJECT* p, double& size) {
72
get_project_dir(p, buf, sizeof(buf));
75
for (i=0; i<active_tasks.active_tasks.size(); i++) {
76
ACTIVE_TASK* atp = active_tasks.active_tasks[i];
77
if (atp->wup->project != p) continue;
78
get_slot_dir(atp->slot, buf, sizeof(buf));
86
int CLIENT_STATE::total_disk_usage(double& size) {
87
return dir_size(".", size);
90
// See if we should suspend processing
92
int CLIENT_STATE::check_suspend_processing() {
93
if (are_cpu_benchmarks_running()) {
94
return SUSPEND_REASON_BENCHMARKS;
97
if (config.start_delay && now < client_start_time + config.start_delay) {
98
return SUSPEND_REASON_INITIAL_DELAY;
101
switch(run_mode.get_current()) {
102
case RUN_MODE_ALWAYS: break;
104
return SUSPEND_REASON_USER_REQ;
106
if (!global_prefs.run_on_batteries
107
&& host_info.host_is_running_on_batteries()
109
return SUSPEND_REASON_BATTERIES;
112
if (!global_prefs.run_if_user_active && user_active) {
113
return SUSPEND_REASON_USER_ACTIVE;
115
if (global_prefs.cpu_times.suspended()) {
116
return SUSPEND_REASON_TIME_OF_DAY;
120
if (global_prefs.suspend_if_no_recent_input) {
121
bool idle = host_info.users_idle(
122
check_all_logins, global_prefs.suspend_if_no_recent_input
125
return SUSPEND_REASON_NO_RECENT_INPUT;
129
if (global_prefs.cpu_usage_limit != 100) {
130
static double last_time=0, debt=0;
131
double diff = now - last_time;
133
if (diff >= POLL_INTERVAL/2. && diff < POLL_INTERVAL*10.) {
134
debt += diff*global_prefs.cpu_usage_limit/100;
136
return SUSPEND_REASON_CPU_USAGE_LIMIT;
143
if (active_tasks.exclusive_app_running) {
144
return SUSPEND_REASON_EXCLUSIVE_APP_RUNNING;
149
static string reason_string(int reason) {
151
if (reason & SUSPEND_REASON_BATTERIES) {
152
s_reason += " - on batteries";
154
if (reason & SUSPEND_REASON_USER_ACTIVE) {
155
s_reason += " - user is active";
157
if (reason & SUSPEND_REASON_USER_REQ) {
158
s_reason += " - user request";
160
if (reason & SUSPEND_REASON_TIME_OF_DAY) {
161
s_reason += " - time of day";
163
if (reason & SUSPEND_REASON_BENCHMARKS) {
164
s_reason += " - running CPU benchmarks";
166
if (reason & SUSPEND_REASON_DISK_SIZE) {
167
s_reason += " - out of disk space - change global prefs";
169
if (reason & SUSPEND_REASON_NO_RECENT_INPUT) {
170
s_reason += " - no recent user activity";
172
if (reason & SUSPEND_REASON_INITIAL_DELAY) {
173
s_reason += " - initial delay";
175
if (reason & SUSPEND_REASON_EXCLUSIVE_APP_RUNNING) {
176
s_reason += " - an exclusive app is running";
181
void print_suspend_tasks_message(int reason) {
182
string s_reason = "Suspending computation" + reason_string(reason);
183
msg_printf(NULL, MSG_INFO, s_reason.c_str());
186
int CLIENT_STATE::suspend_tasks(int reason) {
187
if (reason == SUSPEND_REASON_CPU_USAGE_LIMIT) {
188
if (log_flags.cpu_sched) {
189
msg_printf(NULL, MSG_INFO, "[cpu_sched] Suspending - CPU throttle");
191
active_tasks.suspend_all(true);
193
print_suspend_tasks_message(reason);
194
active_tasks.suspend_all(false);
199
int CLIENT_STATE::resume_tasks(int reason) {
200
if (reason == SUSPEND_REASON_CPU_USAGE_LIMIT) {
201
if (log_flags.cpu_sched) {
202
msg_printf(NULL, MSG_INFO, "[cpu_sched] Resuming - CPU throttle");
204
active_tasks.unsuspend_all();
206
msg_printf(NULL, MSG_INFO, "Resuming computation");
207
active_tasks.unsuspend_all();
208
request_schedule_cpus("Resuming computation");
213
int CLIENT_STATE::check_suspend_network() {
214
switch(network_mode.get_current()) {
215
case RUN_MODE_ALWAYS: return 0;
217
return SUSPEND_REASON_USER_REQ;
219
if (!global_prefs.run_if_user_active && user_active) {
220
return SUSPEND_REASON_USER_ACTIVE;
222
if (global_prefs.net_times.suspended()) {
223
return SUSPEND_REASON_TIME_OF_DAY;
228
int CLIENT_STATE::suspend_network(int reason) {
230
s_reason = "Suspending network activity" + reason_string(reason);
231
msg_printf(NULL, MSG_INFO, s_reason.c_str());
232
pers_file_xfers->suspend();
236
int CLIENT_STATE::resume_network() {
237
msg_printf(NULL, MSG_INFO, "Resuming network activity");
241
// call this only after parsing global prefs
243
PROJECT* CLIENT_STATE::global_prefs_source_project() {
244
return lookup_project(global_prefs.source_project);
247
void CLIENT_STATE::show_global_prefs_source(bool found_venue) {
248
PROJECT* pp = global_prefs_source_project();
250
msg_printf(NULL, MSG_INFO,
251
"General prefs: from %s (last modified %s)",
252
pp->get_project_name(), time_to_string(global_prefs.mod_time)
255
msg_printf(NULL, MSG_INFO,
256
"General prefs: from %s (last modified %s)",
257
global_prefs.source_project,
258
time_to_string(global_prefs.mod_time)
261
if (strlen(main_host_venue)) {
262
msg_printf(NULL, MSG_INFO, "Computer location: %s", main_host_venue);
264
msg_printf(NULL, MSG_INFO,
265
"General prefs: using separate prefs for %s", main_host_venue
268
msg_printf(NULL, MSG_INFO,
269
"General prefs: no separate prefs for %s; using your defaults",
274
msg_printf(NULL, MSG_INFO, "Host location: none");
275
msg_printf(NULL, MSG_INFO, "General prefs: using your defaults");
279
// parse user's project preferences,
280
// generating FILE_REF and FILE_INFO objects for each <app_file> element.
282
int PROJECT::parse_preferences_for_user_files() {
284
string timestamp, open_name, url, filename;
289
size_t n=0, start, end;
291
start = project_specific_prefs.find("<app_file>", n);
292
if (start == string::npos) break;
293
end = project_specific_prefs.find("</app_file>", n);
294
if (end == string::npos) break;
295
start += strlen("<app_file>");
296
string x = project_specific_prefs.substr(start, end);
297
n = end + strlen("</app_file>");
299
strlcpy(buf, x.c_str(), sizeof(buf));
300
if (!parse_str(buf, "<timestamp>", timestamp)) break;
301
if (!parse_str(buf, "<open_name>", open_name)) break;
302
if (!parse_str(buf, "<url>", url)) break;
304
filename = open_name + "_" + timestamp;
305
fip = gstate.lookup_file_info(this, filename.c_str());
309
fip->urls.push_back(url);
310
strcpy(fip->name, filename.c_str());
311
fip->is_user_file = true;
312
gstate.file_infos.push_back(fip);
316
strcpy(fr.open_name, open_name.c_str());
317
user_files.push_back(fr);
322
// Read global preferences into the global_prefs structure.
323
// 1) read the override file to get venue in case it's there
324
// 2) read global_prefs.xml
325
// 3) read the override file again
329
// - on completion of a scheduler or AMS RPC, if they sent prefs
330
// - in response to read_global_prefs_override GUI RPC
332
void CLIENT_STATE::read_global_prefs() {
338
retval = read_file_string(GLOBAL_PREFS_OVERRIDE_FILE, foo);
340
parse_str(foo.c_str(), "<host_venue>", main_host_venue, sizeof(main_host_venue));
343
retval = global_prefs.parse_file(
344
GLOBAL_PREFS_FILE_NAME, main_host_venue, found_venue
347
if (retval == ERR_FOPEN) {
348
msg_printf(NULL, MSG_INFO,
349
"No general preferences found - using BOINC defaults"
352
msg_printf(NULL, MSG_INFO,
353
"Couldn't parse preferences file - using BOINC defaults"
355
boinc_delete_file(GLOBAL_PREFS_FILE_NAME);
359
// check that the source project's venue matches main_host_venue.
360
// If not, read file again.
361
// This is a fix for cases where main_host_venue is out of synch
363
PROJECT* p = global_prefs_source_project();
364
if (p && strcmp(main_host_venue, p->host_venue)) {
365
strcpy(main_host_venue, p->host_venue);
366
global_prefs.parse_file(GLOBAL_PREFS_FILE_NAME, main_host_venue, found_venue);
368
show_global_prefs_source(found_venue);
371
// read the override file
373
f = fopen(GLOBAL_PREFS_OVERRIDE_FILE, "r");
376
GLOBAL_PREFS_MASK mask;
379
global_prefs.parse_override(xp, "", found_venue, mask);
380
msg_printf(NULL, MSG_INFO, "Reading preferences override file");
384
msg_printf(NULL, MSG_INFO,
385
"Preferences limit memory usage when active to %.2fMB",
386
(host_info.m_nbytes*global_prefs.ram_max_used_busy_frac)/MEGA
388
msg_printf(NULL, MSG_INFO,
389
"Preferences limit memory usage when idle to %.2fMB",
390
(host_info.m_nbytes*global_prefs.ram_max_used_idle_frac)/MEGA
394
msg_printf(NULL, MSG_INFO,
395
"Preferences limit disk usage to %.2fGB",
396
allowed_disk_usage(x)/GIGA
398
// max_cpus, bandwidth limits may have changed
401
if (ncpus != host_info.p_ncpus) {
402
msg_printf(NULL, MSG_INFO,
403
"Preferences limit # CPUs to %d", ncpus
406
file_xfers->set_bandwidth_limits(true);
407
file_xfers->set_bandwidth_limits(false);
408
request_schedule_cpus("Prefs update");
409
request_work_fetch("Prefs update");
412
int CLIENT_STATE::save_global_prefs(
413
char* global_prefs_xml, char* master_url, char* scheduler_url
415
FILE* f = boinc_fopen(GLOBAL_PREFS_FILE_NAME, "w");
416
if (!f) return ERR_FOPEN;
418
"<global_preferences>\n"
421
// tag with the project and scheduler URL,
422
// but only if not already tagged
424
if (!strstr(global_prefs_xml, "<source_project>")) {
426
" <source_project>%s</source_project>\n"
427
" <source_scheduler>%s</source_scheduler>\n",
434
"</global_preferences>\n",
441
// amount of RAM usable now
443
double CLIENT_STATE::available_ram() {
445
return host_info.m_nbytes * global_prefs.ram_max_used_busy_frac;
447
return host_info.m_nbytes * global_prefs.ram_max_used_idle_frac;
451
// max amount that will ever be usable
453
double CLIENT_STATE::max_available_ram() {
454
return host_info.m_nbytes*std::max(
455
global_prefs.ram_max_used_busy_frac, global_prefs.ram_max_used_idle_frac
459
const char *BOINC_RCSID_92ad99cddf = "$Id: cs_prefs.cpp 16388 2008-11-02 20:09:59Z davea $";