Issues (1963)

html/user/submit.php (11 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=status>Return to batches page</a>\n";
57
}
58
59
// show order options
60
// sub_asc, sub_desc: submission time ( = ID order)
61
// comp_asc, comp_desc: completion time
62
63
function order_options($url_args, $order) {
64
    $url = "submit.php?$url_args";
65
    echo sprintf(
66
        'Order by: submission time (%s, %s) or completion time (%s, %s)',
67
        order_item($url, $order, 'sub_asc', 'ascending'),
68
        order_item($url, $order, 'sub_desc', 'descending'),
69
        order_item($url, $order, 'comp_asc', 'ascending'),
70
        order_item($url, $order, 'comp_desc', 'descending')
71
    );
72
}
73
74
function order_item($url, $cur_order, $order, $label) {
75
    if ($cur_order == $order) {
76
        return $label;
77
    } else {
78
        $url .= "&order=$order";
79
        return "<a href=$url>$label</a>";
80
    }
81
}
82
83
function order_clause($order) {
84
    switch ($order) {
85
    case 'sub_asc': return 'id';
86
    case 'sub_desc': return 'id desc';
87
    case 'comp_asc': return 'completion_time';
88
    case 'comp_desc': return 'completion_time desc';
89
    }
90
}
91
92
function get_order() {
93
    $order = get_str('order', true);
94
    if (!$order) $order = 'sub_desc';
95
    return $order;
96
}
97
98
// get params of in-progress batches; they might not be in progress anymore.
99
//
100
function get_batches_params($batches) {
101
    $b = [];
102
    foreach ($batches as $batch) {
103
        if ($batch->state == BATCH_STATE_IN_PROGRESS) {
104
            $wus = BoincWorkunit::enum_fields(
105
                'id, name, rsc_fpops_est, canonical_credit, canonical_resultid, error_mask',
106
                "batch = $batch->id"
107
            );
108
            $b[] = get_batch_params($batch, $wus);
109
        } else {
110
            $b[] = $batch;
111
        }
112
    }
113
    return $b;
114
}
115
116
function state_count($batches, $state) {
117
    $n = 0;
118
    foreach ($batches as $batch) {
119
        if ($batch->state == $state) $n++;
120
    }
121
    return $n;
122
}
123
124
function show_all_link($batches, $state, $limit, $user, $app) {
125
    $n = state_count($batches, $state);
126
    if ($n > $limit) {
127
        if ($user) $userid = $user->id;
128
        else $userid = 0;
129
        if ($app) $appid = $app->id;
130
        else $appid = 0;
131
132
        echo "Showing the most recent $limit of $n batches.
133
            <a href=submit.php?action=show_all&state=$state&userid=$userid&appid=$appid>Show all $n</a>
134
            <p>
135
        ";
136
    }
137
}
138
139
// show in-progress batches.
140
//
141
function show_in_progress($all_batches, $limit, $user, $app) {
142
    $batches = [];
143
    foreach ($all_batches as $batch) {
144
        if ($batch->state != BATCH_STATE_IN_PROGRESS) continue;
145
        $batches[] = $batch;
146
    }
147
    echo sprintf('<h3>Batches in progress (%d)</h3>', count($batches));
148
    $first = true;
149
    $n = 0;
150
    foreach ($batches as $batch) {
151
        if ($batch->state != BATCH_STATE_IN_PROGRESS) continue;
152
        if ($limit && $n == $limit) break;
153
        $n++;
154
        if ($first) {
155
            $first = false;
156
            if ($limit) {
157
                show_all_link(
158
                    $batches, BATCH_STATE_IN_PROGRESS, $limit, $user, $app
159
                );
160
            }
161
            form_start('submit.php');
162
            form_input_hidden('action', 'abort_selected');
163
            start_table('table-striped');
164
            $x = [
165
                "Name",
166
                "ID",
167
                "User",
168
                "App",
169
                "# jobs",
170
                "Progress",
171
                "Submitted",
172
                //"Logical end time<br><small>Determines priority</small>"
173
            ];
174
            row_heading_array($x);
175
        }
176
        $pct_done = (int)($batch->fraction_done*100);
177
        $x = [
178
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
179
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
180
            $batch->user_name,
181
            $batch->app_name,
182
            $batch->njobs,
183
            "$pct_done%",
184
            local_time_str($batch->create_time),
185
            //local_time_str($batch->logical_end_time)
186
        ];
187
        row_array($x);
188
    }
189
    if ($first) {
190
        echo "<p>None.\n";
191
    } else {
192
        end_table();
193
        form_end();
194
    }
195
}
196
197
function show_complete($all_batches, $limit, $user, $app) {
198
    $batches = [];
199
    foreach ($all_batches as $batch) {
200
        if ($batch->state != BATCH_STATE_COMPLETE) continue;
201
        $batches[] = $batch;
202
    }
203
    echo sprintf('<h3>Completed batches (%d)</h3>', count($batches));
204
    $first = true;
205
    $n = 0;
206
    foreach ($batches as $batch) {
207
        if ($limit && $n == $limit) break;
208
        $n++;
209
        if ($first) {
210
            $first = false;
211
            if ($limit) {
212
                show_all_link($batches, BATCH_STATE_COMPLETE, $limit, $user, $app);
213
            }
214
            form_start('submit.php', 'get');
215
            form_input_hidden('action', 'retire_multi');
216
            start_table('table-striped');
217
            table_header(
218
                "Name", "ID", "User", "App", "# Jobs", "Submitted", "Completed", "Select"
219
            );
220
        }
221
        table_row(
222
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
223
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
224
            $batch->user_name,
225
            $batch->app_name,
226
            $batch->njobs,
227
            local_time_str($batch->create_time),
228
            local_time_str($batch->completion_time),
229
            sprintf('<input type=checkbox name=retire_%d>', $batch->id)
230
        );
231
    }
232
    if ($first) {
233
        echo "<p>None.\n";
234
    } else {
235
        end_table();
236
        form_submit('Retire selected batches');
237
        form_end();
238
    }
239
}
240
241
function show_aborted($all_batches, $limit, $user, $app) {
242
    $batches = [];
243
    foreach ($all_batches as $batch) {
244
        if ($batch->state != BATCH_STATE_ABORTED) continue;
245
        $batches[] = $batch;
246
    }
247
    if (!$batches) return;
248
    echo sprintf('<h3>Aborted batches (%d)</h3>', count($batches));
249
    $first = true;
250
    $n = 0;
251
    foreach ($batches as $batch) {
252
        if ($limit && $n == $limit) break;
253
        $n++;
254
        if ($first) {
255
            $first = false;
256
            if ($limit) {
257
                show_all_link($batches, BATCH_STATE_ABORTED, $limit, $user, $app);
258
            }
259
            form_start('');
260
            start_table();
261
            table_header("Name", "ID", "User", "App", "# Jobs", "Submitted", "Aborted");
262
        }
263
        table_row(
264
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->name</a>",
265
            "<a href=submit.php?action=query_batch&batch_id=$batch->id>$batch->id</a>",
266
            $batch->user_name,
267
            $batch->app_name,
268
            $batch->njobs,
269
            local_time_str($batch->create_time),
270
            local_time_str($batch->completion_time)
271
        );
272
    }
273
    if (!$first) {
274
        end_table();
275
        form_end();
276
    }
277
}
278
279
// fill in the app and user names in list of batches
280
// 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...
281
// and doing lookup just once.
282
//
283
function fill_in_app_and_user_names(&$batches) {
284
    $apps = [];
285
    foreach ($batches as $batch) {
286
        if (array_key_exists($batch->app_id, $apps)) {
287
            $app = $apps[$batch->app_id];
288
        } else {
289
            $app = BoincApp::lookup_id($batch->app_id);
290
            $apps[$batch->app_id] = $app;
291
        }
292
        if ($app) {
293
            $batch->app_name = $app->name;
294
            if ($batch->description) {
295
                $batch->app_name .= ": $batch->description";
296
            }
297
        } else {
298
            $batch->app_name = "unknown";
299
        }
300
        $user = BoincUser::lookup_id($batch->user_id);
301
        if ($user) {
302
            $batch->user_name = $user->name;
303
        } else {
304
            $batch->user_name = "missing user $batch->user_id";
305
        }
306
    }
307
}
308
309
// show a set of batches
310
//
311
function show_batches($batches, $limit, $user, $app) {
312
    fill_in_app_and_user_names($batches);
313
    $batches = get_batches_params($batches);
314
    show_in_progress($batches, $limit, $user, $app);
315
    show_complete($batches, $limit, $user, $app);
316
    show_aborted($batches, $limit, $user, $app);
317
}
318
319
// show links to per-app job submission forms
320
//
321
function handle_main($user) {
322
    global $remote_apps;
323
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
324
    if (!$user_submit) {
325
        error_page("Ask the project admins for permission to submit jobs");
326
    }
327
328
    page_head("Submit jobs");
329
330
    // show links to per-app job submission pages
331
    //
332
    foreach ($remote_apps as $area => $apps) {
333
        panel($area,
334
            function() use ($apps) {
335
                foreach ($apps as $app) {
336
                    if (empty($app->form)) continue;
337
                    // show app logo if available
338
                    if (!empty($app->logo)) {
339
                        echo sprintf(
340
                            '<a href=%s><img width=100 src=%s></a>&nbsp;',
341
                            $app->form, $app->logo
342
                        );
343
                    } else {
344
                        echo sprintf(
345
                            '<li><a href=%s>%s</a><p>',
346
                            $app->form, $app->long_name
347
                        );
348
                    }
349
                }
350
            }
351
        );
352
    }
353
354
    form_start('submit.php');
355
    form_input_hidden('action', 'update_only_own');
356
    form_radio_buttons(
357
        'Jobs you submit can run', 'only_own',
358
        [
359
            [0, 'on any computer'],
360
            [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...
361
        ],
362
        $user->seti_id
363
    );
364
    form_submit('Update');
365
    form_end();
366
    page_tail();
367
}
368
369
// show batches of logged in user.
370
// They have manage access to these batches.
371
//
372
function handle_show_status($user) {
373
    page_head("Batches");
374
    $order = get_order();
375
    order_options('action=status', $order);
376
    $clause = order_clause($order);
377
    $batches = BoincBatch::enum("user_id = $user->id order by $clause");
378
    get_batches_params($batches);
379
    show_batches($batches, PAGE_SIZE, $user, null);
380
381
    page_tail();
382
}
383
384
function handle_update_only_own($user) {
385
    $val = get_int('only_own');
386
    $user->update("seti_id=$val");
387
    header("Location: submit.php");
388
}
389
390
// get list of app names of remote apps
391
//
392
function get_remote_app_names() {
393
    global $remote_apps;
394
    $x = [];
395
    foreach ($remote_apps as $category => $apps) {
396
        foreach ($apps as $app) {
397
            $x[] = $app->app_name;
398
        }
399
    }
400
    return array_unique($x);
401
}
402
403
// show links for everything the user has admin access to
404
//
405
function handle_admin($user) {
406
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
407
    if (!$user_submit) error_page('no access');
408
    if ($user_submit->manage_all) {
409
        // user can administer all apps
410
        //
411
        page_head("Job submission: manage all apps");
412
        echo "<li> <a href=submit.php?action=admin_all>View/manage all batches</a>
413
        ";
414
        $app_names = get_remote_app_names();
415
        foreach ($app_names as $app_name) {
416
            $app_name = BoincDb::escape_string($app_name);
417
            $app = BoincApp::lookup("name='$app_name'");
418
            echo "
419
                <li>$app->user_friendly_name<br>
420
                <ul>
421
                <li><a href=submit.php?action=admin_app&app_id=$app->id>View/manage batches</a>
422
            ";
423
            if ($app_name == 'buda') {
424
                echo "
425
                    <li> <a href=buda.php>Manage BUDA apps and variants</a>
426
                ";
427
            } else {
428
                echo "
429
                    <li> <a href=manage_app.php?app_id=$app->id&amp;action=app_version_form>Manage app versions</a>
430
                ";
431
            }
432
            echo "
433
                </ul>
434
            ";
435
        }
436
    } else {
437
        // see if user can administer specific apps
438
        //
439
        page_head("Job submission: manage apps");
440
        $usas = BoincUserSubmitApp::enum("user_id=$user->id");
441
        foreach ($usas as $usa) {
442
            $app = BoincApp::lookup_id($usa->app_id);
443
            echo "<li>$app->user_friendly_name<br>
444
                <a href=submit.php?action=admin_app&app_id=$app->id>Batches</a>
445
            ";
446
            if ($usa->manage) {
447
                echo "&middot;
448
                    <a href=manage_app.php?app_id=$app->id&action=app_version_form>Versions</a>
449
                ";
450
            }
451
        }
452
    }
453
    echo "</ul>\n";
454
    page_tail();
455
}
456
457
function handle_admin_app($user) {
458
    $app_id = get_int("app_id");
459
    $app = BoincApp::lookup_id($app_id);
460
    if (!$app) error_page("no such app");
461
    if (!has_manage_access($user, $app_id)) {
462
        error_page('no access');
463
    }
464
465
    $order = get_order();
466
    $clause = order_clause($order);
467
468
    page_head("Manage batches for $app->user_friendly_name");
469
    order_options("action=admin_app&app_id=$app_id", $order);
470
    $batches = BoincBatch::enum("app_id = $app_id order by $clause");
471
    show_batches($batches, PAGE_SIZE, null, $app);
472
    page_tail();
473
}
474
475
function handle_admin_all($user) {
476
    $order = get_order();
477
    $clause = order_clause($order);
478
    page_head("Administer batches (all apps)");
479
    order_options("action=admin_all", $order);
480
    $batches = BoincBatch::enum("true order by $clause");
481
    show_batches($batches, PAGE_SIZE, null, null);
482
    page_tail();
483
}
484
485
486
// show the statics of mem/disk usage of jobs in a batch
487
//
488
function handle_batch_stats($user) {
489
    $batch_id = get_int('batch_id');
490
    $batch = BoincBatch::lookup_id($batch_id);
491
    $results = BoincResult::enum_fields(
492
        'peak_working_set_size, peak_swap_size, peak_disk_usage',
493
        sprintf('batch = %d and outcome=%d',
494
            $batch->id, RESULT_OUTCOME_SUCCESS
495
        )
496
    );
497
    page_head("Statistics for batch $batch_id");
498
    $n = 0;
499
    $wss_sum = 0;
500
    $swap_sum = 0;
501
    $disk_sum = 0;
502
    $wss_max = 0;
503
    $swap_max = 0;
504
    $disk_max = 0;
505
    foreach ($results as $r) {
506
        // pre-7.3.16 clients don't report usage info
507
        //
508
        if ($r->peak_working_set_size == 0) {
509
            continue;
510
        }
511
        $n++;
512
        $wss_sum += $r->peak_working_set_size;
513
        if ($r->peak_working_set_size > $wss_max) {
514
            $wss_max = $r->peak_working_set_size;
515
        }
516
        $swap_sum += $r->peak_swap_size;
517
        if ($r->peak_swap_size > $swap_max) {
518
            $swap_max = $r->peak_swap_size;
519
        }
520
        $disk_sum += $r->peak_disk_usage;
521
        if ($r->peak_disk_usage > $disk_max) {
522
            $disk_max = $r->peak_disk_usage;
523
        }
524
    }
525
    if ($n == 0) {
526
        echo "No qualifying results.";
527
        page_tail();
528
        return;
529
    }
530
    text_start(800);
531
    start_table('table-striped');
532
    row2("qualifying results", $n);
533
    row2("mean WSS", size_string($wss_sum/$n));
534
    row2("max WSS", size_string($wss_max));
535
    row2("mean swap", size_string($swap_sum/$n));
536
    row2("max swap", size_string($swap_max));
537
    row2("mean disk usage", size_string($disk_sum/$n));
538
    row2("max disk usage", size_string($disk_max));
539
    end_table();
540
    text_end();
541
    page_tail();
542
}
543
544
define('COLOR_SUCCESS', 'green');
545
define('COLOR_FAIL', 'red');
546
define('COLOR_IN_PROGRESS', 'deepskyblue');
547
define('COLOR_UNSENT', 'gray');
548
549
// return HTML for a color-coded batch progress bar
550
//
551
function progress_bar($batch, $wus, $width) {
552
    $nsuccess = $batch->njobs_success;
553
    $nerror = $batch->nerror_jobs;
554
    $nin_prog = $batch->njobs_in_prog;
555
    $nunsent = $batch->njobs - $nsuccess - $nerror - $nin_prog;
556
    $w_success = $width*$nsuccess/$batch->njobs;
557
    $w_fail = $width*$nerror/$batch->njobs;
558
    $w_prog = $width*$nin_prog/$batch->njobs;
559
    $w_unsent = $width*$nunsent/$batch->njobs;
560
    $x = '<table height=20><tr>';
561
    if ($w_fail) {
562
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_fail, COLOR_FAIL);
563
    }
564
    if ($w_success) {
565
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_success, COLOR_SUCCESS);
566
    }
