2005-02-10 20:31:11 +00:00
|
|
|
// Berkeley Open Infrastructure for Network Computing
|
|
|
|
// http://boinc.berkeley.edu
|
|
|
|
// Copyright (C) 2005 University of California
|
|
|
|
//
|
|
|
|
// This 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 2.1 of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This software 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.
|
|
|
|
//
|
|
|
|
// To view the GNU Lesser General Public License visit
|
|
|
|
// http://www.gnu.org/copyleft/lesser.html
|
|
|
|
// or write to the Free Software Foundation, Inc.,
|
|
|
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
|
|
// Scheduler code for directing a client to one of several
|
|
|
|
// download servers based on its time zone
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include "parse.h"
|
|
|
|
|
|
|
|
#include "server_types.h"
|
|
|
|
#include "sched_msgs.h"
|
|
|
|
|
|
|
|
#ifdef _USING_FCGI_
|
|
|
|
#include "fcgi_stdio.h"
|
|
|
|
#else
|
|
|
|
#define FCGI_ToFILE(x) (x)
|
|
|
|
#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) {
|
|
|
|
URLTYPE *a=(URLTYPE *)x;
|
|
|
|
URLTYPE *b=(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() {
|
|
|
|
FILE *fp;
|
|
|
|
int count=0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (cached) return cached;
|
|
|
|
|
|
|
|
if (!(fp=fopen("../download_servers", "r"))) {
|
|
|
|
log_messages.printf(
|
|
|
|
SCHED_MSG_LOG::CRITICAL,
|
|
|
|
"File ../download_servers not found or unreadable!\n"
|
|
|
|
);
|
|
|
|
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) return NULL;
|
|
|
|
}
|
|
|
|
// read timezone offset and URL from file, and store in cache
|
|
|
|
// list
|
|
|
|
if (2==fscanf(FCGI_ToFILE(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(
|
|
|
|
SCHED_MSG_LOG::CRITICAL,
|
|
|
|
"File ../download_servers 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"
|
|
|
|
);
|
|
|
|
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(
|
|
|
|
SCHED_MSG_LOG::DEBUG, "Sorted list of URLs follows [host timezone: UTC%+d]\n",
|
|
|
|
tzone
|
|
|
|
);
|
|
|
|
for (i=0; i<count; i++) {
|
|
|
|
log_messages.printf(
|
|
|
|
SCHED_MSG_LOG::DEBUG, "zone=%+d 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, int timezone) {
|
|
|
|
char *start=buffer;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// global variable used in the compare() function
|
|
|
|
tzone=timezone;
|
|
|
|
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); i++) {
|
|
|
|
start+=sprintf(start, "%s<url>%s/%s</url>", i?"\n ":"", serverlist[i].name, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
// make a second copy in the same order
|
|
|
|
//
|
|
|
|
for (i=0; strlen(serverlist[i].name); i++) {
|
|
|
|
start+=sprintf(start, "%s<url>%s/%s</url>", "\n ", serverlist[i].name, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (start-buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns zero on success, non-zero to indicate an error
|
|
|
|
//
|
|
|
|
int add_download_servers(char *old_xml, char *new_xml, int timezone) {
|
|
|
|
char *p, *q, *r;
|
|
|
|
|
|
|
|
p=r=old_xml;
|
|
|
|
|
|
|
|
// search for next URL to do surgery on
|
|
|
|
while ((q=strstr(p, "<url>"))) {
|
|
|
|
char *s;
|
|
|
|
char path[1024];
|
|
|
|
int len = q-p;
|
|
|
|
|
|
|
|
strncpy(new_xml, p, len);
|
|
|
|
|
|
|
|
new_xml += len;
|
|
|
|
|
|
|
|
// locate next instance of </url>
|
|
|
|
//
|
|
|
|
if (!(r=strstr(q, "</url>"))) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
r += strlen("</url>");
|
|
|
|
|
|
|
|
// parse out the URL
|
|
|
|
//
|
|
|
|
if (!parse_str(q, "<url>", path, 1024)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find start of 'download/'
|
|
|
|
//
|
|
|
|
if (!(s=strstr(path,"download/"))) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// insert new download list in place of the original one
|
|
|
|
//
|
|
|
|
len = make_download_list(new_xml, s, timezone);
|
|
|
|
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(
|
|
|
|
SCHEDULER_REPLY& reply, 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=reply.host.timezone;
|
|
|
|
hostid=reply.host.id;
|
|
|
|
retval = add_download_servers(avp->xml_doc, av2.xml_doc, reply.host.timezone);
|
|
|
|
if (retval) {
|
|
|
|
log_messages.printf(
|
|
|
|
SCHED_MSG_LOG::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(
|
|
|
|
SCHEDULER_REPLY& reply, WORKUNIT& wu2, WORKUNIT& wu3
|
|
|
|
) {
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
tzone=reply.host.timezone;
|
|
|
|
hostid=reply.host.id;
|
|
|
|
|
|
|
|
retval = add_download_servers(wu2.xml_doc, wu3.xml_doc, reply.host.timezone);
|
|
|
|
if (retval) {
|
|
|
|
log_messages.printf(
|
|
|
|
SCHED_MSG_LOG::CRITICAL,
|
|
|
|
"add_download_servers(to WU) failed\n"
|
|
|
|
);
|
|
|
|
// restore original WU!
|
|
|
|
wu3=wu2;
|
|
|
|
}
|
|
|
|
}
|
2005-02-11 04:00:51 +00:00
|
|
|
|
|
|
|
const char *BOINC_RCSID_28b6ac7093 = "$Id$";
|
|
|
|
|