Passed
Push — master ( be4d11...9a361d )
by Vitalii
08:27 queued 16s
created

has_access()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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