567
    if ($w_prog) {
568
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_prog, COLOR_IN_PROGRESS);
569
    }
570
    if ($w_unsent) {
571
        $x .= sprintf('<td width=%d bgcolor=%s></td>', $w_unsent, COLOR_UNSENT);
572
    }
573
    $x .= sprintf('</tr></table>
574
        <strong>
575
        <font color=%s>%d failed</font> &middot;
576
        <font color=%s>%d completed</font> &middot;
577
        <font color=%s>%d in progress</font> &middot;
578
        <font color=%s>%d unsent</font>
579
        </strong>',
580
        COLOR_FAIL, $nerror,
581
        COLOR_SUCCESS, $nsuccess,
582
        COLOR_IN_PROGRESS, $nin_prog,
583
        COLOR_UNSENT, $nunsent
584
    );
585
    return $x;
586
}
587
588
// show the details of an existing batch.
589
// $user has access to abort/retire the batch
590
// and to get its output files
591
//
592
function handle_query_batch($user) {
593
    $batch_id = get_int('batch_id');
594
    $batch = BoincBatch::lookup_id($batch_id);
595
    $app = BoincApp::lookup_id($batch->app_id);
596
    $wus = BoincWorkunit::enum_fields(
597
        'id, name, rsc_fpops_est, canonical_credit, canonical_resultid, error_mask',
598
        "batch = $batch->id"
599
    );
600
    $batch = get_batch_params($batch, $wus);
601
    if ($batch->user_id == $user->id) {
602
        $owner = $user;
603
    } else {
604
        $owner = BoincUser::lookup_id($batch->user_id);
605
    }
606
607
    $is_assim_move = is_assim_move($app);
608
609
    page_head("Batch $batch_id");
610
    text_start(800);
611
    start_table();
612
    row2("name", $batch->name);
613
    if ($batch->description) {
614
        row2('description', $batch->description);
615
    }
616
    if ($owner) {
617
        row2('submitter', $owner->name);
618
    }
619
    row2("application", $app?$app->name:'---');
620
    row2("state", batch_state_string($batch->state));
621
    //row2("# jobs", $batch->njobs);
622
    //row2("# error jobs", $batch->nerror_jobs);
623
    //row2("logical end time", time_str($batch->logical_end_time));
624
    if ($batch->expire_time) {
625
        row2("expiration time", time_str($batch->expire_time));
626
    }
627
    if ($batch->njobs) {
628
        row2("progress", progress_bar($batch, $wus, 600));
629
    }
630
    if ($batch->completion_time) {
631
        row2("completed", local_time_str($batch->completion_time));
632
    }
633
    row2("GFLOP/hours, estimated", number_format(credit_to_gflop_hours($batch->credit_estimate), 2));
634
    row2("GFLOP/hours, actual", number_format(credit_to_gflop_hours($batch->credit_canonical), 2));
635
    if (!$is_assim_move) {
636
        row2("Total size of output files",
637
            size_string(batch_output_file_size($batch->id))
638
        );
639
    }
640
    end_table();
641
    echo "<p>";
642
643
    if ($is_assim_move) {
644
        $url = "get_output3.php?action=get_batch&batch_id=$batch->id";
645
    } else {
646
        $url = "get_output2.php?cmd=batch&batch_id=$batch->id";
647
    }
648
    echo "<p>";
649
    show_button($url, "Get zipped output files");
650
    echo "<p>";
651
    switch ($batch->state) {
652
    case BATCH_STATE_IN_PROGRESS:
653
        show_button(
654
            "submit.php?action=abort_batch&batch_id=$batch_id",
655
            "Abort batch"
656
        );
657
        break;
658
    case BATCH_STATE_COMPLETE:
659
    case BATCH_STATE_ABORTED:
660
        show_button(
661
            "submit.php?action=retire_batch&batch_id=$batch_id",
662
            "Retire batch"
663
        );
664
        break;
665
    }
666
    echo "<p>
667
        <h3>Completed jobs</h3>
668
        <ul>
669
        <li>
670
        <a href=submit_stats.php?action=flops_graph&batch_id=$batch_id>Job runtimes</a>
671
        <li>
672
        <a href=submit.php?action=batch_stats&batch_id=$batch_id>Memory/disk usage</a>
673
        <li>
674
        <a href=submit_stats.php?action=show_hosts&batch_id=$batch_id>Grouped by host</a>
675
        </ul>
676
        <h3>Failed jobs</h3>
677
        <ul>
678
        <li>
679
        <a href=submit_stats.php?action=err_host&batch_id=$batch_id>Grouped by host</a>
680
        <li>
681
        <a href=submit_stats.php?action=err_code&batch_id=$batch_id>Grouped by exit code</a>
682
        </ul>
683
    ";
684
685
    echo "<h2>Jobs</h2>\n";
686
    start_table();
687
    $x = [
688
        "Name <br><small>click for details</small>",
689
        "status",
690
        "GFLOPS-hours"
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
691
    ];
692
    row_heading_array($x);
693
    foreach($wus as $wu) {
694
        $y = '';
695
        $c = '---';
696
        switch($wu->status) {
697
        case WU_SUCCESS:
698
            $resultid = $wu->canonical_resultid;
699
            $y = sprintf('<font color="%s">completed</font>', COLOR_SUCCESS);
700
            $c = number_format(
701
                credit_to_gflop_hours($wu->canonical_credit), 2
702
            );
703
            break;
704
        case WU_ERROR:
705
            $y = sprintf('<font color="%s">failed</font>', COLOR_FAIL);
706
            break;
707
        case WU_IN_PROGRESS:
708
            $y = sprintf('<font color="%s">in progress</font>', COLOR_IN_PROGRESS);
709
            break;
710
        case WU_UNSENT:
711
            $y = sprintf('<font color="%s">unsent</font>', COLOR_UNSENT);
712
            break;
713
        }
714
        $x = [
715
            "<a href=submit.php?action=query_job&wuid=$wu->id>$wu->name</a>",
716
            $y,
717
            $c
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
718
        ];
719
        row_array($x);
720
    }
721
    end_table();
722
    return_link();
723
    text_end();
724
    page_tail();
725
}
726
727
// Does the assimilator for the given app move output files
728
// to a results/<batchid>/ directory?
729
// This info is stored in the $remote_apps data structure in project.inc
730
//
731
function is_assim_move($app) {
732
    global $remote_apps;
733
    foreach ($remote_apps as $category => $apps) {
734
        foreach ($apps as $web_app) {
735
            if ($web_app->app_name == $app->name) {
736
                return $web_app->is_assim_move;
737
            }
738
        }
739
    }
740
    return false;
741
}
742
743
// show the details of a job, including links to see the output files
744
//
745
function handle_query_job($user) {
746
    $wuid = get_int('wuid');
747
    $wu = BoincWorkunit::lookup_id($wuid);
748
    if (!$wu) error_page("no such job");
749
750
    $app = BoincApp::lookup_id($wu->appid);
751
    $is_assim_move = is_assim_move($app);
752
753
    page_head("Job '$wu->name'");
754
    text_start(800);
755
756
    echo "
757
        <li><a href=workunit.php?wuid=$wuid>Job details</a>
758
        <p>
759
        <li><a href=submit.php?action=query_batch&batch_id=$wu->batch>Batch details</a>
760
    ";
761
    $d = "<foo>$wu->xml_doc</foo>";
762
    $x = simplexml_load_string($d);
763
    $x = $x->workunit;
764
    //echo "foo: $x->command_line";
765
766
    echo "<h2>Job instances</h2>\n";
767
    start_table('table-striped');
768
    table_header(
769
        "ID<br><small>click for details and stderr</small>",
770
        "State",
771
        "Output files"
772
    );
773
    $results = BoincResult::enum("workunitid=$wuid");
774
    $upload_dir = parse_config(get_config(), "<upload_dir>");
775
    $fanout = parse_config(get_config(), "<uldl_dir_fanout>");
776
    foreach($results as $result) {
777
        $x = [
778
            "<a href=result.php?resultid=$result->id>$result->id</a>",
779
            state_string($result)
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
780
        ];
781
        $i = 0;
782
        if ($result->server_state == RESULT_SERVER_STATE_OVER) {
783
            $phys_names = get_outfile_phys_names($result);
784
            $log_names = get_outfile_log_names($result);
785
            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...
786
                if ($is_assim_move) {
787
                    // file is in
788
                    // project/results/<batchid>/<wu_name>__file_<log_name>
789
                    $path = sprintf('results/%s/%s__file_%s',
790
                        $wu->batch, $wu->name, $log_names[$i]
791
                    );
792
                    $name = $log_names[$i];
793
                    // don't show 'view' link if it's a .zip
794
                    $y = "$name: ";
795
                    if (!strstr($name, '.zip')) {
796
                        $y .= "<a href=get_output3.php?action=get_file&path=$path>view</a> &middot; ";
797
                    }
798
                    $y .= "<a href=get_output3.php?action=get_file&path=$path&download=1>download</a>";
799
                    $x[] = $y;
800
                } else {
801
                    $path = dir_hier_path(
802
                        $phys_names[$i], $upload_dir, $fanout
803
                    );
804
                    if (file_exists($path)) {
805
                        $url = sprintf(
806
                            'get_output2.php?cmd=result&result_id=%d&file_num=%d',
807
                            $result->id, $i
808
                        );
809
                        $s = stat($path);
810
                        $size = $s['size'];
811
                        $x[] = sprintf('<a href=%s>%s</a> (%s bytes)<br/>',
812
                            $url,
813
                            $log_names[$i],
814
                            number_format($size)
815
                        );
816
                    } else {
817
                        $x[] = sprintf("file '%s' is missing", $log_names[$i]);
818
                    }
819
                }
820
            }
821
        } else {
822
            $x[] = '---';
823
        }
824
        row_array($x);
825
    }
