Issues (1965)

html/user/submit.php (14 issues)

1
<?php
2
3
// This file is part of BOINC.
4
// http://boinc.berkeley.edu
5
// Copyright (C) 2024 University of California
6
//
7
// BOINC is free software; you can redistribute it and/or modify it
8
// under the terms of the GNU Lesser General Public License
9
// as published by the Free Software Foundation,
10
// either version 3 of the License, or (at your option) any later version.
11
//
12
// BOINC is distributed in the hope that it will be useful,
13
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
// See the GNU Lesser General Public License for more details.
16
//
17
// You should have received a copy of the GNU Lesser General Public License
18
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
19
20
// remote job submission functions.
21
//
22
// A 'remote app' is one where jobs are submitted remotely:
23
// - via web RPCs
24
// - (and possibly also) via forms on the project web site
25
//
26
// In both cases only users with permission can submit; either
27
// - a user_submit record with submit_all set
28
// - a user_submit_app record
29
//
30
// They are apps are described in $remote_apps in project.inc
31
//
32
// This page has several functions:
33
// - links to app-specific job-submission pages
34
// - Admin (if privileged user)
35
// - manage batches
36
//      view status, get output files, abort, retire
37
//   (this also shows batches created on the server)
38
// - set 'use only my computers'
39
40
require_once("../inc/submit_db.inc");
41
require_once("../inc/util.inc");
42
require_once("../inc/result.inc");
43
require_once("../inc/submit_util.inc");
44
require_once("../project/project.inc");
45
require_once('../project/remote_apps.inc');
46
47
display_errors();
48
49
// in general, if there can be lots of something,
50
// show this many and a link to show all.
51
// TODO: jobs in a batch
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
52
//
53
define("PAGE_SIZE", 20);
54
55
function return_link() {
56
    echo "<p><a href=submit.php?action=show_user_batches>Return to batches page</a>\n";
57
}
58
59
// return subset of batches in given state
60
//
61
function batches_in_state($all_batches, $state) {
62
    $batches = [];
63
    foreach ($all_batches as $batch) {
64
        if ($batch->state != $state) continue;
65
        $batches[] = $batch;
66
    }
67
    return $batches;
68
}
69
70
function sort_batches(&$batches, $order) {
71
    switch ($order) {
72
    case 'sub_asc':
73
        $f = function($a, $b) {
74
            return (int)($a->create_time - $b->create_time);
75
        };
76
        break;
77
    case 'sub_desc':
78
        $f = function($a, $b) {
79
            return (int)($b->create_time - $a->create_time);
80
        };
81
        break;
82
    case 'comp_asc':
83
        $f = function($a, $b) {
84
            return (int)($a->completion_time - $b->completion_time);
85
        };
86
        break;
87
    case 'comp_desc':
88
        $f = function($a, $b) {
89
            return (int)($b->completion_time - $a->completion_time);
90
        };
91
        break;
92
    }
93
    usort($batches, $f);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $f does not seem to be defined for all execution paths leading up to this point.
Loading history...
94
}
95
96
// in progress batches don't have completion time
97
//
98
function in_progress_order($order) {
99
    switch ($order) {
100
    case 'comp_asc': return 'sub_asc';
101
    case 'comp_desc': return 'sub_desc';
102
    }
103
    return $order;
104
}
105
106
// show order options
107
// sub_asc, sub_desc: submission time ( = ID order)
108
// comp_asc, comp_desc: completion time
109
110
function order_options($url_args, $order) {
111
    $url = "submit.php?$url_args";
112
    echo sprintf(
113
        'Order by: submission time (%s, %s) or completion time (%s, %s)',
114
        order_item($url, $order, 'sub_asc', 'ascending'),
115
        order_item($url, $order, 'sub_desc', 'descending'),
116
        order_item($url, $order, 'comp_asc', 'ascending'),
117
        order_item($url, $order, 'comp_desc', 'descending')
118
    );
119
}
120
121
function order_item($url, $cur_order, $order, $label) {
122
    if ($cur_order == $order) {
123
        return $label;
124
    } else {
125
        $url .= "&order=$order";
126
        return "<a href=$url>$label</a>";
127
    }
128
}
129
130
function get_order() {
131
    $order = get_str('order', true);
132
    if (!$order) $order = 'sub_desc';
133
    return $order;
134
}
135
136
// get params of in-progress batches; they might not be in progress anymore.
137
//
138
function get_batches_params($batches) {
139
    $b = [];
140
    foreach ($batches as $batch) {
141
        if ($batch->state == BATCH_STATE_IN_PROGRESS) {
142
            $wus = BoincWorkunit::enum_fields(
143
                'id, name, rsc_fpops_est, canonical_credit, canonical_resultid, error_mask',
144
                "batch = $batch->id"
145
            );
146
            $b[] = get_batch_params($batch, $wus);
147
        } else {
148
            $b[] = $batch;
149
        }
150
    }
151
    return $b;
152
}
153
154
function state_count($batches, $state) {
155
    $n = 0;
156
    foreach ($batches as $batch) {
157
        if ($batch->state == $state) $n++;
158
    }
159
    return $n;
160
}
161
162
function show_all_batches_link($batches, $state, $limit, $user, $app) {
163
    $n = state_count($batches, $state);
164
    if ($n > $limit) {
165
        if ($user) $userid = $user->id;
166
        else $userid = 0;
167
        if ($app) $appid = $app->id;
168
        else $appid = 0;
169
170
        echo "Showing the most recent $limit of $n batches.
171
            <a href=submit.php?action=show_all_batches&state=$state&userid=$userid&appid=$appid>Show all $n</a>
172
            <p>
173
        ";
174
    }
175
}
176
177
// show in-progress batches.
178
//
179
function show_in_progress($all_batches, $order, $limit, $user, $app) {
180
    $batches = batches_in_state($all_batches, BATCH_STATE_IN_PROGRESS);
181
    sort_batches($batches, in_progress_order($order));
182
    echo sprintf('<h3>Batches in progress (%d)</h3>', count($batches));
183
    $first = true;
184
    $n = 0;
185
    foreach ($batches as $batch) {
186
        if ($limit && $n == $limit) break;
187
        $n++;
188
        if ($first) {
189
            $first = false;
190
            if ($limit) {
191
                show_all_batches_link(
192
                    $batches, BATCH_STATE_IN_PROGRESS, $limit, $user, $app
193
                );
194
            }
195
            form_start('submit.php');
196
            form_input_hidden('action', 'abort_selected');
197
            start_table('table-striped');
198
            $x = [
199
                "Name",
200
                "ID",
201
                "User",
202
                "App",
203
                "# jobs",
204
                "Progress",
205
                "Submitted"
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
206
            ];
207
            row_heading_array($x);
208
        }
209
        $pct_done = (int)($batch->fraction_done*100);
210
        $x = [
211
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
212
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
213
            $batch->user_name,
214
            $batch->app_name,
215
            $batch->njobs,
216
            "$pct_done%",
217
            local_time_str($batch->create_time)
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
218
        ];
219
        row_array($x);
220
    }
221
    if ($first) {
222
        echo "<p>None.\n";
223
    } else {
224
        end_table();
225
        form_end();
226
    }
227
}
228
229
function show_complete($all_batches, $order, $limit, $user, $app) {
230
    $batches = batches_in_state($all_batches, BATCH_STATE_COMPLETE);
231
    sort_batches($batches, $order);
232
    echo sprintf('<h3>Completed batches (%d)</h3>', count($batches));
233
    $first = true;
234
    $n = 0;
235
    foreach ($batches as $batch) {
236
        if ($limit && $n == $limit) break;
237
        $n++;
238
        if ($first) {
239
            $first = false;
240
            if ($limit) {
241
                show_all_batches_link($batches, BATCH_STATE_COMPLETE, $limit, $user, $app);
242
            }
243
            form_start('submit.php', 'get');
244
            form_input_hidden('action', 'retire_multi');
245
            start_table('table-striped');
246
            table_header(
247
                "Name", "ID", "User", "App", "# Jobs", "Submitted", "Completed", "Select"
248
            );
249
        }
250
        table_row(
251
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
252
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
253
            $batch->user_name,
254
            $batch->app_name,
255
            $batch->njobs,
256
            local_time_str($batch->create_time),
257
            local_time_str($batch->completion_time),
258
            sprintf('<input type=checkbox name=retire_%d>', $batch->id)
259
        );
260
    }
261
    if ($first) {
262
        echo "<p>None.\n";
263
    } else {
264
        end_table();
265
        form_submit('Retire selected batches');
266
        form_end();
267
    }
268
}
269
270
function show_aborted($all_batches, $order, $limit, $user, $app) {
271
    $batches = batches_in_state($all_batches, BATCH_STATE_ABORTED);
272
    if (!$batches) return;
273
    sort_batches($batches, $order);
274
    echo sprintf('<h3>Aborted batches (%d)</h3>', count($batches));
275
    $first = true;
276
    $n = 0;
277
    foreach ($batches as $batch) {
278
        if ($limit && $n == $limit) break;
279
        $n++;
280
        if ($first) {
281
            $first = false;
282
            if ($limit) {
283
                show_all_batches_link($batches, BATCH_STATE_ABORTED, $limit, $user, $app);
284
            }
285
            form_start('submit.php', 'get');
286
            form_input_hidden('action', 'retire_multi');
287
            start_table();
288
            table_header("Name", "ID", "User", "App", "# Jobs", "Submitted", "Aborted", 'Select');
289
        }
290
        table_row(
291
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
292
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
293
            $batch->user_name,
294
            $batch->app_name,
295
            $batch->njobs,
296
            local_time_str($batch->create_time),
297
            local_time_str($batch->completion_time),
298
            sprintf('<input type=checkbox name=retire_%d>', $batch->id)
299
        );
300
    }
301
    if (!$first) {
302
        end_table();
303
        form_submit('Retire selected batches');
304
        form_end();
305
    }
306
}
307
308
// fill in the app and user names in list of batches
309
// TODO: speed this up by making list of app and user IDs
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
310
// and doing lookup just once.
311
//
312
function fill_in_app_and_user_names(&$batches) {
313
    $apps = [];
314
    foreach ($batches as $batch) {
315
        if (array_key_exists($batch->app_id, $apps)) {
316
            $app = $apps[$batch->app_id];
317
        } else {
318
            $app = BoincApp::lookup_id($batch->app_id);
319
            $apps[$batch->app_id] = $app;
320
        }
321
        if ($app) {
322
            $batch->app_name = $app->name;
323
            if ($batch->description) {
324
                $batch->app_name .= ": $batch->description";
325
            }
326
        } else {
327
            $batch->app_name = "unknown";
328
        }
329
        $user = BoincUser::lookup_id($batch->user_id);
330
        if ($user) {
331
            $batch->user_name = $user->name;
332
        } else {
333
            $batch->user_name = "missing user $batch->user_id";
334
        }
335
    }
336
}
337
338
// show a set of batches: in progress, then completed, then aborted
339
//
340
function show_batches($batches, $order, $limit, $user, $app) {
341
    fill_in_app_and_user_names($batches);
342
    $batches = get_batches_params($batches);
343
    show_in_progress($batches, $order, $limit, $user, $app);
344
    show_complete($batches, $order, $limit, $user, $app);
345
    show_aborted($batches, $order, $limit, $user, $app);
346
}
347
348
// show links to per-app job submission forms
349
//
350
function show_submit_links($user) {
351
    global $remote_apps;
352
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
353
    if (!$user_submit) {
354
        error_page("Ask the project admins for permission to submit jobs");
355
    }
356
357
    page_head("Submit jobs");
358
359
    // show links to per-app job submission pages
360
    //
361
    foreach ($remote_apps as $area => $apps) {
362
        panel($area,
363
            function() use ($apps) {
364
                foreach ($apps as $app) {
365
                    if (empty($app->form)) continue;
366
                    // show app logo if available
367
                    if (!empty($app->logo)) {
368
                        echo sprintf(
369
                            '<a href=%s><img width=100 src=%s></a>&nbsp;',
370
                            $app->form, $app->logo
371
                        );
372
                    } else {
373
                        echo sprintf(
374
                            '<li><a href=%s>%s</a><p>',
375
                            $app->form, $app->long_name
376
                        );
377
                    }
378
                }
379
            }
380
        );
381
    }
382
383
    form_start('submit.php');
384
    form_input_hidden('action', 'update_only_own');
385
    form_radio_buttons(
386
        'Jobs you submit can run', 'only_own',
387
        [
388
            [0, 'on any computer'],
389
            [1, 'only on your computers']
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
390
        ],
391
        $user->seti_id
392
    );
393
    form_submit('Update');
394
    form_end();
395
    page_tail();
396
}
397
398
// show batches of logged in user.
399
// They have manage access to these batches.
400
//
401
function handle_show_user_batches($user) {
402
    page_head("Batches");
403
    $order = get_order();
404
    order_options('action=show_user_batches', $order);
405
    $batches = BoincBatch::enum("user_id = $user->id");
406
    get_batches_params($batches);
407
    show_batches($batches, $order, PAGE_SIZE, $user, null);
408
409
    page_tail();
410
}
411
412
function handle_update_only_own($user) {
413
    $val = get_int('only_own');
414
    $user->update("seti_id=$val");
415
    header("Location: submit.php");
416
}
417
418
// get list of app names of remote apps
419
//
420
function get_remote_app_names() {
421
    global $remote_apps;
422
    $x = [];
423
    foreach ($remote_apps as $category => $apps) {
424
        foreach ($apps as $app) {
425
            $x[] = $app->app_name;
426
        }
427
    }
428
    return array_unique($x);
429
}
430
431
// show links for everything the user has admin access to
432
//
433
function handle_admin($user) {
434
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
435
    if (!$user_submit) error_page('no access');
436
    if ($user_submit->manage_all) {
437
        // user can administer all apps
438
        //
439
        page_head("Job submission: manage all apps");
440
        echo "<li> <a href=submit.php?action=admin_all>View/manage all batches</a>
441
        ";
442
        $app_names = get_remote_app_names();
443
        foreach ($app_names as $app_name) {
444
            $app_name = BoincDb::escape_string($app_name);
445
            $app = BoincApp::lookup("name='$app_name'");
446
            echo "
447
                <li>$app->user_friendly_name<br>
448
                <ul>
449
                <li><a href=submit.php?action=show_batches_admin_app&app_id=$app->id>View/manage batches</a>
450
            ";
451
            if ($app_name == 'buda') {
452
                echo "
453
                    <li> <a href=buda.php>Manage BUDA apps and variants</a>
454
                ";
455
            } else {
456
                echo "
457
                    <li> <a href=manage_app.php?app_id=$app->id&amp;action=app_version_form>Manage app versions</a>
458
                ";
459
            }
460
            echo "
461
                </ul>
462
            ";
463
        }
464
    } else {
465
        // see if user can administer specific apps
466
        //
467
        page_head("Job submission: manage apps");
468
        $usas = BoincUserSubmitApp::enum("user_id=$user->id");
469
        foreach ($usas as $usa) {
470
            $app = BoincApp::lookup_id($usa->app_id);
471
            echo "<li>$app->user_friendly_name<br>
472
                <a href=submit.php?action=show_batches_admin_app&app_id=$app->id>Batches</a>
473
            ";
474
            if ($usa->manage) {
475
                echo "&middot;
476
                    <a href=manage_app.php?app_id=$app->id&action=app_version_form>Versions</a>
477
                ";
478
            }
479
        }
480
    }
481
    echo "</ul>\n";
482
    page_tail();
483
}
484
485
// show all batches for given app to administrator
486
//
487
function show_batches_admin_app($user) {
488
    $app_id = get_int("app_id");
489
    $app = BoincApp::lookup_id($app_id);
490
    if (!$app) error_page("no such app");
491
    if (!has_manage_access($user, $app_id)) {
492
        error_page('no access');
493
    }
494
495
    $order = get_order();
496
497
    page_head("Manage batches for $app->user_friendly_name");
498
    order_options("action=show_batches_admin_app&app_id=$app_id", $order);
499
    $batches = BoincBatch::enum("app_id = $app_id");
500
    show_batches($batches, $order, PAGE_SIZE, null, $app);
501
    page_tail();
502
}
503
504
function handle_admin_all($user) {
505
    $order = get_order();
506
    page_head("Administer batches (all apps)");
507
    order_options("action=admin_all", $order);
508
    $batches = BoincBatch::enum('');
509
    show_batches($batches, $order, PAGE_SIZE, null, null);
510
    page_tail();
511
}
512
513
514
// show the statics of mem/disk usage of jobs in a batch
515
//
516
function handle_batch_stats($user) {
517
    $batch_id = get_int('batch_id');
518
    $batch = BoincBatch::lookup_id($batch_id);
519
    $results = BoincResult::enum_fields(
520
        'peak_working_set_size, peak_swap_size, peak_disk_usage',
521
        sprintf('batch = %d and outcome=%d',
522
            $batch->id, RESULT_OUTCOME_SUCCESS
523
        )
524
    );
525
    page_head("Statistics for batch $batch_id");
526
    $n = 0;
527
    $wss_sum = 0;
528
    $swap_sum = 0;
529
    $disk_sum = 0;
530
    $wss_max = 0;
531
    $swap_max = 0;
532
    $disk_max = 0;
533
    foreach ($results as $r) {
534
        // pre-7.3.16 clients don't report usage info
535
        //
536
        if ($r->peak_working_set_size == 0) {
537
            continue;
538
        }
539
        $n++;
540
        $wss_sum += $r->peak_working_set_size;
541
        if ($r->peak_working_set_size > $wss_max) {
542
            $wss_max = $r->peak_working_set_size;
543
        }
544
        $swap_sum += $r->peak_swap_size;
545
        if ($r->peak_swap_size > $swap_max) {
546
            $swap_max = $r->peak_swap_size;
547
        }
548
        $disk_sum += $r->peak_disk_usage;
549
        if ($r->peak_disk_usage > $disk_max) {
550
            $disk_max = $r->peak_disk_usage;
551
        }
552
    }
553
    if ($n == 0) {
554
        echo "No qualifying results.";
555
        page_tail();
556
        return;
557
    }
558
    text_start(800);
559
    start_table('table-striped');
560
    row2("qualifying results", $n);
561
    row2("mean WSS", size_string($wss_sum/$n));
562
    row2("max WSS", size_string($wss_max));
563
    row2("mean swap", size_string($swap_sum/$n));
564
    row2("max swap", size_string($swap_max));
565
    row2("mean disk usage", size_string($disk_sum/$n));
566
    row2("max disk usage", size_string($disk_max));
567
    end_table();
568
    text_end();
569
    page_tail();
570
}
571
572
define('COLOR_SUCCESS', 'green');
573
define('COLOR_FAIL', 'red');
574
define('COLOR_IN_PROGRESS', 'deepskyblue');
575
define('COLOR_UNSENT', 'gray');
576
577
// return HTML for a color-coded batch progress bar
578
//
579
function progress_bar($batch, $wus, $width) {
580
    $nsuccess = $batch->njobs_success;
581
    $nerror = $batch->nerror_jobs;
582
    $nin_prog = $batch->njobs_in_prog;
583
    $nunsent = $batch->njobs - $nsuccess - $nerror - $nin_prog;
584
    $w_success = $width*$nsuccess/$batch->njobs;
585
    $w_fail = $width*$nerror/$batch->njobs;
586
    $w_prog = $width*$nin_prog/$batch->njobs;
587
    $w_unsent = $width*$nunsent/$batch->njobs;
588
    $x = '<table height=20><tr>';
589
    if ($w_fail) {
590
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_fail, COLOR_FAIL);
591
    }
