Passed
Push — master ( 06ed2c...4458b6 )
by
unknown
58s queued 19s
created

boinc_set_timeout()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 2
c 1
b 0
f 1
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
// This file is part of BOINC.
3
// http://boinc.berkeley.edu
4
// Copyright (C) 2013 University of California
5
//
6
// BOINC is free software; you can redistribute it and/or modify it
7
// under the terms of the GNU Lesser General Public License
8
// as published by the Free Software Foundation,
9
// either version 3 of the License, or (at your option) any later version.
10
//
11
// BOINC is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
// See the GNU Lesser General Public License for more details.
15
//
16
// You should have received a copy of the GNU Lesser General Public License
17
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
18
19
// This file contains a PHP binding of a web-service interface
20
// for submitting jobs to a BOINC server.
21
//
22
// Functions:
23
//      boinc_abort_batch(): abort a batch
24
//      boinc_estimate_batch(); estimate completion time of a batch
25
//      boinc_get_output_file(): get the URL for an output file
26
//      boinc_get_output_files(): get the URL for zipped batch output
27
//      boinc_query_batch(): get details of a batch
28
//      boinc_query_batches(): get list of batches
29
//      boinc_query_job(): get details of a job
30
//      boinc_retire_batch(): retire a batch; delete output files
31
//      boinc_submit_batch(): submit a batch
32
//
33
//      boinc_set_timeout($x): set RPC timeout to X seconds
34
35
//// Implementation stuff follows
36
37
function req_to_xml($req, $op) {
38
    if (!isset($req->batch_name)) {
39
        $req->batch_name = "batch_".time();
40
    }
41
    $x = "<$op>
42
    <authenticator>$req->authenticator</authenticator>
43
    <batch>
44
    <app_name>$req->app_name</app_name>
45
    <batch_name>$req->batch_name</batch_name>
46
";
47
    if ((isset($req->output_template_filename)) && ($req->output_template_filename)) {
48
        $x .= "    <output_template_filename>$req->output_template_filename</output_template_filename>
49
";
50
    }
51
    if ((isset($req->input_template_filename)) && ($req->input_template_filename)) {
52
        $x .= "    <input_template_filename>$req->input_template_filename</input_template_filename>
53
";
54
    }
55
    if ((isset($req->app_version_num)) && ($req->app_version_num)) {
56
        $x .= "    <app_version_num>$req->app_version_num</app_version_num>
57
";
58
    }
59
    foreach ($req->jobs as $job) {
60
        $x .= "    <job>
61
";
62
        if (!empty($job->name)) {
63
            $x .= "    <name>$job->name</name>
64
";
65
        }
66
        if (!empty($job->rsc_fpops_est)) {
67
            $x .= "    <rsc_fpops_est>$job->rsc_fpops_est</rsc_fpops_est>
68
";
69
        }
70
        if (!empty($job->command_line)) {
71
            $x .= "    <command_line>$job->command_line</command_line>
72
";
73
        }
74
        if (!empty($job->target_team)) {
75
            $x .= "        <target_team>$job->target_team</target_team>
76
";
77
        } elseif (!empty($job->target_user)) {
78
            $x .= "        <target_user>$job->target_user</target_user>
79
";
80
        } elseif (!empty($job->target_host)) {
81
            $x .= "        <target_host>$job->target_host</target_host>
82
";
83
        }
84
        foreach ($job->input_files as $file) {
85
            $x .= "        <input_file>\n";
86
            $x .= "            <mode>$file->mode</mode>\n";
87
            if ($file->mode == "remote") {
88
                $x .= "            <url>$file->url</url>\n";
89
                $x .= "            <nbytes>$file->nbytes</nbytes>\n";
90
                $x .= "            <md5>$file->md5</md5>\n";
91
            } else {
92
                $x .= "            <source>$file->source</source>\n";
93
            }
94
            $x .= "        </input_file>\n";
95
        }
96
        if (!empty($job->input_template)) {
97
            $x .= "    $job->input_template\n";
98
        }
99
        if (!empty($job->output_template)) {
100
            $x .= "    $job->output_template\n";
101
        }
102
        $x .= "    </job>
103
";
104
    }
105
    $x .= "    </batch>
106
</$op>
107
";
108
    return $x;
109
}
110
111
function validate_request($req) {
112
    if (!is_object($req)) return "req is not an object";
113
    if (!array_key_exists('project', $req)) return "missing req->project";
0 ignored issues
show
Bug introduced by
$req of type object is incompatible with the type array expected by parameter $search of array_key_exists(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

113
    if (!array_key_exists('project', /** @scrutinizer ignore-type */ $req)) return "missing req->project";
Loading history...
114
    if (!array_key_exists('authenticator', $req)) return "missing req->authenticator";
115
    if (!array_key_exists('app_name', $req)) return "missing req->app_name";
116
    if (!array_key_exists('jobs', $req)) return "missing req->jobs";
117
    if (!is_array($req->jobs)) return "req->jobs is not an array";
118
    foreach ($req->jobs as $job) {
0 ignored issues
show
Unused Code introduced by
This foreach statement is empty and can be removed.

This check looks for foreach loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
119
        // other checks
120
    }
121
    return null;
122
}
123
124
$rpc_timeout = 0;
125
126
function do_http_op($req, $xml, $op) {
127
    global $rpc_timeout;
128
129
    $ch = curl_init("$req->project/submit_rpc_handler.php");
130
    curl_setopt($ch, CURLOPT_POST, 1);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

130
    curl_setopt(/** @scrutinizer ignore-type */ $ch, CURLOPT_POST, 1);
Loading history...
131
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
132
    if ($rpc_timeout) {
133
        curl_setopt($ch, CURLOPT_TIMEOUT, $rpc_timeout);
134
    }
135
136
    // see if we need to send any files
137
    //
138
    $nfiles = 0;
139
    $post = array();
140
    $post["request"] = $xml;
141
    $cwd = getcwd();
142
    if ($op == "submit_batch") {
143
        foreach ($req->jobs as $job) {
144
            foreach ($job->input_files as $file) {
145
                if ($file->mode == "inline") {
146
                    $path = realpath("$cwd/$file->path");
147
                    $post["file$nfiles"] = $path;
148
                    $nfiles++;
149
                }
150
            }
151
        }
152
    }
153
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
154
    $reply = curl_exec($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

154
    $reply = curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
155
    curl_close($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_close() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

155
    curl_close(/** @scrutinizer ignore-type */ $ch);
Loading history...
156
    if (!$reply) return array(null, "HTTP error");
157
    $r = @simplexml_load_string($reply);
0 ignored issues
show
Bug introduced by
It seems like $reply can also be of type true; however, parameter $data of simplexml_load_string() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

157
    $r = @simplexml_load_string(/** @scrutinizer ignore-type */ $reply);
Loading history...
158
    if (!$r) {
159
        return array(null, "Can't parse reply XML:\n$reply");
160
    }
161
    $e = (string)$r->error_msg;
162
    if ($e) {
163
        return array(null, "BOINC server: $e");
164
    } else {
165
        return array($r, null);
166
    }
167
}
168
169
function do_batch_op($req, $op) {
170
    $retval = validate_request($req);
171
    if ($retval) return array(null, $retval);
172
    $xml = req_to_xml($req, $op);
173
    return do_http_op($req, $xml, $op);
174
}
175
176
function batch_xml_to_object($batch) {
177
    $b = new StdClass;
178
    $b->id = (int)($batch->id);
179
    $b->create_time = (double)($batch->create_time);
180
    $b->est_completion_time = (double)($batch->est_completion_time);
181
    $b->njobs = (int)($batch->njobs);
182
    $b->fraction_done = (double) $batch->fraction_done;
183
    $b->nerror_jobs = (int)($batch->nerror_jobs);
184
    $b->state = (int)($batch->state);
185
    $b->completion_time = (double)($batch->completion_time);
186
    $b->credit_estimate = (double)($batch->credit_estimate);
187
    $b->credit_canonical = (double)($batch->credit_canonical);
188
    $b->name = (string)($batch->name);
189
    $b->app_name = (string)($batch->app_name);
190
    $b->total_cpu_time = (double)($batch->total_cpu_time);
191
    return $b;
192
}
193
194
// if RPC had a fatal error, return the message
195
//
196
function get_error($reply, $outer_tag) {
197
    $name = $reply->getName();
198
    if ($name != $outer_tag) {
199
        return "Bad reply outer tag";
200
    }
201
    foreach ($reply->error as $error) {
202
        if (isset($error->error_num)) {
203
            return (string)$error->error_msg;
204
        }
205
    }
206
    return null;
207
}
208
209
//// API functions follow
210
211
function boinc_set_timeout($x) {
212
    global $rpc_timeout;
213
    $rpc_timeout = $x;
214
}
215
216
function boinc_ping($req) {
217
    $req_xml = "<ping>
218
    <authenticator>$req->authenticator</authenticator>
219
</ping>
220
";
221
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
222
    if ($errmsg) return array(null, $errmsg);
223
    return array($reply, null);
224
}
225
226
function boinc_estimate_batch($req) {
227
    list($reply, $errmsg) = do_batch_op($req, "estimate_batch");
228
    if ($errmsg) return array(0, $errmsg);
229
    if ($x = get_error($reply, "estimate_batch")) {
230
        return array(null, $x);
231
    }
232
    return array((string)$reply->seconds, null);
233
}
234
235
function boinc_submit_batch($req) {
236
    list($reply, $errmsg) = do_batch_op($req, "submit_batch");
237
    if ($errmsg) return array(0, $errmsg);
238
    if ($x = get_error($reply, "submit_batch")) {
239
        return array(null, $x);
240
    }
241
    return array((int)$reply->batch_id, null);
242
}
243
244
function boinc_query_batches($req) {
245
    $req_xml = "<query_batches>
246
    <authenticator>$req->authenticator</authenticator>
247
";
248
    if (!empty($req->get_cpu_time)) {
249
        $req_xml .= "   <get_cpu_time>1</get_cpu_time>\n";
250
    }
251
    $req_xml .= "</query_batches>\n";
252
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
253
    if ($errmsg) return array(null, $errmsg);
254
    if ($x = get_error($reply, "query_batches")) {
255
        return array(null, $x);
256
    }
257
    $batches = array();
258
    foreach ($reply->batch as $batch) {
259
        $b = batch_xml_to_object($batch);
260
        $batches[] = $b;
261
    }
262
    return array($batches, null);
263
}
264
265
function boinc_query_batch($req) {
266
    $req_xml = "<query_batch>
267
    <authenticator>$req->authenticator</authenticator>
268
    <batch_id>$req->batch_id</batch_id>
269
";
270
    if (!empty($req->get_cpu_time)) {
271
        $req_xml .= "   <get_cpu_time>1</get_cpu_time>\n";
272
    }
273
    if (!empty($req->get_job_details)) {
274
        $req_xml .= "   <get_job_details>1</get_job_details>\n";
275
    }
276
    $req_xml .= "</query_batch>\n";
277
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
278
    if ($errmsg) return array(null, $errmsg);
279
    if ($x = get_error($reply, "query_batch")) {
280
        return array(null, $x);
281
    }
282
    $jobs = array();
283
    foreach ($reply->job as $job) {
284
        $j = new StdClass;
285
        $j->id = (int)($job->id);
286
        $j->canonical_instance_id = (int)($job->canonical_instance_id);
287
        $jobs[] = $j;
288
    }
289
    $r = batch_xml_to_object($reply);
290
    $r->jobs = $jobs;
291
    return array($r, null);
292
}
293
294
function boinc_query_job($req) {
295
    $req_xml = "<query_job>
296
    <authenticator>$req->authenticator</authenticator>
297
    <job_id>$req->job_id</job_id>
298
</query_job>
299
";
300
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
301
    if ($errmsg) return array(null, $errmsg);
302
    if ($x = get_error($reply, "query_job")) {
303
        return array(null, $x);
304
    }
305
    $instances = array();
306
    foreach ($reply->instance as $instance) {
307
        $i = new StdClass;
308
        $i->name = (string)($instance->name);
309
        $i->id = (int)($instance->id);
310
        $i->state = (string)($instance->state);
311
        $i->outfiles = array();
312
        foreach ($instance->outfile as $outfile) {
313
            $f = new StdClass;
314
            $f->size = (double)$outfile->size;
315
            $i->outfiles[] = $f;
316
        }
317
        $instances[] = $i;
318
    }
319
    $r = new StdClass;
320
    $r->instances = $instances;
321
    return array($r, null);
322
}
323
324
function boinc_abort_batch($req) {
325
    $req_xml = "<abort_batch>
326
    <authenticator>$req->authenticator</authenticator>
327
    <batch_id>$req->batch_id</batch_id>
328
</abort_batch>
329
";
330
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
331
    if ($errmsg) return $errmsg;
332
    if ($x = get_error($reply, "abort_batch")) {
333
        return array(null, $x);
334
    }
335
    return array(true, null);
336
}
337
338
function boinc_get_output_file($req) {
339
    $auth_str = md5($req->authenticator.$req->instance_name);
340
    $name = $req->instance_name;
341
    $file_num = $req->file_num;
342
    return $req->project."/get_output.php?cmd=result_file&result_name=$name&file_num=$file_num&auth_str=$auth_str";
343
}
344
345
function boinc_get_output_files($req) {
346
    $auth_str = md5($req->authenticator.$req->batch_id);
347
    $batch_id = $req->batch_id;
348
    return $req->project."/get_output.php?cmd=batch_files&batch_id=$batch_id&auth_str=$auth_str";
349
}
350
351
function boinc_retire_batch($req) {
352
    $req_xml = "<retire_batch>
353
    <authenticator>$req->authenticator</authenticator>
354
    <batch_id>$req->batch_id</batch_id>
355
</retire_batch>
356
";
357
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
358
    if ($errmsg) return $errmsg;
359
    if ($x = get_error($reply, "retire_batch")) {
360
        return array(null, $x);
361
    }
362
    return array(true, null);
363
}
364
365
//// example usage follows
366
if (0) {
367
    $req = new StdClass;
368
    $req->project = "http://isaac.ssl.berkeley.edu/test/";
369
    $req->authenticator = trim(file_get_contents("test_auth"));
370
    $req->app_name = "uppercase";
371
    $req->batch_name = "batch_name_12";
372
    $req->app_version_num = 710;
373
    $req->jobs = array();
374
375
376
    $f = new StdClass;
377
    $f->mode = "remote";
378
    $f->url = "http://isaac.ssl.berkeley.edu/validate_logic.txt";
379
    $f->md5 = "eec5a142cea5202c9ab2e4575a8aaaa7";
380
    $f->nbytes = 4250;
381
382
if (0) {
383
    $f = new StdClass;
384
    $f->mode = "local";
385
    $f->source = "foobar";
386
    //$job->input_files[] = $f;
387
}
388
389
    $it = "
390
<input_template>
391
    <file_info>
392
    </file_info>
393
    <workunit>
394
        <file_ref>
395
            <open_name>in</open_name>
396
        </file_ref>
397
        <target_nresults>1</target_nresults>
398
        <min_quorum>1</min_quorum>
399
        <rsc_fpops_est>   60e9  </rsc_fpops_est>
400
        <rsc_fpops_bound> 60e12 </rsc_fpops_bound>
401
        <rsc_disk_bound>2e6</rsc_disk_bound>
402
        <rsc_memory_bound>1e6</rsc_memory_bound>
403
        <delay_bound>3600</delay_bound>
404
        <credit>1</credit>
405
    </workunit>
406
</input_template>
407
";
408
    $ot = "
409
<output_template>
410
    <file_info>
411
        <name><OUTFILE_0/></name>
412
        <generated_locally/>
413
        <upload_when_present/>
414
        <max_nbytes>6000000</max_nbytes>
415
        <url><UPLOAD_URL/></url>
416
    </file_info>
417
    <result>
418
        <file_ref>
419
            <file_name><OUTFILE_0/></file_name>
420
            <open_name>out</open_name>
421
        </file_ref>
422
    </result>
423
</output_template>
424
";
425
    for ($i=0; $i<2; $i++) {
426
        $job = new StdClass;
427
        $job->input_files = array();
428
        $job->input_files[] = $f;
429
        $job->name = $req->batch_name."_$i";
430
        //$job->rsc_fpops_est = $i*1e9;
431
        $job->command_line = "--t $i";
432
        $job->input_template = $it;
433
        $job->output_template = $ot;
434
        $req->jobs[] = $job;
435
    }
436
437
    if (0) {
438
        list($e, $errmsg) = boinc_estimate_batch($req);
439
        if ($errmsg) {
440
            echo "Error from server: $errmsg\n";
441
        } else {
442
            echo "Batch completion estimate: $e seconds\n";
443
        }
444
    } else {
445
        list($id, $errmsg) = boinc_submit_batch($req);
446
        if ($errmsg) {
447
            echo "Error from server: $errmsg\n";
448
        } else {
449
            echo "Batch ID: $id\n";
450
        }
451
    }
452
}
453
454
if (0) {
455
    list($batches, $errmsg) = boinc_query_batches($req);
456
    if ($errmsg) {
457
        echo "Error: $errmsg\n";
458
    } else {
459
        print_r($batches);
460
    }
461
}
462
463
if (0) {
464
    $req->batch_id = 20;
465
    list($jobs, $errmsg) = boinc_query_batch($req);
466
    if ($errmsg) {
467
        echo "Error: $errmsg\n";
468
    } else {
469
        print_r($jobs);
470
    }
471
}
472
?>
473