mirror of
https://github.com/akuker/RASCSI.git
synced 2024-12-30 05:29:47 +00:00
New Web Interface (#69)
* gitignore * New Web Interface Fixed spacing/tabs in easy install Added migration check Fix update web not actually updating Migrating from https://github.com/erichelgeson/RaSCSI-web * Allow user to select multiple types when creating images * Show all devices even if nothing is attached. * If attaching an iso to a cd device, dont detach, just insert * UI feedback and restart rascsi service * Check for any non-0 exit code for apache2 detection * Pretty/informative 502 * Add confirms to some actions. Works in netscape 4.7 * Fix order of params for create_new_image * Move non-route method to service * Add method for getting logs * Move settings to single file add ability to mock commands for local dev
This commit is contained in:
parent
3536e8aa02
commit
37f1166fd8
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
venv
|
@ -34,7 +34,7 @@ function initialChecks() {
|
||||
fi
|
||||
|
||||
if [ ! -d ~/RASCSI ]; then
|
||||
echo "You must checkout RASCSI repo into /user/pi/RASCSI"
|
||||
echo "You must checkout RASCSI repo into /home/pi/RASCSI"
|
||||
echo "$ git clone git@github.com:akuker/RASCSI.git"
|
||||
exit 2
|
||||
fi
|
||||
@ -46,9 +46,9 @@ function initialChecks() {
|
||||
function installRaScsi() {
|
||||
sudo apt-get update && sudo apt-get install --yes git libspdlog-dev
|
||||
|
||||
cd ~/RASCSI/src/raspberrypi
|
||||
make all CONNECT_TYPE=FULLSPEC
|
||||
sudo make install CONNECT_TYPE=FULLSPEC
|
||||
cd ~/RASCSI/src/raspberrypi
|
||||
make all CONNECT_TYPE=FULLSPEC
|
||||
sudo make install CONNECT_TYPE=FULLSPEC
|
||||
|
||||
sudoIsReady=$(sudo grep -c "rascsi" /etc/sudoers)
|
||||
|
||||
@ -61,35 +61,28 @@ www-data ALL=NOPASSWD: /bin/systemctl stop rascsi.service
|
||||
www-data ALL=NOPASSWD: /sbin/shutdown, /sbin/reboot
|
||||
" >> /etc/sudoers'
|
||||
fi
|
||||
|
||||
sudo systemctl restart rsyslog
|
||||
sudo systemctl enable rascsi # optional - start rascsi at boot
|
||||
sudo systemctl start rascsi
|
||||
|
||||
|
||||
sudo systemctl restart rsyslog
|
||||
sudo systemctl enable rascsi # optional - start rascsi at boot
|
||||
sudo systemctl start rascsi
|
||||
}
|
||||
|
||||
# install everything required to run an HTTP server (Apache+PHP)
|
||||
# configure PHP
|
||||
# install
|
||||
function stopOldWebInterface() {
|
||||
APACHE_STATUS=$(sudo systemctl status apache2 &> /dev/null; echo $?)
|
||||
if [ $APACHE_STATUS -eq 0 ] ; then
|
||||
echo "Stopping old Apache2 RaSCSI Web..."
|
||||
sudo systemctl disable apache2
|
||||
sudo systemctl stop apache2
|
||||
fi
|
||||
}
|
||||
|
||||
# install everything required to run an HTTP server (Nginx + Python Flask App)
|
||||
function installRaScsiWebInterface() {
|
||||
|
||||
sudo apt install apache2 php libapache2-mod-php -y
|
||||
|
||||
sudo cp ~/RASCSI/src/php/* /var/www/html
|
||||
stopOldWebInterface
|
||||
sudo apt install genisoimage python3 python3-venv nginx -y
|
||||
|
||||
|
||||
PHP_CONFIG_FILE=/etc/php/7.3/apache2/php.ini
|
||||
|
||||
#Comment out any current configuration
|
||||
sudo sed -i.bak 's/^post_max_size/#post_max_size/g' $PHP_CONFIG_FILE
|
||||
sudo sed -i.bak 's/^upload_max_filesize/#upload_max_filesize/g' $PHP_CONFIG_FILE
|
||||
|
||||
sudo bash -c 'PHP_CONFIG_FILE=/etc/php/7.3/apache2/php.ini && echo "
|
||||
# RaSCSI high upload limits
|
||||
upload_max_filesize = 1200M
|
||||
post_max_size = 1200M
|
||||
|
||||
" >> $PHP_CONFIG_FILE'
|
||||
sudo cp -f ~/RASCSI/src/web/service-infra/nginx-default.conf /etc/nginx/sites-available/default
|
||||
sudo cp -f ~/RASCSI/src/web/service-infra/502.html /var/www/html/502.html
|
||||
|
||||
mkdir -p $VIRTUAL_DRIVER_PATH
|
||||
chmod -R 775 $VIRTUAL_DRIVER_PATH
|
||||
@ -97,30 +90,38 @@ post_max_size = 1200M
|
||||
sudo usermod -a -G pi www-data
|
||||
groups www-data
|
||||
|
||||
sudo /etc/init.d/apache2 restart
|
||||
sudo systemctl reload nginx
|
||||
|
||||
sudo cp ~/RASCSI/src/web/service-infra/rascsi-web.service /etc/systemd/system/rascsi-web.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable rascsi-web
|
||||
sudo systemctl start rascsi-web
|
||||
}
|
||||
|
||||
|
||||
function updateRaScsiGit() {
|
||||
cd ~/RASCSI
|
||||
git pull
|
||||
}
|
||||
|
||||
function updateRaScsi() {
|
||||
updateRaScsiGit
|
||||
sudo systemctl stop rascsi
|
||||
|
||||
cd ~/RASCSI/src/raspberrypi
|
||||
|
||||
make clean
|
||||
make all CONNECT_TYPE=FULLSPEC
|
||||
sudo make install CONNECT_TYPE=FULLSPEC
|
||||
sudo systemctl start rascsi
|
||||
cd ~/RASCSI/src/raspberrypi
|
||||
|
||||
make clean
|
||||
make all CONNECT_TYPE=FULLSPEC
|
||||
sudo make install CONNECT_TYPE=FULLSPEC
|
||||
sudo systemctl start rascsi
|
||||
}
|
||||
|
||||
function updateRaScsiWebInterface() {
|
||||
sudo /etc/init.d/apache2 stop
|
||||
cd ~/RASCSI
|
||||
git fetch --all
|
||||
cd ~/RASCSI/src/raspberrypi
|
||||
sudo cp ~/RASCSI/src/php/* /var/www/html
|
||||
|
||||
sudo /etc/init.d/apache2 start
|
||||
stopOldWebInterface
|
||||
updateRaScsiGit
|
||||
sudo cp -f ~/RASCSI/src/web/service-infra/nginx-default.conf /etc/nginx/sites-available/default
|
||||
sudo cp -f ~/RASCSI/src/web/service-infra/502.html /var/www/html/502.html
|
||||
sudo systemctl restart rascsi-web
|
||||
sudo systemctl restart nginx
|
||||
}
|
||||
|
||||
function showRaScsiStatus() {
|
||||
|
@ -1,8 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*.{html,php,htm}]
|
||||
indent_style = space
|
||||
indent_size = 3
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
@ -1,31 +0,0 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
Copyright (C) 2014-2020 GIMONS
|
||||
Copyright (c) 2020, akuker
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
Binary file not shown.
Before Width: | Height: | Size: 161 KiB |
@ -1,260 +0,0 @@
|
||||
<!-- PHP source code for controlling the RaSCSI - 68kmla edition with a web interface. -->
|
||||
<!-- Copyright (c) 2020 akuker -->
|
||||
<!-- Distributed under the BSD-3 Clause License -->
|
||||
|
||||
<!-- Note: the origina rascsi-php project was under MIT license.-->
|
||||
|
||||
<?php
|
||||
|
||||
$DEBUG_ENABLE=0;
|
||||
$FILE_PATH='/home/pi/images';
|
||||
// Limit the maximum upload file size to 1GB
|
||||
$MAX_UPLOAD_FILE_SIZE=1000000000;
|
||||
$ALLOWED_FILE_TYPES=array('iso','hda');
|
||||
|
||||
function html_generate_header(){
|
||||
echo ' <table width="100%" >'. PHP_EOL;
|
||||
echo ' <tr style="background-color: black;">'. PHP_EOL;
|
||||
echo ' <td style="background-color: black;"><a href=http://github.com/akuker/RASCSI><h1>RaSCSI - 68kmla Edition</h1></a></td>'. PHP_EOL;
|
||||
echo ' <td style="background-color: black;">'. PHP_EOL;
|
||||
echo ' <form action="rascsi.php">'. PHP_EOL;
|
||||
echo ' <input type="submit" value="Go Home"/>'. PHP_EOL;
|
||||
if($GLOBALS['DEBUG_ENABLE']){
|
||||
echo ' <p style="color:#595959">Debug Timestamp: '.time().'</p>'. PHP_EOL;
|
||||
}
|
||||
echo ' </form>'. PHP_EOL;
|
||||
echo ' </td>'. PHP_EOL;
|
||||
echo ' </tr>'. PHP_EOL;
|
||||
echo ' </table>'. PHP_EOL;
|
||||
//echo(exec('whoami'));
|
||||
}
|
||||
|
||||
function html_generate_image_file_select_list(){
|
||||
$all_files = get_all_files();
|
||||
foreach(explode(PHP_EOL, $all_files) as $this_file){
|
||||
if(strpos($this_file, 'total') === 0){
|
||||
continue;
|
||||
}
|
||||
$file_name = file_name_from_ls($this_file);
|
||||
if(strlen($file_name) === 0){
|
||||
continue;
|
||||
}
|
||||
// Ignore files that start with a .
|
||||
if(strpos($file_name, '.') === 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
echo '<option value="'.$file_name.'">'.$file_name.'</option>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function html_generate_scsi_id_select_list(){
|
||||
echo '<select>'. PHP_EOL;
|
||||
foreach(range(0,7) as $id){
|
||||
echo '<option value="'.$id.'">'.$id.'</option>'. PHP_EOL;
|
||||
}
|
||||
echo '</select>'. PHP_EOL;
|
||||
}
|
||||
|
||||
function html_generate_scsi_type_select_list(){
|
||||
echo '<select name=type>'. PHP_EOL;
|
||||
$options = array("Hard Disk", "CD-ROM", "Zip Drive", "Ethernet Tap", "Filesystem Bridge");
|
||||
foreach($options as $type){
|
||||
echo '<option value="'.$type.'">'.$type.'</option>'. PHP_EOL;
|
||||
}
|
||||
echo '</select>'. PHP_EOL;
|
||||
}
|
||||
|
||||
function html_generate_warning($message){
|
||||
echo ' <table width="100%" >'. PHP_EOL;
|
||||
echo ' <tr style="background-color: red;">'. PHP_EOL;
|
||||
echo ' <td style="background-color: red;">'. PHP_EOL;
|
||||
echo ' <font size=+2>'.$message.'</font>'. PHP_EOL;
|
||||
echo ' </td>'. PHP_EOL;
|
||||
echo ' </tr>'. PHP_EOL;
|
||||
echo ' </table>'. PHP_EOL;
|
||||
}
|
||||
|
||||
function html_generate_success_message($message){
|
||||
echo ' <table width="100%" >'. PHP_EOL;
|
||||
echo ' <tr style="background-color: green;">'. PHP_EOL;
|
||||
echo ' <td style="background-color: green;">'. PHP_EOL;
|
||||
echo ' <font size=+2>Success</font>'. PHP_EOL;
|
||||
echo ' </td>'. PHP_EOL;
|
||||
echo ' </tr>'. PHP_EOL;
|
||||
if(strlen($message) > 0){
|
||||
echo ' <tr style="background-color: green;">'. PHP_EOL;
|
||||
echo ' <td style="background-color: green;">'. PHP_EOL;
|
||||
echo ' '.$message.PHP_EOL;
|
||||
echo ' </td>'. PHP_EOL;
|
||||
echo ' </tr>'. PHP_EOL;
|
||||
}
|
||||
echo ' </table>'. PHP_EOL;
|
||||
}
|
||||
|
||||
function html_generate_ok_to_go_home(){
|
||||
echo ' <form action="rascsi.php">'. PHP_EOL;
|
||||
echo ' <input type="submit" value="OK"/>'. PHP_EOL;
|
||||
echo ' </form>'. PHP_EOL;
|
||||
}
|
||||
|
||||
|
||||
function current_rascsi_config() {
|
||||
$raw_output = shell_exec("/usr/local/bin/rasctl -l");
|
||||
$rasctl_lines = explode(PHP_EOL, $raw_output);
|
||||
|
||||
echo ' <br>'. PHP_EOL;
|
||||
echo ' <h2>Current RaSCSI Configuration</h2>'. PHP_EOL;
|
||||
echo ' <table border="black" cellpadding="3">'. PHP_EOL;
|
||||
echo ' <tr>'. PHP_EOL;
|
||||
echo ' <td><b>SCSI ID</b></td>'. PHP_EOL;
|
||||
echo ' <td><b>Type</b></td>'. PHP_EOL;
|
||||
echo ' <td><b>File</b></td>'. PHP_EOL;
|
||||
echo ' <td><b>File Ops</b></td>'. PHP_EOL;
|
||||
echo ' <td><b>Device Ops</b></td>'. PHP_EOL;
|
||||
echo ' </tr>'. PHP_EOL;
|
||||
|
||||
$scsi_ids = array();
|
||||
|
||||
foreach ($rasctl_lines as $current_line)
|
||||
{
|
||||
if(strlen($current_line) === 0){
|
||||
continue;
|
||||
}
|
||||
if(strpos($current_line, '+----') === 0){
|
||||
continue;
|
||||
|
||||
}
|
||||
if(strpos($current_line, '| ID | UN') === 0){
|
||||
continue;
|
||||
}
|
||||
$segments = explode("|", $current_line);
|
||||
|
||||
$id_config = array();
|
||||
$id_config['id'] = trim($segments[1]);
|
||||
$id_config['type'] = trim($segments[3]);
|
||||
$id_config['file'] = trim($segments[4]);
|
||||
|
||||
$scsi_ids[$id_config['id']] = $id_config;
|
||||
}
|
||||
|
||||
|
||||
foreach (range(0,7) as $id){
|
||||
echo ' <tr>'. PHP_EOL;
|
||||
echo ' <td style="text-align:center">'.$id.'</td>'. PHP_EOL;
|
||||
if(isset($scsi_ids[$id]))
|
||||
{
|
||||
echo ' <td style="text-align:center">'.$scsi_ids[$id]['type'].'</td>'. PHP_EOL;
|
||||
if(strtolower($scsi_ids[$id]['file']) == "no media"){
|
||||
echo ' <td>'.PHP_EOL;
|
||||
echo ' <form action="rascsi_action.php" method="post">'. PHP_EOL;
|
||||
echo ' <select name="file_name">'.PHP_EOL;
|
||||
echo ' <option value="None">None</option>'.PHP_EOL;
|
||||
html_generate_image_file_select_list();
|
||||
echo ' </select>'.PHP_EOL;
|
||||
echo ' <input type="hidden" name="command" value="insert_disk" />'. PHP_EOL;
|
||||
echo ' <input type="hidden" name="id" value="'.$id.'" />'. PHP_EOL;
|
||||
echo ' <input type="hidden" name="file" value="'.$scsi_ids[$id]['file'].'" />'. PHP_EOL;
|
||||
echo ' </td><td>'.PHP_EOL;
|
||||
echo ' <input type="submit" name="insert_disk" value="Insert" />'. PHP_EOL;
|
||||
echo ' </form>'. PHP_EOL;
|
||||
echo ' </td>'.PHP_EOL;
|
||||
}
|
||||
else{
|
||||
// rascsi inserts "WRITEPROTECT" for the read-only drives. We want to display that differently.
|
||||
echo ' <form action="rascsi_action.php" method="post">'. PHP_EOL;
|
||||
echo ' <td>'.str_replace('(WRITEPROTECT)', '', $scsi_ids[$id]['file']). PHP_EOL;
|
||||
echo ' </td><td>'.PHP_EOL;
|
||||
if(strtolower($scsi_ids[$id]['type']) == 'sccd'){
|
||||
echo ' <input type="hidden" name="command" value="eject_disk" />'. PHP_EOL;
|
||||
echo ' <input type="hidden" name="id" value="'.$id.'" />'. PHP_EOL;
|
||||
echo ' <input type="hidden" name="file" value="'.$scsi_ids[$id]['file'].'" />'. PHP_EOL;
|
||||
echo ' <input type="submit" name="eject_disk" value="Eject" />'. PHP_EOL;
|
||||
}
|
||||
echo ' </td>'.PHP_EOL;
|
||||
echo ' </form>'. PHP_EOL;
|
||||
}
|
||||
echo ' <td>'. PHP_EOL;
|
||||
echo ' <form action="rascsi_action.php" method="post">'. PHP_EOL;
|
||||
echo ' <input type="hidden" name="command" value="remove_device" />'. PHP_EOL;
|
||||
echo ' <input type="hidden" name="id" value="'.$id.'" />'. PHP_EOL;
|
||||
echo ' <input type="submit" name="remove_device" value="Disconnect" />'. PHP_EOL;
|
||||
echo ' </form>'. PHP_EOL;
|
||||
echo ' </td>'. PHP_EOL;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo ' <td style="text-align:center">-</td>'. PHP_EOL;
|
||||
echo ' <td>-</td>'. PHP_EOL;
|
||||
echo ' <td></td>'. PHP_EOL;
|
||||
echo ' <td>'. PHP_EOL;
|
||||
echo ' <form action="rascsi_action.php" method="post">'. PHP_EOL;
|
||||
echo ' <input type="hidden" name="command" value="connect_new_device" />'. PHP_EOL;
|
||||
echo ' <input type="hidden" name="id" value="'.$id.'" />'. PHP_EOL;
|
||||
echo ' <input type="submit" name="connect_new_device" value="Connect New" />'. PHP_EOL;
|
||||
echo ' </form>'. PHP_EOL;
|
||||
echo ' </td>'. PHP_EOL;
|
||||
|
||||
}
|
||||
echo ' </form>'. PHP_EOL;
|
||||
echo ' </tr>'. PHP_EOL;
|
||||
}
|
||||
echo '</table>'. PHP_EOL;
|
||||
}
|
||||
function get_all_files()
|
||||
{
|
||||
$raw_ls_output = shell_exec('ls --time-style="+\"%Y-%m-%d %H:%M:%S\"" -alh --quoting-style=c '.$GLOBALS['FILE_PATH']);
|
||||
return $raw_ls_output;
|
||||
}
|
||||
|
||||
function mod_date_from_ls($value){
|
||||
$ls_pieces = explode("\"", $value);
|
||||
if(count($ls_pieces)<1){
|
||||
return "";
|
||||
}
|
||||
return $ls_pieces[1];
|
||||
}
|
||||
function file_name_from_ls($value){
|
||||
$ls_pieces = explode("\"", $value);
|
||||
if(count($ls_pieces) < 4){
|
||||
return "";
|
||||
}
|
||||
return $ls_pieces[3];
|
||||
}
|
||||
function file_size_from_ls($value){
|
||||
$ls_pieces = explode("\"", $value);
|
||||
$file_props = preg_split("/\s+/", $ls_pieces[0]);
|
||||
return $file_props[4];
|
||||
}
|
||||
function file_category_from_file_name($value){
|
||||
if(strpos($value,".iso") || strpos($value,".cdr") > 0){
|
||||
return "CD-ROM Image";
|
||||
}
|
||||
if(strpos($value,".hda") > 0){
|
||||
return "Hard Disk Image";
|
||||
}
|
||||
return "Unknown type: " . $value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function type_string_to_rasctl_type($typestr){
|
||||
if(strcasecmp($typestr,"Hard Disk") == 0){
|
||||
return "hd";
|
||||
}
|
||||
if(strcasecmp($typestr,"CD-ROM") == 0){
|
||||
return "cd";
|
||||
}
|
||||
if(strcasecmp($typestr,"Zip Drive") == 0){
|
||||
}
|
||||
if(strcasecmp($typestr,"Filesystem bridge") == 0){
|
||||
return "bridge";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
?>
|
@ -1,9 +0,0 @@
|
||||
<HTML>
|
||||
<HEAD> <TITLE>RaSCSI Control Page</TITLE></HEAD>
|
||||
<FRAMESET ROWS="20px,*" BORDER=0>
|
||||
<FRAME SRC="status.php" NAME="rascsi_status">
|
||||
<FRAME SRC="rascsi.php" NAME="rascsi_control">
|
||||
<NOFRAMES>You must use a browser that can display frames
|
||||
to see this page. </NOFRAMES>
|
||||
</FRAMESET>
|
||||
</HTML>
|
@ -1,135 +0,0 @@
|
||||
<!-- PHP source code for controlling the RaSCSI - 68kmla edition with a web interface. -->
|
||||
<!-- Copyright (c) 2020 akuker -->
|
||||
<!-- Distributed under the BSD-3 Clause License -->
|
||||
|
||||
<!-- Note: the origina rascsi-php project was under MIT license.-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>RaSCSI Main Control Page</title>
|
||||
<link rel="stylesheet" href="rascsi_styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<?php
|
||||
|
||||
include 'lib_rascsi.php';
|
||||
html_generate_header();
|
||||
current_rascsi_config();
|
||||
?>
|
||||
|
||||
<br>
|
||||
<h2>Image File Management</h2>
|
||||
<table border="black" cellpadding="3">
|
||||
<tr>
|
||||
<td><b>Location</b></td>
|
||||
<td><b>Filename</b></td>
|
||||
<td><b>Size</b></td>
|
||||
<td><b>Type</b></td>
|
||||
<td><b>Date Modified</b></td>
|
||||
<td><b>Actions</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<?php
|
||||
// Generate the table with all of the file names in it.....
|
||||
$all_files = get_all_files();
|
||||
foreach(explode(PHP_EOL, $all_files) as $this_file){
|
||||
if(strpos($this_file, 'total') === 0){
|
||||
continue;
|
||||
}
|
||||
$file_name = file_name_from_ls($this_file);
|
||||
if(strlen($file_name) === 0){
|
||||
continue;
|
||||
}
|
||||
// Ignore file names that start with .
|
||||
if(strpos($file_name,".") === 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
echo '<tr>'.PHP_EOL;
|
||||
echo ' <td>SD Card</td>'.PHP_EOL;
|
||||
echo ' <td>'.$file_name.'</td>'.PHP_EOL;
|
||||
echo ' <td>'.file_size_from_ls($this_file).'</td>'.PHP_EOL;
|
||||
echo ' <td>'.file_category_from_file_name($file_name).'</td>'.PHP_EOL;
|
||||
echo ' <td>'.mod_date_from_ls($this_file).'</td>'.PHP_EOL;
|
||||
echo ' <td>'.PHP_EOL;
|
||||
echo ' <form action="rascsi_action.php" method="post">'.PHP_EOL;
|
||||
echo ' <input type="hidden" name="command" value="delete_file"/>'.PHP_EOL;
|
||||
echo ' <input type="hidden" name="file_name" value="'.$file_name.'"/>'.PHP_EOL;
|
||||
echo ' <input type="submit" value="Delete">'.PHP_EOL;
|
||||
echo ' </form>'.PHP_EOL;
|
||||
echo ' </td>'.PHP_EOL;
|
||||
echo '</tr>'.PHP_EOL;
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<h2>Upload New Image File</h2>
|
||||
<form action="rascsi_upload.php" method="post" enctype="multipart/form-data">
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<input type="file" id="filename" name="file_name"><br><br>
|
||||
</td>
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<input type="submit" value="Upload" name="submit">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<br>
|
||||
<h2>Create New Empty HD Image</h2>
|
||||
<form action="rascsi_action.php" method="post">
|
||||
<input type="hidden" name="command" value="create_new_image" />
|
||||
<input type="submit" value="Create New">
|
||||
</form>
|
||||
|
||||
<br>
|
||||
<h2>RaSCSI Service Status</h2>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="rascsi_action.php" method="post">
|
||||
<input type="hidden" name="command" value="restart_rascsi_service" />
|
||||
<input type="submit" name="restart_rascsi_service" value="Restart RaSCSI service" />
|
||||
</form>
|
||||
</td>
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="rascsi_action.php" method="post">
|
||||
<input type="hidden" name="command" value="stop_rascsi_service" />
|
||||
<input type="submit" name="stop_rascsi_service" value="Stop RaSCSI service" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
<h2>Raspberry Pi Operations</h2>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="rascsi_action.php" method="post">
|
||||
<input type="hidden" name="command" value="reboot_raspberry_pi" />
|
||||
<input type="submit" name="reboot_rasbperry_pi" value="Reboot Raspberry Pi" />
|
||||
</form>
|
||||
</td>
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="rascsi_action.php" method="post">
|
||||
<input type="hidden" name="command" value="shutdown_raspberry_pi" />
|
||||
<input type="submit" name="shutdown_raspberry_pi" value="Shut Down Raspberry Pi" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,297 +0,0 @@
|
||||
<!-- PHP source code for controlling the RaSCSI - 68kmla edition with a web interface. -->
|
||||
<!-- Copyright (c) 2020 akuker -->
|
||||
<!-- Distributed under the BSD-3 Clause License -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>RaSCSI Action Page</title>
|
||||
<link rel="stylesheet" href="rascsi_styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<?php
|
||||
include 'lib_rascsi.php';
|
||||
html_generate_header();
|
||||
|
||||
echo '<br>';
|
||||
if($GLOBALS['DEBUG_ENABLE']){
|
||||
echo '<table>'.PHP_EOL;
|
||||
echo ' <tr><td><p style="color:gray">Debug stuff</p></td></tr>'.PHP_EOL;
|
||||
echo ' <tr><td>';
|
||||
|
||||
echo '<p style="color:gray">Post values......................'.PHP_EOL;
|
||||
echo '<br> '.PHP_EOL;
|
||||
var_dump($_POST);
|
||||
echo '<br><br>Running command.... '.$_POST['command'].PHP_EOL;
|
||||
echo '<br></p>'.PHP_EOL;
|
||||
echo '</td></tr></table>';
|
||||
}
|
||||
|
||||
if(isset($_POST['command']))
|
||||
{
|
||||
switch(strtolower($_POST['command'])){
|
||||
case "eject_disk":
|
||||
action_eject_disk();
|
||||
break;
|
||||
case "remove_device":
|
||||
action_remove_device();
|
||||
break;
|
||||
case "connect_new_device":
|
||||
action_connect_new_device();
|
||||
break;
|
||||
case "insert_disk":
|
||||
action_insert_disk();
|
||||
break;
|
||||
case "delete_file":
|
||||
action_delete_file();
|
||||
break;
|
||||
case "create_new_image":
|
||||
action_create_new_image();
|
||||
break;
|
||||
case "restart_rascsi_service":
|
||||
action_restart_rascsi_service();
|
||||
break;
|
||||
case "stop_rascsi_service":
|
||||
action_stop_rascsi_service();
|
||||
break;
|
||||
case "reboot_raspberry_pi":
|
||||
action_reboot_raspberry_pi();
|
||||
break;
|
||||
case "shutdown_raspberry_pi":
|
||||
action_shutdown_raspberry_pi();
|
||||
break;
|
||||
default:
|
||||
action_unknown_command();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else{
|
||||
html_generate_warning("HTTP command was missing POST information. Are you trying to access this page directly? That won't work");
|
||||
echo "<br>".PHP_EOL;
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
|
||||
function action_eject_disk(){
|
||||
$command = 'rasctl -i '.$_POST['id'].' -c eject 2>&1'.PHP_EOL;
|
||||
exec($command, $retArray, $result);
|
||||
check_result($result, $command,$retArray);
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
|
||||
function action_remove_device(){
|
||||
// Check to see if the user has confirmed
|
||||
if(isset($_POST['confirmed'])){
|
||||
$command = 'rasctl -i '.$_POST['id'].' -c disconnect 2>&1'.PHP_EOL;
|
||||
exec($command, $retArray, $result);
|
||||
check_result($result, $command,$retArray);
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
else{
|
||||
check_are_you_sure('Are you sure you want to disconnect SCSI ID ' . $_POST['id'].'? If the host is running, this could cause undesirable behavior.');
|
||||
}
|
||||
}
|
||||
// function action_connect_new_device(){}
|
||||
function action_insert_disk(){
|
||||
$command = 'rasctl -i '.$_POST['id'].' -c insert -f '.$GLOBALS['FILE_PATH'].'/'.$_POST['file_name'].' 2>&1'.PHP_EOL;
|
||||
exec($command, $retArray, $result);
|
||||
check_result($result, $command,$retArray);
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
function action_create_new_image(){
|
||||
// If we already know the size & filename, we can go create the image...
|
||||
if(isset($_POST['size']) && isset($_POST['file_name'])){
|
||||
$command = 'dd if=/dev/zero of='.$GLOBALS['FILE_PATH'].'/'.$_POST['file_name'].' bs=1M count='.$_POST['size'];
|
||||
exec($command, $retArray, $result);
|
||||
check_result($result, $command, $retArray);
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
else{
|
||||
echo '<h2>Create a new empty file</h2>'.PHP_EOL;
|
||||
echo '<form action=rascsi_action.php method="post">'.PHP_EOL;
|
||||
echo ' <input type="hidden" name="command" value="'.$_POST['command'].'"/>'.PHP_EOL;
|
||||
echo ' <table style="border: none">'.PHP_EOL;
|
||||
echo ' <tr style="border: none">'.PHP_EOL;
|
||||
echo ' <td style="border: none">File Name:</td>'.PHP_EOL;
|
||||
echo ' <td style="border: none">'.PHP_EOL;
|
||||
echo ' <input type="text" name=file_name value="'.get_new_filename().'"/>'.PHP_EOL;
|
||||
echo ' </td>'.PHP_EOL;
|
||||
echo ' <td style="border: none"> Size:</td>'.PHP_EOL;
|
||||
echo ' <td style="border: none">'.PHP_EOL;
|
||||
echo ' <input type="number" name=size value="10""/>'.PHP_EOL;
|
||||
echo ' </td>'.PHP_EOL;
|
||||
echo ' <td style="border: none">MB</td>'.PHP_EOL;
|
||||
echo ' <td style="border: none">'.PHP_EOL;
|
||||
echo ' <input type="submit" name="create" value="Create New" />'.PHP_EOL;
|
||||
echo ' </td>'.PHP_EOL;
|
||||
echo ' </tr>'.PHP_EOL;
|
||||
echo ' </table>'.PHP_EOL;
|
||||
echo '</form>'.PHP_EOL;
|
||||
echo '<br><i>Note: Creating a large file may take a long time!</i>'.PHP_EOL;
|
||||
echo '<br><br>'.PHP_EOL;
|
||||
echo '<form action="rascsi.php" method="post">'.PHP_EOL;
|
||||
echo ' <input type="submit" name="cancel" value="Cancel" />'.PHP_EOL;
|
||||
echo '</form>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
function action_delete_file(){
|
||||
// Check to see if the user has confirmed
|
||||
if(isset($_POST['confirmed'])){
|
||||
$command = 'rm '.$GLOBALS['FILE_PATH'].'/'.$_POST['file_name'];
|
||||
exec($command, $retArray, $result);
|
||||
check_result($result, $command, $retArray);
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
else{
|
||||
check_are_you_sure('Are you sure you want to PERMANENTLY delete '.$_POST['file_name'].'?');
|
||||
}
|
||||
}
|
||||
|
||||
function action_restart_rascsi_service(){
|
||||
// Restart the RaSCSI service
|
||||
$command = "sudo /bin/systemctl restart rascsi.service 2>&1";
|
||||
exec($command, $retArray, $result);
|
||||
check_result($result, $command,$retArray);
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
|
||||
function action_stop_rascsi_service(){
|
||||
// Stop the RaSCSI service
|
||||
$command = "sudo /bin/systemctl stop rascsi.service 2>&1";
|
||||
exec($command, $retArray, $result);
|
||||
check_result($result, $command,$retArray);
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
|
||||
function action_reboot_raspberry_pi(){
|
||||
// Check to see if the user has confirmed
|
||||
if(isset($_POST['confirmed'])){
|
||||
$command = "sleep 2 && sudo reboot 2>&1";
|
||||
exec($command, $retArray, $result);
|
||||
// The unit should reboot at this point. Doesn't matter what we do now...
|
||||
check_result($result, $command,$retArray);
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
else{
|
||||
check_are_you_sure("Are you sure you want to reboot the Raspberry Pi?");
|
||||
}
|
||||
}
|
||||
|
||||
function action_shutdown_raspberry_pi(){
|
||||
// Check to see if the user has confirmed
|
||||
if(isset($_POST['confirmed'])){
|
||||
$command = "sleep 2 && sudo shutdown -h now 2>&1";
|
||||
exec($command, $retArray, $result);
|
||||
// The unit should shutdown at this point. Doesn't matter what we do now...
|
||||
check_result($result, $command,$retArray);
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
else{
|
||||
check_are_you_sure("Are you sure you want to shut down the Raspberry Pi?");
|
||||
}
|
||||
}
|
||||
|
||||
function action_unknown_command(){
|
||||
html_generate_warning('<br><h2>Unknown command: '.$_POST['command'].'</h2>');
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
|
||||
function check_result($result,$command,$output){
|
||||
if(!$result){
|
||||
html_generate_success_message('Command succeeded!');
|
||||
}
|
||||
else{
|
||||
html_generate_warning('Command failed!');
|
||||
}
|
||||
echo '<br><code>'.$command.'</code><br>'.PHP_EOL;
|
||||
if(count($output) > 0){
|
||||
echo '<br>Output:<code>'.PHP_EOL;
|
||||
foreach($output as $line){
|
||||
echo '<br> Error message: '.$line.PHP_EOL;
|
||||
}
|
||||
echo '</code>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
function check_are_you_sure($prompt){
|
||||
echo '<br><h2>'.$prompt.'</h2>'.PHP_EOL;
|
||||
echo ' <table style="border: none">'.PHP_EOL;
|
||||
echo ' <tr style="border: none">'.PHP_EOL;
|
||||
echo ' <td style="border: none; vertical-align:top;">'.PHP_EOL;
|
||||
echo ' <form action="rascsi_action.php" method="post">'.PHP_EOL;
|
||||
foreach($_POST as $key => $value){
|
||||
echo '<input type="hidden" name="'.$key.'" value="'.$value.'"/>'.PHP_EOL;
|
||||
}
|
||||
echo ' <input type="hidden" name="confirmed" value="yes" />'.PHP_EOL;
|
||||
echo ' <input type="submit" name="do_it" value="Yes" />'.PHP_EOL;
|
||||
echo ' </form>'.PHP_EOL;
|
||||
echo ' </td>'.PHP_EOL;
|
||||
echo ' <td style="border: none; vertical-align:top;">'.PHP_EOL;
|
||||
echo ' <form action="rascsi.php" method="post">'.PHP_EOL;
|
||||
echo ' <input type="submit" name="cancel" value="Cancel" />'.PHP_EOL;
|
||||
echo ' </form>'.PHP_EOL;
|
||||
echo ' </td>'.PHP_EOL;
|
||||
echo ' </tr>'.PHP_EOL;
|
||||
echo '</table>'.PHP_EOL;
|
||||
}
|
||||
|
||||
function action_connect_new_device(){
|
||||
// If we already know the type & filename, we can go connect the device...
|
||||
if(isset($_POST['type']) && isset($_POST['file_name'])){
|
||||
$command = 'rasctl -i '.$_POST['id'].' -c attach -t '.type_string_to_rasctl_type($_POST['type']);
|
||||
if($_POST['file_name'] != "None"){
|
||||
$command = $command.' -f '.$GLOBALS['FILE_PATH'].'/'.$_POST['file_name'];
|
||||
}
|
||||
exec($command, $retArray, $result);
|
||||
check_result($result, $command, $retArray);
|
||||
html_generate_ok_to_go_home();
|
||||
}
|
||||
else{
|
||||
|
||||
|
||||
$id = $_POST['id'];
|
||||
echo '<h2>Add New Device</h2>'.PHP_EOL;
|
||||
echo '<form action=rascsi_action.php method="post">'.PHP_EOL;
|
||||
echo ' <input type="hidden" name="command" value="'.$_POST['command'].'"/>'.PHP_EOL;
|
||||
echo ' <table style="border: none">'.PHP_EOL;
|
||||
echo ' <tr style="border: none">'.PHP_EOL;
|
||||
echo ' <td style="border: none">SCSI ID:</td>'.PHP_EOL;
|
||||
echo ' <td style="border: none">'.PHP_EOL;
|
||||
echo ' <input type="hidden" name=id value="'.$id.'"/>'.PHP_EOL;
|
||||
echo $id;
|
||||
echo ' </td>'.PHP_EOL;
|
||||
echo ' <td style="border: none">Device:</td>'.PHP_EOL;
|
||||
echo ' <td style="border: none">'.PHP_EOL;
|
||||
html_generate_scsi_type_select_list();
|
||||
echo ' </td>'.PHP_EOL;
|
||||
echo ' <td style="border: none">File:</td>'.PHP_EOL;
|
||||
echo ' <td style="border: none">'.PHP_EOL;
|
||||
echo ' <select name="file_name">'.PHP_EOL;
|
||||
echo ' <option value="None">None</option>'.PHP_EOL;
|
||||
html_generate_image_file_select_list();
|
||||
echo ' </select>'.PHP_EOL;
|
||||
echo ' </td>'.PHP_EOL;
|
||||
echo ' <td style="border: none">'.PHP_EOL;
|
||||
echo ' <INPUT type="submit" value="Add"/>'.PHP_EOL;
|
||||
echo ' </td>'.PHP_EOL;
|
||||
echo ' </tr>'.PHP_EOL;
|
||||
echo ' </table>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
function get_new_filename(){
|
||||
// Try to find a new file name that doesn't exist.
|
||||
$i=1;
|
||||
while(file_exists($GLOBALS['FILE_PATH'].'/'.'new_file'.$i.'.hda'))
|
||||
{
|
||||
$i = $i+1;
|
||||
}
|
||||
return 'new_file'.$i.'.hda';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,86 +0,0 @@
|
||||
<!-- PHP source code for controlling the RaSCSI - 68kmla edition with a web interface. -->
|
||||
<!-- Copyright (c) 2020 akuker -->
|
||||
<!-- Distributed under the BSD-3 Clause License -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>RaSCSI Upload Page</title>
|
||||
<link rel="stylesheet" href="rascsi_styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<?php
|
||||
include 'lib_rascsi.php';
|
||||
html_generate_header();
|
||||
|
||||
echo '<br>';
|
||||
|
||||
if($GLOBALS['DEBUG_ENABLE']){
|
||||
echo '<table>'.PHP_EOL;
|
||||
echo ' <tr><td><p style="color:gray">Debug stuff</p></td></tr>'.PHP_EOL;
|
||||
echo ' <tr><td>';
|
||||
|
||||
echo '<p style="color:gray">Post values......................'.PHP_EOL;
|
||||
echo '<br> '.PHP_EOL;
|
||||
var_dump($_POST);
|
||||
echo '<br><br>'.PHP_EOL;
|
||||
var_dump($_FILES);
|
||||
echo '<br></p>'.PHP_EOL;
|
||||
echo '</td></tr></table>';
|
||||
}
|
||||
|
||||
$target_dir = $GLOBALS['FILE_PATH'].'/';
|
||||
$target_file = $target_dir.basename($_FILES['file_name']['name']);
|
||||
$upload_ok=1;
|
||||
$file_type = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
|
||||
|
||||
if(isset($_POST['submit']))
|
||||
{
|
||||
// Check if file already exists
|
||||
if ($upload_ok && (file_exists($target_file))) {
|
||||
html_generate_warning('Error: File '.$target_file.' already exists.');
|
||||
$upload_ok = 0;
|
||||
}
|
||||
|
||||
// Check file size. Limit is specified in lib_rascsi.php
|
||||
if ($upload_ok && ($_FILES["file_name"]["size"] > $GLOBALS['MAX_UPLOAD_FILE_SIZE'])) {
|
||||
html_generate_warning("Error: your file is larger than the maximum size of " . $GLOBALS['MAX_UPLOAD_FILE_SIZE'] . "bytes");
|
||||
$upload_ok = 0;
|
||||
}
|
||||
|
||||
// Allow certain file formats, also specified in lib_rascsi.php
|
||||
if($upload_ok && (!in_array(strtolower($file_type),$GLOBALS['ALLOWED_FILE_TYPES']))){
|
||||
$error_string = 'File type "'. $file_type. '" is not currently allowed.'.
|
||||
'Only the following file types are allowed: <br>'.
|
||||
'<ul>'.PHP_EOL;
|
||||
foreach($GLOBALS['ALLOWED_FILE_TYPES'] as $ft){
|
||||
$error_string = $error_string. '<li>'.$ft.'</li>'.PHP_EOL;
|
||||
}
|
||||
$error_string = $error_string.'</ul>';
|
||||
$error_string = $error_string.'<br>';
|
||||
html_generate_warning($error_string);
|
||||
$upload_ok = 0;
|
||||
}
|
||||
|
||||
//Check if $upload_ok is set to 0 by an error
|
||||
if ($upload_ok != 0) {
|
||||
if (move_uploaded_file($_FILES["file_name"]["tmp_name"], $target_file)) {
|
||||
html_generate_success_message(basename( $_FILES["file_name"]["name"]). " has been uploaded.");
|
||||
} else {
|
||||
html_generate_warning("There was an unknown error uploading your file.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
html_generate_warning('The Submit POST information was not populated. Something went wrong');
|
||||
}
|
||||
echo '<br>';
|
||||
|
||||
html_generate_ok_to_go_home();
|
||||
?>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,44 +0,0 @@
|
||||
|
||||
<!-- Simple file for showing the status of the RaSCSI process. Intended to be loaded as a frame in a larger page -->
|
||||
<!-- Copyright (c) 2020 akuker -->
|
||||
<!-- Distributed under the BSD-3 Clause License -->
|
||||
|
||||
<!-- Note: the origina rascsi-php project was under MIT license.-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = setupRefresh;
|
||||
|
||||
function setupRefresh() {
|
||||
setTimeout("refreshPage();", 30000); // milliseconds
|
||||
}
|
||||
function refreshPage() {
|
||||
window.location = location.href;
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php
|
||||
|
||||
// Blatently copied from stack overflow:
|
||||
// https://stackoverflow.com/questions/53695187/php-function-that-shows-status-of-systemctl-service
|
||||
$output = shell_exec("systemctl is-active rascsi");
|
||||
if (trim($output) == "active") {
|
||||
$color='green';
|
||||
$text='Running';
|
||||
}
|
||||
else{
|
||||
$color='red';
|
||||
$text='Stopped';
|
||||
}
|
||||
|
||||
echo '<body style="background-color: '.$color.';">';
|
||||
echo '<table width="100%" height=100% style="position: absolute; top: 0; bottom: 0; left: 0; right: 0; background-color:'.$color.'">';
|
||||
echo '<tr style:"height: 100%">';
|
||||
echo '<td style="color: white; background-color: '.$color.'; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">'.$text.' '.date("h:i:sa").'</td>';
|
||||
echo '</tr>';
|
||||
echo '</table>';
|
||||
?>
|
||||
</body>
|
||||
</html>
|
12
src/web/README.md
Normal file
12
src/web/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# RaSCSI Web
|
||||
|
||||
## Mocking for local development
|
||||
|
||||
Set a few env vars to point to the mock scripts and base dir
|
||||
|
||||
```
|
||||
BASE_DIR=/tmp/images/
|
||||
PATH=$PATH:`pwd`/mock/bin/
|
||||
```
|
||||
|
||||
Edit response to commands in `mock/bin/*`
|
45
src/web/create_disk.py
Normal file
45
src/web/create_disk.py
Normal file
@ -0,0 +1,45 @@
|
||||
from machfs import Volume, Folder, File
|
||||
|
||||
from settings import *
|
||||
|
||||
|
||||
# Build a cd and attempt to fix resource forks if known
|
||||
def make_cd(file_path, file_type, file_creator):
|
||||
with open(file_path, "rb") as f:
|
||||
file_bytes = f.read()
|
||||
file_name = file_path.split('/')[-1]
|
||||
file_suffix = file_name.split('.')[-1]
|
||||
|
||||
if file_type is None and file_creator is None:
|
||||
if file_suffix.lower() == 'sea':
|
||||
file_type = '.sea'
|
||||
file_creator = 'APPL'
|
||||
|
||||
v = Volume()
|
||||
v.name = "TestName"
|
||||
|
||||
v['Folder'] = Folder()
|
||||
|
||||
v['Folder'][file_name] = File()
|
||||
v['Folder'][file_name].data = file_bytes
|
||||
v['Folder'][file_name].rsrc = b''
|
||||
if not (file_type is None and file_creator is None):
|
||||
v['Folder'][file_name].type = bytearray(file_type)
|
||||
v['Folder'][file_name].creator = bytearray(file_creator)
|
||||
|
||||
padding = (len(file_bytes) % 512) + (512 * 50)
|
||||
print("mod", str(len(file_bytes) % 512))
|
||||
print("padding " + str(padding))
|
||||
print("len " + str(len(file_bytes)))
|
||||
print("total " + str(len(file_bytes) + padding))
|
||||
with open(base_dir + 'test.hda', 'wb') as f:
|
||||
flat = v.write(
|
||||
size=len(file_bytes) + padding,
|
||||
align=512, # Allocation block alignment modulus (2048 for CDs)
|
||||
desktopdb=True, # Create a dummy Desktop Database to prevent a rebuild on boot
|
||||
bootable=False, # This requires a folder with a ZSYS and a FNDR file
|
||||
startapp=('Folder', file_name), # Path (as tuple) to an app to open at boot
|
||||
)
|
||||
f.write(flat)
|
||||
|
||||
return base_dir + 'test.hda'
|
64
src/web/file_cmds.py
Normal file
64
src/web/file_cmds.py
Normal file
@ -0,0 +1,64 @@
|
||||
import fnmatch
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from ractl_cmds import attach_image
|
||||
from settings import *
|
||||
valid_file_types = ['*.hda', '*.iso', '*.cdr']
|
||||
valid_file_types = r'|'.join([fnmatch.translate(x) for x in valid_file_types])
|
||||
|
||||
|
||||
def create_new_image(file_name, type, size):
|
||||
if file_name == "":
|
||||
file_name = "new_image." + str(int(time.time())) + "." + type
|
||||
else:
|
||||
file_name = file_name + "." + type
|
||||
|
||||
return subprocess.run(["dd", "if=/dev/zero", "of=" + base_dir + file_name, "bs=1M", "count=" + size],
|
||||
capture_output=True)
|
||||
|
||||
|
||||
def delete_image(file_name):
|
||||
full_path = base_dir + file_name
|
||||
if os.path.exists(full_path):
|
||||
os.remove(full_path)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def unzip_file(file_name):
|
||||
import zipfile
|
||||
with zipfile.ZipFile(base_dir + file_name, 'r') as zip_ref:
|
||||
zip_ref.extractall(base_dir)
|
||||
return True
|
||||
|
||||
def rascsi_service(action):
|
||||
# start/stop/restart
|
||||
return subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode == 0
|
||||
|
||||
|
||||
def download_file_to_iso(scsi_id, url):
|
||||
import urllib.request
|
||||
file_name = url.split('/')[-1]
|
||||
tmp_ts = int(time.time())
|
||||
tmp_dir = "/tmp/" + str(tmp_ts) + "/"
|
||||
os.mkdir(tmp_dir)
|
||||
tmp_full_path = tmp_dir + file_name
|
||||
iso_filename = base_dir + file_name + ".iso"
|
||||
|
||||
urllib.request.urlretrieve(url, tmp_full_path)
|
||||
# iso_filename = make_cd(tmp_full_path, None, None) # not working yet
|
||||
iso_proc = subprocess.run(["genisoimage", "-hfs", "-o", iso_filename, tmp_full_path], capture_output=True)
|
||||
if iso_proc.returncode != 0:
|
||||
return iso_proc
|
||||
return attach_image(scsi_id, iso_filename, "cd")
|
||||
|
||||
|
||||
def download_image(url):
|
||||
import urllib.request
|
||||
file_name = url.split('/')[-1]
|
||||
full_path = base_dir + file_name
|
||||
|
||||
urllib.request.urlretrieve(url, full_path)
|
12
src/web/mock/bin/journalctl
Executable file
12
src/web/mock/bin/journalctl
Executable file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Mock responses to rascsi-web
|
||||
case $1 in
|
||||
-n)
|
||||
echo "logs $*"
|
||||
;;
|
||||
|
||||
**)
|
||||
echo "default"
|
||||
;;
|
||||
esac
|
12
src/web/mock/bin/rasctl
Executable file
12
src/web/mock/bin/rasctl
Executable file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Mock responses to rascsi-web
|
||||
case $1 in
|
||||
-f)
|
||||
echo "logs here"
|
||||
;;
|
||||
|
||||
**)
|
||||
echo "default"
|
||||
;;
|
||||
esac
|
12
src/web/mock/bin/systemctl
Executable file
12
src/web/mock/bin/systemctl
Executable file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Mock responses to rascsi-web
|
||||
case $1 in
|
||||
is-active)
|
||||
echo "is-active"
|
||||
;;
|
||||
|
||||
**)
|
||||
echo "default"
|
||||
;;
|
||||
esac
|
20
src/web/pi_cmds.py
Normal file
20
src/web/pi_cmds.py
Normal file
@ -0,0 +1,20 @@
|
||||
import subprocess
|
||||
|
||||
|
||||
def rascsi_service(action):
|
||||
# start/stop/restart
|
||||
return subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode == 0
|
||||
|
||||
|
||||
def reboot_pi():
|
||||
return subprocess.run(["sudo", "reboot"]).returncode == 0
|
||||
|
||||
|
||||
def shutdown_pi():
|
||||
return subprocess.run(["sudo", "shutdown", "-h", "now"]).returncode == 0
|
||||
|
||||
|
||||
def running_version():
|
||||
ra_web_version = subprocess.run(["git", "rev-parse", "HEAD"], capture_output=True).stdout.decode("utf-8").strip()
|
||||
pi_version = subprocess.run(["uname", "-a"], capture_output=True).stdout.decode("utf-8").strip()
|
||||
return ra_web_version + " " + pi_version
|
98
src/web/ractl_cmds.py
Normal file
98
src/web/ractl_cmds.py
Normal file
@ -0,0 +1,98 @@
|
||||
import fnmatch
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
from settings import *
|
||||
valid_file_types = ['*.hda', '*.iso', '*.cdr', '*.zip']
|
||||
valid_file_types = r'|'.join([fnmatch.translate(x) for x in valid_file_types])
|
||||
# List of SCSI ID's you'd like to exclude - eg if you are on a Mac, the System is usually 7
|
||||
EXCLUDE_SCSI_IDS = [7]
|
||||
|
||||
|
||||
def is_active():
|
||||
process = subprocess.run(["systemctl", "is-active", "rascsi"], capture_output=True)
|
||||
return process.stdout.decode("utf-8").strip() == "active"
|
||||
|
||||
|
||||
def list_files():
|
||||
files_list = []
|
||||
for path, dirs, files in os.walk(base_dir):
|
||||
# Only list valid file types
|
||||
files = [f for f in files if re.match(valid_file_types, f)]
|
||||
files_list.extend([
|
||||
(os.path.join(path, file),
|
||||
# TODO: move formatting to template
|
||||
'{:,.0f}'.format(os.path.getsize(os.path.join(path, file)) / float(1 << 20)) + " MB")
|
||||
for file in files])
|
||||
return files_list
|
||||
|
||||
|
||||
def get_valid_scsi_ids(devices):
|
||||
invalid_list = EXCLUDE_SCSI_IDS.copy()
|
||||
for device in devices:
|
||||
if device['file'] != "NO MEDIA" and device['file'] != "-":
|
||||
invalid_list.append(int(device['id']))
|
||||
|
||||
valid_list = list(range(8))
|
||||
for id in invalid_list:
|
||||
valid_list.remove(id)
|
||||
valid_list.reverse()
|
||||
|
||||
return valid_list
|
||||
|
||||
|
||||
def get_type(scsi_id):
|
||||
return list_devices()[int(scsi_id)]["type"]
|
||||
|
||||
|
||||
def attach_image(scsi_id, image, type):
|
||||
if type == "cd" and get_type(scsi_id) == "SCCD":
|
||||
return insert(scsi_id, image)
|
||||
else:
|
||||
return subprocess.run(["rasctl", "-c", "attach", "-t", type, "-i", scsi_id, "-f", image], capture_output=True)
|
||||
|
||||
|
||||
def detach_by_id(scsi_id):
|
||||
return subprocess.run(["rasctl", "-c" "detach", "-i", scsi_id], capture_output=True)
|
||||
|
||||
|
||||
def disconnect_by_id(scsi_id):
|
||||
return subprocess.run(["rasctl", "-c", "disconnect", "-i", scsi_id], capture_output=True)
|
||||
|
||||
|
||||
def eject_by_id(scsi_id):
|
||||
return subprocess.run(["rasctl", "-i", scsi_id, "-c", "eject"])
|
||||
|
||||
|
||||
def insert(scsi_id, image):
|
||||
return subprocess.run(["rasctl", "-i", scsi_id, "-c", "insert", "-f", image], capture_output=True)
|
||||
|
||||
|
||||
def rascsi_service(action):
|
||||
# start/stop/restart
|
||||
return subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode == 0
|
||||
|
||||
|
||||
def list_devices():
|
||||
device_list = []
|
||||
for id in range(8):
|
||||
device_list.append({"id": str(id), "un": "-", "type": "-", "file": "-"})
|
||||
output = subprocess.run(["rasctl", "-l"], capture_output=True).stdout.decode("utf-8")
|
||||
for line in output.splitlines():
|
||||
# Valid line to process, continue
|
||||
if not line.startswith("+") and \
|
||||
not line.startswith("| ID |") and \
|
||||
(not line.startswith("No device is installed.") or line.startswith("No images currently attached.")) \
|
||||
and len(line) > 0:
|
||||
line.rstrip()
|
||||
device = {}
|
||||
segments = line.split("|")
|
||||
if len(segments) > 4:
|
||||
idx = int(segments[1].strip())
|
||||
device_list[idx]["id"] = str(idx)
|
||||
device_list[idx]['un'] = segments[2].strip()
|
||||
device_list[idx]['type'] = segments[3].strip()
|
||||
device_list[idx]['file'] = segments[4].strip()
|
||||
|
||||
return device_list
|
12
src/web/requirements.txt
Normal file
12
src/web/requirements.txt
Normal file
@ -0,0 +1,12 @@
|
||||
click==7.1.2
|
||||
Flask==1.1.2
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.11.2
|
||||
machfs==1.2.4
|
||||
macresources==1.2
|
||||
MarkupSafe==1.1.1
|
||||
rsrcfork==1.8.0
|
||||
waitress==1.4.4
|
||||
Werkzeug==1.0.1
|
||||
zope.event==4.5.0
|
||||
zope.interface==5.1.2
|
14
src/web/service-infra/502.html
Normal file
14
src/web/service-infra/502.html
Normal file
@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>RaSCSI-web is Starting</title>
|
||||
<meta http-equiv="refresh" content="2">
|
||||
</head>
|
||||
</html>
|
||||
<body>
|
||||
<center>
|
||||
<h1>RaSCSI Web is starting....</h1>
|
||||
<h2>This page will automatically refresh.</h2>
|
||||
<p>First boot and upgrades can take a second to resolve dependancies.</p>
|
||||
<p>If you're seeing this page for over a minute please check the logs at <tt>sudo journalctl -f</tt></p>
|
||||
</center>
|
||||
</body>
|
12
src/web/service-infra/nginx-default.conf
Normal file
12
src/web/service-infra/nginx-default.conf
Normal file
@ -0,0 +1,12 @@
|
||||
# /etc/nginx/sites-available/default
|
||||
# Simple proxy_pass for RaSCSI-web
|
||||
server {
|
||||
location / {
|
||||
proxy_pass http://localhost:8080;
|
||||
}
|
||||
|
||||
error_page 502 /502.html;
|
||||
location = /502.html {
|
||||
root /var/www/html/;
|
||||
}
|
||||
}
|
14
src/web/service-infra/rascsi-web.service
Normal file
14
src/web/service-infra/rascsi-web.service
Normal file
@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=RaSCSI-Web service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
ExecStart=/home/pi/RASCSI/src/web/start.sh
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
SyslogIdentifier=RASCSIWEB
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
4
src/web/settings.py
Normal file
4
src/web/settings.py
Normal file
@ -0,0 +1,4 @@
|
||||
import os
|
||||
|
||||
base_dir = os.getenv('BASE_DIR', "/home/pi/images/")
|
||||
MAX_FILE_SIZE = os.getenv('MAX_FILE_SIZE', 1024 * 1024 * 1024 * 2) # 2gb
|
58
src/web/start.sh
Executable file
58
src/web/start.sh
Executable file
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
# set -x # Uncomment to Debug
|
||||
|
||||
cd $(dirname $0)
|
||||
# verify packages installed
|
||||
ERROR=0
|
||||
if ! command -v genisoimage &> /dev/null ; then
|
||||
echo "genisoimage could not be found"
|
||||
echo "Run 'sudo apt install genisoimage' to fix."
|
||||
ERROR=1
|
||||
fi
|
||||
if ! command -v python3 &> /dev/null ; then
|
||||
echo "python3 could not be found"
|
||||
echo "Run 'sudo apt install python3' to fix."
|
||||
ERROR=1
|
||||
fi
|
||||
if ! python3 -m venv --help &> /dev/null ; then
|
||||
echo "venv could not be found"
|
||||
echo "Run 'sudo apt install python3-venv' to fix."
|
||||
ERROR=1
|
||||
fi
|
||||
if ! command -v unzip &> /dev/null ; then
|
||||
echo "unzip could not be found"
|
||||
echo "Run 'sudo apt install unzip' to fix."
|
||||
ERROR=1
|
||||
fi
|
||||
if [ $ERROR = 1 ] ; then
|
||||
echo
|
||||
echo "Fix errors and re-run ./start.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! test -e venv; then
|
||||
echo "Creating python venv for web server"
|
||||
python3 -m venv venv
|
||||
echo "Activating venv"
|
||||
source venv/bin/activate
|
||||
echo "Installing requirements.txt"
|
||||
pip install -r requirements.txt
|
||||
git rev-parse HEAD > current
|
||||
fi
|
||||
|
||||
source venv/bin/activate
|
||||
|
||||
# Detect if someone updates - we need to re-run pip install.
|
||||
if ! test -e current; then
|
||||
git rev-parse > current
|
||||
else
|
||||
if [ "$(cat current)" != "$(git rev-parse HEAD)" ]; then
|
||||
echo "New version detected, updating requirements.txt"
|
||||
pip install -r requirements.txt
|
||||
git rev-parse HEAD > current
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Starting web server..."
|
||||
python3 web.py
|
@ -21,8 +21,24 @@ a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
form {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
table, tr, td {
|
||||
border: 1px solid black;
|
||||
border-collapse:collapse;
|
||||
margin: none;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: white;
|
||||
font-size:20px;
|
||||
background-color:red;
|
||||
}
|
||||
|
||||
.message {
|
||||
color: white;
|
||||
font-size:20px;
|
||||
background-color:green;
|
||||
}
|
24
src/web/templates/base.html
Normal file
24
src/web/templates/base.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!doctype html>
|
||||
<title>RaSCSI Control Page</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
{% block header %}{% endblock %}
|
||||
</div>
|
||||
<div class="flash">
|
||||
{% for category, message in get_flashed_messages(with_categories=true) %}
|
||||
{% if category == "stdout" or category == "stderr" %}
|
||||
<pre>{{message}}</pre>
|
||||
{% else %}
|
||||
<div class="{{category}}">{{ message }}</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
<div class="footer">
|
||||
{% block footer %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
213
src/web/templates/index.html
Normal file
213
src/web/templates/index.html
Normal file
@ -0,0 +1,213 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block header %}
|
||||
{% if active %}
|
||||
<span style="display: inline-block; width: 100%; color: white; background-color: green; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">Service Running</span>
|
||||
{% else %}
|
||||
<span style="display: inline-block; width: 100%; color: white; background-color: red; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">Service Stopped</span>
|
||||
{% endif %}
|
||||
<table width="100%">
|
||||
<tbody>
|
||||
<tr style="background-color: black;">
|
||||
<td style="background-color: black;">
|
||||
<a href="http://github.com/akuker/RASCSI">
|
||||
<h1>RaSCSI - 68kmla Edition</h1>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Current RaSCSI Configuration</h2>
|
||||
<table cellpadding="3" border="black">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>ID</b></td>
|
||||
<td><b>Type</b></td>
|
||||
<td><b>File</b></td>
|
||||
<td><b>Action</b></td>
|
||||
</tr>
|
||||
{% for device in devices %}
|
||||
<tr>
|
||||
{% if device.id != "7" %}
|
||||
<td style="text-align:center">{{device.id}}</td>
|
||||
<td style="text-align:center">{{device.type}}</td>
|
||||
<td>{{device.file}}</td>
|
||||
<td>
|
||||
{% if device.type == "SCCD" and device.file != "NO MEDIA" %}
|
||||
<form action="/scsi/eject" method="post" onsubmit="return confirm('Eject Disk?')">
|
||||
<input type="hidden" name="scsi_id" value="{{device.id}}">
|
||||
<input type="submit" value="Eject" />
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/scsi/detach" method="post" onsubmit="return confirm('Detach Disk?')">
|
||||
<input type="hidden" name="scsi_id" value="{{device.id}}">
|
||||
<input type="submit" value="Detach" />
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% else %}
|
||||
<td style="text-align:center">{{device.id}}</td>
|
||||
<td style="text-align:center">-</td>
|
||||
<td>Host Machine</td>
|
||||
<td>-</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Image File Management</h2>
|
||||
<table cellpadding="3" border="black">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>File</b></td>
|
||||
<td><b>Size</b></td>
|
||||
<td><b>Actions</b></td>
|
||||
</tr>
|
||||
{% for file in files %}
|
||||
<tr>
|
||||
<td>{{file[0].replace(base_dir, '')}}</td>
|
||||
<td style="text-align:center">
|
||||
<form action="/files/download" method="post">
|
||||
<input type="hidden" name="image" value="{{file[0].replace(base_dir, '')}}">
|
||||
<input type="submit" value="{{file[1]}} ↓" />
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form action="/scsi/attach" method="post">
|
||||
<input type="hidden" name="file_name" value="{{file[0]}}">
|
||||
<select name="scsi_id">
|
||||
{% for id in scsi_ids %}
|
||||
<option value="{{id}}">{{id}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="Attach" />
|
||||
</form>
|
||||
<form action="/files/delete" method="post" onsubmit="return confirm('Delete file?')">
|
||||
<input type="hidden" name="image" value="{{file[0].replace(base_dir, '')}}">
|
||||
<input type="submit" value="Delete" />
|
||||
</form>
|
||||
{% if file[0].endswith('.zip') or file[0].endswith('.ZIP') %}
|
||||
<form action="/files/unzip" method="post">
|
||||
<input type="hidden" name="image" value="{{file[0].replace(base_dir, '')}}">
|
||||
<input type="submit" value="Unzip" />
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Upload File</h2>
|
||||
<p>Uploads file to <tt>{{base_dir}}</tt>. Max file size is set to {{max_file_size / 1024 /1024 }}MB</p>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/files/upload" method="post" enctype="multipart/form-data">
|
||||
<label for="file">File:</label>
|
||||
<input type="file" name="file"/>
|
||||
<input type="submit" value="Upload" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Download File from Web</h2>
|
||||
<p>Given a URL, download that file to the <tt>{{base_dir}}</tt></p>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/files/download_image" method="post">
|
||||
<label for="url">URL:</label>
|
||||
<input type="text" placeholder="URL" name="url" />
|
||||
<input type="submit" value="Download" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Download File from web and create HFS CD</h2>
|
||||
<p>Given a URL this will download a file, create a HFS iso, and mount it on the device id given.</p>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<label for="scsi_id">SCSI ID:</label>
|
||||
<form action="/files/download_to_iso" method="post">
|
||||
<select name="scsi_id">
|
||||
{% for id in scsi_ids %}
|
||||
<option value="{{id}}">{{id}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="url">URL:</label>
|
||||
<input type="text" placeholder="URL" name="url" />
|
||||
<input type="submit" value="Download and Mount ISO" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
<h2>Create Empty Disk Image File</h2>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/files/create" method="post">
|
||||
<label for="file_name">File Name:</label>
|
||||
<input type="text" placeholder="File name" name="file_name"/>
|
||||
<label for="type">Type:</label>
|
||||
<select name="type">
|
||||
<option value="hda">SCSI Hard Disk image (APPLE GENUINE)</option>
|
||||
<option value="hdn">SCSI Hard Disk image (NEC GENUINE)</option>
|
||||
<option value="hdi">SCSI Hard Disk image (Anex86 HD image)</option>
|
||||
<option value="nhd">SCSI Hard Disk image (T98Next HD image)</option>
|
||||
<option value="hdf">SASI Hard Disk image (XM6 SASI HD image - typically only used with X68000)</option>
|
||||
<option value="hds">SCSI Hard Disk image (XM6 SCSI HD image - typically only used with X68000)</option>
|
||||
<option value="mos">SCSI Magneto-optical image (XM6 SCSI MO image - typically only used with X68000)</option>
|
||||
</select>
|
||||
<label for="size">Size(MB):</label>
|
||||
<input type="number" placeholder="Size(MB)" name="size"/>
|
||||
<input type="submit" value="Create" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2>Raspberry Pi Operations</h2>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/pi/reboot" method="post" onsubmit="return confirm('Reboot Pi?')">
|
||||
<input type="submit" value="Reboot Raspberry Pi" />
|
||||
</form>
|
||||
</td>
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/pi/shutdown" method="post" onsubmit="return confirm('Shutdown Pi?')">
|
||||
<input type="submit" value="Shut Down Raspberry Pi" />
|
||||
</form>
|
||||
</td>
|
||||
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/rascsi/restart" method="post" onsubmit="return confirm('Restart RaSCSI?')">
|
||||
<input type="submit" value="Restart RaSCSI Service" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
<center><tt>{{version}}</tt></center>
|
||||
<center><a href="/logs">Logs</a></center>
|
||||
{% endblock %}
|
206
src/web/web.py
Normal file
206
src/web/web.py
Normal file
@ -0,0 +1,206 @@
|
||||
import os
|
||||
|
||||
from flask import Flask, render_template, request, flash, url_for, redirect, send_file
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from file_cmds import create_new_image, download_file_to_iso, delete_image, unzip_file, download_image
|
||||
from pi_cmds import shutdown_pi, reboot_pi, running_version, rascsi_service
|
||||
from ractl_cmds import attach_image, list_devices, is_active, list_files, detach_by_id, eject_by_id, get_valid_scsi_ids
|
||||
from settings import *
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
devices = list_devices()
|
||||
scsi_ids = get_valid_scsi_ids(devices)
|
||||
return render_template('index.html',
|
||||
devices=devices,
|
||||
active=is_active(),
|
||||
files=list_files(),
|
||||
base_dir=base_dir,
|
||||
scsi_ids=scsi_ids,
|
||||
max_file_size=MAX_FILE_SIZE,
|
||||
version=running_version())
|
||||
|
||||
@app.route('/logs')
|
||||
def logs():
|
||||
import subprocess
|
||||
lines = request.args.get('lines') or "100"
|
||||
process = subprocess.run(["journalctl", "-n", lines], capture_output=True)
|
||||
|
||||
if process.returncode == 0:
|
||||
headers = { 'content-type':'text/plain' }
|
||||
return process.stdout.decode("utf-8"), 200, headers
|
||||
else:
|
||||
flash(u'Failed to get logs')
|
||||
flash(process.stdout.decode("utf-8"), 'stdout')
|
||||
flash(process.stderr.decode("utf-8"), 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/scsi/attach', methods=['POST'])
|
||||
def attach():
|
||||
file_name = request.form.get('file_name')
|
||||
scsi_id = request.form.get('scsi_id')
|
||||
|
||||
# Validate image type by suffix
|
||||
if file_name.lower().endswith('.iso') or file_name.lower().endswith('iso'):
|
||||
image_type = "cd"
|
||||
elif file_name.lower().endswith('.hda'):
|
||||
image_type = "hd"
|
||||
else:
|
||||
flash(u'Unknown file type. Valid files are .iso, .hda, .cdr', 'error')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
process = attach_image(scsi_id, file_name, image_type)
|
||||
if process.returncode == 0:
|
||||
flash('Attached '+ file_name + " to scsi id " + scsi_id + "!")
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
flash(u'Failed to attach '+ file_name + " to scsi id " + scsi_id + "!", 'error')
|
||||
flash(process.stdout.decode("utf-8"), 'stdout')
|
||||
flash(process.stderr.decode("utf-8"), 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/scsi/detach', methods=['POST'])
|
||||
def detach():
|
||||
scsi_id = request.form.get('scsi_id')
|
||||
process = detach_by_id(scsi_id)
|
||||
if process.returncode == 0:
|
||||
flash("Detached scsi id " + scsi_id + "!")
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
flash(u"Failed to detach scsi id " + scsi_id + "!", 'error')
|
||||
flash(process.stdout, 'stdout')
|
||||
flash(process.stderr, 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/scsi/eject', methods=['POST'])
|
||||
def eject():
|
||||
scsi_id = request.form.get('scsi_id')
|
||||
process = eject_by_id(scsi_id)
|
||||
if process.returncode == 0:
|
||||
flash("Ejected scsi id " + scsi_id + "!")
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
flash(u"Failed to eject scsi id " + scsi_id + "!", 'error')
|
||||
flash(process.stdout, 'stdout')
|
||||
flash(process.stderr, 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/pi/reboot', methods=['POST'])
|
||||
def restart():
|
||||
reboot_pi()
|
||||
flash("Restarting...")
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/rascsi/restart', methods=['POST'])
|
||||
def rascsi_restart():
|
||||
rascsi_service("restart")
|
||||
flash("Restarting RaSCSI Service...")
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/pi/shutdown', methods=['POST'])
|
||||
def shutdown():
|
||||
shutdown_pi()
|
||||
flash("Shutting down...")
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/files/download_to_iso', methods=['POST'])
|
||||
def download_file():
|
||||
scsi_id = request.form.get('scsi_id')
|
||||
url = request.form.get('url')
|
||||
process = download_file_to_iso(scsi_id, url)
|
||||
if process.returncode == 0:
|
||||
flash("File Downloaded")
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
flash(u"Failed to download file", 'error')
|
||||
flash(process.stdout, 'stdout')
|
||||
flash(process.stderr, 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/files/download_image', methods=['POST'])
|
||||
def download_img():
|
||||
url = request.form.get('url')
|
||||
# TODO: error handling
|
||||
download_image(url)
|
||||
flash("File Downloaded")
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/files/upload', methods=['POST'])
|
||||
def upload_file():
|
||||
if 'file' not in request.files:
|
||||
flash('No file part', 'error')
|
||||
return redirect(url_for('index'))
|
||||
file = request.files['file']
|
||||
if file:
|
||||
filename = secure_filename(file.filename)
|
||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
||||
return redirect(url_for('index', filename=filename))
|
||||
|
||||
|
||||
@app.route('/files/create', methods=['POST'])
|
||||
def create_file():
|
||||
file_name = request.form.get('file_name')
|
||||
size = request.form.get('size')
|
||||
type = request.form.get('type')
|
||||
|
||||
process = create_new_image(file_name, type, size)
|
||||
if process.returncode == 0:
|
||||
flash("Drive created")
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
flash(u"Failed to create file", 'error')
|
||||
flash(process.stdout, 'stdout')
|
||||
flash(process.stderr, 'stderr')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/files/download', methods=['POST'])
|
||||
def download():
|
||||
image = request.form.get('image')
|
||||
return send_file(base_dir + image, as_attachment=True)
|
||||
|
||||
|
||||
@app.route('/files/delete', methods=['POST'])
|
||||
def delete():
|
||||
image = request.form.get('image')
|
||||
if delete_image(image):
|
||||
flash("File " + image + " deleted")
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
flash(u"Failed to Delete " + image, 'error')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/files/unzip', methods=['POST'])
|
||||
def unzip():
|
||||
image = request.form.get('image')
|
||||
|
||||
if unzip_file(image):
|
||||
flash("Unzipped file " + image)
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
flash(u"Failed to unzip " + image, 'error')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.secret_key = 'rascsi_is_awesome_insecure_secret_key'
|
||||
app.config['SESSION_TYPE'] = 'filesystem'
|
||||
app.config['UPLOAD_FOLDER'] = base_dir
|
||||
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
||||
app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE
|
||||
|
||||
from waitress import serve
|
||||
serve(app, host="0.0.0.0", port=8080)
|
Loading…
Reference in New Issue
Block a user