826
    end_table();
827
828
    // show input files
829
    //
830
    echo "<h2>Input files</h2>\n";
831
    $x = "<in>".$wu->xml_doc."</in>";
832
    $x = simplexml_load_string($x);
833
    start_table('table-striped');
834
    table_header("Name<br><small>(click to view)</small>", "Size (bytes)");
835
    foreach ($x->workunit->file_ref as $fr) {
836
        $pname = (string)$fr->file_name;
837
        $lname = (string)$fr->open_name;
838
        foreach ($x->file_info as $fi) {
839
            if ((string)$fi->name == $pname) {
840
                table_row(
841
                    "<a href=$fi->url>$lname</a>",
842
                    $fi->nbytes
843
                );
844
                break;
845
            }
846
        }
847
    }
848
849
    end_table();
850
    text_end();
851
    return_link();
852
    page_tail();
853
}
854
855
// is user allowed to retire or abort this batch?
856
//
857
function has_access($user, $batch) {
858
    if ($user->id == $batch->user_id) return true;
859
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
860
    if ($user_submit->manage_all) return true;
861
    $usa = BoincUserSubmitApp::lookup("user_id=$user->id and app_id=$batch->app_id");
862
    if ($usa->manage) return true;
863
    return false;
864
}
865
866
function handle_abort_batch($user) {
867
    $batch_id = get_int('batch_id');
868
    $batch = BoincBatch::lookup_id($batch_id);
869
    if (!$batch) error_page("no such batch");
870
    if (!has_access($user, $batch)) {
871
        error_page("no access");
872
    }
873
874
    if (get_int('confirmed', true)) {
875
        abort_batch($batch);
876
        page_head("Batch aborted");
877
        return_link();
878
        page_tail();
879
    } else {
880
        page_head("Confirm abort batch");
881
        echo "
882
            Aborting a batch will cancel all unstarted jobs.
883
            Are you sure you want to do this?
884
            <p>
885
        ";
886
        show_button(
887
            "submit.php?action=abort_batch&batch_id=$batch_id&confirmed=1",
888
            "Yes - abort batch"
889
        );
890
        return_link();
891
        page_tail();
892
    }
893
}
894
895
function handle_retire_batch($user) {
896
    $batch_id = get_int('batch_id');
897
    $batch = BoincBatch::lookup_id($batch_id);
898
    if (!$batch) error_page("no such batch");
899
    if (!has_access($user, $batch)) {
900
        error_page("no access");
901
    }
902
903
    if (get_int('confirmed', true)) {
904
        retire_batch($batch);
905
        page_head("Batch $batch_id retired");
906
        return_link();
907
        page_tail();
908
    } else {
909
        page_head("Confirm retire batch");
910
        echo "
911
            Retiring a batch will remove all of its output files.
912
            Are you sure you want to do this?
913
            <p>
914
        ";
915
        show_button(
916
            "submit.php?action=retire_batch&batch_id=$batch_id&confirmed=1",
917
            "Yes - retire batch"
918
        );
919
        return_link();
920
        page_tail();
921
    }
922
}
923
924
// retire multiple batches
925
//
926
function handle_retire_multi($user) {
927
    $batches = BoincBatch::enum(
928
        sprintf('state=%d', BATCH_STATE_COMPLETE)
929
    );
930
    page_head('Retiring batches');
931
    foreach ($batches as $batch) {
932
        if (!has_access($user, $batch)) {
933
            continue;
934
        }
935
        $x = sprintf('retire_%d', $batch->id);
936
        if (get_str($x, true) == 'on') {
937
            retire_batch($batch);
938
            echo "<p>retired batch $batch->id ($batch->name)\n";
939
        }
940
    }
941
    return_link();
942
    page_tail();
943
}
944
945
// given a list of batches, show the ones in a given state
946
//
947
function show_batches_in_state($batches, $state, $url_args, $order) {
948
    switch ($state) {
949
    case BATCH_STATE_IN_PROGRESS:
950
        page_head("Batches in progress");
951
        order_options($url_args, $order);
952
        show_in_progress($batches, 0, null, null);
953
        break;
954
    case BATCH_STATE_COMPLETE:
955
        page_head("Completed batches");
956
        order_options($url_args, $order);
957
        show_complete($batches, 0, null, null);
958
        break;
959
    case BATCH_STATE_ABORTED:
960
        page_head("Aborted batches");
961
        order_options($url_args, $order);
962
        show_aborted($batches, 0, null, null);
963
        break;
964
    }
965
    page_tail();
966
}
967
968
function handle_show_all($user) {
969
    $userid = get_int("userid");
970
    $appid = get_int("appid");
971
    $state = get_int("state");
972
    $order = get_order();
973
    $url_args = "action=show_all&state=$state&userid=$userid&appid=$appid";
974
    $clause = order_clause($order);
975
    if ($userid) {
976
        // user looking at their own batches
977
        //
978
        if ($userid != $user->id) error_page("wrong user");
979
        $batches = BoincBatch::enum("user_id=$user->id and state=$state order by $clause");
980
        fill_in_app_and_user_names($batches);
981
        show_batches_in_state($batches, $state, $url_args, $order);
982
    } else {
983
        // admin looking at batches
984
        //
985
        if (!has_manage_access($user, $appid)) {
986
            error_page('no access');
987
        }
988
        if ($appid) {
989
            $app = BoincApp::lookup_id($appid);
990
            if (!$app) error_page("no such app");
991
            $batches = BoincBatch::enum("app_id=$appid and state=$state order by $clause");
992
        } else {
993
            $batches = BoincBatch::enum("state=$state order by $clause");
994
        }
995
        fill_in_app_and_user_names($batches);
996
        show_batches_in_state($batches, $state, $url_args, $order);
997
    }
998
}
999
1000
$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...
1001
1002
$action = get_str('action', true);
1003
1004
switch ($action) {
1005
case '': handle_main($user); break;
1006
case 'abort_batch': handle_abort_batch($user); break;
1007
case 'admin': handle_admin($user); break;
1008
case 'admin_app': handle_admin_app($user); break;
1009
case 'admin_all': handle_admin_all($user); break;
1010
case 'batch_stats': handle_batch_stats($user); break;
1011
case 'query_batch': handle_query_batch($user); break;
1012
case 'query_job': handle_query_job($user); break;
1013
case 'retire_batch': handle_retire_batch($user); break;
1014
case 'retire_multi': handle_retire_multi($user); break;
1015
case 'show_all': handle_show_all($user); break;
1016
case 'status': handle_show_status($user); break;
1017
case 'update_only_own': handle_update_only_own($user); break;
1018
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...
1019
    error_page("no such action $action");
1020
}
1021
1022
?>
1023