Issues (1963)

html/user/submit.php (10 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
    show_button("submit_stats.php?action=flops_graph&batch_id=$batch_id",
668
        "Job runtimes"
669
    );
670
    show_button("submit.php?action=batch_stats&batch_id=$batch_id",
671
        "Memory/disk usage"
672
    );
673
    echo "<p>";
674
    show_button("submit_stats.php?action=err_host&batch_id=$batch_id",
675
        "Errors by host"
676
    );
677
    show_button("submit_stats.php?action=err_code&batch_id=$batch_id",
678
        "Errors by exit code"
679
    );
680
681
    echo "<h2>Jobs</h2>\n";
682
    start_table();
683
    $x = [
684
        "Name <br><small>click for details</small>",
685
        "status"
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
686
    ];
687
    row_heading_array($x);
688
    foreach($wus as $wu) {
689
        $y = '';
690
        switch($wu->status) {
691
        case WU_SUCCESS:
692
            $resultid = $wu->canonical_resultid;
693
            $y = sprintf('<font color="%s">completed</font>', COLOR_SUCCESS);
694
            break;
695
        case WU_ERROR:
696
            $y = sprintf('<font color="%s">failed</font>', COLOR_FAIL);
697
            break;
698
        case WU_IN_PROGRESS:
699
            $y = sprintf('<font color="%s">in progress</font>', COLOR_IN_PROGRESS);
700
            break;
701
        case WU_UNSENT:
702
            $y = sprintf('<font color="%s">unsent</font>', COLOR_UNSENT);
703
            break;
704
        }
705
        $x = [
706
            "<a href=submit.php?action=query_job&wuid=$wu->id>$wu->name</a>",
707
            $y,
708
        ];
709
        row_array($x);
710
    }
711
    end_table();
712
    return_link();
713
    text_end();
714
    page_tail();
715
}
716
717
// Does the assimilator for the given app move output files
718
// to a results/<batchid>/ directory?
719
// This info is stored in the $remote_apps data structure in project.inc
720
//
721
function is_assim_move($app) {
722
    global $remote_apps;
723
    foreach ($remote_apps as $category => $apps) {
724
        foreach ($apps as $web_app) {
725
            if ($web_app->app_name == $app->name) {
726
                return $web_app->is_assim_move;
727
            }
728
        }
729
    }
730
    return false;
731
}
732
733
// show the details of a job, including links to see the output files
734
//
735
function handle_query_job($user) {
736
    $wuid = get_int('wuid');
737
    $wu = BoincWorkunit::lookup_id($wuid);
738
    if (!$wu) error_page("no such job");
739
740
    $app = BoincApp::lookup_id($wu->appid);
741
    $is_assim_move = is_assim_move($app);
742
743
    page_head("Job '$wu->name'");
744
    text_start(800);
745
746
    echo "
747
        <li><a href=workunit.php?wuid=$wuid>Job details</a>
748
        <p>
749
        <li><a href=submit.php?action=query_batch&batch_id=$wu->batch>Batch details</a>
750
    ";
751
    $d = "<foo>$wu->xml_doc</foo>";
752
    $x = simplexml_load_string($d);
753
    $x = $x->workunit;
754
    //echo "foo: $x->command_line";
755
756
    echo "<h2>Job instances</h2>\n";
757
    start_table('table-striped');
758
    table_header(
759
        "ID<br><small>click for details and stderr</small>",
760
        "State",
761
        "Output files"
762
    );
763
    $results = BoincResult::enum("workunitid=$wuid");
764
    $upload_dir = parse_config(get_config(), "<upload_dir>");
765
    $fanout = parse_config(get_config(), "<uldl_dir_fanout>");
766
    foreach($results as $result) {
767
        $x = [
768
            "<a href=result.php?resultid=$result->id>$result->id</a>",
769
            state_string($result)
0 ignored issues
show
There should be a trailing comma after the last value of an array declaration.
Loading history...
770
        ];
771
        $i = 0;
772
        if ($result->server_state == RESULT_SERVER_STATE_OVER) {
773
            $phys_names = get_outfile_phys_names($result);
774
            $log_names = get_outfile_log_names($result);
775
            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...
776
                if ($is_assim_move) {
777
                    // file is in
778
                    // project/results/<batchid>/<wu_name>__file_<log_name>
779
                    $path = sprintf('results/%s/%s__file_%s',
780
                        $wu->batch, $wu->name, $log_names[$i]
781
                    );
782
                    $name = $log_names[$i];
783
                    // don't show 'view' link if it's a .zip
784
                    $y = "$name: ";
785
                    if (!strstr($name, '.zip')) {
786
                        $y .= "<a href=get_output3.php?action=get_file&path=$path>view</a> &middot; ";
787
                    }
788
                    $y .= "<a href=get_output3.php?action=get_file&path=$path&download=1>download</a>";
789
                    $x[] = $y;
790
                } else {
791
                    $path = dir_hier_path(
792
                        $phys_names[$i], $upload_dir, $fanout
793
                    );
794
                    if (file_exists($path)) {
795
                        $url = sprintf(
796
                            'get_output2.php?cmd=result&result_id=%d&file_num=%d',
797
                            $result->id, $i
798
                        );
799
                        $s = stat($path);
800
                        $size = $s['size'];
801
                        $x[] = sprintf('<a href=%s>%s</a> (%s bytes)<br/>',
802
                            $url,
803
                            $log_names[$i],
804
                            number_format($size)
805
                        );
806
                    } else {
807
                        $x[] = sprintf("file '%s' is missing", $log_names[$i]);
808
                    }
809
                }
810
            }
811
        } else {
812
            $x[] = '---';
813
        }
814
        row_array($x);
815
    }
