5
// Submit a single job.
6
// Implementation notes:
7
// - The jobs use the app "single_job_PLATFORM".
8
// This app has a single app_version containing the wrapper for that platform
9
// - the executable is part of the WU, and has the sticky bit set,
10
// and has a signature
11
// - The logical and physical name of the executable
12
// (as stored in the download directory) is "program_platform_cksum"
13
// where cksum is the last 8 chars of the MD5
14
// - The physical name of the job file is job_WUID.xml
15
// - The physical names of the input/output files are name_WUID
16
// - a file containing the job directory is stored in
17
// sj_WUID in the upload hierarchy
18
// - a workunit template sj_WUID is created in templates/
19
// - the single_job_assimilator copies the output files to the job dir,
20
// and cleans up the sj_WUID and WU template files
22
ini_set('error_reporting', E_ALL);
28
$platform = 'i686-pc-linux-gnu';
30
$infiles_phys = array();
31
// input filename with _WUID appended
37
// the physical name of the program
40
$wu_template_filename = null;
41
// relative to project dir
42
$result_template_filename = null;
43
// relative to project dir
44
$wrapper_job_filename = null;
48
function get_project_dir() {
50
$project_dir = getenv('BOINC_PROJECT_DIR');
52
echo "You must set the environment variable BOINC_PROJECT_DIR
53
to the path of a BOINC project, e.g.:
55
> setenv BOINC_PROJECT_DIR ~/projects/my_project
63
echo "Usage: boinc_job [boinc-options] program [program-options]
68
Run the program on platform p
70
The program will use f as an input file
72
The program will use f as an output file
74
Direct f to the program's stdin
76
Direct the program's stdout to f
83
function error($msg) {
88
function download_path($filename) {
90
return dir_hier_path($filename, "$project_dir/download", 1024);
93
function upload_path($filename) {
95
return dir_hier_path($filename, "$project_dir/upload", 1024);
98
function do_includes() {
100
chdir("$project_dir/html/ops");
101
require_once("../inc/boinc_db.inc");
102
require_once("../inc/dir_hier.inc");
106
function check_app_version() {
107
global $platform, $app_name;
108
$app_name = "single_job_$platform";
109
$app = BoincApp::lookup("name='$app_name'");
111
error("This project isn't configured to run single jobs.");
115
// make the job.xml file used by the wrapper
117
function make_wrapper_job_file() {
118
global $program_phys, $stdin_file, $stdout_file, $cmdline_args, $wuid;
119
global $project_dir, $wrapper_job_filename;
122
$wrapper_job_filename = "sj_$wuid.xml";
123
$path = download_path($wrapper_job_filename);
124
$f = fopen($path, "w");
126
error("Can't open $path");
131
<application>$program_phys</application>
134
fwrite($f, " <stdin_filename>$stdin_file</stdin_filename>\n");
137
fwrite($f, " <stdout_filename>$stdout_file</stdout_filename>\n");
140
fwrite($f, " <command_line>$cmdline_args</command_line>\n");
142
fwrite($f, " </task>\n</job_desc>\n");
146
function make_wu_template() {
147
global $wuid, $infiles, $stdin_file, $program_phys, $wu_template_filename;
151
$wu_template_filename = "templates/sj_wu_template_$wuid";
152
$f = fopen($wu_template_filename, "w");
154
error("Can't open $wu_template_filename");
156
$n = count($infiles);
157
$n++; // for job file
161
for ($i=0; $i<$n; $i++) {
169
// The program file needs to be executable.
170
// Make it sticky too.
180
fwrite($f, "<workunit>\n");
182
foreach($infiles as $infile) {
185
<file_number>$i</file_number>
186
<open_name>$infile</open_name>
195
<file_number>$i</file_number>
196
<open_name>$stdin_file</open_name>
203
<file_number>$i</file_number>
204
<open_name>job.xml</open_name>
210
<file_number>$i</file_number>
211
<open_name>$program_phys</open_name>
215
" <rsc_fpops_bound>1e18</rsc_fpops_bound>
216
<rsc_fpops_est>1e15</rsc_fpops_est>
222
function make_result_template() {
223
global $wuid, $outfiles, $stdout_file, $project_dir;
224
global $result_template_filename;
227
$result_template_filename = "templates/sj_result_template_$wuid";
228
$f = fopen($result_template_filename, "w");
230
error("Can't open $result_template_filename");
233
foreach($outfiles as $outfile) {
236
<name><OUTFILE_$i/></name>
238
<upload_when_present/>
239
<max_nbytes>1e12</max_nbytes>
240
<url><UPLOAD_URL/></url>
248
<name><OUTFILE_$i/></name>
250
<upload_when_present/>
251
<max_nbytes>1e12</max_nbytes>
252
<url><UPLOAD_URL/></url>
257
fwrite($f, "<result>\n");
260
foreach($outfiles as $outfile) {
263
<file_name><OUTFILE_$i/></file_name>
264
<open_name>$outfile</open_name>
274
<file_name><OUTFILE_$i/></file_name>
275
<open_name>$stdout_file</open_name>
279
fwrite($f, "</result>\n");
283
// make the sj_WUID file
285
function make_job_file() {
286
global $wuid, $job_dir, $project_dir;
289
$filename = "sj_$wuid";
290
$path = upload_path($filename);
292
$f = fopen($path, "w");
294
error("Can't open $path");
297
"<job_dir>$job_dir</job_dir>
302
function create_wu() {
304
$name = md5(uniqid(rand(), true));
305
$wuid = BoincWorkunit::insert("(name, transition_time) values ('$name', ".PHP_INT_MAX.")");
308
function create_job() {
309
global $wuid, $app_name, $infiles_phys, $program_phys, $project_dir;
310
global $result_template_filename, $wu_template_filename;
311
global $wrapper_job_filename, $verbose;
314
$cmd = "bin/create_work --min_quorum 1 --target_nresults 1 --appname $app_name --wu_name sj_$wuid --wu_id $wuid --wu_template $wu_template_filename --result_template $result_template_filename";
315
foreach ($infiles_phys as $infile) {
318
$cmd .= " $wrapper_job_filename";
319
$cmd .= " $program_phys";
322
echo "Executing command: $cmd\n";
327
// copy input files and program file to the download hierarchy
329
function copy_files() {
330
global $infiles, $infiles_phys, $wuid, $job_dir, $program, $program_phys;
334
foreach ($infiles as $infile) {
335
$filename = $infile.'_'.$wuid;
336
$infiles_phys[] = $filename;
337
$path = download_path($filename);
339
echo "copying $infile to $path\n";
341
copy($infile, $path);
343
$path = download_path($program_phys);
345
echo "copying $program to $path\n";
347
copy($program, $path);
351
// make sure the program is there, MD5 it, and get physical name
353
function check_program() {
354
global $program, $job_dir, $program_phys, $platform;
357
if (!is_file($program)) {
358
error("Program file $program not found");
360
$m = md5_file($program);
361
$m = substr($m, 0, 8);
362
$program_phys = $program.'_'.$platform.'_'.$m;
365
function parse_args($argc, $argv) {
366
global $platform, $infiles, $outfiles, $stdin_file, $stdout_file;
367
global $program, $cmdline_args, $wuid;
369
for ($i=1; $i<$argc; $i++) {
374
$platform = $argv[++$i];
377
$infiles[] = $argv[++$i];
380
$outfiles[] = $argv[++$i];
383
$stdin_file = $argv[++$i];
386
$stdout_file = $argv[++$i];
396
$cmdline_args .= ''.$argv[$i];
398
$program = $argv[$i];
403
if (!$program) usage();
406
function show_result($result, $i) {
407
switch ($result->server_state) {
409
echo " Instance $i: unsent\n";
412
echo " Instance $i: in progress on host $result->hostid\n";
415
echo " Instance $i: completed on host $result->hostid\n";
420
function show_wu_status($wu) {
421
$now = date("F j, Y, g:i a e");
422
switch ($wu->assimilate_state) {
424
echo "$now: job $wu->id is in progress\n";
425
$results = BoincResult::enum("workunitid=$wu->id");
426
$n = count($results);
429
foreach ($results as $result) {
430
show_result($result, $i);
434
echo " (no instances yet)\n";
438
echo "$now: job $wu->id is being assimilated\n";
441
echo "$now: job $wu->id completed\n";
450
$wu = BoincWorkunit::lookup_id($wuid);
452
echo "Job $wuid is not in the database\n";
462
parse_args($argc, $argv);
466
make_wrapper_job_file();
469
make_result_template();