- Add web interface for creating BUDA apps and variants

variants are stored in <project>/buda_apps/<app>/<variant>.
    This includes app files (copied from sandbox) and templates
    (generated by handler)
- Add web interface for submitting BUDA jobs (not finished)
- Change implementation of user file sandbox
    old: sandbox dir had 'link files' containing md5 and size;
        actual file is in download hierarchy with sb_md5 name
    new: sandbox dir has actual files.
        parallel .md5/ dir has 'info files' (md5 size)
        Files are not stored in download hierarchy.
    New philosophy: names in the download hierarchy include
        not only an MD5 (for uniqueness)
        but also text describing the use of the file
        (input file for a batch, part of a BUDA app, etc.).
        This may allow duplicate files,
        but it makes it possible to always clean up unused files.

- use readdir() instead of opendir()/scandir()
This commit is contained in:
David Anderson 2024-11-18 13:43:44 -08:00
parent dd16cab2c8
commit bc4cb3bb68
9 changed files with 568 additions and 220 deletions

View File

@ -116,8 +116,8 @@ class DbConn {
function enum_general($classname, $query) {
$result = $this->do_query($query);
if (!$result) return null;
$x = array();
if (!$result) return [];
$x = [];
while ($obj = $result->fetch_object($classname)) {
$x[] = $obj;
}
@ -133,7 +133,7 @@ class DbConn {
$where_clause = "where $where_clause";
}
$query = "select $fields from DBNAME.$table $where_clause $order_clause";
return $this->enum_general($classname,$query);
return $this->enum_general($classname, $query);
}
function enum($table, $classname, $where_clause=null, $order_clause=null) {

View File

@ -120,9 +120,7 @@ function check_download_file($path, $dl_path) {
}
}
// Stage the given file, which is assumed to not be in download dir already.
//
function stage_file_basic($dir, $fname) {
function get_hier_info() {
static $dl_dir = null;
static $fanout = null;
if (!$dl_dir) {
@ -130,6 +128,13 @@ function stage_file_basic($dir, $fname) {
$dl_dir = parse_config($conf, "<download_dir>");
$fanout = parse_config($conf, "<uldl_dir_fanout>");
}
return [$dl_dir, $fanout];
}
// Stage the given file, which is assumed to not be in download dir already.
//
function stage_file_basic($dir, $fname) {
[$dl_dir, $fanout] = get_hier_info();
$old_path = "$dir/$fname";
$new_path = dir_hier_path($fname, $dl_dir, $fanout);
$md5 = md5_file($old_path);
@ -138,4 +143,14 @@ function stage_file_basic($dir, $fname) {
rename($old_path, $new_path);
}
// copy the given file (with known size/md5)
// to the download dir w/ given phys name
//
function stage_file_aux($path, $md5, $size, $phys_name) {
[$dl_dir, $fanout] = get_hier_info();
$phys_path = dir_hier_path($phys_name, $dl_dir, $fanout);
copy($path, $phys_path);
file_put_contents("$phys_path.md5", "$md5 $size\n");
}
?>

View File

@ -19,14 +19,23 @@
// Utility functions for user file sandbox feature
//
// In this system:
// - sandbox files live in the download hierarchy,
// with names of the form sb_userid_md5
// - each user has a "sandbox dir" project/sandbox/userid.
// The files in this have user-visible names, and contents of the form
// sb file_size file_md5
// - each user (job submitter) has a 'sandbox' where they can store files
// on the BOINC server, via a web interface.
// These files are mutable; you can modify a file w/ a given name.
// - files are stored in a dir project/sandbox/<userid>
// - When a file is uploaded, its size and MD5 are computed and stored
// in an 'info file' in a parallel dir, project/sandbox/<userid>/.md5
//
// Sandbox files can be used for web-based job submissions systems
// like BUDA and autodock on BOINC Central.
// Typically they are used as job input files or app files,
// in which case they are downloadable.
// When a file is used in this way,
// it must be copied to the download hierarchy,
// and assigned a physical name that includes its MD5.
// The name depends on the role of the file.
require_once("../inc/util.inc");
require_once("../inc/submit_db.inc");
require_once("../inc/dir_hier.inc");
// Return path of sandbox directory for the given user.
@ -44,75 +53,53 @@ function sandbox_dir($user) {
if (!is_dir($d)) {
mkdir($d);
}
if (!is_dir("$d/.md5")) {
mkdir("$d/.md5");
}
return $d;
}
function sandbox_write_link_file($path, $size, $md5) {
file_put_contents($path, "sb $size $md5");
}
// check if a link with the given md5 exists
// parse a sandbox file's info file.
// If missing, create it.
//
function sandbox_lf_exists($user, $md5) {
$exist = false;
$elf = "";
function sandbox_parse_info_file($user, $name) {
$dir = sandbox_dir($user);
$files = sandbox_file_names($user);
foreach ($files as $file) {
$path = "$dir/$file";
[$err, $file_size, $file_md5] = sandbox_parse_link_file($path);
if (!$err){
if (strcmp($md5, $file_md5) == 0) {
$exist = true;
$elf = $file;
break;
}
}
$info_path = "$dir/.md5/$name";
$info = parse_info_file($info_path);
if ($info) {
return $info;
}
return array($exist, $elf);
}
// parse a link file and return (error, size, md5)
//
function sandbox_parse_link_file($path) {
if (!file_exists($path)) { return array(true, null, null); }
$x = file_get_contents($path);
$n = sscanf($x, "%s %d %s", $s, $size, $md5);
if ($n != 3) return array(true, null, null);
if ($s != 'sb') return array(true, null, null);
return array(false, $size, $md5);
}
$fanout = parse_config(get_config(), "<uldl_dir_fanout>");
// return the physical name of the file
//
function sandbox_file_name($user, $md5) {
return "sb_".$user->id."_".$md5;
}
// return the path of the file in the download directory
//
function sandbox_physical_path($user, $md5) {
global $fanout;
$f = sandbox_file_name($user, $md5);
return dir_hier_path($f, parse_config(get_config(), "<download_dir>"), $fanout);
[$size, $md5] = get_file_info("$dir/$name");
write_info_file($info_path, $md5, $size);
return [$md5, $size];
}
// return list of files in sandbox
//
function sandbox_file_names($user) {
$d = opendir(sandbox_dir($user));
$files = scandir(sandbox_dir($user));
$names = array();
while (($f = readdir($d)) !== false) {
if ($f == '.') continue;
if ($f == '..') continue;
foreach ($files as $f) {
if ($f[0] == '.') continue;
$names[] = $f;
}
natsort($names);
return $names;
}
// return list of files matching given pattern,
// in the format used for form_select() and form_select_multiple()
//
function sandbox_select_items($user, $pattern=null) {
$sbfiles = sandbox_file_names($user);
$sbitems = [];
foreach ($sbfiles as $f) {
if ($pattern && !preg_match($pattern, $f)) continue;
$sbitems[] = [$f, $f];
}
return $sbitems;
}
// return a <select> for files in sandbox
//
function sandbox_file_select(
@ -131,66 +118,13 @@ function sandbox_file_select(
return $x;
}
// convert sandbox (link) name to physical path
// copy file and info file from sandbox to $dir
// (which must have a subdir .md5/)
//
function sandbox_log_to_phys($user, $log_name) {
$dir = sandbox_dir($user);
[$err, $file_size, $file_md5] = sandbox_parse_link_file("$dir/$log_name");
if ($err) return null;
return sandbox_physical_path($user, $file_md5);
}
// convert sandbox name to physical name
//
function sandbox_name_to_phys_name($user, $log_name) {
$dir = sandbox_dir($user);
[$err, $file_size, $file_md5] = sandbox_parse_link_file("$dir/$log_name");
if ($err) return null;
return sandbox_file_name($user, $file_md5);
}
// check if a file is still being used by a unfinished batch
//
// TODO: this is a kludge.
// Should we use the job_file and batch_file_assoc tables instead?
//
function sandbox_file_in_use($user, $file){
$ufiles = array();
$pbatches = BoincBatch::enum(
sprintf('user_id=%d and state!=%d and state!=%d',
$user->id, BATCH_STATE_COMPLETE, BATCH_STATE_ABORTED
)
);
if (!$pbatches) return false;
foreach ($pbatches as $batch){
$wus = BoincWorkUnit::enum("batch = $batch->id limit 1" );
if ($wus == null){
continue;
}
foreach($wus as $wu){
$x = "<in>".$wu->xml_doc."</in>";
$x = simplexml_load_string($x);
global $fanout;
foreach($x->workunit->file_ref as $fr){
$pname = (string)$fr->file_name;
$ufiles[] = $pname;
}
}
}
$dir = sandbox_dir($user);
$path = $dir."/".$file;
list($err, $size, $md5) = sandbox_parse_link_file($path);
if (!$err){
$f = sandbox_file_name($user, $md5);
foreach($ufiles as $uf) {
if (strcmp($f,$uf) == 0){
return true;
}
}
}
return false;
function copy_sandbox_file($user, $fname, $dir) {
$sbdir = sandbox_dir($user);
copy("$sbdir/$fname", "$dir/$fname");
copy("$sbdir/.md5/$fname", "$dir/.md5/$fname");
}
?>

View File

@ -288,4 +288,40 @@ function boinc_get_wu_output_files_url($user, $wu_id) {
return "get_output.php?cmd=workunit_files&wu_id=$wu_id&auth_str=$auth_str";
}
////////////////// FILE INFO FILES //////////////
// these are used:
// 1) in user file sandbox
// 2) in BUDA app variant dirs
// in each case a file dir/foo has an info file dir/.md5/foo
// containing its md5 and size
// (same format as .md5 files in download hierarchy)
// get the MD5 and size of a file
//
function get_file_info($path) {
$md5 = md5_file($path);
$s = stat($path);
$size = $s['size'];
return [$md5, $size];
}
// write a "info file" containing MD5 and size
//
function write_info_file($path, $md5, $size) {
file_put_contents($path, "$md5 $size");
}
// parse info file and return [md5, size]
//
function parse_info_file($path) {
if (!file_exists($path)) return null;
$x = file_get_contents($path);
$n = sscanf($x, "%s %d", $md5, $size);
if ($n != 2 || strlen($md5)!=32) {
return null;
}
return [$md5, $size];
}
?>

View File

@ -1133,10 +1133,8 @@ function credit_to_gflop_hours($c) {
return $c/(200/24);
}
function do_download($path,$name="") {
if (strcmp($name,"") == 0) {
$name=basename($path);
}
function do_download($path) {
$name=basename($path);
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.$name);

330
html/user/buda.php Normal file
View File

@ -0,0 +1,330 @@
<?php
// This file is part of BOINC.
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// web interface for managing BUDA science apps
//
// in the following, 'app' means BUDA science app
// and 'variant' means a variant of one of these
//
// files can have 3 names
// - sandbox name
// - physical name (e.g. sb_<md5>)
// - logical name (how the science app refers to it)
require_once('../inc/util.inc');
require_once('../inc/sandbox.inc');
require_once('../inc/submit_util.inc');
display_errors();
$buda_root = "../../buda_apps";
// show list of BUDA apps and variants,
// w/ buttons for adding and deleting
//
function app_list($notice=null) {
global $buda_root;
if (!is_dir($buda_root)) {
mkdir($buda_root);
}
page_head('Docker apps');
if ($notice) {
echo "$notice <p>\n";
}
$dirs = scandir($buda_root);
foreach ($dirs as $dir) {
if ($dir[0] == '.') continue;
show_app($dir);
}
echo '<hr>';
show_button_small('buda.php?action=app_form', 'Add app');
page_tail();
}
function show_app($dir) {
global $buda_root;
$indent = "&nbsp;&nbsp;&nbsp;&nbsp&nbsp;&nbsp;&nbsp;&nbsp";
echo "<hr><font size=+3>$dir</font>\n";
echo "<p>$indent Variants (click for details):<ul>";
$pcs = scandir("$buda_root/$dir");
foreach ($pcs as $pc) {
if ($pc[0] == '.') continue;
echo "<li><a href=buda.php?action=variant_view&app=$dir&variant=$pc>$pc</href>";
show_button_small("buda.php?action=variant_delete&app=$dir&variant=$pc", 'Delete variant');
show_button_small(
"buda_submit.php?app=$dir&variant=$pc", "Submit jobs"
);
}
echo '</ul>';
echo $indent;
show_button_small("buda.php?action=variant_form&app=$dir", 'Add variant');
echo "<p>";
echo "<p>";
show_button_small(
"buda.php?action=app_delete&app=$dir", "Delete app '$dir'"
);
}
function variant_view() {
global $buda_root;
$app = get_str('app');
$variant = get_str('variant');
page_head("App $app variant $variant");
$dir = "$buda_root/$app/$variant";
start_table();
table_header('name', 'size', 'md5');
foreach(scandir($dir) as $f) {
if ($f[0] == '.') continue;
[$size, $md5] = parse_info_file("$dir/.md5/$f");
table_row(
"<a href=buda.php?action=view_file&app=$app&variant=$variant&fname=$f>$f</a>",
$size,
$md5
);
}
end_table();
page_tail();
}
// form for creating app variant.
// Currently doesn't support indirect files.
// doing this would require checkboxes for indirect
//
// Could have other stuff like
// - min_quorum, max_total_results
// - rsc_disk_bound, rsc_memory_bound
// or does this belong in job submission?
//
function variant_form($user) {
$sbitems = sandbox_select_items($user);
$app = get_str('app');
page_head("Create variant of Docker app $app");
form_start('buda.php');
form_input_hidden('app', $app);
form_input_hidden('action', 'variant_action');
form_input_text('Plan class', 'plan_class');
form_select('Dockerfile', 'dockerfile', $sbitems);
form_select('Main program', 'main_prog', $sbitems);
form_select_multiple('Other application files', 'others', $sbitems);
form_input_text('Input file names', 'input_file_names');
form_input_text('Output file names', 'output_file_names');
form_submit('OK');
form_end();
page_tail();
}
// copy file from sandbox to variant dir, and stage to download hier
//
function copy_and_stage_file($user, $fname, $dir, $variant) {
copy_sandbox_file($user, $fname, $dir);
[$md5, $size] = parse_info_file("$dir/.md5/$fname");
$phys_name = sprintf('buda_%s_%s_%s', $app, $variant, $md5);
stage_file_aux("$dir/$fname", $md5, $size, $phys_name);
}
// create variant
//
function variant_action($user) {
global $buda_root;
$plan_class = get_str('plan_class');
$app = get_str('app');
$dockerfile = get_str('dockerfile');
$main_prog = get_str('main_prog');
$others = get_array('others');
$input_file_names = explode(' ', get_str('input_file_names'));
$output_file_names = explode(' ', get_str('output_file_names'));
if (file_exists("$buda_root/$app/$plan_class")) {
error_page("Variant '$plan_class' already exists.");
}
$dir = "$buda_root/$app/$plan_class";
mkdir($dir);
mkdir("$dir/.md5");
// copy files from sandbox to variant dir
//
copy_and_stage_file($user, $dockerfile, $dir, $app, $variant);
copy_and_stage_file($user, $main_prog, $dir, $app, $variant);
foreach ($others as $fname) {
copy_and_stage_file($user, $fname, $dir, $app, $variant);
}
// create input template
//
$x = "<input_template>\n";
$ninfiles = 2 + count($input_file_names) + count($others);
for ($i=0; $i<$ninfiles; $i++) {
$x .= " <file_info>\n </file_info>\n";
}
$x .= " <workunit>\n";
$x .= file_ref_in('Dockerfile');
$x .= file_ref_in($main_prog);
foreach ($others as $fname) {
$x .= file_ref_in($fname);
}
foreach ($input_file_names as $fname) {
$x .= file_ref_in($fname);
}
$x .= " </workunit>\n<input_template>\n";
file_put_contents("$dir/template_in", $x);
// create output template
//
$x = "<output_template>\n";
$i = 0;
foreach ($output_file_names as $fname) {
$x .= file_info_out($i++);
}
$x .= " <result>\n";
$i = 0;
foreach ($output_file_names as $fname) {
$x .= file_ref_out($i++, $fname);
}
$x .= " </result>\n</output_template>\n";
file_put_contents("$dir/template_out", $x);
// Note: we don't currently allow indirect file access.
// If we did, we'd need to create job.toml to mount project dir
app_list("Variant $plan_class added for app $app.");
}
function file_ref_in($fname) {
return(sprintf(
' <file_ref>
<open_name>%s</open_name>
</file_ref>
',
$fname
));
}
function file_info_out($i) {
return sprintf(
' <file_info>
<name><OUTFILE_%d/></name>
<generated_locally/>
<upload_when_present/>
<max_nbytes>5000000</max_nbytes>
<url><UPLOAD_URL/></url>
</file_info>
',
$i
);
}
function file_ref_out($i, $fname) {
return sprintf(
' <file_ref>
<file_name><OUTFILE_%d/></file_name>
<open_name>%s</open_name>
</file_ref>
', $i, $fname
);
}
function variant_delete() {
global $buda_root;
$app = get_str('app');
$variant = get_str('variant');
$confirmed = get_str('confirmed', true);
if ($confirmed) {
$dir = "$buda_root/$app/$variant";
// delete staged files
//
foreach (scandir("$dir/.md5") as $fname) {
if ($fname[0] == '.') continue;
[$size, $md5] = parse_file_info("$dir/$fname");
$phys_name = buda_app_file_phys_name($app, $variant, $md5);
$phys_path = download_path($phys_name);
unlink($phys_path);
}
system("rm -r $buda_root/$app/$variant", $ret);
if ($ret) {
error_page("delete failed");
}
$notice = "Variant $variant of app $app removed.";
app_list($notice);
} else {
page_head("Confirm");
echo "Are you sure want to delete variant $variant of app $app?
<p>
";
show_button(
"buda.php?action=variant_delete&app=$app&variant=$variant&confirmed=yes",
"Yes"
);
page_tail();
}
}
function app_form() {
page_head("Create Docker app");
form_start();
form_input_text('Name', 'name');
form_submit('OK');
form_end();
page_tail();
}
function app_action() {
global $buda_root;
$name = get_str('name');
$dir = "$buda_root/$name";
if (file_exists($dir)) {
error_page("App $name already exists.");
}
mkdir($dir);
header("Location: buda.php");
}
function view_file() {
global $buda_root;
$app = get_str('app');
$variant = get_str('variant');
$fname = get_str('fname');
echo "<pre>\n";
readfile("$buda_root/$app/$variant/$fname");
}
$user = get_logged_in_user();
$action = get_str('action', true);
switch ($action) {
case 'app_form':
app_form(); break;
case 'app_action':
app_action(); break;
case 'app_delete':
app_delete(); break;
case 'variant_view':
variant_view($user); break;
case 'variant_form':
variant_form($user); break;
case 'variant_action':
variant_action($user); break;
case 'variant_delete':
variant_delete(); break;
case 'view_file':
view_file(); break;
case null:
app_list(); break;
default:
error_page("unknown action $action");
}
?>

73
html/user/buda_submit.php Normal file
View File

@ -0,0 +1,73 @@
<?php
// This file is part of BOINC.
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// web interface for submitting BUDA jobs
require_once('../inc/util.inc');
require_once('../inc/sandbox.inc');
display_errors();
function submit_form($user) {
$sbitems_zip = sandbox_select_items($user, '/.zip$/');
if (!$sbitems_zip) {
error_page("No .zip files in your sandbox.");
}
$app = get_str('app');
$variant = get_str('variant');
page_head("Submit jobs to $app ($variant)");
form_start('buda_submit.php');
form_input_hidden('action', 'submit');
form_input_hidden('app', $app);
form_input_hidden('variant', $variant);
form_input_text('Batch name', 'batch_name');
form_select('Job file', 'job_file', $sbitems_zip);
form_submit('OK');
form_end();
page_tail();
}
function handle_submit() {
// stage app files if not already staged
//
// create batch
//
// unzip batch file
//
// stage top-level input files
//
// scan jobs; stage per-job input files and make create_work input
//
// create jobs
}
$user = get_logged_in_user();
$action = get_str('action', true);
if ($action == 'submit') {
handle_submit();
} else {
submit_form($user);
}
?>

View File

@ -16,14 +16,10 @@
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// Per-user "file sandboxes" for job submission.
// These are stored in project-root/sandbox/USERID/
//
// The entries in a sandbox directory have contents
// size md5
//
// The actual files are stored in the download hierarchy,
// with sb_userid_MD5 as the physical name
// Per-user "file sandboxes".
// Files are stored in <project>/sandbox/<userid>/
// File infos (md5/size) are scored in a parallel dir
// <project>/sandbox/<userid>/.md5/
// NOTE: PHP's default max file upload size is 2MB.
// To increase this, edit /etc/php.ini, and change, e.g.
@ -32,33 +28,31 @@
// post_max_size = 64M
require_once("../inc/sandbox.inc");
require_once("../inc/submit_db.inc");
require_once("../inc/submit_util.inc");
display_errors();
function list_files($user, $notice) {
$dir = sandbox_dir($user);
$d = opendir($dir);
if (!$d) error_page("Can't open sandbox directory");
if (!is_dir($dir)) error_page("Can't open sandbox directory");
page_head("File sandbox");
if ($notice) {
echo "<p>$notice<hr>";
}
echo "
<p>
<h3>Upload files</h3>
<form action=sandbox.php method=post ENCTYPE=\"multipart/form-data\">
<input type=hidden name=action value=upload_file>
Upload files to your sandbox:
<p><p><input size=80 type=file name=\"new_file[]\" multiple=\"multiple\">
<p> <input class=\"btn btn-success\" type=submit value=Upload>
</form>
<hr>
<h3>Sandbox contents</h3>
";
$files = array();
while (($f = readdir($d)) !== false) {
if ($f == '.') continue;
if ($f == '..') continue;
foreach (scandir($dir) as $f) {
if ($f[0] == '.') continue;
$files[] = $f;
}
if (count($files) == 0) {
@ -66,19 +60,10 @@ function list_files($user, $notice) {
} else {
sort($files);
start_table();
table_header("Name<br><p class=\"text-muted\">(click to view)</p>", "Modified", "Size (bytes)", "MD5", "Delete","Download");
table_header("Name<br><p class=\"text-muted\">(click to view text files)</p>", "Modified", "Size (bytes)", "MD5", "Delete","Download");
foreach ($files as $f) {
[$md5, $size] = sandbox_parse_info_file($user, $f);
$path = "$dir/$f";
list($error, $size, $md5) = sandbox_parse_link_file($path);
if ($error) {
table_row($f, "Can't parse link file", "", "<a href=sandbox.php?action=delete_files&name=$f>delete</a>");
continue;
}
$p = sandbox_physical_path($user, $md5);
if (!is_file($p)) {
table_row($f, "Physical file not found", "", "");
continue;
}
$ct = time_str(filemtime($path));
table_row(
"<a href=sandbox.php?action=view_file&name=$f>$f</a>",
@ -100,10 +85,13 @@ function list_files($user, $notice) {
page_tail();
}
// upload one or more files
function upload_file($user) {
$notice = "";
$dir = sandbox_dir($user);
$count = count($_FILES['new_file']['tmp_name']);
for ($i = 0; $i < $count; $i++) {
for ($i=0; $i<$count; $i++) {
$tmp_name = $_FILES['new_file']['tmp_name'][$i];
if (!is_uploaded_file($tmp_name)) {
error_page("$tmp_name is not uploaded file");
@ -112,79 +100,48 @@ function upload_file($user) {
if (strstr($name, "/")) {
error_page("no / allowed");
}
$md5 = md5_file($tmp_name);
$s = stat($tmp_name);
$size = $s['size'];
[$exists, $elf] = sandbox_lf_exists($user, $md5);
if (!$exists){
// move file to download dir
//
$phys_path = sandbox_physical_path($user, $md5);
move_uploaded_file($tmp_name, $phys_path);
if (file_exists("$dir/$name")) {
$notice .= "can't upload $name; file exists.<br>";
continue;
}
move_uploaded_file($tmp_name, "$dir/$name");
// write link file
// write info file
//
$dir = sandbox_dir($user);
$link_path = "$dir/$name";
sandbox_write_link_file($link_path, $size, $md5);
[$md5, $size] = get_file_info("$dir/$name");
write_info_file("$dir/.md5/$name", $md5, $size);
$notice .= "Uploaded file <strong>$name</strong><br/>";
}
list_files($user, $notice);
}
// delete a link to a file.
// check if currently being used by a batch.
// If the last link w/ that contents, delete the file itself
// delete a sandbox file.
//
function delete_file($user) {
$name = get_str('name');
$dir = sandbox_dir($user);
list($error, $size, $md5) = sandbox_parse_link_file("$dir/$name");
if ($error) {
error_page("can't parse link file");
}
$p = sandbox_physical_path($user, $md5);
if (!is_file($p)) {
error_page("physical file is missing");
}
$bused = sandbox_file_in_use($user, $name);
if ($bused){
$notice = "<strong>$name</strong> is being used by batch(es), you can not delete it now!<br/>";
} else{
unlink("$dir/$name");
$notice = "<strong>$name</strong> was deleted from your sandbox<br/>";
[$exists, $elf] = sandbox_lf_exists($user, $md5);
if (!$exists) {
unlink($p);
}
}
unlink("$dir/$name");
unlink("$dir/.md5/$name");
$notice = "<strong>$name</strong> was deleted from your sandbox<br/>";
list_files($user, $notice);
//Header("Location: sandbox.php");
}
function download_file($user) {
$name = get_str('name');
$dir = sandbox_dir($user);
list($err, $size, $md5) = sandbox_parse_link_file("$dir/$name");
if ($err) {
error_page("can't parse link file");
}
$p = sandbox_physical_path($user, $md5);
if (!is_file($p)) {
error_page("$p does not exist!");
}
do_download($p, $name);
do_download("$dir/$name");
}
function view_file($user) {
$name = get_str('name');
$dir = sandbox_dir($user);
list($error, $size, $md5) = sandbox_parse_link_file("$dir/$name");
if ($error) error_page("no such link file");
$p = sandbox_physical_path($user, $md5);
if (!is_file($p)) error_page("no such physical file");
$path = "$dir/$name";
if (!is_file($path)) {
error_path("no such file $name");
}
echo "<pre>\n";
readfile($p);
readfile($path);
echo "</pre>\n";
}

View File

@ -1,35 +1,40 @@
#! /usr/bin/env php
<?php
// submit a job for the Docker universal app (which must be named 'buda')
// submit a job for a BUDA (BOINC Universal Docker app) science app
//
// submit_buda [--buda_dir dir] [--verbose] infile ...
// submit_buda [options] sci_app infile ...
//
// the buda dir (default buda_dir/) must contain
// - buda_in, buda_out: input, output templates
// - other files; typically
// a Dockerfile
// job.toml
// main.sh script
// science executable
// - file_list
// a list of the other filenames (one per line)
// in the order of the input template
// --buda_root dir root of BUDA app hierarchy, default 'buda_apps'
// --variant x variant, default 'cpu'
// --verbose
$buda_dir = 'buda_dir';
$buda_root = 'buda_apps';
$infiles = [];
$verbose = false;
$variant = 'cpu';
$sci_app = null;
for ($i=1; $i<$argc; $i++) {
if ($argv[$i] == '--buda_dir') {
$buda_dir = $argv[++$i];
if ($argv[$i] == '--buda_root') {
$buda_root = $argv[++$i];
} else if ($argv[$i] == '--variant') {
$variant = $argv[++$i];
} else if ($argv[$i] == '--verbose') {
$verbose = true;
} else {
$infiles[] = $argv[$i];
if (!$sci_app) {
$sci_app = $argv[$i];
} else {
$infiles[] = $argv[$i];
}
}
}
$buda_dir = "$buda_root/$sci_app/$variant";
if (!is_dir($buda_dir)) {
die("No version dir $buda_dir\n");
}
if (!file_exists("$buda_dir/template_in")) {
die("no input template\n");
}