// 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 "config.h" #include #include #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) { 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 (xaxb) { 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::MSG_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::MSG_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::MSG_DEBUG, "Sorted list of URLs follows [host timezone: UTC%+d]\n", tzone ); for (i=0; i%s/%s", 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%s/%s", "\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 tz) { char *p, *q, *r; p=r=old_xml; // search for next URL to do surgery on while ((q=strstr(p, ""))) { char *s; char path[1024]; int len = q-p; strncpy(new_xml, p, len); new_xml += len; // locate next instance of // if (!(r=strstr(q, ""))) { return 1; } r += strlen(""); // parse out the URL // if (!parse_str(q, "", 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, tz); if (len<0) { return 1; } new_xml += len; // advance pointer to start looking for next 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::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( 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::MSG_CRITICAL, "add_download_servers(to WU) failed\n" ); // restore original WU! wu3=wu2; } } const char *BOINC_RCSID_28b6ac7093 = "$Id$";