forked from BOINC/boinc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sched_timezone.cpp
326 lines (274 loc) · 8.85 KB
/
sched_timezone.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// Scheduler code for directing a client to one of several
// download servers based on its time zone
#include "config.h"
#include <sys/param.h>
#include <string>
#include <cstdio>
#include <cstring>
#include "filesys.h"
#include "parse.h"
#include "sched_types.h"
#include "sched_msgs.h"
#include "sched_config.h"
#ifdef _USING_FCGI_
#include "boinc_fcgi.h"
#endif
typedef struct urltag {
int zone;
char name[124];
} URLTYPE;
// these global variables are needed to pass information into the
// compare function below.
//
static int tzone=0;
static int hostid=0;
// Evaluate differences between time-zone. Two time zones that differ
// by almost 24 hours are actually very close on the surface of the
// earth. This function finds the 'shortest way around'
//
static int compare(const void *x, const void *y) {
const URLTYPE *a=(const URLTYPE *)x;
const URLTYPE *b=(const URLTYPE *)y;
char longname[512];
const int twelve_hours = 12*3600;
int diffa = abs(tzone - (a->zone));
int diffb = abs(tzone - (b->zone));
if (diffa > twelve_hours) {
diffa = 2*twelve_hours-diffa;
}
if (diffb > twelve_hours) {
diffb = 2*twelve_hours-diffb;
}
if (diffa < diffb) {
return -1;
}
if (diffa > diffb) {
return +1;
}
// In order to ensure uniform distribution, we hash paths that are
// equidistant from the host's timezone in a way that gives a
// unique ordering for each host but which is effectively random
// between hosts.
//
sprintf(longname, "%s%d", a->name, hostid);
std::string sa = md5_string((const unsigned char *)longname, strlen((const char *)longname));
sprintf(longname, "%s%d", b->name, hostid);
std::string sb = md5_string((const unsigned char *)longname, strlen((const char *)longname));
int xa = strtol(sa.substr(1, 7).c_str(), 0, 16);
int xb = strtol(sb.substr(1, 7).c_str(), 0, 16);
if (xa<xb) {
return -1;
}
if (xa>xb) {
return 1;
}
return 0;
}
static URLTYPE *cached=NULL;
#define BLOCKSIZE 32
URLTYPE* read_download_list() {
int count=0;
int i;
if (cached) return cached;
const char *download_servers = config.project_path("download_servers");
#ifndef _USING_FCGI_
FILE *fp=fopen(download_servers, "r");
#else
FCGI_FILE *fp=FCGI::fopen(download_servers, "r");
#endif
if (!fp) {
log_messages.printf(MSG_CRITICAL,
"File %s not found or unreadable!\n", download_servers
);
return NULL;
}
// read in lines from file
//
while (1) {
// allocate memory in blocks
if ((count % BLOCKSIZE)==0) {
cached=(URLTYPE *)realloc(cached, (count+BLOCKSIZE)*sizeof(URLTYPE));
if (!cached) {
fclose(fp);
return NULL;
}
}
// read timezone offset and URL from file, and store in cache list
//
if (2==fscanf(fp, "%d %s", &(cached[count].zone), cached[count].name)) {
count++;
} else {
// provide a null terminator so we don't need to keep
// another global variable for count.
//
cached[count].name[0]='\0';
break;
}
}
fclose(fp);
if (!count) {
log_messages.printf(MSG_CRITICAL,
"File %s contained no valid entries!\n"
"Format of this file is one or more lines containing:\n"
"TIMEZONE_OFFSET_IN_SEC http://some.url.path\n",
download_servers
);
free(cached);
return NULL;
}
// sort URLs by distance from host timezone. See compare() above
// for details.
qsort(cached, count, sizeof(URLTYPE), compare);
log_messages.printf(MSG_DEBUG,
"Sorted list of URLs follows [host timezone: UTC%+d]\n",
tzone
);
for (i=0; i<count; i++) {
log_messages.printf(MSG_DEBUG,
"zone=%+06d url=%s\n", cached[i].zone, cached[i].name
);
}
return cached;
}
// return number of bytes written, or <0 to indicate an error
//
int make_download_list(char *buffer, char *path, unsigned int lim, int tz) {
char *start = buffer;
unsigned int l,len = 0;
int i;
// global variable used in the compare() function
tzone=tz;
URLTYPE *serverlist=read_download_list();
if (!serverlist) return -1;
// print list of servers in sorted order.
// Space is to format them nicely
//
for (i=0;
strlen(serverlist[i].name) && (config.max_download_urls_per_file ?(i < config.max_download_urls_per_file) :true);
i++
) {
l = sprintf(start, "%s<url>%s%s</url>", i?"\n ":"", serverlist[i].name, path);
len += l;
if (len >= lim) {
*start = '\0';
return (start-buffer);
}
start += l;
}
return (start-buffer);
}
// returns zero on success, non-zero to indicate an error
//
int add_download_servers(char *old_xml, char *new_xml, int tz) {
char *p, *q, *r;
int total_free = BLOB_SIZE - strlen(old_xml);
p = (r = old_xml);
// search for next URL to do surgery on
while ((q=strstr(p, "<url>"))) {
// p is at current position
// q is at beginning of next "<url>" tag
char *s;
char path[MAXPATHLEN];
int len = q-p;
// copy everything from p to q to new_xml
//
strlcpy(new_xml, p, len);
new_xml += len;
// locate next instance of </url>
//
if (!(r = strstr(q, "</url>"))) {
return 1;
}
r += strlen("</url>");
// r points to the end of the whole "<url>...</url>" tag
// parse out the URL into 'path'
//
if (!parse_str(q, "<url>", path, 1024)) {
return 1;
}
// check if path contains the string specified in config.xml
//
if (!(s = strstr(path,config.replace_download_url_by_timezone))) {
// if it doesn't, just copy the whole tag as it is
strlcpy(new_xml, q, r-q);
new_xml += r-q;
p=r;
} else {
// calculate free space available for URL replaces
int lim = total_free - (len - (p - old_xml));
// find end of the specified replace string,
// i.e. start of the 'path'
s += strlen(config.replace_download_url_by_timezone);
// insert new download list in place of the original single URL
len = make_download_list(new_xml, s, lim, tz);
if (len == 0) {
// if the replacement would exceed the maximum XML length,
// just keep the original URL
len = r-q;
strlcpy(new_xml, q, len);
} else if (len < 0) {
return 1;
}
new_xml += len;
// advance pointer to start looking for next <url> tag.
//
p=r;
}
}
strcpy(new_xml, r);
return 0;
}
// replace the download URL for apps with a list of
// multiple download servers.
//
void process_av_timezone(APP_VERSION* avp, APP_VERSION& av2) {
int retval;
// set these global variables, needed by the compare()
// function so that the download URL list can be sorted by timezone
//
tzone = g_reply->host.timezone;
hostid = g_reply->host.id;
retval = add_download_servers(avp->xml_doc, av2.xml_doc, g_reply->host.timezone);
if (retval) {
log_messages.printf(MSG_CRITICAL,
"add_download_servers(to APP version) failed\n"
);
// restore original WU!
av2 = *avp;
}
}
// replace the download URL for WU files with a list of
// multiple download servers.
//
void process_wu_timezone(
WORKUNIT& wu2, WORKUNIT& wu3
) {
int retval;
tzone = g_reply->host.timezone;
hostid = g_reply->host.id;
retval = add_download_servers(wu2.xml_doc, wu3.xml_doc, g_reply->host.timezone);
if (retval) {
log_messages.printf(MSG_CRITICAL,
"add_download_servers(to WU) failed\n"
);
// restore original WU!
wu3 = wu2;
}
}
const char *BOINC_RCSID_28b6ac7093 = "$Id$";