Merge branch 'develop' into feature_powerview

This commit is contained in:
akuker 2022-01-07 20:43:28 -06:00
commit d8594a5b03
34 changed files with 5083 additions and 6096 deletions

View File

@ -142,12 +142,14 @@ function installRaScsiScreen() {
SCREEN_HEIGHT="32"
fi
echo ""
echo "Is RaSCSI using token-based authentication? [y/N]"
read -r REPLY
if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
echo -n "Enter the passphrase that you configured: "
read -r TOKEN
if [ -z "$TOKEN" ]; then
echo ""
echo "Did you protect your RaSCSI installation with a token password? [y/N]"
read -r REPLY
if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
echo -n "Enter the password that you configured with RaSCSI at the time of installation: "
read -r TOKEN
fi
fi
stopRaScsiScreen
@ -178,7 +180,7 @@ function installRaScsiScreen() {
if [ ! -z "$TOKEN" ]; then
sudo sed -i "8 i ExecStart=$BASE/src/oled_monitor/start.sh --rotation=$ROTATION --height=$SCREEN_HEIGHT --password=$TOKEN" "$SYSTEMD_PATH/monitor_rascsi.service"
sudo chmod 600 "$SYSTEMD_PATH/monitor_rascsi.service"
echo "Granted access to the OLED Monitor with the token passphrase that you configured for RaSCSI."
echo "Granted access to the OLED Monitor with the password that you configured for RaSCSI."
else
sudo sed -i "8 i ExecStart=$BASE/src/oled_monitor/start.sh --rotation=$ROTATION --height=$SCREEN_HEIGHT" "$SYSTEMD_PATH/monitor_rascsi.service"
fi
@ -277,14 +279,14 @@ function backupRaScsiService() {
fi
}
# Modifies and installs the rascsi service
function enableRaScsiService() {
# Offers the choice of enabling token-based authentication for RaSCSI
function configureTokenAuth() {
echo ""
echo "Do you want to enable token-based access control for RaSCSI? [y/N]"
echo "Do you want to protect your RaSCSI installation with a password? [y/N]"
read REPLY
if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
echo -n "Enter the passphrase that you want to use: "
echo -n "Enter the password that you want to use: "
read -r TOKEN
if [ -f "$HOME/.rascsi_secret" ]; then
sudo rm "$HOME/.rascsi_secret"
@ -293,10 +295,17 @@ function enableRaScsiService() {
echo "$TOKEN" > "$HOME/.rascsi_secret"
sudo chown root:root "$HOME/.rascsi_secret"
sudo chmod 600 "$HOME/.rascsi_secret"
echo ""
echo "Configured RaSCSI to use $HOME/.rascsi_secret for authentication. This file is readable by root only."
echo "Make note of your password: you will need it to use rasctl and other RaSCSI clients."
fi
}
# Modifies and installs the rascsi service
function enableRaScsiService() {
if [ ! -z "$TOKEN" ]; then
sudo sed -i "s@^ExecStart.*@& -F $VIRTUAL_DRIVER_PATH -P $HOME/.rascsi_secret@" "$SYSTEMD_PATH/rascsi.service"
sudo chmod 600 "$SYSTEMD_PATH/rascsi.service"
echo "Configured to use $HOME/.rascsi_secret to secure RaSCSI. This file is readable by root only."
echo "Make note of your passphrase; you will need it to use rasctl, and other RaSCSI clients."
else
sudo sed -i "s@^ExecStart.*@& -F $VIRTUAL_DRIVER_PATH@" "$SYSTEMD_PATH/rascsi.service"
fi
@ -318,7 +327,7 @@ function installWebInterfaceService() {
if [ ! -z "$TOKEN" ]; then
sudo sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh --password=$TOKEN" "$SYSTEMD_PATH/rascsi-web.service"
sudo chmod 600 "$SYSTEMD_PATH/rascsi-web.service"
echo "Granted access to the Web Interface with the token passphrase that you configured for RaSCSI."
echo "Granted access to the Web Interface with the token password that you configured for RaSCSI."
else
sudo sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh" "$SYSTEMD_PATH/rascsi-web.service"
fi
@ -724,7 +733,7 @@ function installMacproxy {
( sudo apt-get update && sudo apt-get install python3 python3-venv --assume-yes ) </dev/null
MACPROXY_VER="21.12.2"
MACPROXY_VER="21.12.3"
MACPROXY_PATH="$HOME/macproxy-$MACPROXY_VER"
if [ -d "$MACPROXY_PATH" ]; then
echo "The $MACPROXY_PATH directory already exists. Delete it to proceed with the installation."
@ -784,11 +793,13 @@ function runChoice() {
echo "- Install additional packages with apt-get"
echo "- Add and modify systemd services"
echo "- Modify and enable Apache2 and Nginx web services"
echo "- Create directories and change permissions"
echo "- Create files and directories"
echo "- Change permissions of files and directories"
echo "- Modify user groups and permissions"
echo "- Install binaries to /usr/local/bin"
echo "- Install manpages to /usr/local/man"
sudoCheck
configureTokenAuth
stopOldWebInterface
updateRaScsiGit
createImagesDir
@ -800,9 +811,13 @@ function runChoice() {
backupRaScsiService
installRaScsi
enableRaScsiService
startRaScsiScreen
if [ -f "$SYSTEMD_PATH/monitor_rascsi.service" ]; then
echo "Detected monitor_rascsi.service; will run the installation steps for the OLED monitor."
installRaScsiScreen
fi
installRaScsiWebInterface
installWebInterfaceService
showRaScsiScreenStatus
showRaScsiStatus
showRaScsiWebStatus
notifyBackup
@ -813,11 +828,13 @@ function runChoice() {
echo "This script will make the following changes to your system:"
echo "- Install additional packages with apt-get"
echo "- Add and modify systemd services"
echo "- Create directories and change permissions"
echo "- Create files ans directories"
echo "- Change permissions of files and directories"
echo "- Modify user groups and permissions"
echo "- Install binaries to /usr/local/bin"
echo "- Install manpages to /usr/local/man"
sudoCheck
configureTokenAuth
updateRaScsiGit
createImagesDir
installPackages
@ -827,7 +844,11 @@ function runChoice() {
backupRaScsiService
installRaScsi
enableRaScsiService
startRaScsiScreen
if [ -f "$SYSTEMD_PATH/monitor_rascsi.service" ]; then
echo "Detected monitor_rascsi.service; will run the installation steps for the OLED monitor."
installRaScsiScreen
fi
showRaScsiScreenStatus
showRaScsiStatus
notifyBackup
echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE:-FULLSPEC}) - Complete!"

View File

@ -15,7 +15,14 @@ ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
init-hook=
# venv hook for pylint
# Requires pylint-venv package:
# $ pip install pylint-venv
try: import pylint_venv
except ImportError: pass
else: pylint_venv.inithook()
# Use multiple processes to speed up Pylint.
jobs=1

View File

@ -31,8 +31,8 @@
THE SOFTWARE.
"""
import argparse
import sys
from time import sleep
from sys import argv
from collections import deque
from board import I2C
from adafruit_ssd1306 import SSD1306_I2C
@ -95,20 +95,22 @@ I2C = I2C()
# 128x32 display with hardware I2C:
OLED = SSD1306_I2C(WIDTH, HEIGHT, I2C, addr=0x3C, reset=OLED_RESET)
OLED.rotation = ROTATION
print("Running with the following display:")
print(OLED)
print()
print("Will update the OLED display every " + str(DELAY_TIME_MS) + "ms (approximately)")
# Clear display.
OLED.rotation = ROTATION
OLED.fill(0)
# Show a startup splash bitmap image before starting the main loop
# Convert the image to mode '1' for 1-bit color (monochrome)
# Make sure the splash bitmap image is in the same dir as this script
IMAGE = Image.open(f"splash_start_{HEIGHT}.bmp").convert("1")
OLED.image(IMAGE)
OLED.show()
# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
IMAGE = Image.new("1", (OLED.width, OLED.height))
# Keep the pretty splash on screen for a number of seconds
sleep(4)
# Get drawing object to draw on image.
DRAW = ImageDraw.Draw(IMAGE)
@ -138,11 +140,6 @@ LINE_SPACING = 8
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
FONT = ImageFont.truetype('type_writer.ttf', FONT_SIZE)
# Load a bitmap image for start and stop splash screens and convert to monocrome
# Make sure the splash bitmap image is in the same dir as this script
SPLASH_START = Image.open(f"splash_start_{HEIGHT}.bmp").convert("1")
SPLASH_STOP = Image.open(f"splash_stop_{HEIGHT}.bmp").convert("1")
IP_ADDR, HOSTNAME = get_ip_and_host()
@ -190,28 +187,7 @@ def formatted_output():
return output
def start_splash():
"""
Displays a splash screen for the startup sequence
"""
OLED.image(SPLASH_START)
OLED.show()
sleep(4)
def stop_splash():
"""
Displays a splash screen for the shutdown sequence
"""
OLED.image(SPLASH_STOP)
OLED.show()
# Show a startup splash bitmap image before starting the main loop
start_splash()
with GracefulInterruptHandler() as handler:
"""
The main loop of displaying attached device info, and other info
"""
while True:
# The reference snapshot of attached devices that will be compared against each cycle
@ -225,10 +201,10 @@ with GracefulInterruptHandler() as handler:
while snapshot == ref_snapshot:
# Draw a black filled box to clear the image.
DRAW.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0)
y_pos = TOP
Y_POS = TOP
for output_line in active_output:
DRAW.text((X_POS, y_pos), output_line, font=FONT, fill=255)
y_pos += LINE_SPACING
DRAW.text((X_POS, Y_POS), output_line, font=FONT, fill=255)
Y_POS += LINE_SPACING
# Shift the index of the array by one to get a scrolling effect
if len(active_output) > LINES:
@ -242,6 +218,8 @@ with GracefulInterruptHandler() as handler:
snapshot = formatted_output()
if handler.interrupted:
# Catch interrupt signals and show a shutdown splash bitmap image
stop_splash()
exit("Shutting down the OLED display...")
# Catch interrupt signals and blank out the screen
DRAW.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0)
OLED.image(IMAGE)
OLED.show()
sys.exit("Shutting down the OLED display...")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -66,9 +66,13 @@ fi
# Test for two known broken venv states
if test -e venv; then
GOOD_VENV=true
! test -e venv/bin/activate && GOOD_VENV=false
pip3 list 1> /dev/null
test $? -eq 1 && GOOD_VENV=false
if ! test -e venv/bin/activate; then
GOOD_VENV=false
else
source venv/bin/activate
pip3 list 1> /dev/null
test $? -eq 1 && GOOD_VENV=false
fi
if ! "$GOOD_VENV"; then
echo "Deleting bad python venv"
sudo rm -rf venv

View File

@ -5,11 +5,14 @@
*.layout
*.log
*.vcd
*.json
*.html
rascsi
scsimon
rasctl
sasidump
rasdump
scisparse
obj
bin
/rascsi_interface.pb.cpp

View File

@ -24,8 +24,13 @@ else
CXXFLAGS += -O3 -Wall -Werror -DNDEBUG
BUILD_TYPE = Release
endif
ifeq ("$(shell uname -s)","Linux")
# -Wno-psabi might not work on non-Linux platforms
CXXFLAGS += -Wno-psabi
endif
CFLAGS += -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP
CXXFLAGS += -std=c++17 -Wno-psabi -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP
CXXFLAGS += -std=c++17 -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP
## EXTRA_FLAGS : Can be used to pass special purpose flags
CFLAGS += $(EXTRA_FLAGS)
@ -105,9 +110,8 @@ SRC_SCSIMON = \
scsimon.cpp \
scsi.cpp \
gpiobus.cpp \
filepath.cpp \
fileio.cpp \
rascsi_version.cpp
SRC_SCSIMON += $(shell find ./monitor -name '*.cpp')
SRC_RASCTL = \
rasctl.cpp\
@ -135,8 +139,8 @@ SRC_SASIDUMP = \
fileio.cpp\
rascsi_version.cpp
vpath %.h ./ ./controllers ./devices
vpath %.cpp ./ ./controllers ./devices
vpath %.h ./ ./controllers ./devices ./monitor
vpath %.cpp ./ ./controllers ./devices ./monitor
vpath %.o ./$(OBJDIR)
vpath ./$(BINDIR)
@ -193,6 +197,16 @@ $(BINDIR)/$(SASIDUMP): $(OBJ_SASIDUMP) | $(BINDIR)
$(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread
# Phony rules for building individual utilities
.PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) $(SCSIMON)
$(RASCSI) : $(BINDIR)/$(RASCSI)
$(RASCTL) : $(BINDIR)/$(RASCTL)
$(RASDUMP) : $(BINDIR)/$(RASDUMP)
$(SASIDUMP): $(BINDIR)/$(SASIDUMP)
$(SCSIMON) : $(BINDIR)/$(SCSIMON)
## clean : Remove all of the object files, intermediate
## compiler files and executable files
.PHONY: clean

View File

@ -1385,6 +1385,7 @@ BOOL GPIOBUS::WaitSignal(int pin, BOOL ast)
//---------------------------------------------------------------------------
void GPIOBUS::DisableIRQ()
{
#ifdef __linux__
if (rpitype == 4) {
// RPI4 is disabled by GICC
giccpmr = gicc[GICC_PMR];
@ -1399,6 +1400,9 @@ void GPIOBUS::DisableIRQ()
irptenb = irpctl[IRPT_ENB_IRQ_1];
irpctl[IRPT_DIS_IRQ_1] = irptenb & 0xf;
}
#else
(void)0;
#endif
}
//---------------------------------------------------------------------------

View File

@ -19,72 +19,114 @@ using namespace std;
Localizer::Localizer()
{
// Supported locales, always lower case
supported_languages = { "en", "de", "sv" };
supported_languages = { "en", "de", "sv", "fr", "es" };
// Positional string arguments are %1, %2, %3
Add(ERROR_AUTHENTICATION, "en", "Authentication failed");
Add(ERROR_AUTHENTICATION, "de", "Authentifizierung fehlgeschlagen");
Add(ERROR_AUTHENTICATION, "sv", "Autentisering misslyckades");
Add(ERROR_AUTHENTICATION, "fr", "Authentification éronnée");
Add(ERROR_AUTHENTICATION, "es", "Fallo de autentificación");
Add(ERROR_OPERATION, "en", "Unknown operation");
Add(ERROR_OPERATION, "de", "Unbekannte Operation");
Add(ERROR_OPERATION, "sv", "Okänd operation");
Add(ERROR_OPERATION, "fr", "Opération inconnue");
Add(ERROR_OPERATION, "es", "Operación desconocida");
Add(ERROR_LOG_LEVEL, "en", "Invalid log level %1");
Add(ERROR_LOG_LEVEL, "de", "Ungültiger Log-Level %1");
Add(ERROR_LOG_LEVEL, "sv", "Ogiltig loggnivå %1");
Add(ERROR_LOG_LEVEL, "fr", "Niveau de journalisation invalide %1");
Add(ERROR_LOG_LEVEL, "es", "Nivel de registro %1 no válido");
Add(ERROR_MISSING_DEVICE_ID, "en", "Missing device ID");
Add(ERROR_MISSING_DEVICE_ID, "de", "Fehlende Geräte-ID");
Add(ERROR_MISSING_DEVICE_ID, "sv", "Enhetens ID saknas");
Add(ERROR_MISSING_DEVICE_ID, "fr", "ID de périphérique manquante");
Add(ERROR_MISSING_DEVICE_ID, "es", "Falta el ID del dispositivo");
Add(ERROR_MISSING_FILENAME, "en", "Missing filename");
Add(ERROR_MISSING_FILENAME, "de", "Fehlender Dateiname");
Add(ERROR_MISSING_FILENAME, "sv", "Filnamn saknas");
Add(ERROR_MISSING_FILENAME, "fr", "Nom de fichier manquant");
Add(ERROR_MISSING_FILENAME, "es", "Falta el nombre del archivo");
Add(ERROR_IMAGE_IN_USE, "en", "Image file '%1' is already being used by ID %2, unit %3");
Add(ERROR_IMAGE_IN_USE, "de", "Image-Datei '%1' wird bereits von ID %2, Einheit %3 benutzt");
Add(ERROR_IMAGE_IN_USE, "sv", "Skivbildfilen '%1' används redan av ID %2, LUN %3");
Add(ERROR_IMAGE_IN_USE, "fr", "Le fichier d'image '%1' est déjà utilisé par l'ID %2, unité %3");
Add(ERROR_IMAGE_IN_USE, "es", "El archivo de imagen '%1' ya está siendo utilizado por el ID %2, unidad %3");
Add(ERROR_RESERVED_ID, "en", "Device ID %1 is reserved");
Add(ERROR_RESERVED_ID, "de", "Geräte-ID %1 ist reserviert");
Add(ERROR_RESERVED_ID, "sv", "Enhets-ID %1 är reserverat");
Add(ERROR_RESERVED_ID, "fr", "ID de périphérique %1 réservée");
Add(ERROR_RESERVED_ID, "es", "El ID de dispositivo %1 está reservado");
Add(ERROR_NON_EXISTING_DEVICE, "en", "Command for non-existing ID %1");
Add(ERROR_NON_EXISTING_DEVICE, "de", "Kommando für nicht existente ID %1");
Add(ERROR_NON_EXISTING_DEVICE, "sv", "Kommando för avsaknat ID %1");
Add(ERROR_NON_EXISTING_DEVICE, "fr", "Commande pour ID %1 non-existant");
Add(ERROR_NON_EXISTING_DEVICE, "es", "Comando para ID %1 no existente");
Add(ERROR_NON_EXISTING_UNIT, "en", "Command for non-existing ID %1, unit %2");
Add(ERROR_NON_EXISTING_UNIT, "de", "Kommando für nicht existente ID %1, Einheit %2");
Add(ERROR_NON_EXISTING_UNIT, "sv", "Kommando för avsaknat ID %1, LUN %2");
Add(ERROR_NON_EXISTING_UNIT, "fr", "Command pour ID %1, unité %2 non-existant");
Add(ERROR_NON_EXISTING_UNIT, "es", "Comando para ID %1 inexistente, unidad %2");
Add(ERROR_UNKNOWN_DEVICE_TYPE, "en", "Unknown device type %1");
Add(ERROR_UNKNOWN_DEVICE_TYPE, "de", "Unbekannter Gerätetyp %1");
Add(ERROR_UNKNOWN_DEVICE_TYPE, "sv", "Obekant enhetstyp: %1");
Add(ERROR_UNKNOWN_DEVICE_TYPE, "fr", "Type de périphérique inconnu %1");
Add(ERROR_UNKNOWN_DEVICE_TYPE, "es", "Tipo de dispositivo desconocido %1");
Add(ERROR_MISSING_DEVICE_TYPE, "en", "Device type required for unknown extension of file '%1'");
Add(ERROR_MISSING_DEVICE_TYPE, "de", "Gerätetyp erforderlich für unbekannte Extension der Datei '%1'");
Add(ERROR_MISSING_DEVICE_TYPE, "sv", "Man måste ange enhetstyp för obekant filändelse '%1'");
Add(ERROR_MISSING_DEVICE_TYPE, "fr", "Type de périphérique requis pour extension inconnue du fichier '%1'");
Add(ERROR_MISSING_DEVICE_TYPE, "es", "Tipo de dispositivo requerido para la extensión desconocida del archivo '%1'");
Add(ERROR_DUPLICATE_ID, "en", "Duplicate ID %1, unit %2");
Add(ERROR_DUPLICATE_ID, "de", "Doppelte ID %1, Einheit %2");
Add(ERROR_DUPLICATE_ID, "sv", "Duplikat ID %1, LUN %2");
Add(ERROR_DUPLICATE_ID, "fr", "ID %1, unité %2 dupliquée");
Add(ERROR_DUPLICATE_ID, "es", "ID duplicado %1, unidad %2");
Add(ERROR_SASI_SCSI, "en", "SASI and SCSI can't be used at the same time");
Add(ERROR_SASI_SCSI, "de", "SASI und SCSI können nicht gleichzeitig verwendet werden");
Add(ERROR_SASI_SCSI, "sv", "SASI och SCSI kan ej användas samtidigt");
Add(ERROR_SASI_SCSI, "fr", "SASI et SCSI ne peuvent être utilisés en même temps");
Add(ERROR_SASI_SCSI, "es", "SASI y SCSI no pueden utilizarse al mismo tiempo");
Add(ERROR_EJECT_REQUIRED, "en", "Existing medium must first be ejected");
Add(ERROR_EJECT_REQUIRED, "de", "Das vorhandene Medium muss erst ausgeworfen werden");
Add(ERROR_EJECT_REQUIRED, "sv", "Nuvarande skiva måste utmatas först");
Add(ERROR_EJECT_REQUIRED, "fr", "Media déjà existant doit d'abord être éjecté");
Add(ERROR_EJECT_REQUIRED, "es", "El medio existente debe ser expulsado primero");
Add(ERROR_DEVICE_NAME_UPDATE, "en", "Once set the device name cannot be changed anymore");
Add(ERROR_DEVICE_NAME_UPDATE, "de", "Ein bereits gesetzter Gerätename kann nicht mehr geändert werden");
Add(ERROR_DEVICE_NAME_UPDATE, "sv", "Enhetsnamn kan ej ändras efter att ha fastställts en gång");
Add(ERROR_DEVICE_NAME_UPDATE, "fr", "Une fois défini, le nom de périphérique ne peut plus être changé");
Add(ERROR_DEVICE_NAME_UPDATE, "es", "Una vez establecido el nombre del dispositivo ya no se puede cambiar");
Add(ERROR_SHUTDOWN_MODE_MISSING, "en", "Missing shutdown mode");
Add(ERROR_SHUTDOWN_MODE_MISSING, "de", "Fehlender Shutdown-Modus");
Add(ERROR_SHUTDOWN_MODE_MISSING, "sv", "Avstängningsläge saknas");
Add(ERROR_SHUTDOWN_MODE_MISSING, "fr", "Mode d'extinction manquant");
Add(ERROR_SHUTDOWN_MODE_MISSING, "es", "Falta el modo de apagado");
Add(ERROR_SHUTDOWN_MODE_INVALID, "en", "Invalid shutdown mode '%1'");
Add(ERROR_SHUTDOWN_MODE_INVALID, "de", "Ungültiger Shutdown-Modus '%1'");
Add(ERROR_SHUTDOWN_MODE_INVALID, "sv", "Ogiltigt avstängsningsläge: '%1'");
Add(ERROR_SHUTDOWN_MODE_INVALID, "fr", "Mode d'extinction invalide '%1'");
Add(ERROR_SHUTDOWN_MODE_INVALID, "es", "Modo de apagado inválido '%1'");
Add(ERROR_SHUTDOWN_PERMISSION, "en", "Missing root permission for shutdown or reboot");
Add(ERROR_SHUTDOWN_PERMISSION, "de", "Fehlende Root-Berechtigung für Shutdown oder Neustart");
Add(ERROR_SHUTDOWN_PERMISSION, "sv", "Root-rättigheter saknas för att kunna stänga av eller starta om systemet");
Add(ERROR_SHUTDOWN_PERMISSION, "fr", "Permissions root manquantes pour extinction ou redémarrage");
Add(ERROR_SHUTDOWN_PERMISSION, "es", "Falta el permiso de root para el apagado o el reinicio");
Add(ERROR_FILE_OPEN, "en", "Invalid or non-existing file '%1': %2");
Add(ERROR_FILE_OPEN, "de", "Ungültige oder fehlende Datei '%1': %2");
Add(ERROR_FILE_OPEN, "sv", "Ogiltig eller saknad fil '%1': %2");
Add(ERROR_FILE_OPEN, "fr", "Fichier invalide ou non-existant '%1': %2");
Add(ERROR_FILE_OPEN, "es", "Archivo inválido o inexistente '%1': %2");
Add(ERROR_BLOCK_SIZE, "en", "Invalid block size %1 bytes");
Add(ERROR_BLOCK_SIZE, "de", "Ungültige Blockgröße %1 Bytes");
Add(ERROR_BLOCK_SIZE, "sv", "Ogiltig blockstorlek: %1 byte");
Add(ERROR_BLOCK_SIZE, "fr", "Taille de bloc invalide %1 octets");
Add(ERROR_BLOCK_SIZE, "es", "Tamaño de bloque inválido %1 bytes");
Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "en", "Block size for device type %1 is not configurable");
Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "de", "Blockgröße für Gerätetyp %1 ist nicht konfigurierbar");
Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "sv", "Enhetstypen %1 kan inte använda andra blockstorlekar");
Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "fr", "Taille de block pour le type de périphérique %1 non configurable");
Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "es", "El tamaño del bloque para el tipo de dispositivo %1 no es configurable");
}
void Localizer::Add(LocalizationKey key, const string& locale, const string& value)

View File

@ -0,0 +1,38 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi
//
// Copyright (C) 2020-2021 akuker
//
// [ SCSI Bus Monitor ]
//
//---------------------------------------------------------------------------
#include "os.h"
#include "scsi.h"
#include "data_sample.h"
const char *GetPhaseStr(const data_capture *sample)
{
return BUS::GetPhaseStrRaw(GetPhase(sample));
}
BUS::phase_t GetPhase(const data_capture *sample)
{
// Selection Phase
if (GetSel(sample))
{
return BUS::selection;
}
// Bus busy phase
if (!GetBsy(sample))
{
return BUS::busfree;
}
// Get target phase from bus signal line
DWORD mci = GetMsg(sample) ? 0x04 : 0x00;
mci |= GetCd(sample) ? 0x02 : 0x00;
mci |= GetIo(sample) ? 0x01 : 0x00;
return BUS::GetPhase(mci);
}

View File

@ -0,0 +1,49 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2020-2021 akuker
//
// [ SCSI Bus Monitor ]
//
//---------------------------------------------------------------------------
#pragma once
#include "scsi.h"
#include "gpiobus.h"
typedef struct data_capture
{
DWORD data;
uint64_t timestamp;
} data_capture_t;
#define GET_PIN(SAMPLE, PIN) ((bool)((SAMPLE->data >> PIN) & 1))
inline bool GetBsy(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_BSY); }
inline bool GetSel(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_SEL); }
inline bool GetAtn(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_ATN); }
inline bool GetAck(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_ACK); }
inline bool GetRst(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_RST); }
inline bool GetMsg(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_MSG); }
inline bool GetCd(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_CD); }
inline bool GetIo(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_IO); }
inline bool GetReq(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_REQ); }
inline bool GetDp(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_DP); }
inline BYTE GetData(const data_capture *sample)
{
DWORD data = sample->data;
return (BYTE)((data >> (PIN_DT0 - 0)) & (1 << 0)) |
((data >> (PIN_DT1 - 1)) & (1 << 1)) |
((data >> (PIN_DT2 - 2)) & (1 << 2)) |
((data >> (PIN_DT3 - 3)) & (1 << 3)) |
((data >> (PIN_DT4 - 4)) & (1 << 4)) |
((data >> (PIN_DT5 - 5)) & (1 << 5)) |
((data >> (PIN_DT6 - 6)) & (1 << 6)) |
((data >> (PIN_DT7 - 7)) & (1 << 7));
}
const char *GetPhaseStr(const data_capture *sample);
BUS::phase_t GetPhase(const data_capture *sample);

View File

@ -0,0 +1,205 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
//
//---------------------------------------------------------------------------
#include <stdio.h>
#include <iostream>
#include <fstream>
#include "os.h"
#include "log.h"
#include "sm_reports.h"
#include "rascsi_version.h"
using namespace std;
const static string html_header = R"(
<html>
<head>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
h1 {
text-align: center;
}
h2 {
text-align: center;
}
h3 {
text-align: center;
}
pre {
text-align: center;
}
.collapsible {
background-color: #777;
color: white;
cursor: pointer;
width: 100%;
border: none;
text-align: left;
outline: none;
}
.active, .collapsible:hover {
background-color: #555;
}
.content {
padding: 0;
display: none;
overflow: hidden;
background-color: #f1f1f1;
}
</style>
</head>
)";
static void print_copyright_info(ofstream& html_fp)
{
html_fp << "<table>" << endl \
<< "<h1>RaSCSI scsimon Capture Tool</h1>" << endl \
<< "<pre>Version " << rascsi_get_version_string() \
<< __DATE__ << " " << __TIME__ << endl \
<< "Copyright (C) 2016-2020 GIMONS" << endl \
<< "Copyright (C) 2020-2021 Contributors to the RaSCSI project" << endl \
<< "</pre></table>" << endl \
<< "<br>" << endl;
}
static const string html_footer = R"(
</table>
<script>
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.display === "block") {
content.style.display = "none";
} else {
content.style.display = "block";
}
});
}
</script>
</html>
)";
static void print_html_data(ofstream& html_fp, const data_capture *data_capture_array, DWORD capture_count)
{
const data_capture *data;
bool prev_data_valid = false;
bool curr_data_valid;
DWORD selected_id = 0;
BUS::phase_t prev_phase = BUS::busfree;
bool close_row = false;
int data_space_count = 0;
bool collapsible_div_active = false;
bool button_active = false;
html_fp << "<table>" << endl;
for (DWORD idx = 0; idx < capture_count; idx++)
{
data = &data_capture_array[idx];
curr_data_valid = GetAck(data) && GetReq(data);
BUS::phase_t phase = GetPhase(data);
if (phase == BUS::selection && !GetBsy(data))
{
selected_id = GetData(data);
}
if (prev_phase != phase)
{
if (close_row)
{
if (collapsible_div_active)
{
html_fp << "</code></div>";
}
else if (button_active)
{
html_fp << "</code></button>";
}
html_fp << "</td>";
if (data_space_count < 1)
{
html_fp << "<td>--</td>";
}
else
{
html_fp << "<td>wc: " << std::dec << "(0x" << std::hex << data_space_count << ")</td>";
}
html_fp << "</tr>" << endl;
data_space_count = 0;
}
html_fp << "<tr>";
close_row = true; // Close the row the next time around
html_fp << "<td>" << (double)data->timestamp / 100000 << "</td>";
html_fp << "<td>" << GetPhaseStr(data) << "</td>";
html_fp << "<td>" << std::hex << selected_id << "</td>";
html_fp << "<td>";
}
if (curr_data_valid && !prev_data_valid)
{
if (data_space_count == 0)
{
button_active = true;
html_fp << "<button type=\"button\" class=\"collapsible\"><code>";
}
if ((data_space_count % 16) == 0)
{
html_fp << std::hex << data_space_count << ": ";
}
html_fp << fmt::format("{0:02X}", GetData(data));
data_space_count++;
if ((data_space_count % 4) == 0)
{
html_fp << " ";
}
if (data_space_count == 16)
{
html_fp << "</code></button><div class=\"content\"><code>" << endl;
collapsible_div_active = true;
button_active = false;
}
if (((data_space_count % 16) == 0) && (data_space_count > 17))
{
html_fp << "<br>" << endl;
}
}
prev_data_valid = curr_data_valid;
prev_phase = phase;
}
}
void scsimon_generate_html(const char *filename, const data_capture *data_capture_array, DWORD capture_count)
{
LOGINFO("Creating HTML report file (%s)", filename);
ofstream html_ofstream;
html_ofstream.open(filename, ios::out);
html_ofstream << html_header;
print_copyright_info(html_ofstream);
print_html_data(html_ofstream, data_capture_array, capture_count);
html_ofstream << html_footer;
html_ofstream.close();
}

View File

@ -0,0 +1,98 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
//
//---------------------------------------------------------------------------
#include "sm_reports.h"
#include "log.h"
#include "spdlog/spdlog.h"
#include "string.h"
#include <iostream>
#include <fstream>
using namespace std;
const char timestamp_label[] = "\"timestamp\":\"0x";
const char data_label[] = "\"data\":\"0x";
DWORD scsimon_read_json(const char *json_filename, data_capture *data_capture_array, DWORD max_sz)
{
char str_buf[1024];
FILE *fp = fopen(json_filename, "r");
DWORD sample_count = 0;
while (fgets(str_buf, sizeof(str_buf), fp))
{
char timestamp[1024];
char data[1024];
uint64_t timestamp_uint;
uint32_t data_uint;
char *ptr;
char *timestamp_str;
char *data_str;
timestamp_str = strstr(str_buf, timestamp_label);
if (!timestamp_str)
continue;
strncpy(timestamp, &timestamp_str[strlen(timestamp_label)], 16);
timestamp[16] = '\0';
timestamp_uint = strtoull(timestamp, &ptr, 16);
data_str = strstr(str_buf, data_label);
if (!data_str)
continue;
strncpy(data, &data_str[strlen(data_label)], 8);
data[8] = '\0';
data_uint = strtoul(data, &ptr, 16);
// printf("Time: %016llX Data: %08X\n", timestamp_uint, data_uint);
data_capture_array[sample_count].timestamp = timestamp_uint;
data_capture_array[sample_count].data = data_uint;
sample_count++;
if (sample_count >= max_sz)
{
LOGWARN("File exceeds maximum buffer size. Some data may be missing.");
LOGWARN("Try re-running the tool with a larger buffer size");
break;
}
}
fclose(fp);
return sample_count;
}
//---------------------------------------------------------------------------
//
// Generate JSON Output File
//
//---------------------------------------------------------------------------
void scsimon_generate_json(const char *filename, const data_capture *data_capture_array, DWORD capture_count)
{
LOGTRACE("Creating JSON file (%s)", filename);
ofstream json_ofstream;
json_ofstream.open(filename, ios::out);
json_ofstream << "[" << endl;
DWORD i = 0;
while (i < capture_count)
{
json_ofstream << fmt::format("{{\"id\": \"{0:d}\", \"timestamp\":\"{1:#016x}\", \"data\":\"{2:#08x}\"}}", i, data_capture_array[i].timestamp, data_capture_array[i].data);
if (i != (capture_count - 1))
{
json_ofstream << ",";
}
json_ofstream << endl;
i++;
}
json_ofstream << "]" << endl;
json_ofstream.close();
}

View File

@ -0,0 +1,17 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi
//
// Copyright (C) 2020-2021 akuker
//
// [ SCSI Bus Monitor ]
//
//---------------------------------------------------------------------------
#include "data_sample.h"
DWORD scsimon_read_json(const char *json_filename, data_capture *data_capture_array, DWORD max_sz);
void scsimon_generate_html(const char *filename, const data_capture *data_capture_array, DWORD capture_count);
void scsimon_generate_json(const char *filename, const data_capture *data_capture_array, DWORD capture_count);
void scsimon_generate_value_change_dump(const char *filename, const data_capture *data_capture_array, DWORD capture_count);

View File

@ -0,0 +1,187 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi
//
// Copyright (C) 2020-2021 akuker
//
// [ SCSI Bus Monitor ]
//
//---------------------------------------------------------------------------
#include "os.h"
#include "log.h"
#include "spdlog/spdlog.h"
#include <sstream>
#include <iostream>
#include <fstream>
#include "data_sample.h"
#include "sm_reports.h"
#include "gpiobus.h"
using namespace std;
//---------------------------------------------------------------------------
//
// Constant declarations
//
//---------------------------------------------------------------------------
// Symbol definition for the VCD file
// These are just arbitrary symbols. They can be anything allowed by the VCD file format,
// as long as they're consistently used.
const char SYMBOL_PIN_DAT = '#';
const char SYMBOL_PIN_ATN = '+';
const char SYMBOL_PIN_RST = '$';
const char SYMBOL_PIN_ACK = '%';
const char SYMBOL_PIN_REQ = '^';
const char SYMBOL_PIN_MSG = '&';
const char SYMBOL_PIN_CD = '*';
const char SYMBOL_PIN_IO = '(';
const char SYMBOL_PIN_BSY = ')';
const char SYMBOL_PIN_SEL = '-';
const char SYMBOL_PIN_PHASE = '=';
// We'll use position 0 in the prev_value array to store the previous phase
#define PIN_PHASE 0
//---------------------------------------------------------------------------
//
// Variable declarations
//
//---------------------------------------------------------------------------
static BYTE prev_value[32] = {0xFF};
extern double ns_per_loop;
static BOOL get_pin_value(DWORD data, int pin)
{
return (data >> pin) & 1;
}
static BYTE get_data_field(DWORD data)
{
DWORD data_out =
((data >> (PIN_DT0 - 0)) & (1 << 7)) |
((data >> (PIN_DT1 - 1)) & (1 << 6)) |
((data >> (PIN_DT2 - 2)) & (1 << 5)) |
((data >> (PIN_DT3 - 3)) & (1 << 4)) |
((data >> (PIN_DT4 - 4)) & (1 << 3)) |
((data >> (PIN_DT5 - 5)) & (1 << 2)) |
((data >> (PIN_DT6 - 6)) & (1 << 1)) |
((data >> (PIN_DT7 - 7)) & (1 << 0));
return (BYTE)data_out;
}
static void vcd_output_if_changed_phase(ofstream& fp, DWORD data, int pin, char symbol)
{
BUS::phase_t new_value = GPIOBUS::GetPhaseRaw(data);
if (prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fp << "s" << GPIOBUS::GetPhaseStrRaw(new_value) << " " << symbol << endl;
}
}
static void vcd_output_if_changed_bool(ofstream& fp, DWORD data, int pin, char symbol)
{
BOOL new_value = get_pin_value(data, pin);
if (prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fp << new_value << symbol << endl;
}
}
static void vcd_output_if_changed_byte(ofstream& fp, DWORD data, int pin, char symbol)
{
BYTE new_value = get_data_field(data);
if (prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fp << "b"
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT7))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT6))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT5))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT4))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT3))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT2))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT1))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT0))
<< " " << symbol << endl;
}
}
void scsimon_generate_value_change_dump(const char *filename, const data_capture *data_capture_array, DWORD capture_count)
{
LOGTRACE("Creating Value Change Dump file (%s)", filename);
ofstream vcd_ofstream;
vcd_ofstream.open(filename, ios::out);
// Get the current time
time_t rawtime;
time(&rawtime);
struct tm *timeinfo = localtime(&rawtime);
char timestamp[256];
strftime(timestamp, sizeof(timestamp), "%d-%m-%Y %H-%M-%S", timeinfo);
vcd_ofstream
<< "$date" << endl
<< timestamp << endl
<< "$end" << endl
<< "$version" << endl
<< " VCD generator tool version info text." << endl
<< "$end" << endl
<< "$comment" << endl
<< " Tool build date:" << __TIMESTAMP__ << endl
<< "$end" << endl
<< "$timescale 1 ns $end" << endl
<< "$scope module logic $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_BSY << " BSY $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_SEL << " SEL $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_CD << " CD $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_IO << " IO $end"<< endl
<< "$var wire 1 " << SYMBOL_PIN_MSG << " MSG $end"<< endl
<< "$var wire 1 " << SYMBOL_PIN_REQ << " REQ $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_ACK << " ACK $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_ATN << " ATN $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_RST << " RST $end" << endl
<< "$var wire 8 " << SYMBOL_PIN_DAT << " data $end" << endl
<< "$var string 1 " << SYMBOL_PIN_PHASE << " phase $end" << endl
<< "$upscope $end" << endl
<< "$enddefinitions $end" << endl;
// Initial values - default to zeros
vcd_ofstream
<< "$dumpvars" << endl
<< "0" << SYMBOL_PIN_BSY << endl
<< "0" << SYMBOL_PIN_SEL << endl
<< "0" << SYMBOL_PIN_CD << endl
<< "0" << SYMBOL_PIN_IO << endl
<< "0" << SYMBOL_PIN_MSG << endl
<< "0" << SYMBOL_PIN_REQ << endl
<< "0" << SYMBOL_PIN_ACK << endl
<< "0" << SYMBOL_PIN_ATN << endl
<< "0" << SYMBOL_PIN_RST << endl
<< "b00000000 " << SYMBOL_PIN_DAT << endl
<< "$end" << endl;
DWORD i = 0;
while (i < capture_count)
{
vcd_ofstream << "#" << (uint64_t)(data_capture_array[i].timestamp * ns_per_loop) << endl;
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_BSY, SYMBOL_PIN_BSY);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_SEL, SYMBOL_PIN_SEL);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_CD, SYMBOL_PIN_CD);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_IO, SYMBOL_PIN_IO);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_MSG, SYMBOL_PIN_MSG);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_REQ, SYMBOL_PIN_REQ);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_ACK, SYMBOL_PIN_ACK);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_ATN, SYMBOL_PIN_ATN);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_RST, SYMBOL_PIN_RST);
vcd_output_if_changed_byte(vcd_ofstream, data_capture_array[i].data, PIN_DT0, SYMBOL_PIN_DAT);
vcd_output_if_changed_phase(vcd_ofstream, data_capture_array[i].data, PIN_PHASE, SYMBOL_PIN_PHASE);
i++;
}
vcd_ofstream.close();
}

View File

@ -58,7 +58,9 @@
#include <pwd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#if defined(__linux__)
#include <sys/epoll.h>
#endif
#include <netinet/in.h>
#if defined(__linux__)

View File

@ -12,9 +12,9 @@
#include <cstdio>
// The following should be updated for each release
const int rascsi_major_version = 21; // Last two digits of year
const int rascsi_minor_version = 12; // Month
const int rascsi_patch_version = -1; // Patch number - increment for each update
const int rascsi_major_version = 22; // Last two digits of year
const int rascsi_minor_version = 01; // Month
const int rascsi_patch_version = 01; // Patch number - increment for each update
static char rascsi_version_string[30]; // Allow for string up to "XX.XX.XXX" + null character + "development build"

View File

@ -10,6 +10,7 @@
//---------------------------------------------------------------------------
#pragma once
#include "os.h"
//===========================================================================
//

View File

@ -9,8 +9,6 @@
//---------------------------------------------------------------------------
#include "os.h"
#include "filepath.h"
#include "fileio.h"
#include "log.h"
#include "gpiobus.h"
#include "rascsi_version.h"
@ -19,7 +17,11 @@
#include <climits>
#include <sstream>
#include <iostream>
#include <getopt.h>
#include "rascsi.h"
#include <sched.h>
#include "monitor/sm_reports.h"
#include "monitor/data_sample.h"
using namespace std;
@ -28,48 +30,47 @@ using namespace std;
// Constant declarations
//
//---------------------------------------------------------------------------
#define MAX_BUFF_SIZE 1000000
// Symbol definition for the VCD file
// These are just arbitrary symbols. They can be anything allowed by the VCD file format,
// as long as they're consistently used.
#define SYMBOL_PIN_DAT '#'
#define SYMBOL_PIN_ATN '+'
#define SYMBOL_PIN_RST '$'
#define SYMBOL_PIN_ACK '%'
#define SYMBOL_PIN_REQ '^'
#define SYMBOL_PIN_MSG '&'
#define SYMBOL_PIN_CD '*'
#define SYMBOL_PIN_IO '('
#define SYMBOL_PIN_BSY ')'
#define SYMBOL_PIN_SEL '-'
#define SYMBOL_PIN_DAT '#'
#define SYMBOL_PIN_ATN '+'
#define SYMBOL_PIN_RST '$'
#define SYMBOL_PIN_ACK '%'
#define SYMBOL_PIN_REQ '^'
#define SYMBOL_PIN_MSG '&'
#define SYMBOL_PIN_CD '*'
#define SYMBOL_PIN_IO '('
#define SYMBOL_PIN_BSY ')'
#define SYMBOL_PIN_SEL '-'
#define SYMBOL_PIN_PHASE '='
// We'll use position 0 in the prev_value array to store the previous phase
#define PIN_PHASE 0
//---------------------------------------------------------------------------
//
// Variable declarations
//
//---------------------------------------------------------------------------
static BYTE prev_value[32] = {0xFF};
static volatile bool running; // Running flag
GPIOBUS *bus; // GPIO Bus
typedef struct data_capture{
DWORD data;
uint64_t timestamp;
} data_capture_t;
static volatile bool running; // Running flag
GPIOBUS *bus; // GPIO Bus
data_capture data_buffer[MAX_BUFF_SIZE];
DWORD buff_size = 1000000;
data_capture *data_buffer;
DWORD data_idx = 0;
double ns_per_loop;
bool print_help = false;
bool import_data = false;
// We don't really need to support 256 character file names - this causes
// all kinds of compiler warnings when the log filename can be up to 256
// characters. _MAX_FNAME/2 is just an arbitrary value.
char log_file_name[_MAX_FNAME/2] = "log.vcd";
char file_base_name[_MAX_FNAME / 2] = "log";
char vcd_file_name[_MAX_FNAME];
char json_file_name[_MAX_FNAME];
char html_file_name[_MAX_FNAME];
char input_file_name[_MAX_FNAME];
//---------------------------------------------------------------------------
//
@ -78,8 +79,84 @@ char log_file_name[_MAX_FNAME/2] = "log.vcd";
//---------------------------------------------------------------------------
void KillHandler(int sig)
{
// Stop instruction
running = false;
// Stop instruction
running = false;
}
void parse_arguments(int argc, char *argv[])
{
int opt;
while ((opt = getopt(argc, argv, "-Hhb:i:")) != -1)
{
switch (opt)
{
// The three options below are kind of a compound option with two letters
case 'h':
case 'H':
print_help = true;
break;
case 'b':
buff_size = atoi(optarg);
break;
case 'i':
strncpy(input_file_name, optarg, sizeof(input_file_name)-1);
import_data = true;
break;
case 1:
strncpy(file_base_name, optarg, sizeof(file_base_name) - 5);
break;
default:
cout << "default: " << optarg << endl;
break;
}
}
/* Process any remaining command line arguments (not options). */
if (optind < argc)
{
while (optind < argc)
strncpy(file_base_name, argv[optind++], sizeof(file_base_name)-1);
}
strcpy(vcd_file_name, file_base_name);
strcat(vcd_file_name, ".vcd");
strcpy(json_file_name, file_base_name);
strcat(json_file_name, ".json");
strcpy(html_file_name, file_base_name);
strcat(html_file_name, ".html");
}
//---------------------------------------------------------------------------
//
// Copyright text
//
//---------------------------------------------------------------------------
void print_copyright_text(int argc, char *argv[])
{
LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) ");
LOGINFO("version %s (%s, %s)",
rascsi_get_version_string(),
__DATE__,
__TIME__);
LOGINFO("Powered by XM6 TypeG Technology ");
LOGINFO("Copyright (C) 2016-2020 GIMONS");
LOGINFO("Copyright (C) 2020-2021 Contributors to the RaSCSI project");
LOGINFO(" ");
}
//---------------------------------------------------------------------------
//
// Help text
//
//---------------------------------------------------------------------------
void print_help_text(int argc, char *argv[])
{
LOGINFO("%s -i [input file json] -b [buffer size] [output file]", argv[0]);
LOGINFO(" -i [input file json] - scsimon will parse the json file instead of capturing new data");
LOGINFO(" If -i option is not specified, scsimon will read the gpio pins");
LOGINFO(" -b [buffer size] - Override the default buffer size of %d.", buff_size);
LOGINFO(" [output file] - Base name of the output files. The file extension (ex: .json)");
LOGINFO(" will be appended to this file name");
}
//---------------------------------------------------------------------------
@ -87,30 +164,23 @@ void KillHandler(int sig)
// Banner Output
//
//---------------------------------------------------------------------------
void Banner(int argc, char* argv[])
void Banner(int argc, char *argv[])
{
LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) ");
LOGINFO("version %s (%s, %s)\n",
rascsi_get_version_string(),
__DATE__,
__TIME__);
LOGINFO("Powered by XM6 TypeG Technology ");
LOGINFO("Copyright (C) 2016-2020 GIMONS");
LOGINFO("Copyright (C) 2020-2021 Contributors to the RaSCSI project");
LOGINFO("Connect type : %s", CONNECT_DESC);
LOGINFO(" %s - Value Change Dump file that can be opened with GTKWave", log_file_name);
if ((argc > 1 && strcmp(argv[1], "-h") == 0) ||
(argc > 1 && strcmp(argv[1], "--help") == 0)){
LOGINFO("Usage: %s [log filename]...", argv[0]);
exit(0);
}
if (import_data)
{
LOGINFO("Reading input file: %s", input_file_name);
}
else
{
LOGINFO(" ");
LOGINFO("Now collecting data.... Press CTRL-C to stop.")
LOGINFO(" ");
LOGINFO("Reading live data from the GPIO pins");
LOGINFO(" Connection type : %s", CONNECT_DESC);
}
LOGINFO(" Data buffer size: %u", buff_size);
LOGINFO(" ");
LOGINFO("Generating output files:");
LOGINFO(" %s - Value Change Dump file that can be opened with GTKWave", vcd_file_name);
LOGINFO(" %s - JSON file with raw data", json_file_name);
LOGINFO(" %s - HTML file with summary of commands", html_file_name);
}
//---------------------------------------------------------------------------
@ -120,246 +190,101 @@ void Banner(int argc, char* argv[])
//---------------------------------------------------------------------------
bool Init()
{
// Interrupt handler settings
if (signal(SIGINT, KillHandler) == SIG_ERR) {
return FALSE;
}
if (signal(SIGHUP, KillHandler) == SIG_ERR) {
return FALSE;
}
if (signal(SIGTERM, KillHandler) == SIG_ERR) {
return FALSE;
}
// Interrupt handler settings
if (signal(SIGINT, KillHandler) == SIG_ERR)
{
return FALSE;
}
if (signal(SIGHUP, KillHandler) == SIG_ERR)
{
return FALSE;
}
if (signal(SIGTERM, KillHandler) == SIG_ERR)
{
return FALSE;
}
// GPIO Initialization
bus = new GPIOBUS();
if (!bus->Init()) {
// GPIO Initialization
bus = new GPIOBUS();
if (!bus->Init())
{
LOGERROR("Unable to intiailize the GPIO bus. Exiting....");
return false;
}
// Bus Reset
bus->Reset();
// Other
running = false;
return true;
}
BOOL get_pin_value(DWORD data, int pin)
{
return (data >> pin) & 1;
}
BYTE get_data_field(DWORD data)
{
DWORD data_out =
((data >> (PIN_DT0 - 0)) & (1 << 7)) |
((data >> (PIN_DT1 - 1)) & (1 << 6)) |
((data >> (PIN_DT2 - 2)) & (1 << 5)) |
((data >> (PIN_DT3 - 3)) & (1 << 4)) |
((data >> (PIN_DT4 - 4)) & (1 << 3)) |
((data >> (PIN_DT5 - 5)) & (1 << 2)) |
((data >> (PIN_DT6 - 6)) & (1 << 1)) |
((data >> (PIN_DT7 - 7)) & (1 << 0));
return (BYTE)data_out;
}
void vcd_output_if_changed_phase(FILE *fp, DWORD data, int pin, char symbol)
{
BUS::phase_t new_value = GPIOBUS::GetPhaseRaw(data);
if (prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fprintf(fp, "s%s %c\n", GPIOBUS::GetPhaseStrRaw(new_value), symbol);
return false;
}
}
void vcd_output_if_changed_bool(FILE *fp, DWORD data, int pin, char symbol)
{
BOOL new_value = get_pin_value(data,pin);
if (prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fprintf(fp, "%d%c\n", new_value, symbol);
}
}
// Bus Reset
bus->Reset();
void vcd_output_if_changed_byte(FILE *fp, DWORD data, int pin, char symbol)
{
BYTE new_value = get_data_field(data);
if(prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fprintf(fp, "b%d%d%d%d%d%d%d%d %c\n",
get_pin_value(data,PIN_DT7),
get_pin_value(data,PIN_DT6),
get_pin_value(data,PIN_DT5),
get_pin_value(data,PIN_DT4),
get_pin_value(data,PIN_DT3),
get_pin_value(data,PIN_DT2),
get_pin_value(data,PIN_DT1),
get_pin_value(data,PIN_DT0), symbol);
}
}
// Other
running = false;
void create_value_change_dump()
{
LOGINFO("Creating Value Change Dump file (%s)", log_file_name);
FILE *fp = fopen(log_file_name,"w");
// Get the current time
time_t rawtime;
time(&rawtime);
struct tm *timeinfo = localtime(&rawtime);
char timestamp[256];
strftime (timestamp,sizeof(timestamp),"%d-%m-%Y %H-%M-%S",timeinfo);
fprintf(fp, "$date\n");
fprintf(fp, "%s\n", timestamp);
fprintf(fp, "$end\n");
fprintf(fp, "$version\n");
fprintf(fp, " VCD generator tool version info text.\n");
fprintf(fp, "$end\n");
fprintf(fp, "$comment\n");
fprintf(fp, " Tool build date:%s\n", __TIMESTAMP__);
fprintf(fp, "$end\n");
fprintf(fp, "$timescale 1 ns $end\n");
fprintf(fp, "$scope module logic $end\n");
fprintf(fp, "$var wire 1 %c BSY $end\n", SYMBOL_PIN_BSY);
fprintf(fp, "$var wire 1 %c SEL $end\n", SYMBOL_PIN_SEL);
fprintf(fp, "$var wire 1 %c CD $end\n", SYMBOL_PIN_CD);
fprintf(fp, "$var wire 1 %c IO $end\n", SYMBOL_PIN_IO);
fprintf(fp, "$var wire 1 %c MSG $end\n", SYMBOL_PIN_MSG);
fprintf(fp, "$var wire 1 %c REQ $end\n", SYMBOL_PIN_REQ);
fprintf(fp, "$var wire 1 %c ACK $end\n", SYMBOL_PIN_ACK);
fprintf(fp, "$var wire 1 %c ATN $end\n", SYMBOL_PIN_ATN);
fprintf(fp, "$var wire 1 %c RST $end\n", SYMBOL_PIN_RST);
fprintf(fp, "$var wire 8 %c data $end\n", SYMBOL_PIN_DAT);
fprintf(fp, "$var string 1 %c phase $end\n", SYMBOL_PIN_PHASE);
fprintf(fp, "$upscope $end\n");
fprintf(fp, "$enddefinitions $end\n");
// Initial values - default to zeros
fprintf(fp, "$dumpvars\n");
fprintf(fp, "0%c\n", SYMBOL_PIN_BSY);
fprintf(fp, "0%c\n", SYMBOL_PIN_SEL);
fprintf(fp, "0%c\n", SYMBOL_PIN_CD);
fprintf(fp, "0%c\n", SYMBOL_PIN_IO);
fprintf(fp, "0%c\n", SYMBOL_PIN_MSG);
fprintf(fp, "0%c\n", SYMBOL_PIN_REQ);
fprintf(fp, "0%c\n", SYMBOL_PIN_ACK);
fprintf(fp, "0%c\n", SYMBOL_PIN_ATN);
fprintf(fp, "0%c\n", SYMBOL_PIN_RST);
fprintf(fp, "b00000000 %c\n", SYMBOL_PIN_DAT);
fprintf(fp, "$end\n");
DWORD i = 0;
while (i < data_idx)
{
ostringstream s;
s << (uint64_t)(data_buffer[i].timestamp*ns_per_loop);
fprintf(fp, "#%s\n",s.str().c_str());
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_BSY, SYMBOL_PIN_BSY);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_SEL, SYMBOL_PIN_SEL);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_CD, SYMBOL_PIN_CD);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_IO, SYMBOL_PIN_IO);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_MSG, SYMBOL_PIN_MSG);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_REQ, SYMBOL_PIN_REQ);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_ACK, SYMBOL_PIN_ACK);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_ATN, SYMBOL_PIN_ATN);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_RST, SYMBOL_PIN_RST);
vcd_output_if_changed_byte(fp, data_buffer[i].data, PIN_DT0, SYMBOL_PIN_DAT);
vcd_output_if_changed_phase(fp, data_buffer[i].data, PIN_PHASE, SYMBOL_PIN_PHASE);
i++;
}
fclose(fp);
return true;
}
void Cleanup()
{
LOGINFO("Stopping data collection....");
create_value_change_dump();
if (!import_data)
{
LOGINFO("Stopping data collection....");
}
LOGINFO(" ");
LOGINFO("Generating %s...", vcd_file_name);
scsimon_generate_value_change_dump(vcd_file_name, data_buffer, data_idx);
LOGINFO("Generating %s...", json_file_name);
scsimon_generate_json(json_file_name, data_buffer, data_idx);
LOGINFO("Generating %s...", html_file_name);
scsimon_generate_html(html_file_name, data_buffer, data_idx);
// Cleanup the Bus
bus->Cleanup();
delete bus;
if (bus)
{
// Cleanup the Bus
bus->Cleanup();
delete bus;
}
}
void Reset()
{
// Reset the bus
bus->Reset();
// Reset the bus
bus->Reset();
}
//---------------------------------------------------------------------------
//
// Pin the thread to a specific CPU
// Pin the thread to a specific CPU (Only applies to Linux)
//
//---------------------------------------------------------------------------
#ifdef __linux__
void FixCpu(int cpu)
{
// Get the number of CPUs
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
sched_getaffinity(0, sizeof(cpu_set_t), &cpuset);
int cpus = CPU_COUNT(&cpuset);
// Get the number of CPUs
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
sched_getaffinity(0, sizeof(cpu_set_t), &cpuset);
int cpus = CPU_COUNT(&cpuset);
// Set the thread affinity
if (cpu < cpus) {
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
}
// Set the thread affinity
if (cpu < cpus)
{
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
}
}
#endif
#ifdef DEBUG
static DWORD high_bits = 0x0;
static DWORD low_bits = 0xFFFFFFFF;
#endif
#endif
//---------------------------------------------------------------------------
//
// Main processing
//
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
int main(int argc, char *argv[])
{
ostringstream s;
#ifdef DEBUG
DWORD prev_high = high_bits;
DWORD prev_low = low_bits;
#endif
DWORD prev_sample = 0xFFFFFFFF;
DWORD this_sample = 0;
struct sched_param schparam;
timeval start_time;
timeval stop_time;
uint64_t loop_count = 0;
timeval time_diff;
uint64_t elapsed_us;
int str_len;
// If there is an argument specified and it is NOT -h or --help
if((argc > 1) && (strcmp(argv[1], "-h")) && (strcmp(argv[1], "--help"))){
str_len = strlen(argv[1]);
if ((str_len >= 1) && (str_len < _MAX_FNAME))
{
strncpy(log_file_name, argv[1], sizeof(log_file_name));
// Append .vcd if its not already there
if((str_len < 4) || strcasecmp(log_file_name + (str_len - 4), ".vcd")) {
strcat(log_file_name, ".vcd");
}
}
else
{
printf("Invalid log name specified. Using log.vcd");
}
}
#ifdef DEBUG
spdlog::set_level(spdlog::level::trace);
@ -367,53 +292,98 @@ int main(int argc, char* argv[])
spdlog::set_level(spdlog::level::info);
#endif
spdlog::set_pattern("%^[%l]%$ %v");
// Output the Banner
Banner(argc, argv);
memset(data_buffer,0,sizeof(data_buffer));
// Initialize
int ret = 0;
if (!Init()) {
ret = EPERM;
goto init_exit;
}
print_copyright_text(argc, argv);
parse_arguments(argc, argv);
// Reset
Reset();
#ifdef DEBUG
DWORD prev_high = high_bits;
DWORD prev_low = low_bits;
#endif
ostringstream s;
DWORD prev_sample = 0xFFFFFFFF;
DWORD this_sample = 0;
timeval start_time;
timeval stop_time;
uint64_t loop_count = 0;
timeval time_diff;
uint64_t elapsed_us;
if (print_help)
{
print_help_text(argc, argv);
exit(0);
}
// Output the Banner
Banner(argc, argv);
data_buffer = (data_capture *)malloc(sizeof(data_capture_t) * buff_size);
bzero(data_buffer, sizeof(data_capture_t) * buff_size);
if (import_data)
{
data_idx = scsimon_read_json(input_file_name, data_buffer, buff_size);
if (data_idx > 0)
{
LOGDEBUG("Read %d samples from %s", data_idx, input_file_name);
Cleanup();
}
exit(0);
}
LOGINFO(" ");
LOGINFO("Now collecting data.... Press CTRL-C to stop.")
LOGINFO(" ");
// Initialize
int ret = 0;
if (!Init())
{
ret = EPERM;
goto init_exit;
}
// Reset
Reset();
#ifdef __linux__
// Set the affinity to a specific processor core
FixCpu(3);
FixCpu(3);
// Scheduling policy setting (highest priority)
schparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &schparam);
// Scheduling policy setting (highest priority)
struct sched_param schparam;
schparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &schparam);
#endif
// Start execution
running = true;
bus->SetACT(FALSE);
// Start execution
running = true;
bus->SetACT(FALSE);
(void)gettimeofday(&start_time, NULL);
LOGDEBUG("ALL_SCSI_PINS %08X\n",ALL_SCSI_PINS);
LOGDEBUG("ALL_SCSI_PINS %08X\n", ALL_SCSI_PINS);
// Main Loop
while (running) {
// Work initialization
this_sample = (bus->Aquire() & ALL_SCSI_PINS);
while (running)
{
// Work initialization
this_sample = (bus->Aquire() & ALL_SCSI_PINS);
loop_count++;
if (loop_count > LLONG_MAX -1)
if (loop_count > LLONG_MAX - 1)
{
LOGINFO("Maximum amount of time has elapsed. SCSIMON is terminating.");
running=false;
running = false;
}
if (data_idx >= (MAX_BUFF_SIZE-2))
if (data_idx >= (buff_size - 2))
{
LOGINFO("Internal data buffer is full. SCSIMON is terminating.");
running=false;
running = false;
}
if (this_sample != prev_sample)
{
if (this_sample != prev_sample)
{
#ifdef DEBUG
// This is intended to be a debug check to see if every pin is set
@ -422,27 +392,28 @@ int main(int argc, char* argv[])
low_bits &= this_sample;
if ((high_bits != prev_high) || (low_bits != prev_low))
{
LOGDEBUG(" %08X %08X\n",high_bits, low_bits);
LOGDEBUG(" %08X %08X\n", high_bits, low_bits);
}
prev_high = high_bits;
prev_low = low_bits;
if((data_idx % 1000) == 0){
s.str("");
s << "Collected " << data_idx << " samples...";
LOGDEBUG("%s", s.str().c_str());
if ((data_idx % 1000) == 0)
{
s.str("");
s << "Collected " << data_idx << " samples...";
LOGDEBUG("%s", s.str().c_str());
}
#endif
data_buffer[data_idx].data = this_sample;
data_buffer[data_idx].timestamp = loop_count;
data_idx++;
prev_sample = this_sample;
}
}
continue;
}
continue;
}
// Collect one last sample, otherwise it looks like the end of the data was cut off
if (data_idx < MAX_BUFF_SIZE)
if (data_idx < buff_size)
{
data_buffer[data_idx].data = this_sample;
data_buffer[data_idx].timestamp = loop_count;
@ -453,7 +424,7 @@ int main(int argc, char* argv[])
timersub(&stop_time, &start_time, &time_diff);
elapsed_us = ((time_diff.tv_sec*1000000) + time_diff.tv_usec);
elapsed_us = ((time_diff.tv_sec * 1000000) + time_diff.tv_usec);
s.str("");
s << "Elapsed time: " << elapsed_us << " microseconds (" << elapsed_us / 1000000 << " seconds)";
LOGINFO("%s", s.str().c_str());
@ -461,14 +432,14 @@ int main(int argc, char* argv[])
s << "Collected " << data_idx << " changes";
LOGINFO("%s", s.str().c_str());
// Note: ns_per_loop is a global variable that is used by Cleanup() to printout the timestamps.
// Note: ns_per_loop is a global variable that is used by Cleanup() to printout the timestamps.
ns_per_loop = (elapsed_us * 1000) / (double)loop_count;
s.str("");
s << "Read the SCSI bus " << loop_count << " times with an average of " << ns_per_loop << " ns for each read";
LOGINFO("%s", s.str().c_str());
Cleanup();
Cleanup();
init_exit:
exit(ret);
exit(ret);
}

View File

@ -24,9 +24,12 @@ A separate mocking solution will be needed for this interface.
It is recommended to run pylint against new code to protect against bugs
and keep the code readable and maintainable.
The local pylint configuration lives in .pylintrc
In order for pylint to recognize venv libraries, the pylint-venv package is required.
```
sudo apt install pylint3
sudo pip install pylint-venv
source venv/bin/activate
pylint3 python_source_file.py
```
@ -53,25 +56,59 @@ We use the Flask-Babel library and Flask/Jinja2 extension for i18n.
It uses the 'pybabel' command line tool for extracting and compiling localizations.
Activate the Python venv in src/web/ to use it:
```
$ cd src/web/
$ source venv/bin/activate
$ pybabel --help
```
To create a new localization, it needs to be added to accept_languages in
the get_locale() method, and also to localizer.cpp in the RaSCSI C++ code.
To create a new localization, it needs to be added to the LANGAUGES constant in
web/settings.py. To localize messages coming from the RaSCSI backend, update also code in
raspberrypi/localizer.cpp in the RaSCSI C++ code.
Once this is done, follow the steps in the [Flask-Babel documentation](https://flask-babel.tkte.ch/#translating-applications)
to generate the messages.po for the new language.
Updating an existing messages.po is also covered above.
Updating the strings in an existing messages.po is also covered above.
When you are ready to contribute new or updated localizations, use the same Gitflow Workflow as used for any code contributions to submit PRs against the develop branch.
### Working with PO files
See the [GNU gettext documentation](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html) for an introduction to the PO file format.
We make heavy use of __python-format__ for formatting, for instance:
```
#: file_cmds.py:353
#, python-format
msgid "%(file_name)s downloaded to %(save_dir)s"
msgstr "Laddade ner %(file_name)s till %(save_dir)s"
```
There are also a few instances of formatting in JavaScript:
```
#: templates/index.html:381
msgid "Server responded with code: {{statusCode}}"
msgstr "Servern svarade med kod: {{statusCode}}"
```
And with html tags:
```
#: templates/index.html:304
#, python-format
msgid ""
"Emulates a SCSI DaynaPORT Ethernet Adapter. <a href=\"%(url)s\">Host "
"drivers and configuration required</a>."
msgstr ""
"Emulerar en SCSI DaynaPORT ethernet-adapter. <a href=\"%(url)s\">Kräver "
"drivrutiner och inställningar</a>."
```
### (Optional) See translation stats for a localization
Install the gettext package and use msgfmt to see the translation progress.
```
$ sudo apt install gettext
$ cd src/web/
$ msgfmt --statistics translations/sv/LC_MESSAGES/messages.po
215 translated messages, 1 untranslated message.
```

View File

@ -418,5 +418,17 @@
"file_type": null,
"description": "Boots DECstations and VAXstations. Use only with workstations of this vintage.",
"url": ""
},
{
"device_type": "SCCD",
"vendor": "MATSHITA",
"product": "CD-ROM CR-8005 ",
"revision": "1.0k",
"block_size": 2048,
"size": null,
"name": "Apple CD 600e",
"file_type": null,
"description": "Emulates Apple CD ROM drive for use with Macintosh computers.",
"url": ""
}
]

View File

@ -66,7 +66,8 @@ def list_images():
command = proto.PbCommand()
command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -123,7 +124,8 @@ def create_new_image(file_name, file_type, size):
command = proto.PbCommand()
command.operation = proto.PbOperation.CREATE_IMAGE
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
command.params["file"] = file_name + "." + file_type
command.params["size"] = str(size)
@ -144,7 +146,8 @@ def delete_image(file_name):
command = proto.PbCommand()
command.operation = proto.PbOperation.DELETE_IMAGE
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
command.params["file"] = file_name
@ -163,7 +166,8 @@ def rename_image(file_name, new_file_name):
command = proto.PbCommand()
command.operation = proto.PbOperation.RENAME_IMAGE
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
command.params["from"] = file_name
command.params["to"] = new_file_name
@ -275,7 +279,7 @@ def download_file_to_iso(url, *iso_args):
tmp_full_path = tmp_dir + file_name
iso_filename = f"{server_info['image_dir']}/{file_name}.iso"
req_proc = download_to_dir(url, tmp_dir)
req_proc = download_to_dir(url, tmp_dir, file_name)
if not req_proc["status"]:
return {"status": False, "msg": req_proc["msg"]}
@ -300,18 +304,16 @@ def download_file_to_iso(url, *iso_args):
delete_file(tmp_full_path)
try:
iso_proc = (
run(
[
"genisoimage",
*iso_args,
"-o",
iso_filename,
tmp_dir,
],
capture_output=True,
check=True,
)
run(
[
"genisoimage",
*iso_args,
"-o",
iso_filename,
tmp_dir,
],
capture_output=True,
check=True,
)
except CalledProcessError as error:
logging.warning("Executed shell command: %s", " ".join(error.cmd))
@ -320,18 +322,20 @@ def download_file_to_iso(url, *iso_args):
return {
"status": True,
"msg": _(u"Created CD-ROM ISO image with arguments \"%(value)s\"", value=" ".join(iso_args)),
"msg": _(
u"Created CD-ROM ISO image with arguments \"%(value)s\"",
value=" ".join(iso_args),
),
"file_name": iso_filename,
}
def download_to_dir(url, save_dir):
def download_to_dir(url, save_dir, file_name):
"""
Takes (str) url, (str) save_dir
Takes (str) url, (str) save_dir, (str) file_name
Returns (dict) with (bool) status and (str) msg
"""
import requests
file_name = PurePath(url).name
logging.info("Making a request to download %s", url)
try:
@ -393,7 +397,10 @@ def write_config(file_name):
json_file,
indent=4
)
return {"status": True, "msg": _(u"Saved configuration file to %(file_name)s", file_name=file_name)}
return {
"status": True,
"msg": _(u"Saved configuration file to %(file_name)s", file_name=file_name),
}
except (IOError, ValueError, EOFError, TypeError) as error:
logging.error(str(error))
delete_file(file_name)

View File

@ -2,11 +2,11 @@
Module for commands sent to the RaSCSI backend service.
"""
from settings import REMOVABLE_DEVICE_TYPES
from socket_cmds import send_pb_command
from flask import current_app, session
from flask_babel import _
import rascsi_interface_pb2 as proto
from settings import REMOVABLE_DEVICE_TYPES
from socket_cmds import send_pb_command
def get_server_info():
@ -25,7 +25,8 @@ def get_server_info():
command = proto.PbCommand()
command.operation = proto.PbOperation.SERVER_INFO
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -84,7 +85,8 @@ def get_reserved_ids():
command = proto.PbCommand()
command.operation = proto.PbOperation.RESERVED_IDS_INFO
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -106,7 +108,8 @@ def get_network_info():
command = proto.PbCommand()
command.operation = proto.PbOperation.NETWORK_INTERFACES_INFO
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -125,7 +128,8 @@ def get_device_types():
command = proto.PbCommand()
command.operation = proto.PbOperation.DEVICE_TYPES_INFO
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -148,7 +152,8 @@ def get_image_files_info():
command = proto.PbCommand()
command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -177,7 +182,8 @@ def attach_image(scsi_id, **kwargs):
"""
command = proto.PbCommand()
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
devices = proto.PbDeviceDefinition()
devices.id = int(scsi_id)
@ -253,7 +259,8 @@ def detach_by_id(scsi_id, unit=None):
command.operation = proto.PbOperation.DETACH
command.devices.append(devices)
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -269,7 +276,8 @@ def detach_all():
command = proto.PbCommand()
command.operation = proto.PbOperation.DETACH_ALL
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -292,7 +300,8 @@ def eject_by_id(scsi_id, unit=None):
command.operation = proto.PbOperation.EJECT
command.devices.append(devices)
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -312,7 +321,8 @@ def list_devices(scsi_id=None, unit=None):
command = proto.PbCommand()
command.operation = proto.PbOperation.DEVICES_INFO
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
# If method is called with scsi_id parameter, return the info on those devices
# Otherwise, return the info on all attached devices
@ -390,7 +400,8 @@ def reserve_scsi_ids(reserved_scsi_ids):
command.operation = proto.PbOperation.RESERVE_IDS
command.params["ids"] = ",".join(reserved_scsi_ids)
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -408,7 +419,8 @@ def set_log_level(log_level):
command.operation = proto.PbOperation.LOG_LEVEL
command.params["level"] = str(log_level)
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -426,7 +438,8 @@ def shutdown_pi(mode):
command.operation = proto.PbOperation.SHUT_DOWN
command.params["mode"] = str(mode)
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()
@ -443,7 +456,8 @@ def is_token_auth():
command = proto.PbCommand()
command.operation = proto.PbOperation.CHECK_AUTHENTICATION
command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"]
if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()

View File

@ -32,4 +32,4 @@ RESERVATIONS = ["" for x in range(0, 8)]
AUTH_GROUP = "rascsi"
# The language locales supported by RaSCSI
LANGUAGES = ["en", "de", "sv"]
LANGUAGES = ["en", "de", "sv", "fr", "es"]

View File

@ -3,9 +3,9 @@ Module for sending and receiving data over a socket connection with the RaSCSI b
"""
import logging
from time import sleep
from flask import abort
from flask_babel import _
from time import sleep
def send_pb_command(payload):
"""

View File

@ -34,9 +34,13 @@ fi
# Test for two known broken venv states
if test -e venv; then
GOOD_VENV=true
! test -e venv/bin/activate && GOOD_VENV=false
pip3 list 1> /dev/null
test $? -eq 1 && GOOD_VENV=false
if ! test -e venv/bin/activate; then
GOOD_VENV=false
else
source venv/bin/activate
pip3 list 1> /dev/null
test $? -eq 1 && GOOD_VENV=false
fi
if ! "$GOOD_VENV"; then
echo "Deleting bad python venv"
sudo rm -rf venv

File diff suppressed because it is too large Load Diff

View File

@ -372,7 +372,25 @@
forceChunking: true,
url: '/files/upload',
maxFilesize: {{ max_file_size }}, // MB
chunkSize: 1000000 // bytes
chunkSize: 1000000, // bytes
dictDefaultMessage: "{{ _("Drop files here to upload") }}",
dictFallbackMessage: "{{ _("Your browser does not support drag'n'drop file uploads.") }}",
dictFallbackText: "{{ _("Please use the fallback form below to upload your files like in the olden days.") }}",
dictFileTooBig: "{{ _("File is too big: {{filesize}}MB. Max filesize: {{maxFilesize}}MB.") }}",
dictInvalidFileType: "{{ _("You can't upload files of this type.") }}",
dictResponseError: "{{ _("Server responded with code: {{statusCode}}") }}",
dictCancelUpload:" {{ _("Cancel upload") }}",
dictUploadCanceled: "{{ _("Upload canceled.") }}",
dictCancelUploadConfirmation: "{{ _("Are you sure you want to cancel this upload?") }}",
dictRemoveFile: "{{ _("Remove file") }}",
dictMaxFilesExceeded: "{{ _("You can not upload any more files.") }}",
dictFileSizeUnits: {
tb: "{{ _("TB") }}",
gb: "{{ _("GB") }}",
mb: "{{ _("MB") }}",
kb: "{{ _("KB") }}",
b: "{{ _("b") }}"
}
}
</script>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: RaSCSI 68kmla Edition\n"
"Report-Msgid-Bugs-To: https://github.com/akuker/RASCSI/issues\n"
"POT-Creation-Date: 2021-12-26 21:59-0800\n"
"POT-Creation-Date: 2021-12-28 14:39-0800\n"
"PO-Revision-Date: 2021-12-24 16:16-0800\n"
"Last-Translator: Daniel Markstedt <markstedt@gmail.com>\n"
"Language: sv\n"
@ -41,53 +41,53 @@ msgstr "Kunde ej flytta filen till %(target_path)s"
#: file_cmds.py:323
#, python-format
msgid "Created CD-ROM ISO image with arguments \"%(value)s\""
msgstr "Skapade en CD-ROM ISO-fil med argumenten \"%(value)s\""
msgstr "Skapade en cd-rom ISO-fil med argumentet \"%(value)s\""
#: file_cmds.py:353
#: file_cmds.py:354
#, python-format
msgid "%(file_name)s downloaded to %(save_dir)s"
msgstr "Laddade ner %(file_name)s till %(save_dir)s"
#: file_cmds.py:396
#: file_cmds.py:399
#, python-format
msgid "Saved configuration file to %(file_name)s"
msgstr "Sparade konfigurationsfilen som %(file_name)s"
#: file_cmds.py:406
#: file_cmds.py:410
#, python-format
msgid "Could not write to file: %(file_name)s"
msgstr "Kunde ej skriva till filen %(file_name)s"
#: file_cmds.py:464
#: file_cmds.py:468
msgid "Invalid configuration file format"
msgstr "Ogiltigt konfigurationsfilformat"
#: file_cmds.py:467
#: file_cmds.py:471
#, python-format
msgid "Loaded configurations from: %(file_name)s"
msgstr "Laddade konfigurationer från %(file_name)s"
#: file_cmds.py:476
#: file_cmds.py:480
#, python-format
msgid "Could not read configuration file: %(file_name)s"
msgstr "Kunde ej läsa konfigurationer från filen %(file_name)s"
#: file_cmds.py:493
#: file_cmds.py:497
#, python-format
msgid "Created properties file: %(file_path)s"
msgstr "Skapade egenskapsfilen %(file_path)s"
#: file_cmds.py:504
#: file_cmds.py:508
#, python-format
msgid "Could not write to properties file: %(file_path)s"
msgstr "Kunde ej spara egenskaper till filen %(file_path)s"
#: file_cmds.py:520
#: file_cmds.py:524
#, python-format
msgid "Read properties from file: %(file_path)s"
msgstr "Läste egenskaper från filen %(file_path)s"
#: file_cmds.py:530
#: file_cmds.py:534
#, python-format
msgid "Could not read properties from file: %(file_path)s"
msgstr "Kunde ej läsa egenskaper från filen %(file_path)s"
@ -113,7 +113,7 @@ msgid ""
" crashed."
msgstr ""
"RaSCSIs webbgränssnitt kunde inte ansluta till RaSCSI på "
"%(host)s:%(port)s med felmeddelande %(error_msg)s. RaSCSI-processen är "
"%(host)s:%(port)s med felmeddelandet %(error_msg)s. RaSCSI-processen är "
"antingen avslagen eller har krashat."
#: socket_cmds.py:79
@ -132,7 +132,7 @@ msgstr ""
"RaSCSIs webbgränssnitt fick en ogiltig respons från RaSCSI. Gå tillbaks "
"och försök igen. Om samma fel upprepas så rapportera en bugg."
#: web.py:97
#: web.py:119
msgid ""
"RaSCSI is password protected. Start the Web Interface with the --password"
" parameter."
@ -140,71 +140,71 @@ msgstr ""
"RaSCSI är lösenordsskyddat. Start webbgränssnittet med parametern "
"--password ."
#: web.py:191
#: web.py:220
#, python-format
msgid "Could not read drive properties from %(properties_file)s"
msgstr "Kunde ej läsa egenskaper från %(properties_file)s"
#: web.py:255
#: web.py:290
#, python-format
msgid "You must log in with credentials for a user in the '%(group)s' group"
msgstr "Du måste logga in som en användare som tillhör %(group)s-gruppen"
#: web.py:308 web.py:843
#: web.py:348 web.py:887
#, python-format
msgid "Image file created: %(file_name)s"
msgstr "Skapade skivbildfil: %(file_name)s"
msgstr "Skapade skivbildsfil %(file_name)s"
#: web.py:433
#: web.py:473
msgid "An error occurred when fetching logs."
msgstr "Ett fel inträffade när vi skaffade loggar."
#: web.py:448
#: web.py:488
#, python-format
msgid "Log level set to %(value)s"
msgstr "Ställde in loggnivån till %(value)s"
msgstr "Bytte loggnivån till %(value)s"
#: web.py:467
#: web.py:507
#, python-format
msgid "Please follow the instructions at %(url)s"
msgstr "Följ instruktionerna på %(url)s"
#: web.py:471
#: web.py:511
msgid "Configure IPv4 forwarding before using a wireless network device."
msgstr ""
"Ställ in IPv4-vidarebefodring innan du använder en trådlös "
"nätverksadapter."
"Ställ in IPv4-vidarebefodring innan du använder ett trådlöst "
"nätverksgränssnitt."
#: web.py:475
#: web.py:515
msgid "Configure NAT before using a wireless network device."
msgstr "Ställ in NAT innan du använder en trådlös nätverksadapter."
msgstr "Ställ in NAT innan du använder ett trådlöst nätverksgränssnitt."
#: web.py:480 web.py:484
#: web.py:520 web.py:524
msgid "Configure the network bridge before using a wired network device."
msgstr "Ställ in nätverksbryggan innan du använder ett nätverksgränssnitt."
#: web.py:497
#: web.py:537
#, python-format
msgid "Attached DaynaPORT to SCSI ID %(id_number)s"
msgstr "Anslöt DaynaPORT till SCSI-id %(id_number)s"
#: web.py:545
#: web.py:585
#, python-format
msgid "Attached %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s"
msgstr "Anslöt %(file_name)s till SCSI-id %(id_number)s LUN %(unit_number)s"
#: web.py:548
#: web.py:588
#, python-format
msgid ""
"The image file size %(file_size)s bytes is not a multiple of "
"%(block_size)s. RaSCSI will ignore the trailing data. The image may be "
"corrupted, so proceed with caution."
msgstr ""
"Filstorleken %(file_size)s bytes är inte en multipel av %(block_size)s. "
"Filstorleken %(file_size)s byte är inte en multipel av %(block_size)s. "
"RaSCSI ignorerar den överflödiga datan. Skivbilden är möjligen förstörd, "
"så var försiktig när du använder den."
#: web.py:554
#: web.py:594
#, python-format
msgid ""
"Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN "
@ -213,133 +213,127 @@ msgstr ""
"Kunde inte ansluta %(file_name)s till SCSI-id %(id_number)s LUN "
"%(unit_number)s"
#: web.py:568
#: web.py:608
msgid "Detached all SCSI devices"
msgstr "Kopplade ifrån alla SCSI-enheter"
#: web.py:585
#: web.py:625
#, python-format
msgid "Detached SCSI ID %(id_number)s LUN %(unit_number)s"
msgstr "Kopplade ifrån SCSI-id %(id_number)s LUN %(unit_number)s"
#: web.py:589
#: web.py:629
#, python-format
msgid ""
"Failed to detach %(file_name)s from SCSI ID %(id_number)s LUN "
"%(unit_number)s"
msgid "Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s"
msgstr ""
"Kunde ej koppla ifrån %(file_name)s från SCSI-id %(id_number)s LUN "
"%(unit_number)s"
"Kunde ej koppla ifrån SCSI-id %(id_number)s LUN %(unit_number)s"
#: web.py:606
#: web.py:646
#, python-format
msgid "Ejected SCSI ID %(id_number)s LUN %(unit_number)s"
msgstr "Utmatade SCSI-id %(id_number)s LUN %(unit_number)s"
#: web.py:610
#: web.py:650
#, python-format
msgid ""
"Failed to eject %(file_name)s from SCSI ID %(id_number)s LUN "
"%(unit_number)s"
msgid "Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s"
msgstr ""
"Kunde ej utmata %(file_name)s från SCSI-id %(id_number)s LUN "
"%(unit_number)s"
"Kunde ej mata ut skiva från SCSI-id %(id_number)s LUN %(unit_number)s"
#: web.py:633
#: web.py:673
msgid "DEVICE INFO"
msgstr "ENHETSDATA"
#: web.py:635
#: web.py:675
#, python-format
msgid "SCSI ID: %(id_number)s"
msgstr "SCSI-id: %(id_number)s"
#: web.py:636
#: web.py:676
#, python-format
msgid "LUN: %(unit_number)s"
msgstr "LUN: %(unit_number)s"
#: web.py:637
#: web.py:677
#, python-format
msgid "Type: %(device_type)s"
msgstr "Typ: %(device_type)s"
#: web.py:638
#: web.py:678
#, python-format
msgid "Status: %(device_status)s"
msgstr "Status: %(device_status)s"
#: web.py:639
#: web.py:679
#, python-format
msgid "File: %(image_file)s"
msgstr "Fil: %(image_file)s"
#: web.py:640
#: web.py:680
#, python-format
msgid "Parameters: %(value)s"
msgstr "Parametrar: %(value)s"
#: web.py:641
#: web.py:681
#, python-format
msgid "Vendor: %(value)s"
msgstr "Tillverkare: %(value)s"
#: web.py:642
#: web.py:682
#, python-format
msgid "Product: %(value)s"
msgstr "Produkt: %(value)s"
#: web.py:643
#: web.py:683
#, python-format
msgid "Revision: %(revision_number)s"
msgstr "Revision: %(revision_number)s"
#: web.py:644
#: web.py:684
#, python-format
msgid "Block Size: %(value)s bytes"
msgstr "Blockstorlek: %(value)s bytes"
msgstr "Blockstorlek: %(value)s byte"
#: web.py:645
#: web.py:685
#, python-format
msgid "Image Size: %(value)s bytes"
msgstr "Skivbildsstorlek: %(value)s bytes"
msgstr "Skivbildsstorlek: %(value)s byte"
#: web.py:664
#: web.py:704
#, python-format
msgid "Reserved SCSI ID %(id_number)s"
msgstr "Reserverat SCSI-id %(id_number)s"
#: web.py:667
#: web.py:707
#, python-format
msgid "Failed to reserve SCSI ID %(id_number)s"
msgstr "Kunde ej reservera SCSI-id %(id_number)s"
#: web.py:683
#: web.py:723
#, python-format
msgid "Released the reservation for SCSI ID %(id_number)s"
msgstr "Avreserverade SCSI-id %(id_number)s"
#: web.py:686
#: web.py:726
#, python-format
msgid "Failed to release the reservation for SCSI ID %(id_number)s"
msgstr "Kunde ej avreservera SCSI-id %(id_number)s"
#: web.py:724
#: web.py:764
#, python-format
msgid "Saved image as: %(file_name)s"
msgstr "Sparade bildfilen som %(file_name)s"
#: web.py:726
#: web.py:766
#, python-format
msgid "Failed to create CD-ROM image from %(url)s"
msgstr "Kunde ej skapa CD-ROM-bildfil från %(url)s"
#: web.py:732
#: web.py:772
#, python-format
msgid "Attached to SCSI ID %(id_number)s"
msgstr "Anslöt till SCSI-id %(id_number)s"
#: web.py:735
#: web.py:775
#, python-format
msgid ""
"Failed to attach image to SCSI ID %(id_number)s. Try attaching it "
@ -348,55 +342,60 @@ msgstr ""
"Kunde ej ansluta bildfilen till SCSI-id %(id_number)s. Försök ansluta den"
" manuellt."
#: web.py:754 web.py:771
#: web.py:794 web.py:811
#, python-format
msgid "Failed to download file from %(url)s"
msgstr "Kunde ej ladda ner filen från %(url)s"
#: web.py:802
#: web.py:842
msgid "The file already exists!"
msgstr "Filen existerar redan!"
#: web.py:810
#: web.py:850
msgid "Unable to write the file to disk!"
msgstr "Kunde ej skriva filen till skivan!"
#: web.py:821
#: web.py:865
msgid "Transferred file corrupted!"
msgstr "Den överförda filen är skadad!"
#: web.py:827
#: web.py:871
msgid "File upload successful!"
msgstr "Filen har laddas upp!"
#: web.py:870
#: web.py:914
#, python-format
msgid "Image file deleted: %(file_name)s"
msgstr "Skivbildfilen %(file_name)s har blivit raderad"
msgstr "Filen %(file_name)s har blivit raderad"
#: web.py:900
#: web.py:944
#, python-format
msgid "Image file renamed to: %(file_name)s"
msgstr "Skivbildfilen har blivit omdöpt till %(file_name)s"
msgstr "Filen har blivit omdöpt till %(file_name)s"
#: web.py:937
#: web.py:981
msgid "Aborted unzip: File(s) with the same name already exists."
msgstr "Uppackning stoppad: En eller flera filer med samma namn existerar."
msgstr "Uppackning stoppad: En eller flera filer med samma namn existerar redan."
#: web.py:939
#: web.py:983
msgid "Unzipped the following files:"
msgstr "Packade up dessa filer:"
#: web.py:943
#: web.py:987
#, python-format
msgid "Properties file(s) have been moved to %(directory)s"
msgstr "En eller flera egenskapsfiler har blivit flyttade till %(directory)s"
#: web.py:946
#: web.py:990
#, python-format
msgid "Failed to unzip %(zip_file)s"
msgstr "Kunde ej packa up %(zip_file)s"
#: web.py:1004
#, python-format
msgid "Changed Web Interface language to %(locale)s"
msgstr "Bytte webbgränssnittets språk till %(locale)s"
#: templates/base.html:4
msgid "RaSCSI Control Page"
msgstr "RaSCSI kontrollsida"
@ -514,7 +513,7 @@ msgid "Save as:"
msgstr "Spara som:"
#: templates/drives.html:41 templates/drives.html:88 templates/drives.html:131
#: templates/index.html:531
#: templates/index.html:549
msgid "Create"
msgstr "Skapa"
@ -557,7 +556,7 @@ msgid ""
"Save and load device configurations, stored as json files in "
"<tt>%(config_dir)s</tt>"
msgstr ""
"Spara och ladda enhetskonfigurationer. Sparas som json-format i "
"Spara och ladda enhetskonfigurationer. Sparas i json-format vid "
"<tt>%(config_dir)s</tt>"
#: templates/index.html:11
@ -626,7 +625,7 @@ msgstr "Anslut"
#: templates/index.html:98
msgid "Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!"
msgstr "Mata ut skiva? VARNING: På Mac OS, mata ut skivan i Finder istället!"
msgstr "Mata ut skiva? VARNING: På Mac OS bör du mata ut skivan i Finder istället!"
#: templates/index.html:101
msgid "Eject"
@ -678,8 +677,8 @@ msgid ""
"Manage image files in the active RaSCSI image directory: "
"<tt>%(directory)s</tt> with a scan depth of %(scan_depth)s."
msgstr ""
"Hantera filer i den aktiva skivbildsfilskatalogen: <tt>%(directory)s</tt>"
" med hierarkiskt djup %(scan_depth)s."
"Hantera filer i den aktiva skivbildskatalogen: <tt>%(directory)s</tt> med"
" hierarkiskt djup %(scan_depth)s."
#: templates/index.html:156
#, python-format
@ -721,9 +720,9 @@ msgstr "Packa upp"
#: templates/index.html:204 templates/index.html:218
msgid "Unzipping a single file..."
msgstr "Packar upp en enda fil..."
msgstr "Packar upp endast en fil..."
#: templates/index.html:233 templates/index.html:530
#: templates/index.html:233 templates/index.html:390 templates/index.html:548
msgid "MB"
msgstr "MB"
@ -763,7 +762,7 @@ msgid ""
"Emulates a SCSI DaynaPORT Ethernet Adapter. <a href=\"%(url)s\">Host "
"drivers and configuration required</a>."
msgstr ""
"Emulerar en SCSI DaynaPORT ethernet-adapter. <a href=\"%(url)s\">Kräver "
"Emulerar ett SCSI DaynaPORT ethernet-gränssnitt. <a href=\"%(url)s\">Kräver "
"drivrutiner och inställningar</a>."
#: templates/index.html:306
@ -799,14 +798,14 @@ msgstr "Gränssnitt:"
msgid "Static IP (optional):"
msgstr "Statisk adress (tillval):"
#: templates/index.html:329 templates/index.html:455
#: templates/index.html:329 templates/index.html:473
msgid "SCSI ID:"
msgstr "SCSI-id:"
#: templates/index.html:343
#, python-format
msgid "Macproxy is running at %(ip_addr)s (default port 5000)"
msgstr "Macproxy är tillgängligt på %(ip_addr)s (förvald port 5000)"
msgstr "Macproxy är tillgängligt på %(ip_addr)s (vanligtvis port 5000)"
#: templates/index.html:345
#, python-format
@ -815,7 +814,7 @@ msgid ""
"vintage browser. It's not just for Macs!"
msgstr ""
"Installera <a href=\"%(url)s\">Macproxy</a> och surfa på nätet med gamla "
"webbläsare. Den är inte bara för Macar!"
"webbläsare. Den är inte bara till för Macar!"
#: templates/index.html:351
msgid "Upload File"
@ -838,56 +837,122 @@ msgid ""
msgstr ""
"Om RaSCSI inte känner igen en filtyp kan du försöka döpa om "
"hårddiskbildfiler till '.hds', cd-bildfiler till '.iso', och utmatbara "
"bildfiler till '.hdr' innan du laddar upp den."
"bildfiler till '.hdr' innan du laddar upp dem."
#: templates/index.html:356
#, python-format
msgid "Recognized file types: %(valid_file_suffix)s"
msgstr "Kända filtyper: %(valid_file_suffix)s"
#: templates/index.html:376
msgid "Drop files here to upload"
msgstr "Släpp filer här för att ladda upp"
#: templates/index.html:377
msgid "Your browser does not support drag'n'drop file uploads."
msgstr "Din webbläsare stöder ej filuppladdning via dra och släpp."
#: templates/index.html:378
msgid ""
"Please use the fallback form below to upload your files like in the olden"
" days."
msgstr ""
"Använd reservformuläret nedan för att ladda upp dina filer på gammaldags "
"vis."
#: templates/index.html:379
msgid "File is too big: {{filesize}}MB. Max filesize: {{maxFilesize}}MB."
msgstr ""
"Filen är för stor: {{filesize}}MB. Största möjliga storlek: "
"{{maxFilesize}}MB."
#: templates/index.html:380
msgid "You can't upload files of this type."
msgstr "Du kan ej ladda upp filer av den här typen."
#: templates/index.html:381
msgid "Server responded with code: {{statusCode}}"
msgstr "Servern svarade med kod: {{statusCode}}"
#: templates/index.html:382
msgid "Cancel upload"
msgstr "Avbryt uppladdning"
#: templates/index.html:383
msgid "Download File to Images"
msgstr "Ladda ner fil till skivbildsfilskatalogen"
msgid "Upload canceled."
msgstr "Uppladdningen avbröts."
#: templates/index.html:384
msgid "Are you sure you want to cancel this upload?"
msgstr "Är du säker på att du vill avbryta uppladdningen?"
#: templates/index.html:385
msgid "Remove file"
msgstr "Radera fil"
#: templates/index.html:386
msgid "You can not upload any more files."
msgstr "Du kan inte ladda upp några fler filer."
#: templates/index.html:388
msgid "TB"
msgstr "TB"
#: templates/index.html:389
msgid "GB"
msgstr "GB"
#: templates/index.html:391
msgid "KB"
msgstr "KB"
#: templates/index.html:392
msgid "b"
msgstr "b"
#: templates/index.html:401
msgid "Download File to Images"
msgstr "Ladda ner fil till skivbildskatalogen"
#: templates/index.html:404
#, python-format
msgid "Given a URL, download that file to the <tt>%(directory)s</tt> directory."
msgstr "Ta ett url och ladda ner en fil till katalogen <tt>%(directory)s</tt>"
msgstr "Ta en webbadress och ladda ner en fil till katalogen <tt>%(directory)s</tt>"
#: templates/index.html:394 templates/index.html:420 templates/index.html:464
#: templates/index.html:412 templates/index.html:438 templates/index.html:482
msgid "URL:"
msgstr "Url:"
msgstr "Webbadress:"
#: templates/index.html:395 templates/index.html:421 templates/index.html:465
#: templates/index.html:413 templates/index.html:439 templates/index.html:483
msgid "URL"
msgstr "Url"
msgstr "Webbadress"
#: templates/index.html:396 templates/index.html:422
#: templates/index.html:414 templates/index.html:440
msgid "Download"
msgstr "Ladda ner"
#: templates/index.html:396
#: templates/index.html:414
msgid "Downloading File to Images..."
msgstr "Laddar ner filen till skivbildsfilskatalogen..."
msgstr "Laddar ner filen till skivbildskatalogen..."
#: templates/index.html:406
#: templates/index.html:424
msgid "Download File to AppleShare"
msgstr "Ladda ner fil till AppleShare"
#: templates/index.html:409
#: templates/index.html:427
#, python-format
msgid ""
"Given a URL, download that file to the <tt>%(directory)s</tt> directory "
"and share it over AFP."
msgstr ""
"Ta ett url och ladda ner en fil till katalogen <tt>%(directory)s</tt> och"
"Ta en webbadress och ladda ner en fil till katalogen <tt>%(directory)s</tt> och"
" fildela den över AFP."
#: templates/index.html:410
#: templates/index.html:428
msgid "Manage the files you download here through AppleShare on your vintage Mac."
msgstr "Hantera dessa filer via AppleShare på en klassisk Mac."
msgstr "Hantera dessa filer via AppleShare på din gamla Mac."
#: templates/index.html:411
#: templates/index.html:429
#, python-format
msgid ""
"Requires <a href=\"%(url)s\">Netatalk</a> to be installed and configured "
@ -896,25 +961,25 @@ msgstr ""
"Kräver att <a href=\"%(url)s\">Netatalk</a> är installerat och inställt "
"på lämpligt vis för ditt nätverk."
#: templates/index.html:422
#: templates/index.html:440
msgid "Downloading File to AppleShare..."
msgstr "Laddar ner fil till AppleShare..."
#: templates/index.html:429
#: templates/index.html:447
msgid "The AppleShare server is running. No active connections."
msgstr "AppleShare-servern är aktiv. Inga klienter är anslutna."
#: templates/index.html:431
#: templates/index.html:449
#, python-format
msgid "%(value)d active AFP connection"
msgstr "%(value)d aktiv AFP-klient"
#: templates/index.html:433
#: templates/index.html:451
#, python-format
msgid "%(value)d active AFP connections"
msgstr "%(value)d aktiva AFP-klienter"
#: templates/index.html:436
#: templates/index.html:454
#, python-format
msgid ""
"Install <a href=\"%(url)s\">Netatalk</a> to use the AppleShare File "
@ -923,56 +988,56 @@ msgstr ""
"Installera <a href=\"%(url)s\">Netatalk</a> innan du kan använda "
"AppleShare-fildelning."
#: templates/index.html:443
#: templates/index.html:461
msgid "Download File and Create CD-ROM image"
msgstr "Ladda ner fil och skapa en cd-bildfil"
#: templates/index.html:446
#: templates/index.html:464
msgid ""
"Create an ISO file system CD-ROM image with the downloaded file, and "
"mount it on the given SCSI ID."
msgstr ""
"Skapar en cd-bildfil med ISO-filsystem som innehåller den nedladdade "
"filen. Sedan ansluts den till det angivna SCSI-idt."
"filen. Sedan ansluts den till det angivna SCSI-id:t."
#: templates/index.html:447
#: templates/index.html:465
msgid "HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX."
msgstr "HFS är för Mac OS, Joliet för Windows, samt Rock Ridge för POSIX."
#: templates/index.html:448
#: templates/index.html:466
#, python-format
msgid "On Mac OS, a <a href=\"%(url)s\">compatible CD-ROM driver</a> is required."
msgstr "På Mac OS krävs <a href=\"%(url)s\">kompatibla cd-drivrutiner</a>."
#: templates/index.html:449
#: templates/index.html:467
msgid ""
"If the downloaded file is a zip archive, we will attempt to unzip it and "
"store the resulting files."
msgstr ""
"Om den nedladdade filen är en zip-fil så försöker vi packa up den och "
"spara de uppackade filerna på cd-bildfilen"
"spara de uppackade filerna på cd-bildfilen."
#: templates/index.html:466 templates/index.html:511
#: templates/index.html:484 templates/index.html:529
msgid "Type:"
msgstr "Typ:"
#: templates/index.html:487
#: templates/index.html:505
msgid "Download and Mount CD-ROM image"
msgstr "Ladda ner och mata in cd-bildfil"
#: templates/index.html:487
#: templates/index.html:505
msgid "Downloading File and generating CD-ROM image..."
msgstr "Laddar ner fil och tillverkar cd-bildfil..."
#: templates/index.html:497
#: templates/index.html:515
msgid "Create Empty Disk Image File"
msgstr "Skapa en tom skivbilsdfil"
#: templates/index.html:500
#: templates/index.html:518
msgid "The Generic image type is recommended for most computer platforms."
msgstr "Bildfilsformatet 'Generic' är rekommederad för de flesta datorsystem."
#: templates/index.html:501
#: templates/index.html:519
msgid ""
"APPLE GENUINE (.hda) and NEC GENUINE (.hdn) image types will make RaSCSI "
"behave as a particular drive type that are recognized by Mac and PC98 "
@ -982,52 +1047,52 @@ msgstr ""
"RaSCSI beter sig som en typ av hårddisk som Macar och PC98-datorer känner"
" igen."
#: templates/index.html:502
#: templates/index.html:520
msgid ""
"SASI images should only be used on the original Sharp X68000, or other "
"legacy systems that utilize this pre-SCSI standard."
msgstr ""
"Bildfilsformatet SASI bör endast användas för den första Sharp "
"X68000-modellen eller andra riktigt gamla system som använder denna "
"Bildfilsformatet SASI bör endast användas med tidiga Sharp "
"X68000-modeller, eller andra riktigt gamla system som använder denna "
"föregångare till SCSI."
#: templates/index.html:509
#: templates/index.html:527
msgid "File Name:"
msgstr "Filnamn:"
#: templates/index.html:510
#: templates/index.html:528
msgid "File Name"
msgstr "Filnamn"
#: templates/index.html:514
#: templates/index.html:532
msgid "SCSI Hard Disk image (Generic) [.hds]"
msgstr "SCSI-hårddisk (Generic) [.hds]"
#: templates/index.html:517
#: templates/index.html:535
msgid "SCSI Hard Disk image (APPLE GENUINE) [.hda]"
msgstr "SCSI-hårddisk (APPLE GENUINE) [.hda]"
#: templates/index.html:520
#: templates/index.html:538
msgid "SCSI Hard Disk image (NEC GENUINE) [.hdn]"
msgstr "SCSI-hårddisk (NEC GENUINE) [.hdn]"
#: templates/index.html:523
#: templates/index.html:541
msgid "SCSI Removable Media Disk image (Generic) [.hdr]"
msgstr "SCSI utmatbart medium (Generic) [.hdr]"
#: templates/index.html:526
#: templates/index.html:544
msgid "SASI Hard Disk image (Legacy) [.hdf]"
msgstr "SASI-hårddisk (föråldrat) [.hdf]"
#: templates/index.html:529
#: templates/index.html:547
msgid "Size:"
msgstr "Storlek:"
#: templates/index.html:541
#: templates/index.html:559
msgid "Create Named Drive"
msgstr "Skapa benämnd skiva"
#: templates/index.html:544
#: templates/index.html:562
msgid ""
"Create pairs of images and properties files from a list of real-life "
"drives."
@ -1035,7 +1100,7 @@ msgstr ""
"Skapar ett par av skivbilds- och egenskapsfiler från en lista av verkliga"
" enheter."
#: templates/index.html:545
#: templates/index.html:563
msgid ""
"This will make RaSCSI use certain vendor strings and block sizes that may"
" improve compatibility with certain systems."
@ -1043,86 +1108,103 @@ msgstr ""
"På så vis kommer RaSCSI använda vissa tillverkarattribut och "
"blockstorlekar som kan hjälpa till med kompatibilitet."
#: templates/index.html:548
#: templates/index.html:566
msgid "Create a named disk image that mimics real-life drives"
msgstr "Skapa en benämnd skivbildfil som låstas vara en riktig enhet"
#: templates/index.html:554
#: templates/index.html:572
msgid "Logging"
msgstr "Loggar"
#: templates/index.html:557
#: templates/index.html:575
msgid "Fetch a certain number of lines of system logs with the given scope."
msgstr "Skaffar ett visst antal loggar för en viss systemprocess."
#: templates/index.html:564
#: templates/index.html:582
msgid "Log Lines:"
msgstr "Antal loggar:"
#: templates/index.html:566
#: templates/index.html:584
msgid "Scope:"
msgstr "Process:"
#: templates/index.html:578
#: templates/index.html:596
msgid "Show Logs"
msgstr "Skaffa loggar"
#: templates/index.html:588
#: templates/index.html:606
msgid "Server Log Level"
msgstr "Serverns loggnivå"
#: templates/index.html:591
#: templates/index.html:609
msgid "Change the log level of the RaSCSI backend process."
msgstr "Ändra loggnivån för RaSCSI-servern"
msgstr "Byt loggnivån för RaSCSI-servern"
#: templates/index.html:592
#: templates/index.html:610
msgid "The current dropdown selection indicates the active log level."
msgstr "Det nuvarande valet i rullgardinsmenyn påvisar aktiv loggnivå."
#: templates/index.html:599
#: templates/index.html:617
msgid "Log Level:"
msgstr "Loggnivå:"
#: templates/index.html:607
#: templates/index.html:625
msgid "Set Log Level"
msgstr "Ställ in loggnivå"
msgstr "Byt loggnivå"
#: templates/index.html:617
#: templates/index.html:635
msgid "Language"
msgstr "Språk"
#: templates/index.html:638
msgid "Change the Web Interface language."
msgstr "Byt webbgränssnittets språk."
#: templates/index.html:645
msgid "Language:"
msgstr "Språk:"
#: templates/index.html:653
msgid "Change Language"
msgstr "Byt språk"
#: templates/index.html:663
msgid "Raspberry Pi Operations"
msgstr "Raspberry Pi-kommandon"
#: templates/index.html:620
#: templates/index.html:666
msgid "Reboot or shut down the Raspberry Pi that RaSCSI is running on."
msgstr "Starta om eller stäng av Raspberry Pi-systemet som RaSCSI körs på."
#: templates/index.html:621
#: templates/index.html:667
msgid ""
"IMPORTANT: Always shut down the Pi before turning off the power. Failing "
"to do so may lead to data loss."
msgstr ""
"VIKTIGT: Stäng alltid av Pi-systemet innan du stänger av strömmen. Det "
"finns risk för dataförlust."
"VIKTIGT: Stäng alltid av Pi-systemet innan du kopplar ur strömmen. På "
"så vis undviker du risken att förlora data."
#: templates/index.html:627
#: templates/index.html:673
msgid "Reboot the Raspberry Pi?"
msgstr "Vill du starta om din Raspberry Pi?"
#: templates/index.html:627
#: templates/index.html:673
msgid "Rebooting the Raspberry Pi..."
msgstr "Startar om Raspberry Pi..."
#: templates/index.html:628
#: templates/index.html:674
msgid "Reboot Raspberry Pi"
msgstr "Starta om Raspberry Pi"
#: templates/index.html:632
#: templates/index.html:678
msgid "Shut down the Raspberry Pi?"
msgstr "Vill du stänga av din Raspberry Pi?"
#: templates/index.html:632
#: templates/index.html:678
msgid "Shutting down the Raspberry Pi..."
msgstr "Stänger av Raspberry Pi..."
#: templates/index.html:633
#: templates/index.html:679
msgid "Shut Down Raspberry Pi"
msgstr "Stäng av Raspberry Pi"

View File

@ -4,9 +4,9 @@ Module for the Flask app rendering and endpoints
import logging
import argparse
from sys import argv
from pathlib import Path
from functools import wraps
from hashlib import md5
from flask import (
Flask,
@ -93,10 +93,13 @@ def get_locale():
try:
language = session["language"]
except KeyError:
language = None
if language is not None:
language = ""
logging.warning("The default locale could not be detected. Falling back to English.")
if language:
return language
return request.accept_languages.best_match(LANGUAGES)
# Hardcoded fallback to "en" when the user agent does not send an accept-language header
language = request.accept_languages.best_match(LANGUAGES) or "en"
return language
def get_supported_locales():
@ -115,7 +118,13 @@ def index():
Sets up data structures for and renders the index page
"""
if not is_token_auth()["status"] and not APP.config["TOKEN"]:
abort(403, _(u"RaSCSI is password protected. Start the Web Interface with the --password parameter."))
abort(
403,
_(
u"RaSCSI is password protected. "
u"Start the Web Interface with the --password parameter."
),
)
locales = get_supported_locales()
server_info = get_server_info()
@ -211,7 +220,13 @@ def drive_list():
return redirect(url_for("index"))
conf = process["conf"]
else:
flash(_("Could not read drive properties from %(properties_file)s", properties_file=drive_properties), "error")
flash(
_(
"Could not read drive properties from %(properties_file)s",
properties_file=drive_properties,
),
"error",
)
return redirect(url_for("index"))
hd_conf = []
@ -275,7 +290,13 @@ def login():
if authenticate(str(username), str(password)):
session["username"] = request.form["username"]
return redirect(url_for("index"))
flash(_(u"You must log in with credentials for a user in the '%(group)s' group", group=AUTH_GROUP), "error")
flash(
_(
u"You must log in with credentials for a user in the '%(group)s' group",
group=AUTH_GROUP,
),
"error",
)
return redirect(url_for("index"))
@ -413,7 +434,7 @@ def config_load():
flash(process['msg'], "error")
return redirect(url_for("index"))
elif "delete" in request.form:
if "delete" in request.form:
process = delete_file(f"{CFG_DIR}/{file_name}")
if process["status"]:
flash(process["msg"])
@ -609,8 +630,8 @@ def detach():
id_number=scsi_id, unit_number=unit))
return redirect(url_for("index"))
flash(_(u"Failed to detach %(file_name)s from SCSI ID %(id_number)s LUN %(unit_number)s",
file_name=file_name, id_number=scsi_id, unit_number=unit), "error")
flash(_(u"Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit), "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
@ -630,8 +651,8 @@ def eject():
id_number=scsi_id, unit_number=unit))
return redirect(url_for("index"))
flash(_(u"Failed to eject %(file_name)s from SCSI ID %(id_number)s LUN %(unit_number)s",
file_name=file_name, id_number=scsi_id, unit_number=unit), "error")
flash(_(u"Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit), "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
@ -769,7 +790,7 @@ def download_img():
"""
url = request.form.get("url")
server_info = get_server_info()
process = download_to_dir(url, server_info["image_dir"])
process = download_to_dir(url, server_info["image_dir"], Path(url).name)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
@ -786,7 +807,19 @@ def download_afp():
Downloads a remote file onto the AFP shared dir on the Pi
"""
url = request.form.get("url")
process = download_to_dir(url, AFP_DIR)
file_name = Path(url).name
# Shorten file names longer than 31 chars, the HFS upper limit
# Append a hash of the removed piece of the file name to make it unique
# The format of the hash is a # followed by the first four symbols of the md4 hash
if len(file_name) > 31:
chars_to_cut = len(file_name) - 31
file_name_stem = Path(file_name).stem
file_name_suffix = Path(file_name).suffix
discarded_portion = file_name_stem[:-abs(chars_to_cut)]
stem_remainder = file_name_stem[:(26 - len(file_name_suffix))]
appendix_hash = (md5(discarded_portion.encode("utf-8"))).hexdigest().upper()
file_name = stem_remainder + "#" + appendix_hash[:4] + file_name_suffix
process = download_to_dir(url, AFP_DIR, file_name)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
@ -837,10 +870,14 @@ def upload_file():
if current_chunk + 1 == total_chunks:
# Validate the resulting file size after writing the last chunk
if path.getsize(save_path) != int(request.form["dztotalfilesize"]):
log.error("Finished transferring %s, "
"but it has a size mismatch with the original file."
"Got %s but we expected %s.",
file_object.filename, path.getsize(save_path), request.form['dztotalfilesize'])
log.error(
"Finished transferring %s, "
"but it has a size mismatch with the original file. "
"Got %s but we expected %s.",
file_object.filename,
path.getsize(save_path),
request.form['dztotalfilesize'],
)
return make_response(_(u"Transferred file corrupted!"), 500)
log.info("File %s has been uploaded successfully", file_object.filename)
@ -973,6 +1010,9 @@ def unzip():
@APP.route("/language", methods=["POST"])
def change_language():
"""
Changes the session language locale and refreshes the Flask app context
"""
locale = request.form.get("locale")
session["language"] = locale
refresh()
@ -1013,9 +1053,9 @@ if __name__ == "__main__":
action="store",
help="Token password string for authenticating with RaSCSI",
)
args = parser.parse_args()
APP.config["TOKEN"] = args.password
arguments = parser.parse_args()
APP.config["TOKEN"] = arguments.password
import bjoern
print("Serving rascsi-web...")
bjoern.run(APP, "0.0.0.0", args.port)
bjoern.run(APP, "0.0.0.0", arguments.port)