816
    end_table();
817
818
    // show input files
819
    //
820
    echo "<h2>Input files</h2>\n";
821
    $x = "<in>".$wu->xml_doc."</in>";
822
    $x = simplexml_load_string($x);
823
    start_table('table-striped');
824
    table_header("Name<br><small>(click to view)</small>", "Size (bytes)");
825
    foreach ($x->workunit->file_ref as $fr) {
826
        $pname = (string)$fr->file_name;
827
        $lname = (string)$fr->open_name;
828
        foreach ($x->file_info as $fi) {
829
            if ((string)$fi->name == $pname) {
830
                table_row(
831
                    "<a href=$fi->url>$lname</a>",
832
                    $fi->nbytes
833
                );
834
                break;
835
            }
836
        }
837
    }
838
839
    end_table();
840
    text_end();
841
    return_link();
842
    page_tail();
843
}
844
845
// is user allowed to retire or abort this batch?
846
//
847
function has_access($user, $batch) {
848
    if ($user->id == $batch->user_id) return true;
849
    $user_submit = BoincUserSubmit::lookup_userid($user->id);
850
    if ($user_submit->manage_all) return true;
851
    $usa = BoincUserSubmitApp::lookup("user_id=$user->id and app_id=$batch->app_id");
852
    if ($usa->manage) return true;
853
    return false;
854
}
855
856
function handle_abort_batch($user) {
857
    $batch_id = get_int('batch_id');
858
    $batch = BoincBatch::lookup_id($batch_id);
859
    if (!$batch) error_page("no such batch");
860
    if (!has_access($user, $batch)) {
861
        error_page("no access");
862
    }
863
864
    if (get_int('confirmed', true)) {
865
        abort_batch($batch);
866
        page_head("Batch aborted");
867
        return_link();
868
        page_tail();
869
    } else {
870
        page_head("Confirm abort batch");
871
        echo "
872
            Aborting a batch will cancel all unstarted jobs.
873
            Are you sure you want to do this?
874
            <p>
875
        ";
876
        show_button(
877
            "submit.php?action=abort_batch&batch_id=$batch_id&confirmed=1",
878
            "Yes - abort batch"
879
        );
880
        return_link();
881
        page_tail();
882
    }
883
}
884
885
function handle_retire_batch($user) {
886
    $batch_id = get_int('batch_id');
887
    $batch = BoincBatch::lookup_id($batch_id);
888
    if (!$batch) error_page("no such batch");
889
    if (!has_access($user, $batch)) {
890
        error_page("no access");
891
    }
892
893
    if (get_int('confirmed', true)) {
894
        retire_batch($batch);
895
        page_head("Batch $batch_id retired");
896
        return_link();
897
        page_tail();
898
    } else {
899
        page_head("Confirm retire batch");
900
        echo "
901
            Retiring a batch will remove all of its output files.
902
            Are you sure you want to do this?
903
            <p>
904
        ";
905
        show_button(
906
            "submit.php?action=retire_batch&batch_id=$batch_id&confirmed=1",
907
            "Yes - retire batch"
908
        );
909
        return_link();
910
        page_tail();
911
    }
912
}
913
914
// retire multiple batches
915
//
916
function handle_retire_multi($user) {
917
    $batches = BoincBatch::enum(
918
        sprintf('state=%d', BATCH_STATE_COMPLETE)
919
    );
920
    page_head('Retiring batches');
921
    foreach ($batches as $batch) {
922
        if (!has_access($user, $batch)) {
923
            continue;
924
        }
925
        $x = sprintf('retire_%d', $batch->id);
926
        if (get_str($x, true) == 'on') {
927
            retire_batch($batch);
928
            echo "<p>retired batch $batch->id ($batch->name)\n";
929
        }
930
    }
931
    return_link();
932
    page_tail();
933
}
934
935
// given a list of batches, show the ones in a given state
936
//
937
function show_batches_in_state($batches, $state, $url_args, $order) {
938
    switch ($state) {
939
    case BATCH_STATE_IN_PROGRESS:
940
        page_head("Batches in progress");
941
        order_options($url_args, $order);
942
        show_in_progress($batches, 0, null, null);
943
        break;
944
    case BATCH_STATE_COMPLETE:
945
        page_head("Completed batches");
946
        order_options($url_args, $order);
947
        show_complete($batches, 0, null, null);
948
        break;
949
    case BATCH_STATE_ABORTED:
950
        page_head("Aborted batches");
951
        order_options($url_args, $order);
952
        show_aborted($batches, 0, null, null);
953
        break;
954
    }
955
    page_tail();
956
}
957
958
function handle_show_all($user) {
959
    $userid = get_int("userid");
960
    $appid = get_int("appid");
961
    $state = get_int("state");
962
    $order = get_order();
963
    $url_args = "action=show_all&state=$state&userid=$userid&appid=$appid";
964
    $clause = order_clause($order);
965
    if ($userid) {
966
        // user looking at their own batches
967
        //
968
        if ($userid != $user->id) error_page("wrong user");
969
        $batches = BoincBatch::enum("user_id=$user->id and state=$state order by $clause");
970
        fill_in_app_and_user_names($batches);
971
        show_batches_in_state($batches, $state, $url_args, $order);
972
    } else {
973
        // admin looking at batches
974
        //
975
        if (!has_manage_access($user, $appid)) {
976
            error_page('no access');
977
        }
978
        if ($appid) {
979
            $app = BoincApp::lookup_id($appid);
980
            if (!$app) error_page("no such app");
981
            $batches = BoincBatch::enum("app_id=$appid and state=$state order by $clause");
982
        } else {
983
            $batches = BoincBatch::enum("state=$state order by $clause");
984
        }
985
        fill_in_app_and_user_names($batches);
986
        show_batches_in_state($batches, $state, $url_args, $order);
987
    }
988
}
989
990
$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...
991
992
$action = get_str('action', true);
993
994
switch ($action) {
995
case '': handle_main($user); break;
996
case 'abort_batch': handle_abort_batch($user); break;
997
case 'admin': handle_admin($user); break;
998
case 'admin_app': handle_admin_app($user); break;
999
case 'admin_all': handle_admin_all($user); break;
1000
case 'batch_stats': handle_batch_stats($user); break;
1001
case 'query_batch': handle_query_batch($user); break;
1002
case 'query_job': handle_query_job($user); break;
1003
case 'retire_batch': handle_retire_batch($user); break;
1004
case 'retire_multi': handle_retire_multi($user); break;
1005
case 'show_all': handle_show_all($user); break;
1006
case 'status': handle_show_status($user); break;
1007
case 'update_only_own': handle_update_only_own($user); break;
1008
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...
1009
    error_page("no such action $action");
1010
}
1011
1012
?>
1013