user-specified files in app versions

svn path=/trunk/boinc/; revision=1219
This commit is contained in:
David Anderson 2003-05-20 00:03:39 +00:00
parent 92e8b08d70
commit 9844f440f4
26 changed files with 441 additions and 195 deletions

View File

@ -1,3 +1,22 @@
// The contents of this file are subject to the Mozilla Public License
// Version 1.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is the Berkeley Open Infrastructure for Network Computing.
//
// The Initial Developer of the Original Code is the SETI@home project.
// Portions created by the SETI@home project are Copyright (C) 2002
// University of California at Berkeley. All Rights Reserved.
//
// Contributor(s):
//
#include "graphics_data.h"
void GRAPHICS_BUFFER::clear() {

View File

@ -17,7 +17,7 @@
// Contributor(s):
//
// The class GRAPHICS_DOUBLE_BUFFER provides a handy mechanism
// The class GRAPHICS_DOUBLE_BUFFER provides a mechanism
// for synchronizing the generation of graphics information
// (done by the "science thread")
// with the graphics rendering (done by the "GUI thread",

View File

@ -1,3 +1,22 @@
// The contents of this file are subject to the Mozilla Public License
// Version 1.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is the Berkeley Open Infrastructure for Network Computing.
//
// The Initial Developer of the Original Code is the SETI@home project.
// Portions created by the SETI@home project are Copyright (C) 2002
// University of California at Berkeley. All Rights Reserved.
//
// Contributor(s):
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -14,7 +33,7 @@
#endif
#include "gutil.h"
GLfloat mat_diffuse[] = {0.7, 0.5, 1.0, 0.4};
//GLfloat mat_diffuse[] = {0.7, 0.5, 1.0, 0.4};
GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
GLfloat mat_shininess[] = {40.0};
@ -36,7 +55,6 @@ void mode_texture() {
glLoadIdentity();
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
//glTranslatef( 0.0f, 0.0f, -4.0f );
}
void mode_unshaded() {

View File

@ -1,3 +1,26 @@
// The contents of this file are subject to the Mozilla Public License
// Version 1.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is the Berkeley Open Infrastructure for Network Computing.
//
// The Initial Developer of the Original Code is the SETI@home project.
// Portions created by the SETI@home project are Copyright (C) 2002
// University of California at Berkeley. All Rights Reserved.
//
// Contributor(s):
//
// various utility classes for OpenGL programming,
// used in Astropulse and SETI@home
// See also graphics_data.C,h
struct COLOR {
GLfloat r;
GLfloat g;
@ -26,7 +49,8 @@ extern GLfloat text_width(char* text);
extern void draw_text_panel(
GLfloat* _pos, GLfloat* size, GLfloat margin, COLOR color,
GLfloat char_height, GLfloat line_width, GLfloat line_spacing,
char* text);
char* text
);
extern void mode_texture();
extern void mode_shaded(GLfloat*);
@ -59,7 +83,10 @@ public:
void add_tick(float x, float yfrac);
};
// read a portable pixmap file
//
extern int read_ppm(char* name, int& w, int& h, unsigned char** arrayp);
extern void init_texture(char* filename);
extern void draw_texture(float* pos, float* size);
extern void draw_texture(float* pos, float* size);

View File

@ -4212,3 +4212,63 @@ Eric May 16, 2003
net_xfer.C
win/
wingui_mainwindow.cpp
Erik May 19 2003
- added support for user-selected files to be included
as part of an app_version.
If there is an element of the form
<app_file>
<url>X</url>
<open_name>Y</open_name>
</app_file>
in a user's project preferences,
then for every APP_VERSION that is sent to the user,
elements of the form
<file_info>
<name>X'</name>
<url>X</url>
</file_info>
... and in the <app_version> element
<file_ref>
<file_name>X'</file_name>
<open_name>Y</open_name>
</file_ref>
are added to the app version's XML description,
where X' is an escaped version of X.
This can be used (for example) to allow applications to
have user-specified images in their graphics
- Handle it correctly if the "insert" part of a file transfer fails
- The "main program" of an app version is the one labeled <main_program/>
(not necessarily the first one)
- Only files labeled <executable/> are required to be signed
- added copyright notice to some files
- added sgets() for parsing a string that is made up of multiple lines
- write MD5 checksum elements only if checksum is present
- created escape_url_readable
TODO: app_versions are currently treated as immutable by client.
Need to change this.
todo
api/
graphics_data.C,h
util.cpp,h
client/
app.C
client_state.C
client_types.C
cs_files.C
file_names.C
pers_file_xfer.C,h
doc/
api.html
boinc_dev.html
client_app.html
client_app_graphics.html
graphics.html
lib/
parse.C,h
util.C,h
sched/
handle_request.C
server_types.C,h

View File

@ -211,9 +211,10 @@ int ACTIVE_TASK::start(bool first_time) {
// make soft links to the executable(s)
//
for (i=0; i<app_version->app_files.size(); i++) {
fip = app_version->app_files[i].file_info;
FILE_REF fref = app_version->app_files[i];
fip = fref.file_info;
get_pathname(fip, file_path);
if (i == 0) {
if (fref.main_program) {
safe_strcpy(exec_name, fip->name);
safe_strcpy(exec_path, file_path);
}

View File

@ -976,9 +976,11 @@ int CLIENT_STATE::link_app_version(PROJECT* p, APP_VERSION* avp) {
return 1;
}
// any file associated with an app version must be signed
// any executable file associated with an app version must be signed
//
fip->signature_required = true;
if (fip->executable) {
fip->signature_required = true;
}
avp->app_files[i].file_info = fip;
}
return 0;

View File

@ -436,11 +436,16 @@ int FILE_INFO::write(FILE* out, bool to_server) {
fprintf(out,
"<file_info>\n"
" <name>%s</name>\n"
" <md5_cksum>%s</md5_cksum>\n"
" <nbytes>%f</nbytes>\n"
" <max_nbytes>%f</max_nbytes>\n",
name, md5_cksum, nbytes, max_nbytes
name, nbytes, max_nbytes
);
if (strlen(md5_cksum)) {
fprintf(out,
" <md5_cksum>%s</md5_cksum>\n",
md5_cksum
);
}
if (!to_server) {
if (generated_locally) fprintf(out, " <generated_locally/>\n");
fprintf(out, " <status>%d</status>\n", status);

View File

@ -103,7 +103,7 @@ int verify_downloaded_file(char* pathname, FILE_INFO& file_info) {
fprintf(stderr, "error: verify_file2: file not verified\n");
return ERR_RSA_FAILED;
}
} else if (file_info.md5_cksum) {
} else if (strlen(file_info.md5_cksum)) {
retval = md5_file(pathname, cksum, file_info.nbytes);
if (strcmp(cksum, file_info.md5_cksum) || retval) {
fprintf(stderr, "error: verify_file2: MD5 check failed\n");

View File

@ -396,18 +396,13 @@ int CLIENT_STATE::handle_scheduler_reply(
// deal with project preferences (should always be there)
//
if (sr.project_prefs_xml) {
strcpy(project->project_specific_prefs, sr.project_prefs_xml);
retval = project->write_account_file();
if (retval) return retval;
#if 0
char path[256];
get_account_filename(project->master_url, path);
f = fopen(path, "r");
if (!f) return ERR_FOPEN;
project->parse_account(f);
fclose(f);
#endif
if (strcmp(
project->project_specific_prefs, sr.project_prefs_xml
)) {
strcpy(project->project_specific_prefs, sr.project_prefs_xml);
retval = project->write_account_file();
if (retval) return retval;
}
}
// if the scheduler reply includes a code-signing key,

View File

@ -32,38 +32,8 @@
#include "file_names.h"
#include "util.h"
// Escape a URL for the project directory, cutting off the "http://",
// converting '\' '/' and ' ' to '_',
// and converting the non alphanumeric characters to %XY
// where XY is their hexadecimal equivalent
//
void escape_project_url(char *in, char* out) {
int x, y;
char *temp;
char buf[256];
temp = strstr(in,"://");
if (temp) {
in = temp + strlen("://");
}
for (x=0, y=0; in[x]; ++x) {
if (isalnum(in[x]) || in[x]=='.' || in[x]=='-' || in[x]=='_') {
out[y] = in[x];
++y;
} else if (in[x] == '/' || in[x] == '\\' || in[x] == ' ') {
out[y] = '_';
++y;
} else {
out[y] = '%';
++y;
out[y] = 0;
sprintf(buf, "%d", (char)in[x]);
c2x(buf);
strcat(out, buf);
y += 2;
}
}
out[y] = 0;
escape_url_readable(in, out);
}
// Gets the pathname of a file

View File

@ -93,17 +93,21 @@ int PERS_FILE_XFER::start_xfer() {
(is_upload ? "upload" : "download"), fip->get_url(), retval
);
show_message(fip->project, buf, MSG_ERROR);
handle_xfer_failure();
return retval;
// TODO: do we need to do anything here?
}
retval = gstate.file_xfers->insert(file_xfer);
fxp = file_xfer;
if (retval) {
if (log_flags.file_xfer) {
show_message(fip->project, "file_xfer insert failed", MSG_ERROR);
}
sprintf(buf,
"Couldn't start %s for %s: error %d",
(is_upload ? "upload" : "download"), fip->get_url(), retval
);
show_message(fip->project, buf, MSG_ERROR);
fxp->file_xfer_retval = retval;
handle_xfer_failure(time(0));
handle_xfer_failure();
delete fxp;
fxp = NULL;
return retval;
}
@ -209,7 +213,7 @@ bool PERS_FILE_XFER::poll(time_t now) {
} else if (fxp->file_xfer_retval == ERR_UPLOAD_PERMANENT) {
giveup();
} else {
handle_xfer_failure(now);
handle_xfer_failure();
}
// remove fxp from file_xfer_set and deallocate it
//
@ -240,7 +244,8 @@ void PERS_FILE_XFER::giveup() {
// Handle a transfer failure
//
void PERS_FILE_XFER::handle_xfer_failure(time_t cur_time) {
void PERS_FILE_XFER::handle_xfer_failure() {
time_t now = time(0);
// If it was a bad range request, delete the file and start over
//
@ -248,11 +253,11 @@ void PERS_FILE_XFER::handle_xfer_failure(time_t cur_time) {
fip->delete_file();
}
retry_or_backoff(cur_time);
retry_or_backoff();
// See if it's time to give up on the persistent file xfer
//
if ((cur_time - first_request_time) > gstate.file_xfer_giveup_period) {
if ((now - first_request_time) > gstate.file_xfer_giveup_period) {
giveup();
}
}
@ -260,15 +265,15 @@ void PERS_FILE_XFER::handle_xfer_failure(time_t cur_time) {
// Cycle to the next URL, or if we've hit all URLs in this cycle,
// backoff and try again later
//
void PERS_FILE_XFER::retry_or_backoff(time_t cur_time) {
void PERS_FILE_XFER::retry_or_backoff() {
double exp_backoff;
int backoff;
struct tm *newtime;
time_t aclock;
time_t now;
char buf[256];
time(&aclock);
newtime = localtime(&aclock);
now = time(0);
newtime = localtime(&now);
// Cycle to the next URL to try
//
@ -286,7 +291,7 @@ void PERS_FILE_XFER::retry_or_backoff(time_t cur_time) {
// PERS_RETRY_DELAY_MAX
//
backoff = (int)max(PERS_RETRY_DELAY_MIN, min(PERS_RETRY_DELAY_MAX, exp_backoff));
next_request_time = cur_time + backoff;
next_request_time = now + backoff;
}
if (log_flags.file_xfer_debug) {
sprintf(buf,

View File

@ -59,8 +59,8 @@ public:
PERS_FILE_XFER();
int init(FILE_INFO*, bool is_file_upload);
bool poll(time_t now);
void handle_xfer_failure(time_t cur_time);
void retry_or_backoff(time_t cur_time);
void handle_xfer_failure();
void retry_or_backoff();
void giveup();
int write(FILE* fout);
int parse(FILE* fin);

View File

@ -157,101 +157,3 @@ must get the child's CPU time, then call
void boinc_child_done(double total_cpu);
</pre>
before forking the next child process.
<hr>
<h3>Implementation</h3>
<p>
Application are executed in separate "catbox" directories,
allowing them to create and use temporary files without name conflicts.
Input and output files are kept outside the catbox.
The mappings from virtual to physical filenames use
"symbolic link" files in the catbox directory.
The name of such a file is the virtual name,
and the file contains an XML tag with the physical name.
(This scheme is used because of the lack of filesystem links in Windows.)
<p>
Communication between the core client and applications
is done through XML files in the catbox directory.
Several files are used.
<p>
<b>Files created by the core client, read by the app:</b>
(Once, at start of app)
<ul>
<li> Symbolic link files
<li> <b>fd_init.xml</b>:
specifies the mappings of file descriptors (stdin/stdout/stderr)
to physical files.
<li> <b>init_data.xml</b>: this contains the initialization data
returned by <tt>boinc_init()</tt> (see above),
as well as the minimum checkpoint period.
</ul>
<p>
<b>Files created by the API implementation, read by the core client:</b>
<ul>
<li>
<b>fraction_done.xml</b>:
contains the WU fraction done and the current CPU time from start of WU.
Written by the timer routine as needed.
<li>
<b>checkpoint_cpu.xml</b>
CPU time (from start of WU) at last checkpoint.
Written by checkpoint_completed.
</ul>
<p>
The API implementation uses a timer (60Hz);
the real-time clock is not available to applications.
This timer is used for several purposes:
<ul>
<li> To tell the app when to checkpoint;
<li> To regenerate the fraction done file
<li> To refresh graphics
</ul>
<p>
<b>Exit status</b>
The core client does a wait() to get the status.
boinc_finish() ends with an exit(status);
<p>
<b>Accounting of CPU time</b>:
(note: in Unix, a parent can't get the CPU time of a child
until the child exits. So we're forced to measure it in the child.)
The core passes the WU CPU time in init_data.xml.
boinc_checkpoint_done() and boinc_finish() compute the new WU CPU time,
and write it to checkpoint_cpu.xml.
The core deletes this after reading.
If on exit there is no checkpoint_cpu.xml, it means the app
called exit(0) rather than boinc_finish().
In this case the core measures the child CPU itself.
<p>
The core client maintains
<p>
<b>Timing of checkpoints</b>
<p>
The app library maintains time_until_checkpoint,
decremented from the timer handler.
boinc_time_to_checkpoint() returns true if this is zero or less.
boinc_checkpoint_done() resets it.
<p>
<b>Maintaining fraction done and current CPU</b>
<p>
These two quantities are transferred from the app library to
the core client in the file fraction_done.xml.
The parameter <tt>time_until_fraction_done_update</tt>,
passed in the initialization file,
determines how often this file is written.
It is written from the timer handler.
<p>
For multi-program applications, only the active application
must write the file.
The functions boinc_child_start() and boinc_child_done()
tell the app library to stop and start writing the file.
<p>
TO DO: this creates disk traffic.
Either figure out a way of increasing the period for users who don't
want disk access, or don't use disk files.

View File

@ -27,7 +27,9 @@ Core client
<li> <a href=client_data.html>Data structures</a>
<li> <a href=client_logic.html>Main loop logic</a>
<li> <a href=client_debug.html>Debugging</a>
<li> <a href=host_measure.html>Host measurements</a>
<li> <a href=host_measure.html>Host measurement and identification</a>
<li> <a href=client_app.html>Core client/application interaction (basic)</a>
<li> <a href=client_app_graphic.html>Core client/application interaction (graphics)</a>
</ul>
<font size=+1><b>
Scheduling server

View File

@ -7,3 +7,99 @@ Explain startup files
Explain shared memory mechanism in general
<p>
Explain work-related use of shmem
<p>
Application are executed in separate "catbox" directories,
allowing them to create and use temporary files without name conflicts.
Input and output files are kept outside the catbox.
The mappings from virtual to physical filenames use
"symbolic link" files in the catbox directory.
The name of such a file is the virtual name,
and the file contains an XML tag with the physical name.
(This scheme is used because of the lack of filesystem links in Windows.)
<p>
Communication between the core client and applications
is done through XML files in the catbox directory.
Several files are used.
<p>
<b>Files created by the core client, read by the app:</b>
(Once, at start of app)
<ul>
<li> Symbolic link files
<li> <b>fd_init.xml</b>:
specifies the mappings of file descriptors (stdin/stdout/stderr)
to physical files.
<li> <b>init_data.xml</b>: this contains the initialization data
returned by <tt>boinc_init()</tt> (see above),
as well as the minimum checkpoint period.
</ul>
<p>
<b>Files created by the API implementation, read by the core client:</b>
<ul>
<li>
<b>fraction_done.xml</b>:
contains the WU fraction done and the current CPU time from start of WU.
Written by the timer routine as needed.
<li>
<b>checkpoint_cpu.xml</b>
CPU time (from start of WU) at last checkpoint.
Written by checkpoint_completed.
</ul>
<p>
The API implementation uses a timer (60Hz);
the real-time clock is not available to applications.
This timer is used for several purposes:
<ul>
<li> To tell the app when to checkpoint;
<li> To regenerate the fraction done file
<li> To refresh graphics
</ul>
<p>
<b>Exit status</b>
The core client does a wait() to get the status.
boinc_finish() ends with an exit(status);
<p>
<b>Accounting of CPU time</b>:
(note: in Unix, a parent can't get the CPU time of a child
until the child exits. So we're forced to measure it in the child.)
The core passes the WU CPU time in init_data.xml.
boinc_checkpoint_done() and boinc_finish() compute the new WU CPU time,
and write it to checkpoint_cpu.xml.
The core deletes this after reading.
If on exit there is no checkpoint_cpu.xml, it means the app
called exit(0) rather than boinc_finish().
In this case the core measures the child CPU itself.
<p>
The core client maintains
<p>
<b>Timing of checkpoints</b>
<p>
The app library maintains time_until_checkpoint,
decremented from the timer handler.
boinc_time_to_checkpoint() returns true if this is zero or less.
boinc_checkpoint_done() resets it.
<p>
<b>Maintaining fraction done and current CPU</b>
<p>
These two quantities are transferred from the app library to
the core client in the file fraction_done.xml.
The parameter <tt>time_until_fraction_done_update</tt>,
passed in the initialization file,
determines how often this file is written.
It is written from the timer handler.
<p>
For multi-program applications, only the active application
must write the file.
The functions boinc_child_start() and boinc_child_done()
tell the app library to stop and start writing the file.
<p>
TO DO: this creates disk traffic.
Either figure out a way of increasing the period for users who don't
want disk access, or don't use disk files.

View File

@ -3,7 +3,24 @@
TO BE WRITTEN
<p>
Explain graphics startup files
The graphics API uses a file <b>graphics.xml</b>
that is created and occasionally modified by the
core client or screensaver.
This file has the format
<pre>
&lt;graphics_info>
&lt;do_graphics/>
&lt;xsize>500&lt;/xsize>
&lt;ysize>400&lt;/ysize>
&lt;full_screen/>
&lt;/graphics_info>
</pre>
<p>
The graphics API implementation uses a 60 Hz timer.
Every 0.5 sec, it sees if graphics.xml has been modified, and if so parses it.
Every 1/60 sec, it sees if it's time for a new frame,
and if so calls <tt>app_render()</tt>.
<p>
Explain graphics modes of apps
<p>

View File

@ -33,26 +33,3 @@ It can refer to the user name, CPU time etc. obtained from
Applications that don't do graphics must also supply a
dummy <tt>app_render</tt> to link with the API.
<tt>boinc_draw_gl</tt> is called to draw graphics
<hr>
<h3>Implementation</h3>
<p>
The graphics API uses a file <b>graphics.xml</b>
that is created and occasionally modified by the
core client or screensaver.
This file has the format
<pre>
&lt;graphics_info>
&lt;do_graphics/>
&lt;xsize>500&lt;/xsize>
&lt;ysize>400&lt;/ysize>
&lt;full_screen/>
&lt;/graphics_info>
</pre>
<p>
The graphics API implementation uses a 60 Hz timer.
Every 0.5 sec, it sees if graphics.xml has been modified, and if so parses it.
Every 1/60 sec, it sees if it's time for a new frame,
and if so calls <tt>app_render()</tt>.

View File

@ -204,3 +204,18 @@ void extract_venue(char* in, char* venue_name, char* out) {
}
}
}
// copy a line from the given string.
// kinda like fgets() when you're reading from a string
//
char* sgets(char* buf, int len, char*& in) {
char* p;
p = strstr(in, "\n");
if (!p) return NULL;
*p = 0;
safe_strncpy(buf, in, len);
*p = '\n';
in = p+1;
return buf;
}

View File

@ -32,3 +32,4 @@ extern int copy_element_contents(FILE* in, char* end_tag, char* p, int len);
extern int read_file_malloc(char* pathname, char*& str);
extern void replace_element(char* buf, char* start, char* end, char* replacement);
extern void extract_venue(char* in, char* venue_name, char* out);
extern char* sgets(char* buf, int len, char* &in);

View File

@ -291,6 +291,40 @@ void escape_url(char *in, char*out) {
out[y] = 0;
}
// Escape a URL for the project directory, cutting off the "http://",
// converting '\' '/' and ' ' to '_',
// and converting the non alphanumeric characters to %XY
// where XY is their hexadecimal equivalent
//
void escape_url_readable(char *in, char* out) {
int x, y;
char *temp;
char buf[256];
temp = strstr(in,"://");
if (temp) {
in = temp + strlen("://");
}
for (x=0, y=0; in[x]; ++x) {
if (isalnum(in[x]) || in[x]=='.' || in[x]=='-' || in[x]=='_') {
out[y] = in[x];
++y;
} else if (in[x] == '/' || in[x] == '\\' || in[x] == ' ') {
out[y] = '_';
++y;
} else {
out[y] = '%';
++y;
out[y] = 0;
sprintf(buf, "%d", (char)in[x]);
c2x(buf);
strcat(out, buf);
y += 2;
}
}
out[y] = 0;
}
void safe_strncpy(char* dst, char* src, int len) {
strncpy(dst, src, len);
dst[len-1]=0;

View File

@ -29,6 +29,7 @@ extern void c2x(char *what);
extern void strip_whitespace(char *str);
extern void unescape_url(char *url);
extern void escape_url(char *in, char*out);
extern void escape_url_readable(char* in, char* out);
extern void safe_strncpy(char*, char*, int);
#define safe_strcpy(x, y) safe_strncpy(x, y, sizeof(x))
#define safe_strcat(x, y) if (strlen(x)+strlen(y)<sizeof(x)) strcat(x, y)

View File

@ -120,6 +120,68 @@ int insert_wu_tags(WORKUNIT& wu, double seconds, APP& app) {
return insert_after(wu.xml_doc, "<workunit>\n", buf);
}
void parse_project_prefs(char* prefs, vector<APP_FILE>& app_files) {
char buf[256];
APP_FILE af;
while (sgets(buf, 256, prefs)) {
if (match_tag(buf, "<app_file>")) {
af.parse(prefs);
app_files.push_back(af);
}
}
}
// if the user's project prefs include elements of the form
// <app_file>
// <url>X</url>
// <open_name>Y</open_name>
// </app_file>
// then insert appropriate elements in app_version XML doc, namely:
// <file_info>
// <name>X'</name>
// <url>X</url>
// </file_info>
// ... (in the <app_version> element)
// <file_ref>
// <file_name>X'</file_name>
// <open_name>Y</open_name>
// </file_ref>
// where X' is the escaped version of X
//
int insert_app_file_tags(APP_VERSION& av, USER& user) {
vector<APP_FILE> app_files;
APP_FILE af;
unsigned int i;
char buf[256], namebuf[256];
int retval;
parse_project_prefs(user.project_prefs, app_files);
for (i=0; i<app_files.size(); i++) {
af = app_files[i];
escape_url_readable(af.url, namebuf);
sprintf(buf,
"<file_info>\n"
" <name>%s</name>\n"
" <url>%s</url>\n"
"</file_info>\n",
namebuf, af.url
);
retval = insert_after(av.xml_doc, "", buf);
if (retval) return retval;
sprintf(buf,
" <file_ref>\n"
" <file_name>%s</file_name>\n"
" <open_name>%s</open_name>\n"
" </file_ref>\n",
namebuf, af.open_name
);
retval = insert_after(av.xml_doc, "<app_version>\n", buf);
if (retval) return retval;
}
return 0;
}
// add the given workunit to a reply.
// look up its app, and make sure there's a version for this platform.
// Add the app and app_version to the reply also.
@ -153,8 +215,18 @@ int add_wu_to_reply(
// add the app, app_version, and workunit to the reply,
// but only if they aren't already there
//
reply.insert_app_unique(*app);
// If the user's project prefs include any <app_file> tags,
// make appropriate modifications to the app_version XML
//
retval = insert_app_file_tags(*app_version, reply.user);
if (retval) {
write_log("insert_app_file_tags failed\n");
return retval;
}
reply.insert_app_version_unique(*app_version);
// add time estimate to reply

View File

@ -192,6 +192,7 @@ int SCHEDULER_REPLY::write(FILE* fout) {
for (i=0; i<apps.size(); i++) {
apps[i].write(fout);
}
for (i=0; i<app_versions.size(); i++) {
for (j=0; j<apps.size(); j++) {
if (apps[j].id == app_versions[i].appid) {
@ -200,17 +201,21 @@ int SCHEDULER_REPLY::write(FILE* fout) {
}
}
}
for (i=0; i<wus.size(); i++) {
fputs(wus[i].xml_doc, fout);
}
for (i=0; i<results.size(); i++) {
fputs(results[i].xml_doc_in, fout);
}
if (strlen(code_sign_key)) {
fputs("<code_sign_key>\n", fout);
fputs(code_sign_key, fout);
fputs("</code_sign_key>\n", fout);
}
if (strlen(code_sign_key_signature)) {
fputs("<code_sign_key_signature>\n", fout);
fputs(code_sign_key_signature, fout);
@ -360,3 +365,18 @@ int HOST::parse_net_stats(FILE* fin) {
}
return 1;
}
int APP_FILE::parse(char*& in) {
char buf[256], ebuf[256];
while (sgets(buf, 256, in)) {
if (match_tag(buf, "</app_file>")) return 0;
else if (parse_str(buf, "<url>", url, sizeof(url))) continue;
else if (parse_str(buf, "<open_name>", open_name, sizeof(open_name))) continue;
else {
sprintf(ebuf, "APP_FILE::parse(): unrecognized %s\n", buf);
write_log(ebuf);
}
}
return 1;
}

View File

@ -85,4 +85,12 @@ struct SCHEDULER_REPLY {
void insert_result(RESULT&);
};
// represents an <app_file> element in user's project prefs
//
struct APP_FILE {
char url[256];
char open_name[256];
int parse(char*&);
};
#endif

1
todo
View File

@ -9,7 +9,6 @@ BUGS (arranged from high to low priority)
- Win GUI: line between menus and tabs
- "show graphics" should not use right-click
- win GUI: reduce flicker?
- on major version change, should discard any pending WUs
- labels on disk graph are not clear
- document and add to global prefs?
start_saver