592
    if ($w_success) {
593
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_success, COLOR_SUCCESS);
594
    }
595
    if ($w_prog) {
596
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_prog, COLOR_IN_PROGRESS);
597
    }
598
    if ($w_unsent) {
599
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_unsent, COLOR_UNSENT);
600
    }
601
    $x .= sprintf('</tr></table>
602
        <strong>
603
        <font color=%s>%d failed</font> &middot;
604
        <font color=%s>%d completed</font> &middot;
605
        <font color=%s>%d in progress</font> &middot;
606
        <font color=%s>%d unsent</font>
607
        </strong>',
608
        COLOR_FAIL, $nerror,
609
        COLOR_SUCCESS, $nsuccess,
610
        COLOR_IN_PROGRESS, $nin_prog,
611
        COLOR_UNSENT, $nunsent
612
    );
613
    return $x;
614
}
615
616
// show the details of an existing batch.
617
// $user has access to abort/retire the batch
618
// and to get its output files
619
//
620
function handle_query_batch($user) {
621
    $batch_id = get_int('batch_id');
622
    $batch = BoincBatch::lookup_id($batch_id);
623
    $app = BoincApp::lookup_id($batch->app_id);
624
    $wus = BoincWorkunit::enum_fields(
625
        'id, name, rsc_fpops_est, canonical_credit, canonical_resultid, error_mask',
626
        "batch = $batch->id"
627
    );
628
    $batch = get_batch_params($batch, $wus);
629
    if ($batch->user_id == $user->id) {
630
        $owner = $user;
631
    } else {
632
        $owner = BoincUser::lookup_id($batch->user_id);
633
    }
634
635
    $is_assim_move = is_assim_move($app);
636
637
    page_head("Batch $batch_id");
638
    text_start(800);
639
    start_table();
640
    row2("name", $batch->name);
641
    if ($batch->description) {
642
        row2('description', $batch->description);
643
    }
644
    if ($owner) {
645
        row2('submitter', $owner->name);
646
    }
647
    row2("application", $app?$app->name:'---');
648
    row2("state", batch_state_string($batch->state));
649
    //row2("# jobs", $batch->njobs);
650
    //row2("# error jobs", $batch->nerror_jobs);
651
    //row2("logical end time", time_str($batch->logical_end_time));
652
    if ($batch->expire_time) {
653
        row2("expiration time", time_str($batch->expire_time));
654
    }
655
    if ($batch->njobs) {
656
        row2("progress", progress_bar($batch, $wus, 600));
657
    }
658
    if ($batch->completion_time) {
659
        row2("completed", local_time_str($batch->completion_time));
660
    }
661
    row2("GFLOP/hours, estimated", number_format(credit_to_gflop_hours($batch->credit_estimate), 2));
662
    row2("GFLOP/hours, actual", number_format(credit_to_gflop_hours($batch->credit_canonical), 2));
663
    if (!$is_assim_move) {
664
        row2("Total size of output files",
665
            size_string(batch_output_file_size($batch->id))
666
        );
667
    }
668
    end_table();
669
    echo "<p>";
670
671
    if ($is_assim_move) {
672
        $url = "get_output3.php?action=get_batch&batch_id=$batch->id";
673
    } else {
674
        $url = "get_output2.php?cmd=batch&batch_id=$batch->id";
675
    }
676
    echo "<p>";
677
    show_button($url, "Get zipped output files");
678
    echo "<p>";
679
    switch ($batch->state) {
680
    case BATCH_STATE_IN_PROGRESS:
681
        show_button(
682
            "submit.php?action=abort_batch&batch_id=$batch_id",
683
            "Abort batch"
684
        );
685
        break;
686
    case BATCH_STATE_COMPLETE:
687
    case BATCH_STATE_ABORTED:
688
        show_button(
689
            "submit.php?action=retire_batch&batch_id=$batch_id",
690
            "Retire batch"
691
        );
692
        break;
693
    }
694
    echo "<p>
695
        <h3>Completed jobs</h3>
696
        <ul>
697
        <li>
698
        <a href=submit_stats.php?action=flops_graph&batch_id=$batch_id>Job runtimes</a>
699
        <li>
700
        <a href=submit.php?action=batch_stats&batch_id=$batch_id>Memory/disk usage</a>
701
        <li>
702
        <a href=submit_stats.php?action=show_hosts&batch_id=$batch_id>Grouped by host</a>
703
        </ul>
704
        <h3>Failed jobs</h3>
705
        <ul>
706
        <li>
707
        <a href=submit_stats.php?action=err_host&batch_id=$batch_id>Grouped by host</a>
708
        <li>
709
        <a href=submit_stats.php?action=err_code&batch_id=$batch_id>Grouped by exit code</a>
710
        </ul>
711
    ";
712
713
    echo "<h2>Jobs</h2>\n";
714
    start_table();
715
    $x = [
716
        "Name <br><small>click for details</small>",
717
        "status",
718
        "GFLOPS-hours"
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
719
    ];
720
    row_heading_array($x);
721
    foreach($wus as $wu) {
722
        $y = '';
723
        $c = '---';
724
        switch($wu->status) {
725
        case WU_SUCCESS:
726
            $resultid = $wu->canonical_resultid;
727
            $y = sprintf('<font color="%s">completed</font>', COLOR_SUCCESS);
728
            $c = number_format(
729
                credit_to_gflop_hours($wu->canonical_credit), 2
730
            );
731
            break;
732
        case WU_ERROR:
733
            $y = sprintf('<font color="%s">failed</font>', COLOR_FAIL);
734
            break;
735
        case WU_IN_PROGRESS:
736
            $y = sprintf('<font color="%s">in progress</font>', COLOR_IN_PROGRESS);
737
            break;
738
        case WU_UNSENT:
739
            $y = sprintf('<font color="%s">unsent</font>', COLOR_UNSENT);
740
            break;
741
        }
742
        $x = [
743
            "<a href=submit.php?action=query_job&wuid=$wu->id>$wu->name</a>",
744
            $y,
745
            $c
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
746
        ];
747
        row_array($x);
748
    }
749
    end_table();
750
    return_link();
751
    text_end();
752
    page_tail();
753
}
754
755
// Does the assimilator for the given app move output files
756
// to a results/<batchid>/ directory?
757
// This info is stored in the $remote_apps data structure in project.inc
758
//
759
function is_assim_move($app) {
760
    global $remote_apps;
761
    foreach ($remote_apps as $category => $apps) {
762
        foreach ($apps as $web_app) {
763
            if ($web_app->app_name == $app->name) {
764
                return $web_app->is_assim_move;
765
            }
766
        }
767
    }
768
    return false;
769
}
770
771
// show the details of a job, including links to see the output files
772
//
773
function handle_query_job($user) {
774
    $wuid = get_int('wuid');
775
    $wu = BoincWorkunit::lookup_id($wuid);
776
    if (!$wu) error_page("no such job");
777
778
    $app = BoincApp::lookup_id($wu->appid);
779
    $is_assim_move = is_assim_move($app);
780
781
    page_head("Job '$wu->name'");
782
    text_start(800);
783
784
    echo "
785
        <li><a href=workunit.php?wuid=$wuid>Job details</a>
786
        <p>
787
        <li><a href=submit.php?action=query_batch&batch_id=$wu->batch>Batch details</a>
788
    ";
789
    $d = "<foo>$wu->xml_doc</foo>";
790
    $x = simplexml_load_string($d);
791
    $x = $x->workunit;
792
    //echo "foo: $x->command_line";
793
794
    echo "<h2>Job instances</h2>\n";
795
    start_table('table-striped');
796
    table_header(
797
        "ID<br><small>click for details and stderr</small>",
798
        "State",
799
        "Output files"
800
    );
801
    $results = BoincResult::enum("workunitid=$wuid");
802
    $upload_dir = parse_config(get_config(), "<upload_dir>");
803
    $fanout = parse_config(get_config(), "<uldl_dir_fanout>");
804
    foreach($results as $result) {
805
        $x = [
806
            "<a href=result.php?resultid=$result->id>$result->id</a>",
807
            state_string($result)
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
808
        ];
809
        $i = 0;
810
        if ($result->server_state == RESULT_SERVER_STATE_OVER) {
811
            $phys_names = get_outfile_phys_names($result);
812
            $log_names = get_outfile_log_names($result);
813
            for ($i=0; $i<count($phys_names); $i++) {
0 ignored issues
show
Coding Style Performance introduced by
The use of count() inside a loop condition is not allowed; assign the return value to a variable and use the variable in the loop condition instead
Loading history...
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
814
                if ($is_assim_move) {
815
                    // file is in
816
                    // project/results/<batchid>/<wu_name>__file_<log_name>
817
                    $path = sprintf('results/%s/%s__file_%s',
818
                        $wu->batch, $wu->name, $log_names[$i]
819
                    );
820
                    $name = $log_names[$i];
821
                    // don't show 'view' link if it's a .zip
822
                    $y = "$name: ";
823
                    if (!strstr($name, '.zip')) {
824
                        $y .= "<a href=get_output3.php?action=get_file&path=$path>view</a> &middot; ";
825
                    }
826
                    $y .= "<a href=get_output3.php?action=get_file&path=$path&download=1>download</a>";
827
                    $x[] = $y;
828
                } else {
829
                    $path = dir_hier_path(
830
                        $phys_names[$i], $upload_dir, $fanout
831
                    );
832
                    if (file_exists($path)) {
833
                        $url = sprintf(
834
                            'get_output2.php?cmd=result&result_id=%d&file_num=%d',
835
                            $result->id, $i
836
                        );
837
                        $s = stat($path);
838
                        $size = $s['size'];
839
                        $x[] = sprintf('<a href=%s>%s</a> (%s bytes)<br/>',
840
                            $url,
841
                            $log_names[$i],
842
                            number_format($size)
843
                        );
844
                    } else {
845
                        $x[] = sprintf("file '%s' is missing", $log_names[$i]);
846
                    }
847
                }
848
            }
849
        } else {
850
            $x[] = '---';
851
        }
852
        row_array($x);
853
    }
854
    end_table();
855
856
    // show input files
857
    //
858
    echo "<h2>Input files</h2>\n";
859
    $x = "<in>".$wu->xml_doc."</in>";
860
    $x = simplexml_load_string($x);
861
    start_table('table-striped');
862
    table_header("Name<br><small>(click to view)</small>", "Size (bytes)");
863
    foreach ($x->workunit->file_ref as $fr) {
864
        $pname = (string)$fr->file_name;
865
        $lname = (string)$fr->open_name;
866
        foreach ($x->file_info as $fi) {
867
            if ((string)$fi->name == $pname) {
868
                table_row(
869
                    "<a href=$fi->url>$lname</a>",
870
                    $fi->nbytes
871
                );
872
                break;
873
            }
874
        }
875
    }
876
877
    end_table();
878
    text_end();
879
    return_link();
880
    page_tail();
881
}
882
883
// is user allowed to retire or abort this batch?
884
//
885
function has_access($user, $batch) {
886
    if ($user->id == $batch->user_id) return true;
887
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
888
    if ($user_submit->manage_all) return true;
889
    $usa = BoincUserSubmitApp::lookup("user_id=$user->id and app_id=$batch->app_id");
890
    if ($usa->manage) return true;
891
    return false;
892
}
893
894
function handle_abort_batch($user) {
895
    $batch_id = get_int('batch_id');
896
    $batch = BoincBatch::lookup_id($batch_id);
897
    if (!$batch) error_page("no such batch");
898
    if (!has_access($user, $batch)) {
899
        error_page("no access");
900
    }
901
902
    if (get_int('confirmed', true)) {
903
        abort_batch($batch);
904
        page_head("Batch aborted");
905
        return_link();
906
        page_tail();
907
    } else {
908
        page_head("Confirm abort batch");
909
        echo "
910
            Aborting a batch will cancel all unstarted jobs.
911
            Are you sure you want to do this?
912
            <p>
913
        ";
914
        show_button(
915
            "submit.php?action=abort_batch&batch_id=$batch_id&confirmed=1",
916
            "Yes - abort batch"
917
        );
918
        return_link();
919
        page_tail();
920
    }
921
}
922
923
function handle_retire_batch($user) {
924
    $batch_id = get_int('batch_id');
925
    $batch = BoincBatch::lookup_id($batch_id);
926
    if (!$batch) error_page("no such batch");
927
    if (!has_access($user, $batch)) {
928
        error_page("no access");
929
    }
930
931
    if (get_int('confirmed', true)) {
932
        retire_batch($batch);
933
        page_head("Batch $batch_id retired");
934
        return_link();
935
        page_tail();
936
    } else {
937
        page_head("Confirm retire batch");
938
        echo "
939
            Retiring a batch will remove all of its output files.
940
            Are you sure you want to do this?
941
            <p>
942
        ";
943
        show_button(
944
            "submit.php?action=retire_batch&batch_id=$batch_id&confirmed=1",
945
            "Yes - retire batch"
946
        );
947
        return_link();
948
        page_tail();
949
    }
950
}
951
952
// retire multiple batches
953
//
954
function handle_retire_multi($user) {
955
    $batches = BoincBatch::enum(
956
        sprintf('state=%d', BATCH_STATE_COMPLETE)
957
    );
958
    page_head('Retiring batches');
959
    foreach ($batches as $batch) {
960
        if (!has_access($user, $batch)) {
961
            continue;
962
        }
963
        $x = sprintf('retire_%d', $batch->id);
964
        if (get_str($x, true) == 'on') {
965
            retire_batch($batch);
966
            echo "<p>retired batch $batch->id ($batch->name)\n";
967
        }
968
    }
969
    return_link();
970
    page_tail();
971
}
972
973
// given a list of batches, show the ones in a given state
974
//
975
function show_batches_in_state($batches, $state, $url_args, $order) {
976
    switch ($state) {
977
    case BATCH_STATE_IN_PROGRESS:
978
        page_head("Batches in progress");
979
        order_options($url_args, $order);
980
        show_in_progress($batches, $order, 0, null, null);
981
        break;
982
    case BATCH_STATE_COMPLETE:
983
        page_head("Completed batches");
984
        order_options($url_args, $order);
985
        show_complete($batches, $order, 0, null, null);
986
        break;
987
    case BATCH_STATE_ABORTED:
988
        page_head("Aborted batches");
989
        order_options($url_args, $order);
990
        show_aborted($batches, $order, 0, null, null);
991
        break;
992
    }
993
    page_tail();
994
}
995
996
// show all batches visible to user, possibly limited by user/app/state
997
function handle_show_all_batches($user) {
998
    $userid = get_int("userid");
999
    $appid = get_int("appid");
1000
    $state = get_int("state");
1001
    $order = get_order();
1002
    $url_args = "action=show_all_batches&state=$state&userid=$userid&appid=$appid";
1003
    if ($userid) {
1004
        // user looking at their own batches
1005
        //
1006
        if ($userid != $user->id) error_page("wrong user");
1007
        $batches = BoincBatch::enum("user_id=$user->id and state=$state");
1008
        fill_in_app_and_user_names($batches);
1009
        show_batches_in_state($batches, $state, $url_args, $order);
1010
    } else {
1011
        // admin looking at batches
1012
        //
1013
        if (!has_manage_access($user, $appid)) {
1014
            error_page('no access');
1015
        }
1016
        if ($appid) {
1017
            $app = BoincApp::lookup_id($appid);
1018
            if (!$app) error_page("no such app");
1019
            $batches = BoincBatch::enum("app_id=$appid and state=$state");
1020
        } else {
1021
            $batches = BoincBatch::enum("state=$state");
1022
        }
1023
        fill_in_app_and_user_names($batches);
1024
        show_batches_in_state($batches, $state, $url_args, $order);
1025
    }
1026
}
1027
1028
$user = get_logged_in_user();
0 ignored issues
show
Are you sure the assignment to $user is correct as get_logged_in_user() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1029
1030
$action = get_str('action', true);
1031
1032
switch ($action) {
1033
1034
// links to job submission forms
1035
case '': show_submit_links($user); break;
1036
1037
// show lists of batches
1038
case 'show_all_batches': handle_show_all_batches($user); break;
1039
case 'show_user_batches': handle_show_user_batches($user); break;
1040
case 'show_batches_admin_app': show_batches_admin_app($user); break;
1041
case 'admin_all': handle_admin_all($user); break;
1042
1043
// show info about a batch or job
1044
case 'batch_stats': handle_batch_stats($user); break;
1045
case 'query_batch': handle_query_batch($user); break;
1046
case 'query_job': handle_query_job($user); break;
1047
1048
// operations on batches
1049
case 'retire_batch': handle_retire_batch($user); break;
1050
case 'retire_multi': handle_retire_multi($user); break;
1051
case 'abort_batch': handle_abort_batch($user); break;
1052
1053
// access control
1054
case 'admin': handle_admin($user); break;
1055
1056
// 'run jobs only on my computers' flag (stored in user.seti_id)
1057
case 'update_only_own': handle_update_only_own($user); break;
1058
1059
default:
0 ignored issues
show
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
DEFAULT case must have a breaking statement
Loading history...
1060
    error_page("no such action $action");
1061
}
1062
1063
?>
1064