Merge branch 'develop' into dependabot/pip/python/web/jinja2-3.1.3

This commit is contained in:
Daniel Markstedt 2024-01-13 16:00:49 +09:00 committed by GitHub
commit 83f4feaa21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
91 changed files with 1483 additions and 1022 deletions

View File

@ -35,7 +35,7 @@ jobs:
run: make -j $(nproc) test
- name: Run unit tests
run: (set -o pipefail && bin/fullspec/piscsi_test | tee piscsi_test_log.txt)
run: (set -o pipefail && bin/piscsi_test | tee piscsi_test_log.txt)
- name: Upload logs
uses: actions/upload-artifact@v3

View File

@ -1,12 +0,0 @@
BasedOnStyle: Microsoft
IndentWidth: 4
AlwaysBreakAfterReturnType: None
AllowShortFunctionsOnASingleLine: Empty
KeepEmptyLinesAtTheStartOfBlocks: false
BreakBeforeBraces: Linux
AlignEscapedNewlines: Left
AlignTrailingComments: True
AllowShortEnumsOnASingleLine: True
AlignConsecutiveAssignments: Consecutive
ColumnLimit: 120
PointerAlignment: Left

3
cpp/.gitignore vendored
View File

@ -12,6 +12,3 @@ obj
bin
coverage
generated
.project
.cproject
.settings

View File

@ -1,53 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "rascsi (gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/bin/fullspec/rascsi",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"targetArchitecture": "arm",
"miDebuggerPath": "${workspaceFolder}/launch_sudo.sh",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
{
"name": "rascsi_test (gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/bin/fullspec/rascsi_test",
"args": [],
"stopAtEntry": true,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
]
}

View File

@ -1,19 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "g++ build active file",
"command": "make",
"args": ["all", "DEBUG=1", "-j4"],
"options": {
"cwd": "${workspaceFolder}/"
},
"problemMatcher": ["$gcc"],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@ -60,8 +60,8 @@ COVERAGE_DIR = coverage
COVERAGE_FILE = piscsi.dat
OS_FILES = ../os_integration
OBJDIR := obj/$(shell echo $(CONNECT_TYPE) | tr '[:upper:]' '[:lower:]')
BINDIR := bin/$(shell echo $(CONNECT_TYPE) | tr '[:upper:]' '[:lower:]')
OBJDIR := obj
BINDIR := bin
BIN_ALL = \
$(BINDIR)/$(PISCSI) \

View File

@ -865,6 +865,10 @@ bool ScsiController::XferOutBlockOriented(bool cont)
LogTrace("Done with DaynaPort Set Multicast Address");
break;
case scsi_command::eCmdSetIfaceMode:
LogTrace("Done with setting DaynaPort MAC address (ignore)");
break;
default:
stringstream s;
s << "Received an unexpected command ($" << setfill('0') << setw(2) << hex

View File

@ -7,7 +7,7 @@
//
//---------------------------------------------------------------------------
#include "shared/network_util.h"
#include "shared/piscsi_util.h"
#include "scsihd.h"
#include "scsihd_nec.h"
#include "scsimo.h"
@ -20,39 +20,14 @@
using namespace std;
using namespace piscsi_util;
using namespace network_util;
DeviceFactory::DeviceFactory()
{
sector_sizes[SCHD] = { 512, 1024, 2048, 4096 };
sector_sizes[SCRM] = { 512, 1024, 2048, 4096 };
sector_sizes[SCMO] = { 512, 1024, 2048, 4096 };
sector_sizes[SCCD] = { 512, 2048};
extension_mapping["hd1"] = SCHD;
extension_mapping["hds"] = SCHD;
extension_mapping["hda"] = SCHD;
extension_mapping["hdn"] = SCHD;
extension_mapping["hdi"] = SCHD;
extension_mapping["nhd"] = SCHD;
extension_mapping["hdr"] = SCRM;
extension_mapping["mos"] = SCMO;
extension_mapping["iso"] = SCCD;
extension_mapping["is1"] = SCCD;
device_mapping["bridge"] = SCBR;
device_mapping["daynaport"] = SCDP;
device_mapping["printer"] = SCLP;
device_mapping["services"] = SCHS;
}
PbDeviceType DeviceFactory::GetTypeForFile(const string& filename) const
{
if (const auto& it = extension_mapping.find(GetExtensionLowerCase(filename)); it != extension_mapping.end()) {
if (const auto& it = EXTENSION_MAPPING.find(GetExtensionLowerCase(filename)); it != EXTENSION_MAPPING.end()) {
return it->second;
}
if (const auto& it = device_mapping.find(filename); it != device_mapping.end()) {
if (const auto& it = DEVICE_MAPPING.find(filename); it != DEVICE_MAPPING.end()) {
return it->second;
}
@ -75,8 +50,7 @@ shared_ptr<PrimaryDevice> DeviceFactory::CreateDevice(PbDeviceType type, int lun
if (const string ext = GetExtensionLowerCase(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") {
device = make_shared<SCSIHD_NEC>(lun);
} else {
device = make_shared<SCSIHD>(lun, sector_sizes.find(type)->second, false,
ext == "hd1" ? scsi_level::scsi_1_ccs : scsi_level::scsi_2);
device = make_shared<SCSIHD>(lun, false, ext == "hd1" ? scsi_level::scsi_1_ccs : scsi_level::scsi_2);
// Some Apple tools require a particular drive identification
if (ext == "hda") {
@ -88,17 +62,17 @@ shared_ptr<PrimaryDevice> DeviceFactory::CreateDevice(PbDeviceType type, int lun
}
case SCRM:
device = make_shared<SCSIHD>(lun, sector_sizes.find(type)->second, true);
device = make_shared<SCSIHD>(lun, true, scsi_level::scsi_2);
device->SetProduct("SCSI HD (REM.)");
break;
case SCMO:
device = make_shared<SCSIMO>(lun, sector_sizes.find(type)->second);
device = make_shared<SCSIMO>(lun);
device->SetProduct("SCSI MO");
break;
case SCCD:
device = make_shared<SCSICD>(lun, sector_sizes.find(type)->second,
device = make_shared<SCSICD>(lun,
GetExtensionLowerCase(filename) == "is1" ? scsi_level::scsi_1_ccs : scsi_level::scsi_2);
device->SetProduct("SCSI CD-ROM");
break;
@ -135,15 +109,3 @@ shared_ptr<PrimaryDevice> DeviceFactory::CreateDevice(PbDeviceType type, int lun
return device;
}
// TODO Move to respective device, which may require changes in the SCSI_HD/SCSIHD_NEC inheritance hierarchy
unordered_set<uint32_t> DeviceFactory::GetSectorSizes(PbDeviceType type) const
{
const auto& it = sector_sizes.find(type);
if (it != sector_sizes.end()) {
return it->second;
}
else {
return {};
}
}

View File

@ -11,9 +11,7 @@
#pragma once
#include "shared/piscsi_util.h"
#include <string>
#include <unordered_set>
#include <unordered_map>
#include "generated/piscsi_interface.pb.h"
@ -27,19 +25,32 @@ class DeviceFactory
public:
DeviceFactory();
DeviceFactory() = default;
~DeviceFactory() = default;
shared_ptr<PrimaryDevice> CreateDevice(PbDeviceType, int, const string&) const;
PbDeviceType GetTypeForFile(const string&) const;
unordered_set<uint32_t> GetSectorSizes(PbDeviceType type) const;
const auto& GetExtensionMapping() const { return extension_mapping; }
const auto& GetExtensionMapping() const { return EXTENSION_MAPPING; }
private:
unordered_map<PbDeviceType, unordered_set<uint32_t>> sector_sizes;
const inline static unordered_map<string, PbDeviceType, piscsi_util::StringHash, equal_to<>> EXTENSION_MAPPING = {
{ "hd1", SCHD },
{ "hds", SCHD },
{ "hda", SCHD },
{ "hdn", SCHD },
{ "hdi", SCHD },
{ "nhd", SCHD },
{ "hdr", SCRM },
{ "mos", SCMO },
{ "is1", SCCD },
{ "iso", SCCD }
};
unordered_map<string, PbDeviceType, piscsi_util::StringHash, equal_to<>> extension_mapping;
unordered_map<string, PbDeviceType, piscsi_util::StringHash, equal_to<>> device_mapping;
const inline static unordered_map<string, PbDeviceType, piscsi_util::StringHash, equal_to<>> DEVICE_MAPPING = {
{ "bridge", SCBR },
{ "daynaport", SCDP },
{ "printer", SCLP },
{ "services", SCHS }
};
};

View File

@ -39,16 +39,14 @@ void DeviceLogger::Error(const string& message) const
void DeviceLogger::Log(level::level_enum level, const string& message) const
{
if (!message.empty() &&
(log_device_id == -1 ||
(log_device_id == id && (log_device_lun == -1 || log_device_lun == lun)))) {
if (lun == -1) {
log(level, "(ID " + to_string(id) + ") - " + message);
}
else {
log(level, "(ID:LUN " + to_string(id) + ":" + to_string(lun) + ") - " + message);
}
}
if ((log_device_id == -1 || log_device_id == id) && (lun == -1 || log_device_lun == -1 || log_device_lun == lun)) {
if (lun == -1) {
log(level, "(ID " + to_string(id) + ") - " + message);
}
else {
log(level, "(ID:LUN " + to_string(id) + ":" + to_string(lun) + ") - " + message);
}
}
}
void DeviceLogger::SetIdAndLun(int i, int l)

View File

@ -695,12 +695,18 @@ uint32_t Disk::GetSectorSizeInBytes() const
void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes)
{
if (DeviceFactory device_factory; !device_factory.GetSectorSizes(GetType()).contains(size_in_bytes)) {
throw io_exception("Invalid sector size of " + to_string(size_in_bytes) + " byte(s)");
if (!GetSupportedSectorSizes().contains(size_in_bytes)) {
throw io_exception("Invalid sector size of " + to_string(size_in_bytes) + " byte(s)");
}
uint64_t current_blocks = GetBlockCount();
uint32_t current_size_shift_count = size_shift_count;
uint64_t current_size = current_blocks << current_size_shift_count;
size_shift_count = CalculateShiftCount(size_in_bytes);
assert(size_shift_count);
if ((current_blocks > 0) && (current_size_shift_count > 0)) {
SetBlockCount(current_size >> size_shift_count);
}
}
uint32_t Disk::GetConfiguredSectorSize() const
@ -708,13 +714,13 @@ uint32_t Disk::GetConfiguredSectorSize() const
return configured_sector_size;
}
bool Disk::SetConfiguredSectorSize(const DeviceFactory& device_factory, uint32_t configured_size)
bool Disk::SetConfiguredSectorSize(uint32_t configured_size)
{
if (!device_factory.GetSectorSizes(GetType()).contains(configured_size)) {
if (!supported_sector_sizes.contains(configured_size)) {
return false;
}
configured_sector_size = configured_size;
configured_sector_size = configured_size;
return true;
}

View File

@ -16,7 +16,6 @@
#include "shared/scsi.h"
#include "shared/piscsi_util.h"
#include "device_factory.h"
#include "disk_track.h"
#include "disk_cache.h"
#include "interfaces/scsi_block_commands.h"
@ -35,8 +34,7 @@ class Disk : public StorageDevice, private ScsiBlockCommands
unique_ptr<DiskCache> cache;
// The supported configurable sector sizes, empty if not configurable
unordered_set<uint32_t> sector_sizes;
unordered_set<uint32_t> supported_sector_sizes;
uint32_t configured_sector_size = 0;
// Sector size shift count (9=512, 10=1024, 11=2048, 12=4096)
@ -50,7 +48,9 @@ class Disk : public StorageDevice, private ScsiBlockCommands
public:
using StorageDevice::StorageDevice;
Disk(PbDeviceType type, int lun, const unordered_set<uint32_t>& s)
: StorageDevice(type, lun), supported_sector_sizes(s) {}
~Disk() override = default;
bool Init(const param_map&) override;
void CleanUp() override;
@ -64,8 +64,9 @@ public:
virtual int Read(span<uint8_t> , uint64_t);
uint32_t GetSectorSizeInBytes() const;
bool IsSectorSizeConfigurable() const { return !sector_sizes.empty(); }
bool SetConfiguredSectorSize(const DeviceFactory&, uint32_t);
bool IsSectorSizeConfigurable() const { return supported_sector_sizes.size() > 1; }
const auto& GetSupportedSectorSizes() const { return supported_sector_sizes; }
bool SetConfiguredSectorSize(uint32_t);
void FlushCache() override;
vector<PbStatistics> GetStatistics() const override;
@ -111,7 +112,7 @@ protected:
void SetUpCache(off_t, bool = false);
void ResizeCache(const string&, bool);
bool GetRawMode() const { return (cache?cache->GetRawMode():false); }
void SetUpModePages(map<int, vector<byte>>&, int, bool) const override;
void AddErrorPage(map<int, vector<byte>>&, bool) const;
virtual void AddFormatPage(map<int, vector<byte>>&, bool) const;
@ -119,7 +120,6 @@ protected:
void AddCachePage(map<int, vector<byte>>&, bool) const;
unordered_set<uint32_t> GetSectorSizes() const;
void SetSectorSizes(const unordered_set<uint32_t>& sizes) { sector_sizes = sizes; }
void SetSectorSizeInBytes(uint32_t);
uint32_t GetSectorSizeShiftCount() const { return size_shift_count; }
void SetSectorSizeShiftCount(uint32_t count) { size_shift_count = count; }

View File

@ -51,6 +51,7 @@ public:
~DiskCache() = default;
void SetRawMode(bool b) { cd_raw = b; } // CD-ROM raw mode setting
bool GetRawMode() const { return cd_raw; }
bool Save(); // Save and release all
bool ReadSector(span<uint8_t>, uint32_t); // Sector Read

View File

@ -114,7 +114,7 @@ void ModePageDevice::ModeSense10() const
EnterDataInPhase();
}
void ModePageDevice::ModeSelect(scsi_command, cdb_t, span<const uint8_t>, int) const
void ModePageDevice::ModeSelect(scsi_command, cdb_t, span<const uint8_t>, int)
{
// There is no default implementation of MODE SELECT
throw scsi_exception(sense_key::illegal_request, asc::invalid_command_operation_code);

View File

@ -23,7 +23,7 @@ public:
bool Init(const param_map&) override;
virtual void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) const;
virtual void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int);
protected:

View File

@ -40,7 +40,8 @@ string scsi_command_util::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uin
}
length -= offset;
bool has_valid_page_code = false;
// treat zero length as valid
bool has_valid_page_code = (length == 0);
// Parse the pages
while (length > 0) {
@ -62,6 +63,10 @@ string scsi_command_util::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uin
has_valid_page_code = true;
}
else if (page == 0x01) {
// OpenVMS Alpha 7.3 uses this
has_valid_page_code = true;
}
else {
stringstream s;
s << "Unknown MODE SELECT page code: $" << setfill('0') << setw(2) << hex << page;
@ -71,7 +76,7 @@ string scsi_command_util::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uin
// Advance to the next page
const int size = buf[offset + 1] + 2;
length -= size;
length -= size + 1;
offset += size;
}

View File

@ -6,6 +6,7 @@
// Copyright (C) 2020 akuker
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2023 Uwe Seimet
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
@ -212,13 +213,15 @@ int SCSIDaynaPort::Read(cdb_t cdb, vector<uint8_t>& buf, uint64_t)
// response->flags = e_no_more_data;
// }
int size = rx_packet_size;
if (size < 64) {
// A frame must have at least 64 bytes (see https://github.com/PiSCSI/piscsi/issues/619)
// Note that this work-around breaks the checksum. As currently there are no known drivers
// that care for the checksum, and the Daynaport driver for the Atari expects frames of
// 64 bytes it was decided to accept the broken checksum. If a driver should pop up that
// breaks because of this, the work-around has to be re-evaluated.
size = 64;
if (size < 128) {
// A frame must have at least 64 bytes for the Atari driver, see https://github.com/PiSCSI/piscsi/issues/619,
// but also works with 128 bytes.
// The NetBSD driver requires at least 128 bytes, see https://github.com/PiSCSI/piscsi/issues/1098.
// The Mac driver is also fine with 128 bytes.
// Note that this work-around breaks the checksum. As currently there are no known drivers
// that care for the checksum it was decided to accept the broken checksum.
// If a driver should pop up that breaks because of this, the work-around has to be re-evaluated.
size = 128;
}
SetInt16(buf, 0, size);
SetInt32(buf, 2, tap.HasPendingPackets() ? 0x10 : 0x00);

View File

@ -6,6 +6,7 @@
// Copyright (C) 2020 akuker
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2023 Uwe Seimet
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.

View File

@ -197,7 +197,7 @@ bool SCSIPrinter::WriteByteSequence(span<const uint8_t> buf)
out.write((const char *)buf.data(), buf.size());
const bool status = out.fail();
if (!status) {
if (status) {
++print_error_count;
}

View File

@ -21,11 +21,8 @@
using namespace scsi_defs;
using namespace scsi_command_util;
SCSICD::SCSICD(int lun, const unordered_set<uint32_t>& sector_sizes, scsi_defs::scsi_level level)
: Disk(SCCD, lun), scsi_level(level)
SCSICD::SCSICD(int lun, scsi_defs::scsi_level level) : Disk(SCCD, lun, { 512, 2048 }), scsi_level(level)
{
SetSectorSizes(sector_sizes);
SetReadOnly(true);
SetRemovable(true);
SetLockable(true);
@ -168,6 +165,29 @@ vector<uint8_t> SCSICD::InquiryInternal() const
return HandleInquiry(device_type::cd_rom, scsi_level, true);
}
void SCSICD::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length)
{
int sector_size = 1 << GetSectorSizeShiftCount();
int wanted_sector_size;
// skip Block Descriptor
int offset = 4;
// evaluate Mode Parameter Block Descriptor, sector size
wanted_sector_size = scsi_command_util::GetInt16(buf, offset + 6);
if (wanted_sector_size != sector_size) {
LogDebug("Changing sector size from " + to_string(sector_size) + " to " + to_string(wanted_sector_size));
SetSectorSizeInBytes(wanted_sector_size);
ClearTrack();
CreateDataTrack();
FlushCache();
ResizeCache(GetFilename(), GetRawMode());
}
if (const string result = scsi_command_util::ModeSelect(cmd, cdb, buf, length, sector_size);
!result.empty()) {
LogWarn(result);
}
}
void SCSICD::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
{
Disk::SetUpModePages(pages, page, changeable);

View File

@ -25,7 +25,7 @@ class SCSICD : public Disk, private ScsiMmcCommands
{
public:
SCSICD(int, const unordered_set<uint32_t>&, scsi_defs::scsi_level = scsi_level::scsi_2);
SCSICD(int, scsi_defs::scsi_level = scsi_level::scsi_2);
~SCSICD() override = default;
bool Init(const param_map&) override;
@ -34,6 +34,7 @@ public:
vector<uint8_t> InquiryInternal() const override;
int Read(span<uint8_t>, uint64_t) override;
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) override;
protected:

View File

@ -19,11 +19,9 @@
using namespace scsi_command_util;
SCSIHD::SCSIHD(int lun, const unordered_set<uint32_t>& sector_sizes, bool removable, scsi_defs::scsi_level level)
: Disk(removable ? SCRM : SCHD, lun), scsi_level(level)
SCSIHD::SCSIHD(int lun, bool removable, scsi_defs::scsi_level level, const unordered_set<uint32_t>& sector_sizes)
: Disk(removable ? SCRM : SCHD, lun, sector_sizes), scsi_level(level)
{
SetSectorSizes(sector_sizes);
SetProtectable(true);
SetRemovable(removable);
SetLockable(removable);
@ -84,7 +82,7 @@ vector<uint8_t> SCSIHD::InquiryInternal() const
return HandleInquiry(device_type::direct_access, scsi_level, IsRemovable());
}
void SCSIHD::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length) const
void SCSIHD::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length)
{
if (const string result = scsi_command_util::ModeSelect(cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
!result.empty()) {

View File

@ -28,7 +28,7 @@ class SCSIHD : public Disk
public:
SCSIHD(int, const unordered_set<uint32_t>&, bool, scsi_defs::scsi_level = scsi_level::scsi_2);
SCSIHD(int, bool, scsi_defs::scsi_level, const unordered_set<uint32_t>& = { 512, 1024, 2048, 4096 });
~SCSIHD() override = default;
void FinalizeSetup(off_t);
@ -37,7 +37,7 @@ public:
// Commands
vector<uint8_t> InquiryInternal() const override;
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) const override;
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) override;
void AddFormatPage(map<int, vector<byte>>&, bool) const override;
void AddVendorPage(map<int, vector<byte>>&, int, bool) const override;

View File

@ -33,7 +33,7 @@ class SCSIHD_NEC : public SCSIHD //NOSONAR The inheritance hierarchy depth is ac
{
public:
explicit SCSIHD_NEC(int lun) : SCSIHD(lun, { 512 }, false) {}
explicit SCSIHD_NEC(int lun) : SCSIHD(lun, false, scsi_level::scsi_1_ccs, { 512 }) {}
~SCSIHD_NEC() override = default;
void Open() override;

View File

@ -19,10 +19,8 @@
using namespace scsi_command_util;
SCSIMO::SCSIMO(int lun, const unordered_set<uint32_t>& sector_sizes) : Disk(SCMO, lun)
SCSIMO::SCSIMO(int lun) : Disk(SCMO, lun, { 512, 1024, 2048, 4096 })
{
SetSectorSizes(sector_sizes);
// 128 MB, 512 bytes per sector, 248826 sectors
geometries[512 * 248826] = { 512, 248826 };
// 230 MB, 512 bytes per block, 446325 sectors
@ -90,7 +88,7 @@ void SCSIMO::AddOptionPage(map<int, vector<byte>>& pages, bool) const
// Do not report update blocks
}
void SCSIMO::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length) const
void SCSIMO::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length)
{
if (const string result = scsi_command_util::ModeSelect(cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
!result.empty()) {

View File

@ -26,13 +26,13 @@ class SCSIMO : public Disk
{
public:
SCSIMO(int, const unordered_set<uint32_t>&);
explicit SCSIMO(int);
~SCSIMO() override = default;
void Open() override;
vector<uint8_t> InquiryInternal() const override;
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) const override;
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) override;
protected:

View File

@ -60,23 +60,28 @@ const char* BUS::GetPhaseStrRaw(phase_t current_phase) {
return it != phase_str_mapping.end() ? it->second : "INVALID";
}
//---------------------------------------------------------------------------
// Phase Table
// Reference Table 8: https://www.staff.uni-mainz.de/tacke/scsi/SCSI2-06.html
// This determines the phase based upon the Msg, C/D and I/O signals.
//
// Phase Table
// Reference Table 8: https://www.staff.uni-mainz.de/tacke/scsi/SCSI2-06.html
// This determines the phase based upon the Msg, C/D and I/O signals.
//
//---------------------------------------------------------------------------
// |MSG|C/D|I/O| Phase
// | 0 | 0 | 0 | DATA OUT
// | 0 | 0 | 1 | DATA IN
// | 0 | 1 | 0 | COMMAND
// | 0 | 1 | 1 | STATUS
// | 1 | 0 | 0 | RESERVED
// | 1 | 0 | 1 | RESERVED
// | 1 | 1 | 0 | MESSAGE OUT
// | 1 | 1 | 1 | MESSAGE IN
const array<phase_t, 8> BUS::phase_table = {
// | MSG|C/D|I/O |
phase_t::dataout, // | 0 | 0 | 0 |
phase_t::datain, // | 0 | 0 | 1 |
phase_t::command, // | 0 | 1 | 0 |
phase_t::status, // | 0 | 1 | 1 |
phase_t::reserved, // | 1 | 0 | 0 |
phase_t::reserved, // | 1 | 0 | 1 |
phase_t::msgout, // | 1 | 1 | 0 |
phase_t::msgin // | 1 | 1 | 1 |
phase_t::dataout,
phase_t::datain,
phase_t::command,
phase_t::status,
phase_t::reserved,
phase_t::reserved,
phase_t::msgout,
phase_t::msgin
};
//---------------------------------------------------------------------------

View File

@ -54,7 +54,7 @@ const static int SCSI_DELAY_FAST_NEGATION_PERIOD_NS = 30;
// The DaynaPort SCSI Link do a short delay in the middle of transfering
// a packet. This is the number of uS that will be delayed between the
// header and the actual data.
const static int SCSI_DELAY_SEND_DATA_DAYNAPORT_US = 100;
const static int SCSI_DELAY_SEND_DATA_DAYNAPORT_NS = 100'000;
class bus_exception : public runtime_error
@ -68,8 +68,7 @@ class BUS : public PinControl
// Operation modes definition
enum class mode_e {
TARGET = 0,
INITIATOR = 1,
MONITOR = 2,
INITIATOR = 1
};
static int GetCommandByteCount(uint8_t);
@ -86,7 +85,6 @@ class BUS : public PinControl
// Get the string phase name, based upon the raw data
static const char *GetPhaseStrRaw(phase_t current_phase);
virtual int GetMode(int pin) = 0;
virtual uint32_t Acquire() = 0;
virtual unique_ptr<DataSample> GetSample(uint64_t timestamp = 0) = 0;
@ -97,9 +95,6 @@ class BUS : public PinControl
// SEL signal event polling
virtual bool PollSelectEvent() = 0;
// Clear SEL signal event
virtual void ClearSelectEvent() = 0;
virtual bool GetSignal(int pin) const = 0;
// Get SCSI input signal value
virtual void SetSignal(int pin, bool ast) = 0;

View File

@ -31,7 +31,6 @@ class DataSample
virtual bool GetREQ() const = 0;
virtual bool GetACT() const = 0;
virtual uint8_t GetDAT() const = 0;
virtual bool GetDP() const = 0;
virtual uint32_t GetRawCapture() const = 0;

View File

@ -1,14 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Copyright (C) 2022 akuker
//
// [ SCSI Bus Monitor ]
//
//---------------------------------------------------------------------------
#include "shared/scsi.h"
#include "data_sample.h"

View File

@ -74,10 +74,6 @@ class DataSample_Raspberry final : public DataSample
{
return GetSignal(PIN_ACT);
}
bool GetDP() const override
{
return GetSignal(PIN_DP);
}
uint8_t GetDAT() const override
{
uint8_t ret_val = 0;
@ -106,4 +102,4 @@ class DataSample_Raspberry final : public DataSample
private:
uint32_t data = 0;
};
};

View File

@ -1,10 +1,11 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2023 Uwe Seimet
//
//---------------------------------------------------------------------------
@ -14,10 +15,11 @@
#include <spdlog/spdlog.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <time.h>
#ifdef __linux__
#include <sys/epoll.h>
#endif
#include <chrono>
using namespace std;
@ -276,9 +278,12 @@ int GPIOBUS::SendHandShake(uint8_t *buf, int count, int delay_after_bytes)
if (actmode == mode_e::TARGET) {
for (i = 0; i < count; i++) {
if (i == delay_after_bytes) {
spdlog::trace("DELAYING for " + to_string(SCSI_DELAY_SEND_DATA_DAYNAPORT_US) + " us after " +
spdlog::trace("DELAYING for " + to_string(SCSI_DELAY_SEND_DATA_DAYNAPORT_NS) + " ns after " +
to_string(delay_after_bytes) + " bytes");
SysTimer::SleepUsec(SCSI_DELAY_SEND_DATA_DAYNAPORT_US);
EnableIRQ();
const timespec ts = { .tv_sec = 0, .tv_nsec = SCSI_DELAY_SEND_DATA_DAYNAPORT_NS};
nanosleep(&ts, nullptr);
DisableIRQ();
}
// Set the DATA signals
@ -403,42 +408,23 @@ bool GPIOBUS::PollSelectEvent()
#endif
}
//---------------------------------------------------------------------------
//
// Cancel SEL signal event
//
//---------------------------------------------------------------------------
void GPIOBUS::ClearSelectEvent()
{
GPIO_FUNCTION_TRACE
}
//---------------------------------------------------------------------------
//
// Wait for signal change
//
//---------------------------------------------------------------------------
bool GPIOBUS::WaitSignal(int pin, bool ast)
{
// Get current time
const uint32_t now = SysTimer::GetTimerLow();
// Calculate timeout (3000ms)
const uint32_t timeout = 3000 * 1000;
const auto now = chrono::steady_clock::now();
// Wait up to 3 s
do {
// Immediately upon receiving a reset
Acquire();
if (GetRST()) {
return false;
}
// Check for the signal edge
if (GetSignal(pin) == ast) {
return true;
}
} while ((SysTimer::GetTimerLow() - now) < timeout);
// We timed out waiting for the signal
// Abort on a reset
if (GetRST()) {
return false;
}
} while ((chrono::duration_cast<chrono::seconds>(chrono::steady_clock::now() - now).count()) < 3);
return false;
}

View File

@ -182,8 +182,6 @@ class GPIOBUS : public BUS
// SEL signal event polling
bool PollSelectEvent() override;
// Clear SEL signal event
void ClearSelectEvent() override;
protected:
virtual void MakeTable() = 0;

View File

@ -410,32 +410,30 @@ void GPIOBUS_Raspberry::SetBSY(bool ast)
// Set BSY signal
SetSignal(PIN_BSY, ast);
if (actmode == mode_e::TARGET) {
if (ast) {
// Turn on ACTIVE signal
SetControl(PIN_ACT, ACT_ON);
if (ast) {
// Turn on ACTIVE signal
SetControl(PIN_ACT, ACT_ON);
// Set Target signal to output
SetControl(PIN_TAD, TAD_OUT);
// Set Target signal to output
SetControl(PIN_TAD, TAD_OUT);
SetMode(PIN_BSY, OUT);
SetMode(PIN_MSG, OUT);
SetMode(PIN_CD, OUT);
SetMode(PIN_REQ, OUT);
SetMode(PIN_IO, OUT);
} else {
// Turn off the ACTIVE signal
SetControl(PIN_ACT, ACT_OFF);
SetMode(PIN_BSY, OUT);
SetMode(PIN_MSG, OUT);
SetMode(PIN_CD, OUT);
SetMode(PIN_REQ, OUT);
SetMode(PIN_IO, OUT);
} else {
// Turn off the ACTIVE signal
SetControl(PIN_ACT, ACT_OFF);
// Set the target signal to input
SetControl(PIN_TAD, TAD_IN);
// Set the target signal to input
SetControl(PIN_TAD, TAD_IN);
SetMode(PIN_BSY, IN);
SetMode(PIN_MSG, IN);
SetMode(PIN_CD, IN);
SetMode(PIN_REQ, IN);
SetMode(PIN_IO, IN);
}
SetMode(PIN_BSY, IN);
SetMode(PIN_MSG, IN);
SetMode(PIN_CD, IN);
SetMode(PIN_REQ, IN);
SetMode(PIN_IO, IN);
}
}
@ -608,55 +606,33 @@ uint8_t GPIOBUS_Raspberry::GetDAT()
return (uint8_t)data;
}
//---------------------------------------------------------------------------
//
// Set data signals
//
//---------------------------------------------------------------------------
void GPIOBUS_Raspberry::SetDAT(uint8_t dat)
{
// Write to port
// Write to ports
#if SIGNAL_CONTROL_MODE == 0
uint32_t fsel = gpfsel[0];
fsel &= tblDatMsk[0][dat];
fsel |= tblDatSet[0][dat];
if (fsel != gpfsel[0]) {
gpfsel[0] = fsel;
gpio[GPIO_FSEL_0] = fsel;
}
gpfsel[0] = fsel;
gpio[GPIO_FSEL_0] = fsel;
fsel = gpfsel[1];
fsel &= tblDatMsk[1][dat];
fsel |= tblDatSet[1][dat];
if (fsel != gpfsel[1]) {
gpfsel[1] = fsel;
gpio[GPIO_FSEL_1] = fsel;
}
gpfsel[1] = fsel;
gpio[GPIO_FSEL_1] = fsel;
fsel = gpfsel[2];
fsel &= tblDatMsk[2][dat];
fsel |= tblDatSet[2][dat];
if (fsel != gpfsel[2]) {
gpfsel[2] = fsel;
gpio[GPIO_FSEL_2] = fsel;
}
gpfsel[2] = fsel;
gpio[GPIO_FSEL_2] = fsel;
#else
gpio[GPIO_CLR_0] = tblDatMsk[dat];
gpio[GPIO_SET_0] = tblDatSet[dat];
#endif // SIGNAL_CONTROL_MODE
#endif
}
bool GPIOBUS_Raspberry::GetDP() const
{
return GetSignal(PIN_DP);
}
//---------------------------------------------------------------------------
//
// Create work table
//
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//
// Signal table

View File

@ -137,8 +137,6 @@ class GPIOBUS_Raspberry : public GPIOBUS
// Set REQ signal
void SetREQ(bool ast) override;
bool GetDP() const override;
// Get DAT signal
uint8_t GetDAT() override;
// Set DAT signal
@ -174,12 +172,6 @@ class GPIOBUS_Raspberry : public GPIOBUS
// Set Control Signal
void SetMode(int pin, int mode) override;
// Set SCSI I/O mode
int GetMode(int pin) override
{
// Not implemented (or needed for thist gpio bus type)
(void)pin;
return -1;
}
bool GetSignal(int pin) const override;
// Get SCSI input signal value
void SetSignal(int pin, bool ast) override;

View File

@ -372,11 +372,6 @@ void GPIOBUS_Virtual::SetREQ(bool ast)
SetSignal(PIN_REQ, ast);
}
bool GPIOBUS_Virtual::GetDP() const
{
return GetSignal(PIN_DP);
}
//---------------------------------------------------------------------------
//
// Get data signals

View File

@ -97,8 +97,6 @@ class GPIOBUS_Virtual final : public GPIOBUS
void SetREQ(bool ast) override;
// Set REQ signal
bool GetDP() const override;
bool WaitREQ(bool ast) override
{
return WaitSignal(PIN_REQ, ast);
@ -120,12 +118,6 @@ class GPIOBUS_Virtual final : public GPIOBUS
// Set Control Signal
void SetMode(int pin, int mode) override;
// Set SCSI I/O mode
int GetMode(int pin) override
{
// Not implemented (or needed for thist gpio bus type)
(void)pin;
return -1;
}
bool GetSignal(int pin) const override;
// Get SCSI input signal value
void SetSignal(int pin, bool ast) override;

View File

@ -52,9 +52,6 @@ class PinControl
// Set ENB signal
virtual void SetENB(bool ast) = 0;
// Get parity signal
virtual bool GetDP() const = 0;
// GPIO pin direction setting
virtual void PinConfig(int pin, int mode) = 0;
// GPIO pin pull up/down resistor setting
@ -65,4 +62,4 @@ class PinControl
PinControl() = default;
virtual ~PinControl() = default;
};
};

View File

@ -144,7 +144,7 @@ uint32_t SBC_Version::GetPeripheralAddress(void)
return address;
}
#elif defined __NetBSD__
#elif defined __NetBSD__ && (!defined(__x86_64__) || defined(__X86__))
uint32_t SBC_Version::GetPeripheralAddress(void)
{
char buf[1024];

View File

@ -43,12 +43,6 @@ uint32_t SysTimer::GetTimerLow()
return systimer_ptr->GetTimerLow();
}
// Get system timer high byte
uint32_t SysTimer::GetTimerHigh()
{
return systimer_ptr->GetTimerHigh();
}
// Sleep for N nanoseconds
void SysTimer::SleepNsec(uint32_t nsec)
{

View File

@ -29,8 +29,6 @@ class PlatformSpecificTimer
virtual void Init() = 0;
// Get system timer low byte
virtual uint32_t GetTimerLow() = 0;
// Get system timer high byte
virtual uint32_t GetTimerHigh() = 0;
// Sleep for N nanoseconds
virtual void SleepNsec(uint32_t nsec) = 0;
// Sleep for N microseconds
@ -48,8 +46,6 @@ class SysTimer
static void Init();
// Get system timer low byte
static uint32_t GetTimerLow();
// Get system timer high byte
static uint32_t GetTimerHigh();
// Sleep for N nanoseconds
static void SleepNsec(uint32_t nsec);
// Sleep for N microseconds
@ -57,7 +53,6 @@ class SysTimer
private:
static bool initialized;
static bool is_allwinnner;
static bool is_raspberry;
static std::unique_ptr<PlatformSpecificTimer> systimer_ptr;

View File

@ -92,16 +92,6 @@ uint32_t SysTimer_Raspberry::GetTimerLow()
return systaddr[SYST_CLO];
}
//---------------------------------------------------------------------------
//
// Get system timer high byte
//
//---------------------------------------------------------------------------
uint32_t SysTimer_Raspberry::GetTimerHigh()
{
return systaddr[SYST_CHI];
}
//---------------------------------------------------------------------------
//
// Sleep in nanoseconds

View File

@ -32,8 +32,6 @@ class SysTimer_Raspberry : public PlatformSpecificTimer
void Init() override;
// Get system timer low byte
uint32_t GetTimerLow() override;
// Get system timer high byte
uint32_t GetTimerHigh() override;
// Sleep for N nanoseconds
void SleepNsec(uint32_t nsec) override;
// Sleep for N microseconds

View File

@ -1,3 +0,0 @@
# This is used for debugging. VisualStudio code will call this file when launching
# the debugger, instead of directly calling GDB. That way we can add the pkexec
sudo /usr/bin/gdb "$@"

View File

@ -17,11 +17,9 @@
#include "shared/piscsi_version.h"
#include "controllers/scsi_controller.h"
#include "devices/device_logger.h"
#include "devices/device_factory.h"
#include "devices/storage_device.h"
#include "hal/gpiobus_factory.h"
#include "hal/gpiobus.h"
#include "hal/systimer.h"
#include "piscsi/piscsi_core.h"
#include <spdlog/spdlog.h>
#include <netinet/in.h>
@ -30,6 +28,7 @@
#include <iostream>
#include <fstream>
#include <vector>
#include <chrono>
using namespace std;
using namespace filesystem;
@ -685,21 +684,19 @@ bool Piscsi::ShutDown(AbstractController::piscsi_shutdown_mode shutdown_mode)
bool Piscsi::IsNotBusy() const
{
// Wait until BSY is released as there is a possibility for the
// initiator to assert it while setting the ID (for up to 3 seconds)
if (bus->GetBSY()) {
const uint32_t now = SysTimer::GetTimerLow();
// initiator to assert it while setting the ID (for up to 3 seconds)
if (bus->GetBSY()) {
const auto now = chrono::steady_clock::now();
while ((chrono::duration_cast<chrono::seconds>(chrono::steady_clock::now() - now).count()) < 3) {
bus->Acquire();
// Wait for 3s
while ((SysTimer::GetTimerLow() - now) < 3'000'000) {
bus->Acquire();
if (!bus->GetBSY()) {
return true;
}
}
if (!bus->GetBSY()) {
return true;
}
}
return false;
}
return false;
}
return true;
return true;
}

View File

@ -67,7 +67,7 @@ private:
PiscsiImage piscsi_image;
PiscsiResponse response;
[[no_unique_address]] PiscsiResponse response;
PiscsiService service;

View File

@ -10,7 +10,6 @@
#include "shared/piscsi_util.h"
#include "shared/protobuf_util.h"
#include "shared/piscsi_exceptions.h"
#include "devices/device_factory.h"
#include "devices/disk.h"
#include "localizer.h"
#include "command_context.h"
@ -530,7 +529,7 @@ bool PiscsiExecutor::SetSectorSize(const CommandContext& context, shared_ptr<Pri
if (size) {
const auto disk = dynamic_pointer_cast<Disk>(device);
if (disk != nullptr && disk->IsSectorSizeConfigurable()) {
if (!disk->SetConfiguredSectorSize(device_factory, size)) {
if (!disk->SetConfiguredSectorSize(size)) {
return context.ReturnLocalizedError(LocalizationKey::ERROR_BLOCK_SIZE, to_string(size));
}
}

View File

@ -11,6 +11,7 @@
#include "hal/bus.h"
#include "controllers/controller_manager.h"
#include "devices/device_factory.h"
#include <unordered_set>
class DeviceFactory;
@ -60,7 +61,7 @@ private:
ControllerManager& controller_manager;
const DeviceFactory device_factory;
[[no_unique_address]] const DeviceFactory device_factory;
unordered_set<int> reserved_ids;
};

View File

@ -24,26 +24,29 @@ using namespace piscsi_util;
using namespace network_util;
using namespace protobuf_util;
void PiscsiResponse::GetDeviceProperties(const Device& device, PbDeviceProperties& properties) const
void PiscsiResponse::GetDeviceProperties(shared_ptr<Device> device, PbDeviceProperties& properties) const
{
properties.set_luns(ControllerManager::GetScsiLunMax());
properties.set_read_only(device.IsReadOnly());
properties.set_protectable(device.IsProtectable());
properties.set_stoppable(device.IsStoppable());
properties.set_removable(device.IsRemovable());
properties.set_lockable(device.IsLockable());
properties.set_supports_file(device.SupportsFile());
properties.set_supports_params(device.SupportsParams());
properties.set_read_only(device->IsReadOnly());
properties.set_protectable(device->IsProtectable());
properties.set_stoppable(device->IsStoppable());
properties.set_removable(device->IsRemovable());
properties.set_lockable(device->IsLockable());
properties.set_supports_file(device->SupportsFile());
properties.set_supports_params(device->SupportsParams());
if (device.SupportsParams()) {
for (const auto& [key, value] : device.GetDefaultParams()) {
if (device->SupportsParams()) {
for (const auto& [key, value] : device->GetDefaultParams()) {
auto& map = *properties.mutable_default_params();
map[key] = value;
}
}
for (const auto& block_size : device_factory.GetSectorSizes(device.GetType())) {
properties.add_block_sizes(block_size);
shared_ptr<Disk> disk = dynamic_pointer_cast<Disk>(device);
if (disk != nullptr && disk->IsSectorSizeConfigurable()) {
for (const auto& sector_size : disk->GetSupportedSectorSizes()) {
properties.add_block_sizes(sector_size);
}
}
}
@ -52,7 +55,7 @@ void PiscsiResponse::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_inf
auto type_properties = device_types_info.add_properties();
type_properties->set_type(type);
const auto device = device_factory.CreateDevice(type, 0, "");
GetDeviceProperties(*device, *type_properties->mutable_properties());
GetDeviceProperties(device, *type_properties->mutable_properties());
}
void PiscsiResponse::GetDeviceTypesInfo(PbDeviceTypesInfo& device_types_info) const
@ -67,37 +70,37 @@ void PiscsiResponse::GetDeviceTypesInfo(PbDeviceTypesInfo& device_types_info) co
}
}
void PiscsiResponse::GetDevice(const Device& device, PbDevice& pb_device, const string& default_folder) const
void PiscsiResponse::GetDevice(shared_ptr<Device> device, PbDevice& pb_device, const string& default_folder) const
{
pb_device.set_id(device.GetId());
pb_device.set_unit(device.GetLun());
pb_device.set_vendor(device.GetVendor());
pb_device.set_product(device.GetProduct());
pb_device.set_revision(device.GetRevision());
pb_device.set_type(device.GetType());
pb_device.set_id(device->GetId());
pb_device.set_unit(device->GetLun());
pb_device.set_vendor(device->GetVendor());
pb_device.set_product(device->GetProduct());
pb_device.set_revision(device->GetRevision());
pb_device.set_type(device->GetType());
GetDeviceProperties(device, *pb_device.mutable_properties());
auto status = pb_device.mutable_status();
status->set_protected_(device.IsProtected());
status->set_stopped(device.IsStopped());
status->set_removed(device.IsRemoved());
status->set_locked(device.IsLocked());
status->set_protected_(device->IsProtected());
status->set_stopped(device->IsStopped());
status->set_removed(device->IsRemoved());
status->set_locked(device->IsLocked());
if (device.SupportsParams()) {
for (const auto& [key, value] : device.GetParams()) {
if (device->SupportsParams()) {
for (const auto& [key, value] : device->GetParams()) {
SetParam(pb_device, key, value);
}
}
if (const auto disk = dynamic_cast<const Disk*>(&device); disk) {
pb_device.set_block_size(device.IsRemoved()? 0 : disk->GetSectorSizeInBytes());
pb_device.set_block_count(device.IsRemoved() ? 0: disk->GetBlockCount());
if (const auto disk = dynamic_pointer_cast<const Disk>(device); disk) {
pb_device.set_block_size(device->IsRemoved()? 0 : disk->GetSectorSizeInBytes());
pb_device.set_block_count(device->IsRemoved() ? 0: disk->GetBlockCount());
}
const auto storage_device = dynamic_cast<const StorageDevice *>(&device);
const auto storage_device = dynamic_pointer_cast<const StorageDevice>(device);
if (storage_device != nullptr) {
GetImageFile(*pb_device.mutable_file(), default_folder, device.IsReady() ? storage_device->GetFilename() : "");
GetImageFile(*pb_device.mutable_file(), default_folder, device->IsReady() ? storage_device->GetFilename() : "");
}
}
@ -191,7 +194,7 @@ void PiscsiResponse::GetDevices(const unordered_set<shared_ptr<PrimaryDevice>>&
{
for (const auto& device : devices) {
PbDevice *pb_device = server_info.mutable_devices_info()->add_devices();
GetDevice(*device, *pb_device, default_folder);
GetDevice(device, *pb_device, default_folder);
}
}
@ -218,7 +221,7 @@ void PiscsiResponse::GetDevicesInfo(const unordered_set<shared_ptr<PrimaryDevice
for (const auto& [id, lun] : id_sets) {
for (const auto& d : devices) {
if (d->GetId() == id && d->GetLun() == lun) {
GetDevice(*d, *devices_info->add_devices(), default_folder);
GetDevice(d, *devices_info->add_devices(), default_folder);
break;
}
}

View File

@ -25,7 +25,7 @@ class PiscsiResponse
{
public:
PiscsiResponse() = default;
PiscsiResponse() { }
~PiscsiResponse() = default;
bool GetImageFile(PbImageFile&, const string&, const string&) const;
@ -47,11 +47,10 @@ private:
inline static const vector<string> EMPTY_VECTOR;
// TODO Try to get rid of this field by having the device instead of the factory providing the device data
const DeviceFactory device_factory;
[[no_unique_address]] const DeviceFactory device_factory;
void GetDeviceProperties(const Device&, PbDeviceProperties&) const;
void GetDevice(const Device&, PbDevice&, const string&) const;
void GetDeviceProperties(shared_ptr<Device>, PbDeviceProperties&) const;
void GetDevice(shared_ptr<Device>, PbDevice&, const string&) const;
void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType) const;
void GetAvailableImages(PbImageFilesInfo&, const string&, const string&, const string&, int) const;
void GetAvailableImages(PbServerInfo&, const string&, const string&, const string&, int) const;

View File

@ -42,7 +42,8 @@ string PiscsiService::Init(const callback& cb, int port)
server.sin_family = PF_INET;
server.sin_port = htons((uint16_t)port);
server.sin_addr.s_addr = INADDR_ANY;
if (bind(service_socket, reinterpret_cast<const sockaddr *>(&server), sizeof(sockaddr_in)) < 0) { //NOSONAR bit_cast is not supported by the bullseye compiler
if (bind(service_socket, reinterpret_cast<const sockaddr*>(&server), //NOSONAR bit_cast is not supported by the bullseye compiler
static_cast<socklen_t>(sizeof(sockaddr_in))) < 0) {
Stop();
return "Port " + to_string(port) + " is in use, is piscsi already running?";
}

View File

@ -14,7 +14,7 @@
// The following should be updated for each release
const int piscsi_major_version = 23; // Last two digits of year
const int piscsi_minor_version = 11; // Month
const int piscsi_patch_version = 1; // Patch number - increment for each update
const int piscsi_patch_version = -1; // Patch number - increment for each update
using namespace std;

View File

@ -39,41 +39,6 @@ TEST(DeviceFactoryTest, GetTypeForFile)
EXPECT_EQ(device_factory.GetTypeForFile("test.iso.suffix"), UNDEFINED);
}
TEST(DeviceFactoryTest, GetSectorSizes)
{
DeviceFactory device_factory;
unordered_set<uint32_t> sector_sizes = device_factory.GetSectorSizes(SCHD);
EXPECT_EQ(4, sector_sizes.size());
EXPECT_TRUE(sector_sizes.contains(512));
EXPECT_TRUE(sector_sizes.contains(1024));
EXPECT_TRUE(sector_sizes.contains(2048));
EXPECT_TRUE(sector_sizes.contains(4096));
sector_sizes = device_factory.GetSectorSizes(SCRM);
EXPECT_EQ(4, sector_sizes.size());
EXPECT_TRUE(sector_sizes.contains(512));
EXPECT_TRUE(sector_sizes.contains(1024));
EXPECT_TRUE(sector_sizes.contains(2048));
EXPECT_TRUE(sector_sizes.contains(4096));
sector_sizes = device_factory.GetSectorSizes(SCMO);
EXPECT_EQ(4, sector_sizes.size());
EXPECT_TRUE(sector_sizes.contains(512));
EXPECT_TRUE(sector_sizes.contains(1024));
EXPECT_TRUE(sector_sizes.contains(2048));
EXPECT_TRUE(sector_sizes.contains(4096));
sector_sizes = device_factory.GetSectorSizes(SCCD);
EXPECT_EQ(2, sector_sizes.size());
EXPECT_TRUE(sector_sizes.contains(512));
EXPECT_TRUE(sector_sizes.contains(2048));
}
TEST(DeviceFactoryTest, GetExtensionMapping)
{
DeviceFactory device_factory;

View File

@ -774,14 +774,8 @@ TEST(DiskTest, SectorSize)
{
MockDisk disk;
unordered_set<uint32_t> sizes = { 512, 1024 };
disk.SetSectorSizes(sizes);
EXPECT_TRUE(disk.IsSectorSizeConfigurable());
sizes.clear();
disk.SetSectorSizes(sizes);
EXPECT_FALSE(disk.IsSectorSizeConfigurable());
disk.SetSectorSizeShiftCount(9);
EXPECT_EQ(9, disk.GetSectorSizeShiftCount());
EXPECT_EQ(512, disk.GetSectorSizeInBytes());
@ -815,13 +809,12 @@ TEST(DiskTest, SectorSize)
TEST(DiskTest, ConfiguredSectorSize)
{
DeviceFactory device_factory;
MockSCSIHD disk(0, {}, false);
MockSCSIHD disk(0, false);
EXPECT_TRUE(disk.SetConfiguredSectorSize(device_factory, 512));
EXPECT_TRUE(disk.SetConfiguredSectorSize(512));
EXPECT_EQ(512, disk.GetConfiguredSectorSize());
EXPECT_FALSE(disk.SetConfiguredSectorSize(device_factory, 1234));
EXPECT_FALSE(disk.SetConfiguredSectorSize(1234));
EXPECT_EQ(512, disk.GetConfiguredSectorSize());
}

View File

@ -219,16 +219,3 @@ TEST(GpiobusRaspberry, GetREQ)
bus.Acquire();
EXPECT_EQ(false, bus.GetREQ());
}
TEST(GpiobusRaspberry, GetDP)
{
SetableGpiobusRaspberry bus;
bus.TestSetGpios(0x00);
bus.TestSetGpioPin(PIN_DP, true);
bus.Acquire();
EXPECT_EQ(true, bus.GetDP());
bus.TestSetGpioPin(PIN_DP, false);
bus.Acquire();
EXPECT_EQ(false, bus.GetDP());
}

View File

@ -53,7 +53,6 @@ public:
MOCK_METHOD(void, SetENB, (bool), (override));
MOCK_METHOD(uint8_t, GetDAT, (), (override));
MOCK_METHOD(void, SetDAT, (uint8_t), (override));
MOCK_METHOD(bool, GetDP, (), (const override));
MOCK_METHOD(uint32_t, Acquire, (), (override));
MOCK_METHOD(int, CommandHandShake, (vector<uint8_t>&), (override));
MOCK_METHOD(int, ReceiveHandShake, (uint8_t *, int), (override));
@ -61,13 +60,11 @@ public:
MOCK_METHOD(bool, GetSignal, (int), (const override));
MOCK_METHOD(void, SetSignal, (int, bool), (override));
MOCK_METHOD(bool, PollSelectEvent, (), (override));
MOCK_METHOD(void, ClearSelectEvent, (), (override));
MOCK_METHOD(unique_ptr<DataSample>, GetSample, (uint64_t), (override));
MOCK_METHOD(void, PinConfig, (int, int), (override));
MOCK_METHOD(void, PullConfig, (int , int ), (override));
MOCK_METHOD(void, SetControl, (int , bool ), (override));
MOCK_METHOD(void, SetMode, (int , int ), (override));
MOCK_METHOD(int, GetMode, (int ), (override));
MockBus() = default;
~MockBus() override = default;
@ -352,7 +349,7 @@ public:
MOCK_METHOD(void, FlushCache, (), (override));
MOCK_METHOD(void, Open, (), (override));
MockDisk() : Disk(SCHD, 0) {}
MockDisk() : Disk(SCHD, 0, { 512, 1024, 2048, 4096 }) {}
~MockDisk() override = default;
};
@ -363,10 +360,15 @@ class MockSCSIHD : public SCSIHD //NOSONAR Ignore inheritance hierarchy depth in
FRIEND_TEST(ScsiHdTest, FinalizeSetup);
FRIEND_TEST(ScsiHdTest, GetProductData);
FRIEND_TEST(ScsiHdTest, SetUpModePages);
FRIEND_TEST(PiscsiExecutorTest, SetSectorSize);
FRIEND_TEST(ScsiHdTest, GetSectorSizes);
FRIEND_TEST(ScsiHdTest, ModeSelect);
FRIEND_TEST(PiscsiExecutorTest, SetSectorSize);
using SCSIHD::SCSIHD;
public:
MockSCSIHD(int lun, bool removable) : SCSIHD(lun, removable, scsi_level::scsi_2) {}
explicit MockSCSIHD(const unordered_set<uint32_t>& sector_sizes) : SCSIHD(0, false, scsi_level::scsi_2, sector_sizes) {}
~MockSCSIHD() override = default;
};
class MockSCSIHD_NEC : public SCSIHD_NEC //NOSONAR Ignore inheritance hierarchy depth in unit tests
@ -382,8 +384,10 @@ class MockSCSIHD_NEC : public SCSIHD_NEC //NOSONAR Ignore inheritance hierarchy
class MockSCSICD : public SCSICD //NOSONAR Ignore inheritance hierarchy depth in unit tests
{
FRIEND_TEST(ScsiCdTest, GetSectorSizes);
FRIEND_TEST(ScsiCdTest, SetUpModePages);
FRIEND_TEST(ScsiCdTest, ReadToc);
FRIEND_TEST(ScsiCdTest, ModeSelect);
using SCSICD::SCSICD;
};

View File

@ -252,8 +252,6 @@ TEST(PiscsiExecutorTest, Attach)
TEST(PiscsiExecutorTest, Insert)
{
DeviceFactory device_factory;
auto bus = make_shared<MockBus>();
ControllerManager controller_manager;
auto [controller, device] = CreateDevice(SCHD);
@ -500,13 +498,17 @@ TEST(PiscsiExecutorTest, SetSectorSize)
CommandContext context(command, "", "");
unordered_set<uint32_t> sizes;
auto hd = make_shared<MockSCSIHD>(0, sizes, false);
auto hd = make_shared<MockSCSIHD>(sizes);
EXPECT_FALSE(executor.SetSectorSize(context, hd, 512));
sizes.insert(512);
hd = make_shared<MockSCSIHD>(0, sizes, false);
hd = make_shared<MockSCSIHD>(sizes);
EXPECT_TRUE(executor.SetSectorSize(context, hd, 0));
EXPECT_FALSE(executor.SetSectorSize(context, hd, 1));
EXPECT_FALSE(executor.SetSectorSize(context, hd, 512));
sizes.insert(1024);
hd = make_shared<MockSCSIHD>(sizes);
EXPECT_TRUE(executor.SetSectorSize(context, hd, 512));
}

View File

@ -46,6 +46,11 @@ TEST(ScsiCommandUtilTest, ModeSelect6)
Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list))))
<< "Unsupported page 0 was not rejected";
// Page 1
buf[12] = 0x01;
EXPECT_NO_THROW(ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512))
<< "Page 1 is supported";
// Page 3 (Format Device Page)
buf[12] = 0x03;
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512); },
@ -62,7 +67,25 @@ TEST(ScsiCommandUtilTest, ModeSelect6)
Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list))))
<< "Not enough command parameters";
EXPECT_FALSE(ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512).empty());
// check length computation
buf[3] = 8;
buf[10] = 2;
buf[12] = 1;
buf[13] = 10;
buf[14] = 0x24;
buf[24] = 0;
EXPECT_NO_THROW(ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512))
<< "Multi-page length computation";
// check length computation
buf[3] = 8;
buf[10] = 12;
buf[12] = 0;
buf[13] = 0;
buf[14] = 0;
buf[24] = 0;
EXPECT_NO_THROW(ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, 12, 512))
<< "Empty ModeSelect6";
}
TEST(ScsiCommandUtilTest, ModeSelect10)
@ -111,8 +134,6 @@ TEST(ScsiCommandUtilTest, ModeSelect10)
Property(&scsi_exception::get_sense_key, sense_key::illegal_request),
Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list))))
<< "Not enough command parameters";
EXPECT_FALSE(ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 512).empty());
}
TEST(ScsiCommandUtilTest, EnrichFormatPage)

View File

@ -34,10 +34,21 @@ TEST(ScsiCdTest, Inquiry)
TestInquiry::Inquiry(SCCD, device_type::cd_rom, scsi_level::scsi_1_ccs, "PiSCSI SCSI CD-ROM ", 0x1f, true, "file.is1");
}
TEST(ScsiCdTest, GetSectorSizes)
{
MockSCSICD cd(0);
const auto& sector_sizes = cd.GetSupportedSectorSizes();
EXPECT_EQ(2, sector_sizes.size());
EXPECT_TRUE(sector_sizes.contains(512));
EXPECT_TRUE(sector_sizes.contains(2048));
}
TEST(ScsiCdTest, SetUpModePages)
{
map<int, vector<byte>> pages;
MockSCSICD cd(0, {});
MockSCSICD cd(0);
// Non changeable
cd.SetUpModePages(pages, 0x3f, false);
@ -51,10 +62,10 @@ TEST(ScsiCdTest, SetUpModePages)
TEST(ScsiCdTest, Open)
{
MockSCSICD cd_iso(0, {});
MockSCSICD cd_cue(0, {});
MockSCSICD cd_raw(0, {});
MockSCSICD cd_physical(0, {});
MockSCSICD cd_iso(0);
MockSCSICD cd_cue(0);
MockSCSICD cd_raw(0);
MockSCSICD cd_physical(0);
EXPECT_THROW(cd_iso.Open(), io_exception) << "Missing filename";
@ -111,8 +122,7 @@ TEST(ScsiCdTest, Open)
TEST(ScsiCdTest, ReadToc)
{
auto controller = make_shared<MockAbstractController>();
const unordered_set<uint32_t> sector_sizes;
auto cd = make_shared<MockSCSICD>(0, sector_sizes);
auto cd = make_shared<MockSCSICD>(0);
EXPECT_TRUE(cd->Init({}));
controller->AddDevice(cd);
@ -123,3 +133,26 @@ TEST(ScsiCdTest, ReadToc)
// Further testing requires filesystem access
}
TEST(ScsiCdTest, ModeSelect)
{
MockSCSICD cd(0);
vector<int> cmd(6);
vector<uint8_t> buf(255);
cd.SetSectorSizeInBytes(2048);
// PF
cmd[1] = 0x10;
// Length
buf[3] = 0x08;
// 2048 bytes per sector
buf[10] = 0x08;
// Page 3 (Device Format Page)
buf[12] = 0x01;
EXPECT_NO_THROW(cd.ModeSelect(scsi_command::eCmdModeSelect6, cmd, buf, 255)) << "MODE SELECT(6) with sector size 2048 is supported";
// 512 bytes per sector
buf[10] = 0x02;
EXPECT_NO_THROW(cd.ModeSelect(scsi_command::eCmdModeSelect6, cmd, buf, 255)) << "MODE SELECT(6) with sector size 512 is supported";
}

View File

@ -30,14 +30,14 @@ TEST(ScsiHdTest, Inquiry)
TEST(ScsiHdTest, SupportsSaveParameters)
{
MockSCSIHD hd(0, {}, false);
MockSCSIHD hd(0, false);
EXPECT_TRUE(hd.SupportsSaveParameters());
}
TEST(ScsiHdTest, FinalizeSetup)
{
MockSCSIHD hd(0, {}, false);
MockSCSIHD hd(0, false);
hd.SetSectorSizeInBytes(1024);
EXPECT_THROW(hd.FinalizeSetup(0), io_exception) << "Device has 0 blocks";
@ -45,9 +45,9 @@ TEST(ScsiHdTest, FinalizeSetup)
TEST(ScsiHdTest, GetProductData)
{
MockSCSIHD hd_kb(0, {}, false);
MockSCSIHD hd_mb(0, {}, false);
MockSCSIHD hd_gb(0, {}, false);
MockSCSIHD hd_kb(0, false);
MockSCSIHD hd_mb(0, false);
MockSCSIHD hd_gb(0, false);
const path filename = CreateTempFile(1);
hd_kb.SetFilename(string(filename));
@ -73,10 +73,23 @@ TEST(ScsiHdTest, GetProductData)
remove(filename);
}
TEST(ScsiHdTest, GetSectorSizes)
{
MockSCSIHD hd(0, false);
const auto& sector_sizes = hd.GetSupportedSectorSizes();
EXPECT_EQ(4, sector_sizes.size());
EXPECT_TRUE(sector_sizes.contains(512));
EXPECT_TRUE(sector_sizes.contains(1024));
EXPECT_TRUE(sector_sizes.contains(2048));
EXPECT_TRUE(sector_sizes.contains(4096));
}
TEST(ScsiHdTest, SetUpModePages)
{
map<int, vector<byte>> pages;
MockSCSIHD hd(0, {}, false);
MockSCSIHD hd(0, false);
// Non changeable
hd.SetUpModePages(pages, 0x3f, false);
@ -90,7 +103,7 @@ TEST(ScsiHdTest, SetUpModePages)
TEST(ScsiHdTest, ModeSelect)
{
MockSCSIHD hd(0, { 512 }, false);
MockSCSIHD hd({ 512 });
vector<int> cmd(10);
vector<uint8_t> buf(255);

View File

@ -28,15 +28,28 @@ TEST(ScsiMoTest, Inquiry)
TEST(ScsiMoTest, SupportsSaveParameters)
{
map<int, vector<byte>> pages;
MockSCSIMO mo(0, {});
MockSCSIMO mo(0);
EXPECT_TRUE(mo.SupportsSaveParameters());
}
TEST(ScsiMoTest, GetSectorSizes)
{
MockSCSIMO mo(0);
const auto& sector_sizes = mo.GetSupportedSectorSizes();
EXPECT_EQ(4, sector_sizes.size());
EXPECT_TRUE(sector_sizes.contains(512));
EXPECT_TRUE(sector_sizes.contains(1024));
EXPECT_TRUE(sector_sizes.contains(2048));
EXPECT_TRUE(sector_sizes.contains(4096));
}
TEST(ScsiMoTest, SetUpModePages)
{
map<int, vector<byte>> pages;
MockSCSIMO mo(0, {});
MockSCSIMO mo(0);
// Non changeable
mo.SetUpModePages(pages, 0x3f, false);
@ -51,7 +64,7 @@ TEST(ScsiMoTest, SetUpModePages)
TEST(ScsiMoTest, TestAddVendorPage)
{
map<int, vector<byte>> pages;
MockSCSIMO mo(0, {});
MockSCSIMO mo(0);
mo.SetReady(true);
mo.SetUpModePages(pages, 0x21, false);
@ -122,7 +135,7 @@ TEST(ScsiMoTest, TestAddVendorPage)
TEST(ScsiMoTest, ModeSelect)
{
MockSCSIMO mo(0, { 1024, 2048 });
MockSCSIMO mo(0);
vector<int> cmd(10);
vector<uint8_t> buf(255);

View File

@ -40,10 +40,11 @@ The following environment variables are available when using Docker Compose:
| `WEB_HTTP_PORT` | 8080 |
| `WEB_HTTPS_PORT` | 8443 |
| `WEB_LOG_LEVEL` | info |
| `BACKEND_HOST` | backend |
| `BACKEND_PORT` | 6868 |
| `BACKEND_PASSWORD` | *[None]* |
| `BACKEND_LOG_LEVEL` | debug |
| `BACKEND_HOST` | backend |
| `BACKEND_PORT` | 6868 |
| `BACKEND_PASSWORD` | *[None]* |
| `BACKEND_LOG_LEVEL` | debug |
| `RESET_VENV` | *[None]* |
**Examples:**
@ -57,6 +58,12 @@ Start the web UI with the log level set to debug:
WEB_LOG_LEVEL=debug docker compose up
```
Force resetting & reinstalling Python web `venv` directory:
```
RESET_VENV=1 docker compose up
```
## Volumes
When using Docker Compose the following volumes will be mounted automatically:

View File

@ -18,7 +18,7 @@ FROM debian:bullseye-slim AS runner
USER root
WORKDIR /home/pi
COPY --from=build /home/pi/piscsi/cpp/bin/fullspec/* /usr/local/bin/
COPY --from=build /home/pi/piscsi/cpp/bin/* /usr/local/bin/
COPY docker/backend/piscsi_wrapper.sh /usr/local/bin/piscsi_wrapper.sh
RUN chmod +x /usr/local/bin/*
RUN mkdir -p /home/pi/images

View File

@ -36,6 +36,7 @@ services:
- "127.0.0.1:${WEB_HTTPS_PORT:-8443}:443"
environment:
- BACKEND_PASSWORD=${BACKEND_PASSWORD:-}
- RESET_VENV=${RESET_VENV:-}
init: true
command: [
"--backend-host=${BACKEND_HOST:-backend}",

View File

@ -4,7 +4,7 @@ ARG OS_VERSION=bullseye
FROM "debian:${OS_VERSION}-slim"
RUN apt-get update \
&& apt-get install -y --no-install-recommends sudo systemd rsyslog procps man-db wget git \
&& apt-get install -y --no-install-recommends sudo systemd rsyslog procps man-db wget git gcc \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

View File

@ -76,7 +76,7 @@ SECRET_FILE="$HOME/.config/piscsi/secret"
FILE_SHARE_PATH="$HOME/shared_files"
FILE_SHARE_NAME="Pi File Server"
APT_PACKAGES_COMMON="build-essential git protobuf-compiler bridge-utils ca-certificates"
APT_PACKAGES_COMMON="build-essential git protobuf-compiler bridge-utils ca-certificates rsyslog"
APT_PACKAGES_BACKEND="libspdlog-dev libpcap-dev libprotobuf-dev protobuf-compiler libgmock-dev clang"
APT_PACKAGES_PYTHON="python3 python3-dev python3-pip python3-venv python3-setuptools python3-wheel libev-dev libevdev2"
APT_PACKAGES_WEB="nginx-light genisoimage man2html hfsutils dosfstools kpartx unzip unar disktype gettext"
@ -92,7 +92,7 @@ function initialChecks() {
}
# Only to be used for pi-gen automated install
function cacheSudo() {
function sudoCache() {
echo "Caching sudo password"
echo raspberry | sudo -v -S
}
@ -948,8 +948,9 @@ function installSamba() {
# Installs and configures Webmin
function installWebmin() {
WEBMIN_PATH="/usr/share/webmin"
WEBMIN_MODULE_CONFIG="/etc/webmin/netatalk2/config"
WEBMIN_MODULE_VERSION="1.0"
WEBMIN_NETATALK_MODULE_CONFIG="/etc/webmin/netatalk2/config"
WEBMIN_NETATALK_MODULE_VERSION="1.0"
WEBMIN_VSFTPD_MODULE_VERSION="2023-12-10"
if [ -d "$WEBMIN_PATH" ]; then
echo
@ -967,25 +968,30 @@ function installWebmin() {
echo "Installing packages..."
sudo apt-get install curl libcgi-session-perl --no-install-recommends --assume-yes </dev/null
curl -o setup-repos.sh https://raw.githubusercontent.com/webmin/webmin/master/setup-repos.sh
sudo sh setup-repos.sh
sudo sh setup-repos.sh -f
rm setup-repos.sh
sudo apt-get install webmin --install-recommends </dev/null
sudo apt-get install webmin --no-install-recommends --assume-yes </dev/null
echo
echo "Downloading and installing Webmin module..."
if [[ -f "$WEBMIN_MODULE_CONFIG" ]]; then
echo "$WEBMIN_MODULE_CONFIG already exists; will not modify..."
WEBMIN_MODULE_FLAG=1
echo "Downloading and installing Webmin modules..."
if [[ -f "$WEBMIN_NETATALK_MODULE_CONFIG" ]]; then
echo "$WEBMIN_NETATALK_MODULE_CONFIG already exists; will not modify..."
WEBMIN_MODULE_FLAG=1
fi
rm netatalk2-wbm.tgz 2> /dev/null || true
wget -O netatalk2-wbm.tgz "https://github.com/Netatalk/netatalk-webmin/releases/download/netatalk2-$WEBMIN_MODULE_VERSION/netatalk2-wbm-$WEBMIN_MODULE_VERSION.tgz" </dev/null
wget -O netatalk2-wbm.tgz "https://github.com/Netatalk/netatalk-webmin/releases/download/netatalk2-$WEBMIN_NETATALK_MODULE_VERSION/netatalk2-wbm-$WEBMIN_NETATALK_MODULE_VERSION.tgz" </dev/null
sudo "$WEBMIN_PATH/install-module.pl" netatalk2-wbm.tgz
if [[ ! $WEBMIN_MODULE_FLAG ]]; then
echo "Modifying $WEBMIN_MODULE_CONFIG..."
sudo sed -i 's@/usr/sbin@/usr/local/sbin@' "$WEBMIN_MODULE_CONFIG"
echo "Modifying $WEBMIN_NETATALK_MODULE_CONFIG..."
sudo sed -i 's@/usr/sbin@/usr/local/sbin@' "$WEBMIN_NETATALK_MODULE_CONFIG"
fi
rm netatalk2-wbm.tgz || true
rm vsftpd.wbm.gz 2> /dev/null || true
wget -O vsftpd.wbm.tgz "https://github.com/rdmark/vsftpd-webmin/releases/download/$WEBMIN_VSFTPD_MODULE_VERSION/vsftpd-$WEBMIN_VSFTPD_MODULE_VERSION.wbm.gz" </dev/null
sudo "$WEBMIN_PATH/install-module.pl" vsftpd.wbm.tgz
rm vsftpd.wbm.tgz || true
}
# updates configuration files and installs packages needed for the OLED screen script
@ -1111,7 +1117,7 @@ function installPiscsiCtrlBoard() {
if [[ $SKIP_PACKAGES ]]; then
echo "Skipping package installation"
else
sudo apt-get update && sudo apt-get install libjpeg-dev libpng-dev libopenjp2-7-dev i2c-tools raspi-config --assume-yes --no-install-recommends </dev/null
sudo apt-get update && sudo apt-get install libjpeg-dev libpng-dev libopenjp2-7-dev i2c-tools raspi-config python3-rpi.gpio --assume-yes --no-install-recommends </dev/null
# install python packages through apt that need compilation
sudo apt-get install python3-cbor2 --assume-yes --no-install-recommends </dev/null
fi
@ -1261,7 +1267,6 @@ function runChoice() {
echo "Detected piscsi control board service; will run the installation steps for the control board ui."
installPiscsiCtrlBoard
fi
cachePipPackages
installPiscsiWebInterface
installWebInterfaceService
showServiceStatus "piscsi-oled"
@ -1465,7 +1470,8 @@ function runChoice() {
echo "This script will make the following changes to your system:"
echo "- Add a 3rd party apt repository"
echo "- Install and start the Webmin webapp"
echo "- Install the netatalk2 Webmin module"
echo "- Install the netatalk2 Webmin module"
echo "- Install the vsftpd Webmin module"
installWebmin
echo "Install Webmin - Complete!"
echo "The Webmin webapp should now be listening to port 10000 on this system"
@ -1490,9 +1496,6 @@ function runChoice() {
installWebInterfaceService
echo "Automated install of the PiSCSI Service $(CONNECT_TYPE) complete!"
;;
-h|--help|h|help)
showMenu
;;
*)
echo "${1} is not a valid option, exiting..."
exit 1
@ -1503,8 +1506,8 @@ function runChoice() {
function readChoice() {
choice=-1
until [ $choice -ge "0" ] && ([ $choice -eq "99" ] || [ $choice -le "17" ]) ; do
echo -n "Enter your choice (0-17) or CTRL-C to exit: "
until [ $choice -ge "1" ] && ([ $choice -eq "99" ] || [ $choice -le "17" ]) ; do
echo -n "Enter your choice (1-17) or CTRL-C to exit: "
read -r choice
done
@ -1513,7 +1516,13 @@ function readChoice() {
# Shows the interactive main menu of the script
function showMenu() {
echo "For command line options, rerun with ./easyinstall.sh --help"
echo "Board Type: $CONNECT_TYPE | Compiler: $COMPILER | Compiler Cores: $CORES"
if [[ $SKIP_MAKE_CLEAN ]]; then
echo "Skip 'make clean': YES"
else
echo "Skip 'make clean': NO (will compile from scratch every time!)"
fi
echo ""
echo "Choose among the following options:"
echo "INSTALL/UPDATE PISCSI"
@ -1537,7 +1546,7 @@ function showMenu() {
echo "EXPERIMENTAL FEATURES"
echo " 15) Share the images dir over AppleShare (requires Netatalk)"
echo " 16) Compile PiSCSI binaries"
echo " 17) Install Webmin to manage Netatalk and Samba"
echo " 17) Install Webmin to manage the system and companion apps"
}
# parse arguments passed to the script
@ -1585,6 +1594,19 @@ while [ "$1" != "" ]; do
-l | --skip_make_clean)
SKIP_MAKE_CLEAN=1
;;
--help)
echo "Usage: ./easyinstall.sh [options]"
echo
echo "-c=TYPE, --connect_type=TYPE Connect type (FULLSPEC, STANDARD, AIBOM, GAMERNIUM)"
echo "-r=CHOICE, --run_choice=CHOICE Choose a menu option (1 to 16)"
echo "-j=CORES, --cores=CORES Compile on this many cores in parallel"
echo "-t=TOKEN, --token=TOKEN Token password for protecting PiSCSI"
echo "-h, --headless Don't ask questions (use with -r=CHOICE)"
echo "-g, --with_gcc Compile with g++ instead of clang++"
echo "-s, --skip_packages Don't install Debian packages"
echo "-l, --skip_make_clean Don't recompile from scratch every time"
exit
;;
*)
echo "ERROR: Unknown parameter \"$PARAM\""
exit 1

5
ide_setup/README Normal file
View File

@ -0,0 +1,5 @@
The Eclipse code formatter configuration shall be used together with
Eclipse CDT in order to unify the formatting of the C++ code. Ensure to keep
your formatting rules up to date.
This formatter can also be imported into Intellij IDEA.

View File

@ -0,0 +1,193 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="1">
<profile kind="CodeFormatterProfile" name="PiSCSI" version="1">
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_exception_specification" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_pointer_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.format_header_comment" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_structured_binding_name_list" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_base_clause" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_linkage_declaration" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_conditional_expression" value="34"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_structured_binding_name_list" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_binary_expression" value="20"/>
<setting id="org.eclipse.cdt.core.formatter.brace_position_for_method_declaration" value="next_line"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_pointer_in_declarator_list" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_expression_list" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_base_clause" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_base_types" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.comment.line_up_line_comment_in_blocks_on_first_column" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_namespace_header" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.indent_access_specifier_extra_spaces" value="0"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_lambda_return" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.comment.never_indent_line_comments_on_first_column" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_expression_list" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_bracket" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_base_clause_in_type_declaration" value="80"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_parameters" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_member_access" value="0"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_after_colon_in_constructor_initializer_list" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.brace_position_for_namespace_declaration" value="next_line"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.cdt.core.formatter.lineSplit" value="120"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.comment_formatter_off_tag" value="@formatter:off"/>
<setting id="org.eclipse.cdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_brackets" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_arguments" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_bracket" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_arguments" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_arguments" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_declarator_list" value="16"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_expressions_in_array_initializer" value="20"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_exception_specification" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.continuation_indentation_for_array_initializer" value="4"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_lambda_expression" value="20"/>
<setting id="org.eclipse.cdt.core.formatter.comment.min_distance_between_code_and_line_comment" value="1"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_bracket" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_constructor_initializer_list" value="0"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_exception_specification" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_declarator_list" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.format_line_comment" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_parameters" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.format_block_comment" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_parameters" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_overloaded_left_shift_chain" value="20"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_identifier_in_function_declaration" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.comment_formatter_on_tag" value="@formatter:on"/>
<setting id="org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_linkage" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_lambda_return" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_declarator_list" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_namespace_declaration" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.indent_access_specifier_compare_to_type_header" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_parameters" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_pointer_in_declarator_list" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_conditional_expression_chain" value="18"/>
<setting id="org.eclipse.cdt.core.formatter.tabulation.char" value="space"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_after_template_declaration" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_ref_qualifier_in_structured_binding" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_colon_in_constructor_initializer_list" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.brace_position_for_type_declaration" value="next_line"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.continuation_indentation" value="0"/>
<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_structured_binding_name_list" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_structured_binding_name_list" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_parameters" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_expression_list" value="0"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_base_types" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.indent_label_compare_to_statements" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.indent_declaration_compare_to_template_header" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.use_comment_formatter_tag" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_arguments" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_arguments_in_method_invocation" value="20"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_exception_specification" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.join_wrapped_lines" value="false"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_assignment" value="16"/>
<setting id="org.eclipse.cdt.core.formatter.brace_position_for_linkage_declaration" value="next_line"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_arguments" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_arguments" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_parameters" value="do not insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_access_specifier" value="true"/>
<setting id="org.eclipse.cdt.core.formatter.alignment_for_enumerator_list" value="48"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_structured_binding_name_list" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_pointer_in_method_declaration" value="insert"/>
<setting id="org.eclipse.cdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
</profile>
</profiles>

View File

@ -1,10 +1,10 @@
#adafruit-circuitpython-busdevice==5.1.1
#adafruit-circuitpython-framebuf==1.4.8
#adafruit-circuitpython-ssd1306==2.12.3
luma-oled==3.8.1
luma.core==2.4.1
luma.oled==3.8.1
Pillow==10.0.1
RPi.GPIO==0.7.0
protobuf==3.19.5
unidecode==1.3.2
pyftdi==0.55.0
pyserial==3.5
pyusb==1.2.1
smbus==1.1.post2
smbus2==0.4.3
Unidecode==1.3.2

View File

@ -106,8 +106,9 @@ class CtrlBoardMenuUpdateEventHandler(Observer):
handler_function(info_object)
except AttributeError:
log = logging.getLogger(__name__)
log.error(
"Handler function not found or returned an error. Skipping.",
log.debug(
"Handler function [%s] not found or returned an error. Skipping.",
str(handler_function_name),
)
# noinspection PyUnusedLocal
@ -194,7 +195,7 @@ class CtrlBoardMenuUpdateEventHandler(Observer):
"""Method handles the rotary button press in the profile selection menu
for selecting a profile to load."""
if info_object is not None and "name" in info_object:
file_cmd = FileCmds(sock_cmd=self.sock_cmd, piscsi=self.piscsi_cmd)
file_cmd = FileCmds(piscsi=self.piscsi_cmd)
result = file_cmd.read_config(file_name=info_object["name"])
if result["status"] is True:
self._menu_controller.show_message("Profile loaded!")

View File

@ -28,12 +28,7 @@ class CtrlBoardMenuBuilder(MenuBuilder):
def __init__(self, piscsi_cmd: PiscsiCmds):
super().__init__()
self._piscsi_client = piscsi_cmd
self.file_cmd = FileCmds(
sock_cmd=piscsi_cmd.sock_cmd,
piscsi=piscsi_cmd,
token=piscsi_cmd.token,
locale=piscsi_cmd.locale,
)
self.file_cmd = FileCmds(piscsi=piscsi_cmd)
def build(self, name: str, context_object=None) -> Menu:
if name == CtrlBoardMenuBuilder.SCSI_ID_MENU:
@ -48,7 +43,7 @@ class CtrlBoardMenuBuilder(MenuBuilder):
return self.create_device_info_menu(context_object)
log = logging.getLogger(__name__)
log.error("Provided menu name cannot be built!")
log.debug("Provided menu name [%s] cannot be built!", name)
return self.create_scsi_id_list_menu(context_object)
@ -142,7 +137,7 @@ class CtrlBoardMenuBuilder(MenuBuilder):
def create_images_menu(self, context_object=None):
"""Creates a sub menu showing all the available images"""
menu = Menu(CtrlBoardMenuBuilder.IMAGES_MENU)
images_info = self.piscsi_cmd.list_images()
images_info = self._piscsi_client.list_images()
menu.add_entry("Return", {"context": self.IMAGES_MENU, "action": self.ACTION_RETURN})
images = images_info["files"]
sorted_images = sorted(images, key=lambda d: d["name"])

View File

@ -23,7 +23,7 @@ class Cycler:
self._menu_controller = menu_controller
self.sock_cmd = sock_cmd
self.piscsi_cmd = piscsi_cmd
self.file_cmd = FileCmds(sock_cmd=self.sock_cmd, piscsi=self.piscsi_cmd)
self.file_cmd = FileCmds(piscsi=self.piscsi_cmd)
self.cycle_entries = self.populate_cycle_entries()
self.return_string = return_string
self.return_entry = return_entry

View File

@ -30,7 +30,7 @@ class MenuRenderer(ABC):
self.draw = ImageDraw.Draw(self.image)
self.font = ImageFont.truetype(config.font_path, size=config.font_size)
# just a sample text to work with the font height
_, self.font_height = self.font.getsize("ABCabc")
self.font_height = self.font.getbbox("ABCabc")[3]
self.cursor_position = 0
self.frame_start_row = 0
self.render_timestamp = None
@ -153,7 +153,8 @@ class MenuRenderer(ABC):
def draw_fullsceen_message(self, text: str):
"""Draws a fullscreen message, i.e., a full-screen message."""
font_width, font_height = self.font.getsize(text)
font_width = self.font.getlength(text)
font_height = self.font.getbbox(text)[3]
centered_width = (self.disp.width - font_width) / 2
centered_height = (self.disp.height - font_height) / 2
@ -171,7 +172,7 @@ class MenuRenderer(ABC):
def draw_mini_message(self, text: str):
"""Draws a fullscreen message, i.e., a message covering only the center portion of
the screen. The remaining areas stay visible."""
font_width, _ = self.font.getsize(text)
font_width = self.font.getlength(text)
centered_width = (self.disp.width - font_width) / 2
centered_height = (self.disp.height - self.font_height) / 2
@ -270,7 +271,7 @@ class MenuRenderer(ABC):
def setup_horizontal_scrolling(self, text):
"""Configure horizontal scrolling based on the configured screen dimensions."""
font_width, _ = self.font.getsize(text)
font_width = self.font.getlength(text)
self._current_line_horizontal_overlap = font_width - self.disp.width
def update(self):

View File

@ -0,0 +1,322 @@
# This file is used by the genisoimage command-line tool when generating an iso of type HFS
# to derive the CREATOR and TYPE resource fork attributes of each file in the generated image.
#
# genisoimage will look up the file's extension in the 1st column below, and then use the
# 3rd and 4th columns to derive the CREATOR and TYPE.
#
# This file ends with a "catch-all" file extension of ".*" which will be used if the file extension
# was not found in this map.
#
# Note: genisoimage does not support custom CREATOR/TYPE per EACH file in the image,
# so the best alternative to use the "per file extension" approach here.
#
# The five columns below are: file extension, file translation, CREATOR, TYPE and Description.
# Lines starting with the '#' character are comment lines and are ignored.
#
# See more info at the genisoimage Linux man page: https://linux.die.net/man/1/genisoimage
#
# The mapping entries below are derived from
# https://github.com/Netatalk/netatalk/blob/branch-netatalk-2-3/config/AppleVolumes.system
# Few entries, e.g the one for ".img" were modified; few more (e.g. ".dc42") were added.
#
# EXTN XLate CREATOR TYPE Application - Description (mime type)
.1st Ascii 'ttxt' 'TEXT' "SimpleText - Text Readme (application/text)"
.669 Raw 'SNPL' '6669' "PlayerPro - 669 MOD Music"
.8med Raw 'SCPL' 'STrk' "SoundApp - Amiga OctaMed music"
.8svx Raw 'SCPL' '8SVX' "SoundApp - Amiga 8-bit sound"
.aif Raw 'SCPL' 'AIFF' "SoundApp - AIFF Sound (audio/x-aiff)"
.aifc Raw 'SCPL' 'AIFC' "SoundApp - AIFF Sound Compressed (audio/x-aiff)"
.aiff Raw 'SCPL' 'AIFF' "SoundApp - AIFF Sound (audio/x-aiff)"
.al Raw 'SCPL' 'ALAW' "SoundApp - ALAW Sound"
.ani Raw 'GKON' 'ANIi' "GraphicConverter - Animated NeoChrome"
.apd Ascii 'ALD3' 'TEXT' "Aldus PageMaker - Aldus Printer Description"
.arc Raw 'SITx' 'mArc' "ArcMac - PC ARChive"
.arj Raw 'DArj' 'BINA' "DeArj - ARJ Archive"
.arr Raw 'GKON' 'ARR ' "GraphicConverter - Amber ARR image"
.art Raw 'GKON' 'ART ' "GraphicConverter - First Publisher"
.ascii Ascii 'ttxt' 'TEXT' "SimpleText - ASCII Text (text/plain)"
.asc Ascii 'ttxt' 'TEXT' "SimpleText - ASCII Text (text/plain)"
.asf Ascii 'Ms01' 'ASF_' "Netshow Player (video/x-ms-asf)"
.asm Ascii 'ttxt' 'TEXT' "SimpleText - Assembly Source"
.asx Ascii 'Ms01' 'ASX_' "Netshow Player (video/x-ms-asf)"
.a Ascii 'ttxt' 'TEXT' "SimpleText - Assembly Source"
.au Raw 'TVOD' 'ULAW' "QuickTime Player - Sun Sound (audio/basic)"
.avi Raw 'TVOD' 'VfW ' "QuickTime Player - AVI Movie (video/avi)"
.bar Raw 'S691' 'BARF' "SunTar - Unix BAR Archive"
.bas Ascii 'ttxt' 'TEXT' "SimpleText - BASIC Source"
.bat Ascii 'ttxt' 'TEXT' "SimpleText - MS-DOS Batch File"
.bga Raw 'ogle' 'BMPp' "PictureViewer - OS/2 Bitmap"
.bib Ascii 'ttxt' 'TEXT' "SimpleText - BibTex Bibliography"
.binary Raw 'hDmp' 'BINA' "HexEdit - Untyped Binary Data (application/octet-stream)"
.bin Raw 'SITx' 'SIT!' "StuffIt Expander - MacBinary (application/macbinary)"
.bld Raw 'GKON' 'BLD ' "GraphicConverter - BLD"
.bmp Raw 'ogle' 'BMPp' "PictureViewer - Windows Bitmap"
.boo Ascii 'ttxt' 'TEXT' "SimpleText - BOO encoded"
.bst Ascii 'ttxt' 'TEXT' "SimpleText - BibTex Style"
.bw Raw 'GKON' 'SGI ' "GraphicConverter - SGI Image"
.cel Raw 'GKON' 'CEL ' "GraphicConverter - KISS CEL"
.cgm Raw 'GKON' 'CGMm' "GraphicConverter - Computer Graphics Meta"
.class Raw 'CWIE' 'Clss' "CodeWarrior - Java Class File"
.clp Raw 'GKON' 'CLPp' "GraphicConverter - Windows Clipboard"
.cmd Ascii 'ttxt' 'TEXT' "SimpleText - OS/2 Batch File"
.com Raw 'SWIN' 'PCFA' "SoftWindows - MS-DOS Executable"
.cpp Ascii 'CWIE' 'TEXT' "CodeWarrior - C++ Source"
.cp Ascii 'CWIE' 'TEXT' "CodeWarrior - C++ Source"
.cpt Raw 'SITx' 'PACT' "StuffIt Expander - Compact Pro Archive"
.csv Ascii 'XCEL' 'TEXT' "Excel - Comma Separated Vars"
.ct Raw 'GKON' '..CT' "GraphicConverter - Scitex-CT"
.c Ascii 'CWIE' 'TEXT' "CodeWarrior - C Source"
.cur Raw 'GKON' 'CUR ' "GraphicConverter - Windows Cursor"
.cut Raw 'GKON' 'Halo' "GraphicConverter - Dr Halo Image"
.cvs Raw 'DAD2' 'drw2' "Canvas - Canvas Drawing"
.cwj Raw 'cwkj' 'CWSS' "ClarisWorks 4.0 - ClarisWorks Document"
.dat Raw 'GKON' 'TCLl' "GraphicConverter - TCL image"
.dbf Raw 'FOX+' 'COMP' "FoxBase+ - DBase Document"
.dc42 Raw 'dCpy' 'dImg' "DiskCopy - Floppy Disk image"
.dcx Raw 'GKON' 'DCXx' "GraphicConverter - Some PCX Images"
.dif Ascii 'XCEL' 'TEXT' "Excel - Data Interchange Format"
.diz Ascii 'R*Ch' 'TEXT' "BBEdit - BBS Descriptive Text"
.dl Raw 'AnVw' 'DL ' "MacAnim Viewer - DL Animation"
.dll Raw 'SWIN' 'PCFL' "SoftWindows - Windows DLL"
.doc Raw 'MSWD' 'WDBN' "Microsoft Word - Word Document (application/msword)"
.dot Raw 'MSWD' 'sDBN' "Microsoft Word - Word for Windows Template"
.dsk Raw 'dCpy' 'dImg' "Disk Copy - Apple DiskCopy Image"
.dvi Raw 'xdvi' 'ODVI' "xdvi - TeX DVI Document (application/x-dvi)"
.dwt Ascii 'DmWr' 'TEXT' "Dreamweaver - Dreamweaver Template"
.dxf Ascii 'SWVL' 'TEXT' "Swivel Pro - AutoCAD 3D Data"
.eps Raw 'vgrd' 'EPSF' "LaserWriter 8 - Postscript (application/postscript)"
.epsf Raw 'vgrd' 'EPSF' "LaserWriter 8 - Postscript (application/postscript)"
.etx Ascii 'ezVu' 'TEXT' "Easy View - SEText (text/x-setext)"
.evy Raw 'ENVY' 'EVYD' "Envoy - Envoy Document"
.exe Raw 'SWIN' 'PCFA' "SoftWindows - MS-DOS Executable"
.faq Ascii 'ttxt' 'TEXT' "SimpleText - ASCII Text (text/x-usenet-faq)"
.fit Raw 'GKON' 'FITS' "GraphicConverter - Flexible Image Transport (image/x-fits)"
.fla Raw 'MFL2' 'SPA ' "Macromedia Flash - Flash source"
.flc Raw 'TVOD' 'FLI ' "QuickTime Player - FLIC Animation"
.fli Raw 'TVOD' 'FLI ' "QuickTime Player - FLI Animation"
.fm Raw 'FMPR' 'FMPR' "FileMaker Pro - FileMaker Pro Database"
.for Ascii 'MPS ' 'TEXT' "MPW Shell - Fortran Source"
.fts Raw 'GKON' 'FITS' "GraphicConverter - Flexible Image Transport"
.gem Raw 'GKON' 'GEM-' "GraphicConverter - GEM Metafile"
.gif Raw 'ogle' 'GIFf' "PictureViewer - GIF Picture (image/gif)"
.gl Raw 'AnVw' 'GL ' "MacAnim Viewer - GL Animation"
.grp Raw 'GKON' 'GRPp' "GraphicConverter - GRP Image"
.gz Raw 'SITx' 'SIT!' "StuffIt Expander - Gnu ZIP Archive (application/x-gzip)"
.hcom Raw 'SCPL' 'FSSD' "SoundApp - SoundEdit Sound ex SOX"
.hpgl Raw 'GKON' 'HPGL' "GraphicConverter - HP GL/2"
.hpp Ascii 'CWIE' 'TEXT' "CodeWarrior - C Include File"
.hp Ascii 'CWIE' 'TEXT' "CodeWarrior - C Include File"
.hqx Ascii 'SITx' 'TEXT' "StuffIt Expander - BinHex (application/mac-binhex40)"
.hr Raw 'GKON' 'TR80' "GraphicConverter - TSR-80 HR
.h Ascii 'CWIE' 'TEXT' "CodeWarrior - C Include File"
.html Ascii 'MOSS' 'TEXT' "Netscape Communicator - HyperText (text/html)"
.htm Ascii 'MOSS' 'TEXT' "Netscape Communicator - HyperText (text/html)"
.i3 Ascii 'R*ch' 'TEXT' "BBEdit - Modula 3 Interface"
.ic1 Raw 'GKON' 'IMAG' "GraphicConverter - Atari Image"
.ic2 Raw 'GKON' 'IMAG' "GraphicConverter - Atari Image"
.ic3 Raw 'GKON' 'IMAG' "GraphicConverter - Atari Image"
.icn Raw 'GKON' 'ICO ' "GraphicConverter - Windows Icon"
.ico Raw 'GKON' 'ICO ' "GraphicConverter - Windows Icon"
.ief Raw 'GKON' 'IEF ' "GraphicConverter - IEF image (image/ief)"
.iff Raw 'GKON' 'ILBM' "GraphicConverter - Amiga IFF Image"
.ilbm Raw 'GKON' 'ILBM' "GraphicConverter - Amiga ILBM Image"
.image Raw 'dCpy' 'dImg' "DiskCopy - Apple DiskCopy Image"
.img Raw 'dCpy' 'dImg' "DiskCopy - Apple DiskCopy Image"
.ini Ascii 'ttxt' 'TEXT' "SimpleText - Windows INI File"
.iso Raw 'ddsk' 'rodh' "Disk Copy - Apple ISO Image"
.iss Raw 'GKON' 'ISS ' "GraphicConverter - ISS"
.java Ascii 'CWIE' 'TEXT' "CodeWarrior - Java Source File"
.jfif Raw 'ogle' 'JFIF' "PictureViewer - JFIF Image"
.jif Raw 'GKON' 'JIFf' "GraphicConverter - JIF99a"
.jpeg Raw 'ogle' 'JPEG' "PictureViewer - JPEG Picture (image/jpeg)"
.jpe Raw 'ogle' 'JPEG' "PictureViewer - JPEG Picture (image/jpeg)"
.jpg Raw 'ogle' 'JPEG' "PictureViewer - JPEG Picture (image/jpeg)"
.latex Ascii 'OTEX' 'TEXT' "OzTex - Latex (application/x-latex)"
.lbm Raw 'GKON' 'ILBM' "GraphicConverter - Amiga IFF Image"
.lha Raw 'SITx' 'LHA ' "StuffIt Expander - LHArc Archive"
.lwf Raw 'GKON' 'lwfF' "GraphicConverter - LuraWave(LWF)"
.lzh Raw 'SITx' 'LHA ' "StuffIt Expander - LHArc Archive"
.m1a Raw 'TVOD' 'MPEG' "MoviePlayer - MPEG-1 audiostream (audio/x-mpeg)"
.m1s Raw 'TVOD' 'MPEG' "MoviePlayer - MPEG-1 systemstream"
.m1v Raw 'TVOD' 'M1V ' "MoviePlayer - MPEG-1 IPB videostream (video/mpeg)"
.m2 Ascii 'R*ch' 'TEXT' "BBEdit - Modula 2 Source"
.m2v Raw 'MPG2' 'MPG2' "MPEG2decoder - MPEG-2 IPB videostream"
.m3 Ascii 'R*ch' 'TEXT' "BBEdit - Modula 3 Source"
.mac Raw 'ogle' 'PICT' "PictureViewer - PICT Picture (image/x-pict)"
.mak Ascii 'R*ch' 'TEXT' "BBEdit - Makefile"
.mbm Raw 'GKON' 'MBM ' "GraphicConverter - PSION 5(MBM)"
.mcw Raw 'MSWD' 'WDBN' "Microsoft Word - Mac Word Document"
.med Raw 'SCPL' 'STrk' "SoundApp - Amiga MED Sound"
.me Ascii 'ttxt' 'TEXT' "SimpleText - Text Readme"
.mf Ascii '*MF*' 'TEXT' "Metafont - Metafont"
.midi Raw 'TVOD' 'Midi' "MoviePlayer - MIDI Music"
.mid Raw 'TVOD' 'Midi' "MoviePlayer - MIDI Music"
.mif Ascii 'Fram' 'TEXT' "FrameMaker - FrameMaker MIF (application/x-framemaker)"
.mime Ascii 'SITx' 'TEXT' "StuffIt Expander - MIME Message (message/rfc822)"
.ml Ascii 'R*ch' 'TEXT' "BBEdit - ML Source"
.mod Raw 'SCPL' 'STrk' "SoundApp - MOD Music"
.mol Ascii 'RSML' 'TEXT' "RasMac - MDL Molfile"
.moov Raw 'TVOD' 'MooV' "MoviePlayer - QuickTime Movie (video/quicktime)"
.mov Raw 'TVOD' 'MooV' "MoviePlayer - QuickTime Movie (video/quicktime)"
.mp2 Raw 'TVOD' 'MPEG' "MoviePlayer - MPEG-1 audiostream (audio/x-mpeg)"
.mp3 Raw 'TVOD' 'MPG3' "MoviePlayer - MPEG-3 audiostream (audio/x-mpeg)"
.mpa Raw 'TVOD' 'MPEG' "MoviePlayer - MPEG-1 audiostream (audio/x-mpeg)"
.mpeg Raw 'TVOD' 'MPEG' "MoviePlayer - MPEG Movie of some sort (video/mpeg)"
.mpe Raw 'TVOD' 'MPEG' "MoviePlayer - MPEG Movie of some sort (video/mpeg)"
.mpg Raw 'TVOD' 'MPEG' "MoviePlayer - MPEG Movie of some sort (video/mpeg)"
.msp Raw 'GKON' 'MSPp' "GraphicConverter - Microsoft Paint"
.mtm Raw 'SNPL' 'MTM ' "PlayerPro - MultiMOD Music"
.mwii Raw 'MWII' 'MW2D' "MacWrite II - MacWrite Document (application/macwriteii)"
.mw Raw 'MWII' 'MW2D' "MacWrite II - MacWrite Document (application/macwriteii)"
.neo Raw 'GKON' 'NeoC' "GraphicConverter - Atari NeoChrome"
.nfo Ascii 'ttxt' 'TEXT' "SimpleText - Info Text (application/text)"
.ngg Raw 'GKON' 'NGGC' "GraphicConverter - Mobile Phone (Nokia) Format"
.nol Raw 'GKON' 'NOL ' "GraphicConverter - Phone (Nokia) Format"
.nst Raw 'SCPL' 'STrk' "SoundApp - MOD Music"
.obj Raw 'SWIN' 'PCFL' "SoftWindows - Object (DOS/Windows)"
.oda Raw 'ODA ' 'ODIF' "MacODA XTND Translator - ODA Document (application/oda)"
.okt Raw 'SCPL' 'OKTA' "SoundApp - Oktalyser MOD Music"
.out Raw 'hDmp' 'BINA' "HexEdit - Output File"
.ovl Raw 'SWIN' 'PCFL' "SoftWindows - Overlay (DOS/Windows)"
.pac Raw 'GKON' 'STAD' "GraphicConverter - Atari STAD Image"
.pal Raw '8BIM' '8BCT' "GraphicConverter - Color Table"
.pas Ascii 'CWIE' 'TEXT' "CodeWarrior - Pascal Source"
.pbm Raw 'GKON' 'PPGM' "GraphicConverter - Portable Bitmap (image/x-portable-bitmap)"
.pc1 Raw 'GKON' 'Dega' "GraphicConverter - Atari Degas Image"
.pc2 Raw 'GKON' 'Dega' "GraphicConverter - Atari Degas Image"
.pc3 Raw 'GKON' 'Dega' "GraphicConverter - Atari Degas Image"
.pcs Raw 'GKON' 'PICS' "GraphicConverter - Animated PICTs"
.pct Raw 'ogle' 'PICT' "PictureViewer - PICT Picture (image/x-pict)"
.pcx Raw 'GKON' 'PCXx' "GraphicConverter - PC PaintBrush"
.pdb Ascii 'RSML' 'TEXT' "RasMac - Brookhaven PDB file"
.pdf Raw 'CARO' 'PDF ' "Acrobat Reader - Portable Document Format (application/pdf)"
.pdx Ascii 'ALD5' 'TEXT' "PageMaker - Printer Description"
.pf Raw 'SITx' 'CSIT' "StuffIt Expander - Private File"
.pgc Raw 'GKON' 'PGCF' "GraphicConverter - PGC/PGF Atari Portfolio PCG"
.pgm Raw 'GKON' 'PPGM' "GraphicConverter - Portable Graymap (image/x-portable-graymap)"
.pi1 Raw 'GKON' 'Dega' "GraphicConverter - Atari Degas Image"
.pi2 Raw 'GKON' 'Dega' "GraphicConverter - Atari Degas Image"
.pi3 Raw 'GKON' 'Dega' "GraphicConverter - Atari Degas Image"
.pic Raw 'ogle' 'PICT' "PictureViewer - PICT Picture (image/x-pict)"
.pics Raw 'GKON' 'PICS' "GraphicConverter - PICS-PICT Sequence"
.pict Raw 'ogle' 'PICT' "PictureViewer - PICT Picture (image/x-macpict)"
.pit Raw 'SITx' 'PIT ' "StuffIt Expander - PackIt Archive"
.pkg Raw 'SITx' 'HBSF' "StuffIt Expander - AppleLink Package"
.pl Ascii 'McPL' 'TEXT' "MacPerl - Perl Source"
.plt Raw 'GKON' 'HPGL' "GraphicConverter - HP GL/2"
.pm3 Raw 'ALD3' 'ALB3' "PageMaker - PageMaker 3 Document"
.pm4 Raw 'ALD4' 'ALB4' "PageMaker - PageMaker 4 Document"
.pm5 Raw 'ALD5' 'ALB5' "PageMaker - PageMaker 5 Document"
.pm Raw 'GKON' 'PMpm' "GraphicConverter - Bitmap from xv"
.png Raw 'ogle' 'PNG ' "PictureViewer - Portable Network Graphic"
.pntg Raw 'ogle' 'PNTG' "PictureViewer - Macintosh Painting"
.ppd Ascii 'ALD5' 'TEXT' "PageMaker - Printer Description"
.ppm Raw 'GKON' 'PPGM' "GraphicConverter - Portable Pixmap (image/x-portable-pixmap)"
.prn Ascii 'R*ch' 'TEXT' "BBEdit - Printer Output File"
.psd Raw '8BIM' '8BPS' "Photoshop - PhotoShop Document"
.ps Ascii 'vgrd' 'TEXT' "LaserWriter 8 - PostScript (application/postscript)"
.pt4 Raw 'ALD4' 'ALT4' "PageMaker - PageMaker 4 Template"
.pt5 Raw 'ALD5' 'ALT5' "PageMaker - PageMaker 5 Template"
.p Ascii 'CWIE' 'TEXT' "CodeWarrior - Pascal Source"
.pxr Raw '8BIM' 'PXR ' "Photoshop - Pixar Image"
.qdv Raw 'GKON' 'QDVf' "GraphicConverter - QDV image"
.qt Raw 'TVOD' 'MooV' "MoviePlayer - QuickTime Movie (video/quicktime)"
.qxd Raw 'XPR3' 'XDOC' "QuarkXpress - QuarkXpress Document"
.qxt Raw 'XPR3' 'XTMP' "QuarkXpress - QuarkXpress Template"
.raw Raw 'ddsk' 'rodh' "Disk Copy - Apple raw disk Image"
.readme Ascii 'ttxt' 'TEXT' "SimpleText - Text Readme (application/text)"
.rgba Raw 'GKON' 'SGI ' "GraphicConverter - SGI Image (image/x-rgb)"
.rgb Raw 'GKON' 'SGI ' "GraphicConverter - SGI Image (image/x-rgb)"
.rib Ascii 'RINI' 'TEXT' "Renderman - Renderman 3D Data"
.rif Raw 'GKON' 'RIFF' "GraphicConverter - RIFF Graphic"
.rle Raw 'GKON' 'RLE ' "GraphicConverter - RLE image"
.rme Ascii 'ttxt' 'TEXT' "SimpleText - Text Readme"
.rpl Raw 'REP!' 'FRL!' "Replica - Replica Document"
.rsc Raw 'RSED' 'rsrc' "ResEdit - Resource File"
.rsrc Raw 'RSED' 'rsrc' "ResEdit - Resource File"
.rtf Ascii 'MSWD' 'TEXT' "Microsoft Word - Rich Text Format (application/rtf)"
.rtx Ascii 'R*ch' 'TEXT' "BBEdit - Rich Text (text/richtext)"
.s3m Raw 'SNPL' 'S3M ' "PlayerPro - ScreamTracker 3 MOD"
.scc Raw 'GKON' 'MSX ' "GraphicConverter - MSX pitcure"
.scg Raw 'GKON' 'RIX3' "GraphicConverter - ColoRIX"
.sci Raw 'GKON' 'RIX3' "GraphicConverter - ColoRIX"
.scp Raw 'GKON' 'RIX3' "GraphicConverter - ColoRIX"
.scr Raw 'GKON' 'RIX3' "GraphicConverter - ColoRIX"
.scu Raw 'GKON' 'RIX3' "GraphicConverter - ColoRIX"
.sea Raw '????' 'APPL' "Self Extracting Archive - Self-Extracting Archive"
.sf Raw 'SDHK' 'IRCM' "SoundHack - IRCAM Sound"
.sgi Raw 'ogle' 'SGI ' "PictureViewer - SGI Image"
.shar Ascii 'UnSh' 'TEXT' "UnShar - Unix Shell Archive (application/x-shar)"
.sha Ascii 'UnSh' 'TEXT' "UnShar - Unix Shell Archive (application/x-shar)"
.shp Raw 'GKON' 'SHPp' "GraphicConverter - Printmaster Icon Library"
.sithqx Ascii 'SITx' 'TEXT' "StuffIt Expander - BinHexed StuffIt Archive (application/mac-binhex40)"
.sit Raw 'SITx' 'SIT!' "StuffIt Expander - StuffIt 1.5.1 Archive (application/x-stuffit)"
.six Raw 'GKON' 'SIXE' "GraphicConverter - SIXEL image"
.slk Ascii 'XCEL' 'TEXT' "Excel - SYLK Spreadsheet"
.snd Raw 'SCPL' 'BINA' "SoundApp - Sound of various types"
.spc Raw 'GKON' 'Spec' "GraphicConverter - Atari Spectrum 512"
.sr Raw 'GKON' 'SUNn' "GraphicConverter - Sun Raster Image"
.sty Ascii '*TEX' 'TEXT' "Textures - TeX Style"
.sun Raw 'GKON' 'SUNn' "GraphicConverter - Sun Raster Image"
.sup Raw 'GKON' 'SCRN' "GraphicConverter - StartupScreen"
.svx Raw 'SCPL' '8SVX' "SoundApp - Amiga IFF Sound"
.swf Raw 'SWF2' 'SWFL' "Macromedia Flash - Flash"
.syk Ascii 'XCEL' 'TEXT' "Excel - SYLK Spreadsheet"
.sylk Ascii 'XCEL' 'TEXT' "Excel - SYLK Spreadsheet"
.targa Raw 'GKON' 'TPIC' "GraphicConverter - Truevision Image"
.tar Raw 'SITx' 'TARF' "StuffIt Expander - Unix Tape ARchive (application/x-tar)"
.taz Raw 'SITx' 'ZIVU' "StuffIt Expander - Compressed Tape ARchive (application/x-compress)"
.texinfo Ascii 'OTEX' 'TEXT' "OzTeX - TeX Document (application/x-texinfo)"
.texi Ascii 'OTEX' 'TEXT' "OzTeX - TeX Document"
.tex Ascii 'OTEX' 'TEXT' "OzTeX - TeX Document (application/x-tex)"
.text Ascii 'ttxt' 'TEXT' "SimpleText - ASCII Text (text/plain)"
.tga Raw 'GKON' 'TPIC' "GraphicConverter - Truevision Image"
.tgz Raw 'SITx' 'Gzip' "StuffIt Expander - Gnu ZIPed Tape ARchive (application/x-gzip)"
.tiff Raw 'ogle' 'TIFF' "PictureViewer - TIFF Picture (image/tiff)"
.tif Raw 'ogle' 'TIFF' "PictureViewer - TIFF Picture (image/tiff)"
.tny Raw 'GKON' 'TINY' "GraphicConverter - Atari TINY Bitmap"
.tsv Ascii 'XCEL' 'TEXT' "Excel - Tab Separated Values (text/tab-separated-values)"
.tx8 Ascii 'ttxt' 'TEXT' "SimpleText - 8-bit ASCII Text"
.txt Ascii 'ttxt' 'TEXT' "SimpleText - ASCII Text (text/plain)"
.ul Raw 'TVOD' 'ULAW' "MoviePlayer - Mu-Law Sound (audio/basic)"
.url Raw 'Arch' 'AURL' "Anarchie - URL Bookmark (message/external-body)"
.uue Ascii 'SITx' 'TEXT' "StuffIt Expander - UUEncode"
.uu Ascii 'SITx' 'TEXT' "StuffIt Expander - UUEncode"
.vff Raw 'GKON' 'VFFf' "GraphicConverter - DESR VFF Greyscale Image"
.vga Raw 'ogle' 'BMPp' "PictureViewer - OS/2 Bitmap"
.voc Raw 'SCPL' 'VOC ' "SoundApp - VOC Sound"
.vpb Raw 'GKON' 'VPB ' "GraphicConverter - VPB QUANTEL"
.w51 Raw 'WPC2' '.WP5' "WordPerfect - WordPerfect PC 5.1 Doc (application/wordperfect5.1)"
.wav Raw 'TVOD' 'WAVE' "MoviePlayer - Windows WAV Sound (audio/x-wav)"
.wbmp Raw 'GKON' 'WBMP' "GraphicConverter - WBMP"
.wk1 Raw 'XCEL' 'XLBN' "Excel - Lotus Spreadsheet r2.1"
.wks Raw 'XCEL' 'XLBN' "Excel - Lotus Spreadsheet r1.x"
.wmf Raw 'GKON' 'WMF ' "GraphicConverter - Windows Metafile"
.wp4 Raw 'WPC2' '.WP4' "WordPerfect - WordPerfect PC 4.2 Doc"
.wp5 Raw 'WPC2' '.WP5' "WordPerfect - WordPerfect PC 5.x Doc (application/wordperfect5.1)"
.wp6 Raw 'WPC2' '.WP6' "WordPerfect - WordPerfect PC 6.x Doc"
.wpg Raw 'GKON' 'WPGf' "GraphicConverter - WordPerfect Graphic"
.wpm Raw 'WPC2' 'WPD1' "WordPerfect - WordPerfect Mac"
.wp Raw 'WPC2' '.WP5' "WordPerfect - WordPerfect PC 5.x Doc (application/wordperfect5.1)"
.wri Raw 'MSWD' 'WDBN' "Microsoft Word - MS Write/Windows"
.wve Raw 'SCPL' 'BINA' "SoundApp - PSION sound"
.x10 Raw 'GKON' 'XWDd' "GraphicConverter - X-Windows Dump (image/x-xwd)"
.x11 Raw 'GKON' 'XWDd' "GraphicConverter - X-Windows Dump (image/x-xwd)"
.xbm Raw 'GKON' 'XBM ' "GraphicConverter - X-Windows Bitmap (image/x-xbm)"
.x-face Raw 'GKON' 'TEXT' "GraphicConverter - X-Face"
.xlc Raw 'XCEL' 'XLC ' "Excel - Excel Chart"
.xlm Raw 'XCEL' 'XLM ' "Excel - Excel Macro"
.xls Raw 'XCEL' 'XLS ' "Excel - Excel Spreadsheet"
.xlw Raw 'XCEL' 'XLW ' "Excel - Excel Workspace"
.xl Raw 'XCEL' 'XLS ' "Excel - Excel Spreadsheet"
.xm Raw 'SNPL' 'XM ' "PlayerPro - FastTracker MOD Music"
.xpm Raw 'GKON' 'XPM ' "GraphicConverter - X-Windows Pixmap (image/x-xpm)"
.xpm Raw 'GKON' 'XPM ' "GraphicConverter - X-Windows Pixmap (image/x-xpixmap)"
.xwd Raw 'GKON' 'XWDd' "GraphicConverter - X-Windows Dump (image/x-xwd)"
.zip Raw 'SITx' 'ZIP ' "StuffIt Expander - PC ZIP Archive (application/zip)"
.zoo Raw 'Booz' 'Zoo ' "MacBooz - Zoo Archive"
.z Raw 'SITx' 'ZIVU' "StuffIt Expander - Unix Compress Archive (application/x-compress)"
#
# The default translation for anything not recognized:
#
* Ascii 'ttxt' 'TEXT' "SimpleText - Text file"

View File

@ -58,6 +58,15 @@ div.footer div.theme-change-hint {
margin-bottom: 15px;
}
div.login-status {
text-align: right;
}
div.login-status a {
color: white;
text-decoration: underline;
}
div.logged-in {
background-color: green;
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-external-link"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>

After

Width:  |  Height:  |  Size: 388 B

View File

@ -282,18 +282,6 @@ div.header div.login-form-title {
display: none;
}
div.header div.authentication-disabled span.separator {
display: none;
}
div.header div.authentication-disabled span.wiki-help-text {
display: block;
}
div.header div.authentication-disabled a {
color: #fff;
}
@media (max-width: 900px) {
div.header {
flex-wrap: wrap;
@ -663,10 +651,11 @@ table#attached-devices td.actions {
table#attached-devices td.parameters form {
display: flex;
align-items: center;
}
table#attached-devices td.parameters form label {
display: none;
padding: 0 0.5rem 0 0;
}
table#attached-devices td.parameters form select {
@ -775,6 +764,11 @@ section#files p {
margin-top: 1rem;
}
section#files details.subdir {
padding-left: 1rem;
padding-right: 1rem;
}
section#files details.subdir summary.dirname {
text-decoration: underline;
font-family: monospace;
@ -864,7 +858,7 @@ section#upload a p {
/*
------------------------------------------------------------------------------
Index > Section: Attach peripheral devices
Index > Section: Attach devices
------------------------------------------------------------------------------
*/
section#attach-devices table th:last-child,
@ -876,6 +870,10 @@ section#attach-devices form {
display: block;
}
section#attach-devices table form select.table-dropdown {
width: 16rem;
}
@media (max-width: 900px) {
section#attach-devices table tr th:nth-child(2),
section#attach-devices table tr td:nth-child(2) {
@ -971,6 +969,10 @@ section#services li.disabled {
background: url("icons/cloud-off.svg") no-repeat left center;
}
section#services li.extlink {
background: url("icons/external-link.svg") no-repeat left center;
}
/*
------------------------------------------------------------------------------
Drives page

View File

@ -115,23 +115,25 @@
<ul>
<li>{{ _("If you want to add a service, run the easyinstall.sh script and choose the one to install.") }}</li>
<li>{{ _("In order to manage the services in the Web UI, you may install Webmin as well.") }}</li>
<li>{{ _("To browse the modern web, install a vintage web proxy such as <a href=\"%(url)s\" target=\"_blank\">Macproxy</a>.", url="https://github.com/PiSCSI/piscsi/wiki/Vintage-Web-Proxy#macproxy") }}
</li>
</ul>
</details>
<ul class="service_status">
{% if netatalk_configured %}
<li class="service-item enabled">
{{ _("Mac AFP file sharing is enabled.") }}
{{ _("Mac AFP file sharing is enabled.") }}
{% else %}
<li class="service-item disabled">
{{ _("Mac AFP file sharing is disabled.") }}
{% endif %}
</li>
{% if webmin_configured %}
<li>
<a href="https://{{ env["ip_addr"] }}:10000/netatalk2/" target=\"_blank\">
{{ _("Manage the AFP server") }}
</a>
</li>
<ul>
<li class="service-item extlink">
<a href="https://{{ env["ip_addr"] }}:10000/netatalk2/" target=\"_blank\">{{ _("Manage the AFP server") }}</a>
</li>
</ul>
{% endif %}
{% if samba_configured %}
<li class="service-item enabled">
@ -142,11 +144,11 @@
{% endif %}
</li>
{% if webmin_configured %}
<li>
<a href="https://{{ env["ip_addr"] }}:10000/samba/" target=\"_blank\">
{{ _("Manage the SMB server") }}
</a>
</li>
<ul>
<li class="service-item extlink">
<a href="https://{{ env["ip_addr"] }}:10000/samba/" target=\"_blank\">{{ _("Manage the SMB server") }}</a>
</li>
</ul>
{% endif %}
{% if ftp_configured %}
<li class="service-item enabled">
@ -156,6 +158,13 @@
{{ _("FTP file sharing is disabled.") }}
{% endif %}
</li>
{% if webmin_configured %}
<ul>
<li class="service-item extlink">
<a href="https://{{ env["ip_addr"] }}:10000/vsftpd/" target=\"_blank\">{{ _("Manage the FTP server") }}</a>
</li>
</ul>
{% endif %}
{% if macproxy_configured %}
<li class="service-item enabled">
{{ _("Vintage web proxy is running at %(ip_addr)s (default port 5000)", ip_addr=env['ip_addr']) }}
@ -164,6 +173,11 @@
{{ _("Vintage web proxy is disabled.") }}
{% endif %}
</li>
{% if webmin_configured %}
<li class="service-item extlink">
<a href="https://{{ env["ip_addr"] }}:10000/" target=\"_blank\">{{ _("Manage PiSCSI services & Linux with Webmin") }}</a>
</li>
{% endif %}
</ul>
</section>

View File

@ -27,37 +27,31 @@
<body class="{{ body_classes|join(' ') }}">
<div class="header">
{% if env["auth_active"] %}
{% if env["logged_in"] or not env["auth_active"] %}
<div align="center" class="login-status logged-in">
{% if env["logged_in"] %}
<div align="center" class="login-status logged-in">
<span class="logged-in-as-text">{{ _("Logged in as <em>%(username)s</em>", username=env["username"]) }}</span>
<span class="separator">-</span>
<span class="log-out-button"><a href="/logout">{{ _("Log Out") }}</a></span>
<span class="separator">-</span>
<span class="admin-button"><a href="/sys/admin">{{ _("Settings") }}</a></span>
</div>
{% else %}
<div align="center" class="login-status logged-out">
<form method="POST" action="/login">
<div class="login-form-title">{{ _("Log in to use Web Interface") }}</div>
<span>
<label for="username">{{ _("Username:") }}</label>
<input type="text" name="username" id="username">
</span>
<span>
<label for="password">{{ _("Password:") }}</label>
<input type="password" name="password" id="password">
</span>
<input type="submit" value="Login">
</form>
</div>
<span class="logged-in-as-text">{{ _("Logged in as <em>%(username)s</em>", username=env["username"]) }}</span>
<span class="separator">-</span>
<span class="log-out-button"><a href="/logout">{{ _("Log Out") }}</a></span>
<span class="separator">-</span>
{% endif %}
<span class="admin-button"><a href="/sys/admin">{{ _("Settings") }}</a></span>
</div>
{% else %}
<div align="center" class="login-status authentication-disabled">
<span class="authentication-disabled-text">{{ _("Web Interface Authentication Disabled") }}</span>
<span class="separator">-</span>
<span class="wiki-help-text">{{ _("See <a href=\"%(url)s\" target=\"_blank\">Wiki</a> for more information", url="https://github.com/PiSCSI/piscsi/wiki/Web-Interface#enable-authentication") }}</span>
</div>
<div align="center" class="login-status logged-out">
<form method="POST" action="/login">
<div class="login-form-title">{{ _("Log in to use Web Interface") }}</div>
<span>
<label for="username">{{ _("Username:") }}</label>
<input type="text" name="username" id="username">
</span>
<span>
<label for="password">{{ _("Password:") }}</label>
<input type="password" name="password" id="password">
</span>
<input type="submit" value="Login">
</form>
</div>
{% endif %}
<div align="center" class="title">

View File

@ -24,6 +24,7 @@
</ul>
</details>
{% if env["cfg_dir_exists"] %}
<p>
<form action="/config/action" method="post" id="config-actions">
<label for="config_load_name">{{ _("File Name:") }}</label>
@ -54,6 +55,13 @@
<input type="submit" value="{{ _("Save") }}">
</form>
</p>
{% else %}
<div class="notice">
{{ _("Please create the PiSCSI configuration dir to use configurations:")}} {{ CFG_DIR }}
</div>
{% endif %}
<table id="attached-devices" border="black" cellpadding="3" summary="List of attached devices">
<tbody>
@ -82,13 +90,12 @@
{% endif %}
<td class="name" align="center">{{ device.device_name }}</td>
<td class="parameters">
{% if "No Media" in device.status %}
{% if "No Media" in device.status %}
<form action="/scsi/attach" method="post">
<label for="device_list_file_name_{{ device.id }}_{{ device.unit }}">{{ _("File:") }}</label>
<input name="scsi_id" type="hidden" value="{{ device.id }}">
<input name="unit" type="hidden" value="{{ device.unit }}">
<input name="type" type="hidden" value="{{ device.device_type }}">
<input name="file_size" type="hidden" value="{{ device.size }}">
<label for="device_list_file_name_{{ device.id }}_{{ device.unit }}">{{ _("File name") }}</label>
<select type="select" name="file_name" id="device_list_file_name_{{ device.id }}_{{ device.unit }}">
{% for f in files|sort(attribute='name') %}
{% if device.device_type == "SCCD" %}
@ -106,7 +113,7 @@
{% endif %}
{% endfor %}
</select>
<input type="submit" value="{{ _("Attach") }}">
<input type="submit" value="{{ _("Insert") }}">
</form>
{% else %}
{% if device.params %}
@ -120,7 +127,14 @@
{% endif %}
{% endfor %}
{% elif device.file %}
<span class="filename">{{ device.file }}</span>
<form action="/scsi/eject" method="post" onsubmit="return confirm('{{ _("Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!") }}')">
<label>{{ device.file }}</label>
{% if device.device_type in REMOVABLE_DEVICE_TYPES and "No Media" not in device.status %}
<input name="scsi_id" type="hidden" value="{{ device.id }}">
<input name="unit" type="hidden" value="{{ device.unit }}">
<input type="submit" value="{{ _("Eject") }}">
{% endif %}
</form>
{% endif %}
{% endif %}
</td>
@ -135,13 +149,6 @@
</td>
<td class="actions" align="center">
{% if device.id in scsi_ids["occupied_ids"] %}
{% if device.device_type in REMOVABLE_DEVICE_TYPES and "No Media" not in device.status %}
<form action="/scsi/eject" method="post" onsubmit="return confirm('{{ _("Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!") }}')">
<input name="scsi_id" type="hidden" value="{{ device.id }}">
<input name="unit" type="hidden" value="{{ device.unit }}">
<input type="submit" value="{{ _("Eject") }}">
</form>
{% endif %}
<form action="/scsi/detach" method="post" onsubmit="return confirm('{{ _("Detach Device?") }}')">
<input name="scsi_id" type="hidden" value="{{ device.id }}">
<input name="unit" type="hidden" value="{{ device.unit }}">
@ -209,12 +216,8 @@
</ul>
</details>
{% if not files|length: %}
<div class="notice">
{{ _("The images directory is currently empty.") }}
</div>
{% else %}
{% if env["image_dir_exists"] %}
{% if files|length %}
<div>
{% for subdir, group in formatted_image_files.items() %}
@ -309,7 +312,6 @@
{% else %}
<form action="/scsi/attach" method="post" class="file-attach">
<input name="file_name" type="hidden" value="{{ file['name'] }}">
<input name="file_size" type="hidden" value="{{ file['size'] }}">
<label for="image_list_scsi_id_{{ file["name"] }}">{{ _("ID") }}</label>
<select name="scsi_id" id="image_list_scsi_id_{{ file["name"] }}">
{% for id in scsi_ids["valid_ids"] %}
@ -370,8 +372,170 @@
</details>
{% endfor %}
</div>
{% else %}
<div class="notice">
{{ _("The images directory is currently empty.") }}
</div>
{% endif %}
<p><small>{{ _("%(disk_space)s MiB disk space remaining for images", disk_space=env["free_disk_space"]) }}</small></p>
<p>
<small>{{ _("%(disk_space)s MiB disk space remaining for images", disk_space=env["free_disk_space"]) }}</small>
</p>
{% else %}
<div class="notice">
{{ _("Please create the PiSCSI images dir to work with disk images:")}} {{ env["image_dir"] }}
</div>
{% endif %}
</section>
<hr/>
<section id="attach-devices">
<details>
<summary class="heading">
{{ _("Attach Device") }}
</summary>
<ul>
</li>
{% if bridge_configured %}
<li>{{ _("The <tt>piscsi_bridge</tt> network bridge is active and ready to be used by an emulated network adapter!") }}</li>
{% else %}
<li>{{ _("Please configure the <tt>piscsi_bridge</tt> network bridge before attaching an emulated network adapter!") }}</li>
{% endif %}
<li>{{ _("Read more about <a href=\"%(url)s\" target=\"_blank\">supported device types</a> on the wiki.", url="https://github.com/PiSCSI/piscsi/wiki/Supported-Device-Types") }}
</li>
</ul>
</details>
<table border="black" cellpadding="3" summary="List of peripheral devices">
<tr>
<th scope="col">{{ _("Device") }}</th>
<th scope="col">{{ _("Key") }}</th>
<th scope="col">{{ _("Actions") }}</th>
</tr>
{% for type in device_types.keys() %}
<tr>
<td>
{% if device_types[type]["name"] == type %}
{% if type in REMOVABLE_DEVICE_TYPES %}
<div>{{ _("Unknown Removable Disk Drive") }}</div>
{% elif type in DISK_DEVICE_TYPES %}
<div>{{ _("Unknown Fixed Disk Drive") }}</div>
{% else %}
<div>{{ _("Unknown Device") }}</div>
{% endif %}
{% else %}
<div>{{ device_types[type]["name"] }}</div>
{% endif %}
</td>
<td>
<div>{{ type }}</div>
</td>
<td>
<form action="/scsi/attach" method="post" class="device-attach">
<input name="type" type="hidden" value="{{ type }}">
{% for key, value in device_types[type]["params"] | dictsort %}
<label for="param_{{ type }}_{{ key }}">{{ key }}:</label>
{% if value.isnumeric() %}
<input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="number" value="{{ value }}">
{% elif key == "interface" %}
<select name="param_{{ key }}" id="param_{{ type }}_{{ key }}">
{% for if in netinfo["ifs"] %}
<option value="{{ if }}">
{{ if }}
</option>
{% endfor %}
</select>
{% else %}
<input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="text" size="{{ value|length }}" placeholder="{{ value }}">
{% endif %}
{% endfor %}
{% if type in DISK_DEVICE_TYPES %}
<label for="{{ type }}_drive_name">{{ _("Identify as:") }}</label>
<select name="drive_name" id="{{ type }}_drive_name" class="table-dropdown">
<option value="">
{{ _("Generic device") }}
</option>
{% if type == "SCHD" %}
{% for drive in drive_properties["hd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
{% endif %}
{% if type == "SCCD" %}
{% for drive in drive_properties["cd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
{% endif %}
{% if type == "SCRM" %}
{% for drive in drive_properties["rm_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
{% endif %}
{% if type == "SCMO" %}
{% for drive in drive_properties["mo_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
{% endif %}
</select>
<label for="{{ type }}_image_file_name">{{ _("Image file:") }}</label>
<select name="file_name" id="{{ type }}_image_file_name" class="table-dropdown" {% if type not in REMOVABLE_DEVICE_TYPES %}required{% endif %}>
<option value="" selected {% if type not in REMOVABLE_DEVICE_TYPES %}disabled{% endif %}>
{% if type in REMOVABLE_DEVICE_TYPES %}
{{ _("None") }}
{% else %}
{{ _("Choose a file...") }}
{% endif %}
</option>
{% for f in files|sort(attribute='name') %}
{% if type == "SCHD" %}
{% if f["name"].lower().endswith(env['hd_suffixes']) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %}
{% elif type == "SCCD" %}
{% if f["name"].lower().endswith(env['cd_suffixes']) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %}
{% elif type == "SCRM" %}
{% if f["name"].lower().endswith(env['rm_suffixes']) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %}
{% elif type == "SCMO" %}
{% if f["name"].lower().endswith(env['mo_suffixes']) %}
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %}
{% else %}
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
{% endif %}
{% endfor %}
</select>
{% endif %}
<label for="{{ type }}_scsi_id">{{ _("ID") }}</label>
<select name="scsi_id" id="{{ type }}_scsi_id">
{% for id in scsi_ids["valid_ids"] %}
<option value="{{ id }}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
{{ id }}
</option>
{% endfor %}
</select>
<label for="{{ type }}_unit">{{ _("LUN") }}</label>
<input class="lun" name="unit" id="{{ type }}_unit" type="number" value="0" min="0" max="31" step="1" size="3">
<input type="submit" value="{{ _("Attach") }}" title="{{ _("Attach") }}">
</form>
</td>
</tr>
{% endfor %}
</table>
</section>
<hr/>
@ -432,6 +596,69 @@
<hr/>
<section id="create-image">
<details>
<summary class="heading">
{{ _("Create Empty Disk Image") }}
</summary>
<ul>
<li>{{ _("Please refer to <a href=\"%(url)s\" target=\"_blank\">wiki documentation</a> to learn more about the supported image file types.", url="https://github.com/PiSCSI/piscsi/wiki/Supported-Device-Types#image-types") }}</li>
<li>{{ _("It is not recommended to use the Lido hard disk driver with the Macintosh Plus.") }}</li>
</ul>
</details>
<form action="/files/create" method="post">
<label for="image_create_file_name">{{ _("File Name:") }}</label>
<input name="file_name" id="image_create_file_name" required="" type="text">
<label for="image_create_type">{{ _("Type:") }}</label>
<select name="type" id="image_create_type">
{% for key, value in image_suffixes_to_create.items() %}
<option value="{{ key }}">
{{ value }} [.{{ key }}]
</option>
{% endfor %}
</select>
<label for="image_create_size">{{ _("Size:") }}</label>
<input name="size" id="image_create_size" type="number" placeholder="{{ _("MiB") }}" min="1" max="262144" required>
<label for="image_create_drive_name">{{ _("Identify as:") }}</label>
<select name="drive_name" id="image_create_drive_name">
<option value="">
{{ _("Generic device") }}
</option>
{% for drive in drive_properties["hd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
</select>
<label for="drive_format">{{ _("Format as:") }}</label>
<select name="drive_format" id="drive_format">
<option value="">
{{ _("Unformatted") }}
</option>
<option value="Lido 7.56">
HFS + Lido
</option>
<option value="SpeedTools 3.6">
HFS + SpeedTools
</option>
<option value="FAT16">
FAT16
</option>
<option value="FAT32">
FAT32
</option>
</select>
<input type="submit" value="{{ _("Create") }}">
</form>
</section>
<section id="create-drive">
<a href="/drive/list"><p>{{ _("Create Disk Image With Properties") }}</p></a>
</section>
<hr/>
<section id="create-iso">
<details>
<summary class="heading">
@ -507,166 +734,4 @@
<hr/>
<section id="create-image">
<details>
<summary class="heading">
{{ _("Create Empty Disk Image") }}
</summary>
<ul>
<li>{{ _("Please refer to <a href=\"%(url)s\" target=\"_blank\">wiki documentation</a> to learn more about the supported image file types.", url="https://github.com/PiSCSI/piscsi/wiki/Supported-Device-Types#image-types") }}</li>
<li>{{ _("It is not recommended to use the Lido hard disk driver with the Macintosh Plus.") }}</li>
</ul>
</details>
<form action="/files/create" method="post">
<label for="image_create_file_name">{{ _("File Name:") }}</label>
<input name="file_name" id="image_create_file_name" required="" type="text">
<label for="image_create_type">{{ _("Type:") }}</label>
<select name="type" id="image_create_type">
{% for key, value in image_suffixes_to_create.items() %}
<option value="{{ key }}">
{{ value }} [.{{ key }}]
</option>
{% endfor %}
</select>
<label for="image_create_size">{{ _("Size:") }}</label>
<input name="size" id="image_create_size" type="number" placeholder="{{ _("MiB") }}" min="1" max="262144" required>
<label for="image_create_drive_name">{{ _("Masquerade as:") }}</label>
<select name="drive_name" id="image_create_drive_name">
<option value="">
{{ _("None") }}
</option>
{% for drive in drive_properties["hd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
</select>
<label for="drive_format">{{ _("Format as:") }}</label>
<select name="drive_format" id="drive_format">
<option value="">
{{ _("None") }}
</option>
<option value="Lido 7.56">
HFS + Lido
</option>
<option value="SpeedTools 3.6">
HFS + SpeedTools
</option>
<option value="FAT16">
FAT16
</option>
<option value="FAT32">
FAT32
</option>
</select>
<input type="submit" value="{{ _("Create") }}">
</form>
</section>
<section id="create-drive">
<a href="/drive/list"><p>{{ _("Create Disk Image With Properties") }}</p></a>
</section>
<hr/>
<section id="attach-devices">
<details>
<summary class="heading">
{{ _("Attach Peripheral Device") }}
</summary>
<ul>
</li>
{% if bridge_configured %}
<li>{{ _("The <tt>piscsi_bridge</tt> network bridge is active and ready to be used by an emulated network adapter!") }}</li>
{% else %}
<li>{{ _("Please configure the <tt>piscsi_bridge</tt> network bridge before attaching an emulated network adapter!") }}</li>
{% endif %}
<li>{{ _("To browse the modern web, install a vintage web proxy such as <a href=\"%(url)s\" target=\"_blank\">Macproxy</a>.", url="https://github.com/PiSCSI/piscsi/wiki/Vintage-Web-Proxy#macproxy") }}</li>
</li>
<li>{{ _("Read more about <a href=\"%(url)s\" target=\"_blank\">supported device types</a> on the wiki.", url="https://github.com/PiSCSI/piscsi/wiki/Supported-Device-Types") }}
</li>
</ul>
</details>
<table border="black" cellpadding="3" summary="List of peripheral devices">
<tr>
<th scope="col">{{ _("Device") }}</th>
<th scope="col">{{ _("Key") }}</th>
<th scope="col">{{ _("Parameters and Actions") }}</th>
</tr>
{% for type in REMOVABLE_DEVICE_TYPES + PERIPHERAL_DEVICE_TYPES %}
<tr>
<td>
<div>{{ device_types[type]["name"] }}</div>
</td>
<td>
<div>{{ type }}</div>
</td>
<td>
<form action="/scsi/attach_device" method="post" class="device-attach">
<input name="type" type="hidden" value="{{ type }}">
{% for key, value in device_types[type]["params"] | dictsort %}
<label for="param_{{ type }}_{{ key }}">{{ key }}:</label>
{% if value.isnumeric() %}
<input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="number" value="{{ value }}">
{% elif key == "interface" %}
<select name="param_{{ key }}" id="param_{{ type }}_{{ key }}">
{% for if in netinfo["ifs"] %}
<option value="{{ if }}">
{{ if }}
</option>
{% endfor %}
</select>
{% else %}
<input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="text" size="{{ value|length }}" placeholder="{{ value }}">
{% endif %}
{% endfor %}
{% if type in REMOVABLE_DEVICE_TYPES %}
<label for="{{ type }}_drive_name">{{ _("Masquerade as:") }}</label>
<select name="drive_name" id="{{ type }}_drive_name">
<option value="">
{{ _("None") }}
</option>
{% if type == "SCCD" %}
{% for drive in drive_properties["cd_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
{% endif %}
{% if type == "SCRM" %}
{% for drive in drive_properties["rm_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
{% endif %}
{% if type == "SCMO" %}
{% for drive in drive_properties["mo_conf"] | sort(attribute='name') %}
<option value="{{ drive.name }}">
{{ drive.name }}
</option>
{% endfor %}
{% endif %}
</select>
{% endif %}
<label for="{{ type }}_scsi_id">{{ _("ID") }}</label>
<select name="scsi_id" id="{{ type }}_scsi_id">
{% for id in scsi_ids["valid_ids"] %}
<option value="{{ id }}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
{{ id }}
</option>
{% endfor %}
</select>
<label for="{{ type }}_unit">{{ _("LUN") }}</label>
<input class="lun" name="unit" id="{{ type }}_unit" type="number" value="0" min="0" max="31" step="1" size="3">
<input type="submit" value="{{ _("Attach") }}" title="{{ _("Attach") }}">
</form>
</td>
</tr>
{% endfor %}
</table>
</section>
<hr/>
{% endblock content %}

View File

@ -41,8 +41,8 @@
<script
type="application/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.js"
integrity="sha384-PwiT+fWTPpIySx6DrH1FKraKo+LvVpOClsjx0TSdMYTKi7BR1hR149f4VHLUUnfA"
src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/6.0.0-beta.2/dropzone-min.js"
integrity="sha384-apgrFcysJQwl0SG2LvOoA/UOUUVW0bOoBmKVZOeMZkMCvWmeO+Cnw/r3P4rmbIh9"
crossorigin="anonymous"
></script>
@ -78,6 +78,7 @@
b: "{{ _("B") }}"
}
}
Dropzone.discover()
</script>
<noscript>

View File

@ -1309,7 +1309,7 @@ msgstr ""
#: src/templates/index.html:594
msgid "Key"
msgstr "Taste"
msgstr "Kürzel"
#: src/templates/index.html:595
msgid "Parameters and Actions"

View File

@ -46,7 +46,6 @@ from return_code_mapper import ReturnCodeMapper
from socket_cmds_flask import SocketCmdsFlask
from web_utils import (
working_dirs_exist,
sort_and_format_devices,
get_valid_scsi_ids,
map_device_types_and_names,
@ -125,6 +124,9 @@ def get_env_info():
"image_dir": server_info["image_dir"],
"image_root_dir": Path(server_info["image_dir"]).name,
"shared_root_dir": Path(FILE_SERVER_DIR).name,
"image_dir_exists": Path(server_info["image_dir"]).exists(),
"cfg_dir_exists": Path(CFG_DIR).exists(),
"hd_suffixes": tuple(server_info["schd"]),
"cd_suffixes": tuple(server_info["sccd"]),
"rm_suffixes": tuple(server_info["scrm"]),
"mo_suffixes": tuple(server_info["scmo"]),
@ -219,7 +221,6 @@ def index():
Sets up data structures for and renders the index page
"""
server_info = piscsi_cmd.get_server_info()
working_dirs_exist((server_info["image_dir"], CFG_DIR))
devices = piscsi_cmd.list_devices()
device_types = map_device_types_and_names(piscsi_cmd.get_device_types()["device_types"])
@ -304,9 +305,6 @@ def drive_list():
"""
Sets up the data structures and kicks off the rendering of the drive list page
"""
server_info = piscsi_cmd.get_server_info()
working_dirs_exist((server_info["image_dir"], CFG_DIR))
return response(
template="drives.html",
page_title=_("PiSCSI Create Drive"),
@ -342,7 +340,6 @@ def upload_page():
Sets up the data structures and kicks off the rendering of the file uploading page
"""
server_info = piscsi_cmd.get_server_info()
working_dirs_exist((server_info["image_dir"], CFG_DIR))
return response(
template="upload.html",
@ -544,7 +541,6 @@ def show_diskinfo():
if not safe_path["status"]:
return response(error=True, message=safe_path["msg"])
server_info = piscsi_cmd.get_server_info()
working_dirs_exist((server_info["image_dir"], CFG_DIR))
returncode, diskinfo = sys_cmd.get_diskinfo(Path(server_info["image_dir"]) / file_name)
if returncode == 0:
return response(
@ -647,16 +643,17 @@ def log_level():
return response(error=True, message=process["msg"])
@APP.route("/scsi/attach_device", methods=["POST"])
@APP.route("/scsi/attach", methods=["POST"])
@login_required
def attach_device():
"""
Attaches a peripheral device that doesn't take an image file as argument
Attaches device of any type
"""
scsi_id = request.form.get("scsi_id")
unit = request.form.get("unit")
device_type = request.form.get("type")
drive_name = request.form.get("drive_name")
file_name = request.form.get("file_name")
if not scsi_id:
return response(error=True, message=_("No SCSI ID specified"))
@ -690,11 +687,29 @@ def attach_device():
"device_type": device_type,
"params": params,
}
if file_name:
kwargs["params"]["file"] = file_name
# If drive_props is defined use properies from this dict,
# otherwise fall back to the properties file if it exists
if drive_props:
kwargs["vendor"] = drive_props["vendor"]
kwargs["product"] = drive_props["product"]
kwargs["revision"] = drive_props["revision"]
kwargs["block_size"] = drive_props["block_size"]
else:
drive_properties = Path(CFG_DIR) / f"{file_name}.{PROPERTIES_SUFFIX}"
if drive_properties.is_file():
process = file_cmd.read_drive_properties(drive_properties)
process = ReturnCodeMapper.add_msg(process)
if not process["status"]:
return response(error=True, message=process["msg"])
conf = process["conf"]
kwargs["vendor"] = conf["vendor"]
kwargs["product"] = conf["product"]
kwargs["revision"] = conf["revision"]
kwargs["block_size"] = conf["block_size"]
process = piscsi_cmd.attach_device(scsi_id, **kwargs)
process = ReturnCodeMapper.add_msg(process)
@ -711,70 +726,6 @@ def attach_device():
return response(error=True, message=process["msg"])
@APP.route("/scsi/attach", methods=["POST"])
@login_required
def attach_image():
"""
Attaches a file image as a device
"""
file_name = request.form.get("file_name")
file_size = request.form.get("file_size")
scsi_id = request.form.get("scsi_id")
unit = request.form.get("unit")
device_type = request.form.get("type")
if not scsi_id:
return response(error=True, message=_("No SCSI ID specified"))
if not file_name:
return response(error=True, message=_("No image file to insert"))
kwargs = {"unit": int(unit), "params": {"file": file_name}}
if device_type:
kwargs["device_type"] = device_type
device_types = piscsi_cmd.get_device_types()
expected_block_size = min(device_types["device_types"][device_type]["block_sizes"])
# Attempt to load the device properties file:
# same file name with PROPERTIES_SUFFIX appended
drive_properties = Path(CFG_DIR) / f"{file_name}.{PROPERTIES_SUFFIX}"
if drive_properties.is_file():
process = file_cmd.read_drive_properties(drive_properties)
process = ReturnCodeMapper.add_msg(process)
if not process["status"]:
return response(error=True, message=process["msg"])
conf = process["conf"]
kwargs["vendor"] = conf["vendor"]
kwargs["product"] = conf["product"]
kwargs["revision"] = conf["revision"]
kwargs["block_size"] = conf["block_size"]
expected_block_size = conf["block_size"]
process = piscsi_cmd.attach_device(scsi_id, **kwargs)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
if int(file_size) % int(expected_block_size):
logging.warning(
"The image file size %s bytes is not a multiple of %s. "
"PiSCSI will ignore the trailing data. "
"The image may be corrupted, so proceed with caution.",
file_size,
expected_block_size,
)
return response(
message=_(
"Attached %(file_name)s as %(device_type)s to "
"SCSI ID %(id_number)s LUN %(unit_number)s",
file_name=file_name,
device_type=get_device_name(device_type),
id_number=scsi_id,
unit_number=unit,
)
)
return response(error=True, message=process["msg"])
@APP.route("/scsi/detach_all", methods=["POST"])
@login_required
def detach_all_devices():
@ -940,7 +891,27 @@ def download_to_iso():
local_file = request.form.get("file")
if iso_type == "HFS":
iso_args = ["-hfs"]
# The file genisoimage_hfs_resource_fork_map.txt is part of the piscsi
# repository tree, so it should be present in the parent folder; trust but verify:
genisoimage_hfs_resource_fork_map_file_path = Path(
f"{WEB_DIR}/../genisoimage_hfs_resource_fork_map.txt"
)
if genisoimage_hfs_resource_fork_map_file_path.exists():
# genisoimage will look up the file extension in this map file to
# derive the file's CREATOR and TYPE resource fork attributes.
# See more at https://linux.die.net/man/1/genisoimage
iso_args = ["-hfs", "-map", str(genisoimage_hfs_resource_fork_map_file_path)]
logging.info(
"Found and using the genisoimage hfs map file at %s",
str(genisoimage_hfs_resource_fork_map_file_path),
)
else:
logging.warning(
"The genisoimage hfs map file is not present at %s. "
"Will not set resource fork attributes of files in the iso image!",
str(genisoimage_hfs_resource_fork_map_file_path),
)
iso_args = ["-hfs"]
elif iso_type == "ISO-9660 Level 1":
iso_args = ["-iso-level", "1"]
elif iso_type == "ISO-9660 Level 2":

View File

@ -8,26 +8,13 @@ from pathlib import Path
from ua_parser import user_agent_parser
from re import findall
from flask import request, abort
from flask import request
from flask_babel import _
from werkzeug.utils import secure_filename
from piscsi.sys_cmds import SysCmds
def working_dirs_exist(working_dirs):
"""
Method for validating that working dirs exist.
Takes (tuple) of (str) working_dirs with paths to required dirs.
"""
for dir_path in working_dirs:
if not Path(dir_path).exists():
abort(
503,
_(f"Please create directory: {dir_path}"),
)
def get_valid_scsi_ids(devices, reserved_ids):
"""
Takes a list of (dict)s devices, and list of (int)s reserved_ids.

View File

@ -36,6 +36,12 @@ if [ $ERROR = 1 ] ; then
exit 1
fi
# Force rebuild the venv if RESET_VENV is set to any non-empty value
if [[ "$RESET_VENV" ]]; then
echo "Force-removing old venv"
sudo rm -rf venv
fi
# Test for two known broken venv states
if test -e venv; then
GOOD_VENV=true

View File

@ -8,6 +8,41 @@ FILE_SIZE_1_MIB = 1048576
STATUS_SUCCESS = "success"
STATUS_ERROR = "error"
ENV_ENDPOINT = "/env"
HEALTHCHECK_ENDPOINT = "/healthcheck"
PWA_FAVICON_ENDPOINT = "/pwa/favicon.ico"
LOGIN_ENDPOINT = "/login"
LOGOUT_ENDPOINT = "/logout"
ATTACH_ENDPOINT = "/scsi/attach"
DETACH_ENDPOINT = "/scsi/detach"
DETACH_ALL_ENDPOINT = "/scsi/detach_all"
EJECT_ENDPOINT = "/scsi/eject"
RESERVE_ENDPOINT = "/scsi/reserve"
RELEASE_ENDPOINT = "/scsi/release"
INFO_ENDPOINT = "/scsi/info"
CREATE_ENDPOINT = "/files/create"
RENAME_ENDPOINT = "/files/rename"
COPY_ENDPOINT = "/files/copy"
DELETE_ENDPOINT = "/files/delete"
DOWNLOAD_URL_ENDPOINT = "/files/download_url"
DOWNLOAD_IMAGE_ENDPOINT = "/files/download_image"
DOWNLOAD_CONFIG_ENDPOINT = "/files/download_config"
EXTRACT_IMAGE_ENDPOINT = "/files/extract_image"
UPLOAD_ENDPOINT = "/files/upload"
CREATE_ISO_ENDPOINT = "/files/create_iso"
DISKINFO_ENDPOINT = "/files/diskinfo"
DRIVE_LIST_ENDPOINT = "/drive/list"
DRIVE_CREATE_ENDPOINT = "/drive/create"
DRIVE_CDROM_ENDPOINT = "/drive/cdrom"
MANPAGE_ENDPOINT = "/sys/manpage?app=piscsi"
LANGUAGE_ENDPOINT = "/language"
LOG_LEVEL_ENDPOINT = "/logs/level"
LOG_SHOW_ENDPOINT = "/logs/show"
CONFIG_SAVE_ENDPOINT = "/config/save"
CONFIG_ACTION_ENDPOINT = "/config/action"
THEME_ENDPOINT = "/theme"
SYS_RENAME_ENDPOINT = "/sys/rename"
@pytest.fixture(scope="function")
def create_test_image(request, http_client):
@ -18,7 +53,7 @@ def create_test_image(request, http_client):
file_name = f"{file_prefix}.{image_type}"
response = http_client.post(
"/files/create",
CREATE_ENDPOINT,
data={
"file_name": file_prefix,
"type": image_type,
@ -42,7 +77,7 @@ def create_test_image(request, http_client):
def delete():
for image in images:
response = http_client.post("/files/delete", data={"file_name": image["file_name"]})
response = http_client.post(DELETE_ENDPOINT, data={"file_name": image["file_name"]})
if response.status_code != 200 or response.json()["status"] != STATUS_SUCCESS:
warnings.warn(
f"Failed to auto-delete file created with create_test_image fixture: {image}"
@ -71,7 +106,7 @@ def list_attached_images(http_client):
@pytest.fixture(scope="function")
def delete_file(http_client):
def delete(file_name):
response = http_client.post("/files/delete", data={"file_name": file_name})
response = http_client.post(DELETE_ENDPOINT, data={"file_name": file_name})
if response.status_code != 200 or response.json()["status"] != STATUS_SUCCESS:
warnings.warn(f"Failed to delete file via delete_file fixture: {file_name}")
@ -81,7 +116,7 @@ def delete_file(http_client):
@pytest.fixture(scope="function")
def detach_devices(http_client):
def detach():
response = http_client.post("/scsi/detach_all")
response = http_client.post(DETACH_ALL_ENDPOINT)
if response.json()["status"] == STATUS_SUCCESS:
return True
raise Exception("Failed to detach SCSI devices")

View File

@ -1,11 +1,10 @@
from conftest import STATUS_SUCCESS, STATUS_ERROR
from conftest import STATUS_SUCCESS, STATUS_ERROR, LOGIN_ENDPOINT, LOGOUT_ENDPOINT
# route("/login", methods=["POST"])
def test_login_with_valid_credentials(pytestconfig, http_client_unauthenticated):
# Note: This test depends on the piscsi group existing and 'username' a member the group
response = http_client_unauthenticated.post(
"/login",
LOGIN_ENDPOINT,
data={
"username": pytestconfig.getoption("piscsi_username"),
"password": pytestconfig.getoption("piscsi_password"),
@ -19,10 +18,9 @@ def test_login_with_valid_credentials(pytestconfig, http_client_unauthenticated)
assert "env" in response_data["data"]
# route("/login", methods=["POST"])
def test_login_with_invalid_credentials(http_client_unauthenticated):
response = http_client_unauthenticated.post(
"/login",
LOGIN_ENDPOINT,
data={
"username": "__INVALID_USER__",
"password": "__INVALID_PASS__",
@ -38,7 +36,6 @@ def test_login_with_invalid_credentials(http_client_unauthenticated):
)
# route("/logout")
def test_logout(http_client):
response = http_client.get("/logout")
response = http_client.get(LOGOUT_ENDPOINT)
assert response.status_code == 200

View File

@ -2,20 +2,24 @@ import pytest
from conftest import (
SCSI_ID,
FILE_SIZE_1_MIB,
STATUS_SUCCESS,
ATTACH_ENDPOINT,
DETACH_ENDPOINT,
DETACH_ALL_ENDPOINT,
EJECT_ENDPOINT,
RESERVE_ENDPOINT,
RELEASE_ENDPOINT,
INFO_ENDPOINT,
)
# route("/scsi/attach", methods=["POST"])
def test_attach_image(http_client, create_test_image, detach_devices):
def test_attach_device_with_image(http_client, create_test_image, detach_devices):
test_image = create_test_image()
response = http_client.post(
"/scsi/attach",
ATTACH_ENDPOINT,
data={
"file_name": test_image,
"file_size": FILE_SIZE_1_MIB,
"scsi_id": SCSI_ID,
"unit": 0,
"type": "SCHD",
@ -26,14 +30,13 @@ def test_attach_image(http_client, create_test_image, detach_devices):
assert response.status_code == 200
assert response_data["status"] == STATUS_SUCCESS
assert response_data["messages"][0]["message"] == (
f"Attached {test_image} as Hard Disk Drive to SCSI ID {SCSI_ID} LUN 0"
f"Attached Hard Disk Drive to SCSI ID {SCSI_ID} LUN 0"
)
# Cleanup
detach_devices()
# route("/scsi/attach_device", methods=["POST"])
@pytest.mark.parametrize(
"device_name,device_config",
[
@ -89,7 +92,7 @@ def test_attach_device(env, http_client, detach_devices, device_name, device_con
device_config["unit"] = 0
response = http_client.post(
"/scsi/attach_device",
ATTACH_ENDPOINT,
data=device_config,
)
@ -105,15 +108,13 @@ def test_attach_device(env, http_client, detach_devices, device_name, device_con
detach_devices()
# route("/scsi/detach", methods=["POST"])
def test_detach_device(http_client, create_test_image):
test_image = create_test_image()
http_client.post(
"/scsi/attach",
ATTACH_ENDPOINT,
data={
"file_name": test_image,
"file_size": FILE_SIZE_1_MIB,
"scsi_id": SCSI_ID,
"unit": 0,
"type": "SCHD",
@ -121,7 +122,7 @@ def test_detach_device(http_client, create_test_image):
)
response = http_client.post(
"/scsi/detach",
DETACH_ENDPOINT,
data={
"scsi_id": SCSI_ID,
"unit": 0,
@ -135,7 +136,6 @@ def test_detach_device(http_client, create_test_image):
assert response_data["messages"][0]["message"] == f"Detached SCSI ID {SCSI_ID} LUN 0"
# route("/scsi/detach_all", methods=["POST"])
def test_detach_all_devices(http_client, create_test_image, list_attached_images):
test_images = []
scsi_ids = [4, 5, 6]
@ -145,10 +145,9 @@ def test_detach_all_devices(http_client, create_test_image, list_attached_images
test_images.append(test_image)
http_client.post(
"/scsi/attach",
ATTACH_ENDPOINT,
data={
"file_name": test_image,
"file_size": FILE_SIZE_1_MIB,
"scsi_id": scsi_id,
"unit": 0,
"type": "SCHD",
@ -157,7 +156,7 @@ def test_detach_all_devices(http_client, create_test_image, list_attached_images
assert list_attached_images() == test_images
response = http_client.post("/scsi/detach_all")
response = http_client.post(DETACH_ALL_ENDPOINT)
response_data = response.json()
assert response.status_code == 200
@ -165,15 +164,13 @@ def test_detach_all_devices(http_client, create_test_image, list_attached_images
assert list_attached_images() == []
# route("/scsi/eject", methods=["POST"])
def test_eject_device(http_client, create_test_image, detach_devices):
test_image = create_test_image()
http_client.post(
"/scsi/attach",
ATTACH_ENDPOINT,
data={
"file_name": test_image,
"file_size": FILE_SIZE_1_MIB,
"scsi_id": SCSI_ID,
"unit": 0,
"type": "SCCD", # CD-ROM
@ -181,7 +178,7 @@ def test_eject_device(http_client, create_test_image, detach_devices):
)
response = http_client.post(
"/scsi/eject",
EJECT_ENDPOINT,
data={
"scsi_id": SCSI_ID,
"unit": 0,
@ -198,15 +195,13 @@ def test_eject_device(http_client, create_test_image, detach_devices):
detach_devices()
# route("/scsi/info", methods=["POST"])
def test_show_device_info(http_client, create_test_image, detach_devices):
test_image = create_test_image()
http_client.post(
"/scsi/attach",
ATTACH_ENDPOINT,
data={
"file_name": test_image,
"file_size": FILE_SIZE_1_MIB,
"scsi_id": SCSI_ID,
"unit": 0,
"type": "SCHD",
@ -214,7 +209,7 @@ def test_show_device_info(http_client, create_test_image, detach_devices):
)
response = http_client.post(
"/scsi/info",
INFO_ENDPOINT,
)
response_data = response.json()
@ -228,13 +223,11 @@ def test_show_device_info(http_client, create_test_image, detach_devices):
detach_devices()
# route("/scsi/reserve", methods=["POST"])
# route("/scsi/release", methods=["POST"])
def test_reserve_and_release_device(http_client):
scsi_id = 0
response = http_client.post(
"/scsi/reserve",
RESERVE_ENDPOINT,
data={
"scsi_id": scsi_id,
"memo": "TEST",
@ -248,7 +241,7 @@ def test_reserve_and_release_device(http_client):
assert response_data["messages"][0]["message"] == f"Reserved SCSI ID {scsi_id}"
response = http_client.post(
"/scsi/release",
RELEASE_ENDPOINT,
data={
"scsi_id": scsi_id,
},

View File

@ -5,16 +5,26 @@ import os
from conftest import (
FILE_SIZE_1_MIB,
STATUS_SUCCESS,
CREATE_ENDPOINT,
RENAME_ENDPOINT,
COPY_ENDPOINT,
DELETE_ENDPOINT,
DOWNLOAD_URL_ENDPOINT,
DOWNLOAD_IMAGE_ENDPOINT,
DOWNLOAD_CONFIG_ENDPOINT,
EXTRACT_IMAGE_ENDPOINT,
UPLOAD_ENDPOINT,
CREATE_ISO_ENDPOINT,
DISKINFO_ENDPOINT,
)
# route("/files/create", methods=["POST"])
def test_create_file(http_client, list_files, delete_file):
file_prefix = str(uuid.uuid4())
file_name = f"{file_prefix}.hds"
response = http_client.post(
"/files/create",
CREATE_ENDPOINT,
data={
"file_name": file_prefix,
"type": "hds",
@ -34,13 +44,12 @@ def test_create_file(http_client, list_files, delete_file):
delete_file(file_name)
# route("/files/create", methods=["POST"])
def test_create_file_with_properties(http_client, list_files, delete_file):
file_prefix = str(uuid.uuid4())
file_name = f"{file_prefix}.hds"
response = http_client.post(
"/files/create",
CREATE_ENDPOINT,
data={
"file_name": file_prefix,
"type": "hds",
@ -64,13 +73,12 @@ def test_create_file_with_properties(http_client, list_files, delete_file):
delete_file(file_name)
# route("/files/create", methods=["POST"])
def test_create_file_and_format_hfs(http_client, list_files, delete_file):
file_prefix = str(uuid.uuid4())
file_name = f"{file_prefix}.hda"
response = http_client.post(
"/files/create",
CREATE_ENDPOINT,
data={
"file_name": file_prefix,
"type": "hda",
@ -91,7 +99,6 @@ def test_create_file_and_format_hfs(http_client, list_files, delete_file):
delete_file(file_name)
# route("/files/create", methods=["POST"])
def test_create_file_and_format_fat(env, http_client, list_files, delete_file):
if env["is_docker"]:
pytest.skip("Test not supported in Docker environment.")
@ -99,7 +106,7 @@ def test_create_file_and_format_fat(env, http_client, list_files, delete_file):
file_name = f"{file_prefix}.hdr"
response = http_client.post(
"/files/create",
CREATE_ENDPOINT,
data={
"file_name": file_prefix,
"type": "hdr",
@ -120,13 +127,12 @@ def test_create_file_and_format_fat(env, http_client, list_files, delete_file):
delete_file(file_name)
# route("/files/rename", methods=["POST"])
def test_rename_file(http_client, create_test_image, list_files, delete_file):
original_file = create_test_image(auto_delete=False)
renamed_file = f"{uuid.uuid4()}.rename"
response = http_client.post(
"/files/rename",
RENAME_ENDPOINT,
data={"file_name": original_file, "new_file_name": renamed_file},
)
@ -141,13 +147,12 @@ def test_rename_file(http_client, create_test_image, list_files, delete_file):
delete_file(renamed_file)
# route("/files/copy", methods=["POST"])
def test_copy_file(http_client, create_test_image, list_files, delete_file):
original_file = create_test_image()
copy_file = f"{uuid.uuid4()}.copy"
response = http_client.post(
"/files/copy",
COPY_ENDPOINT,
data={
"file_name": original_file,
"copy_file_name": copy_file,
@ -167,11 +172,10 @@ def test_copy_file(http_client, create_test_image, list_files, delete_file):
delete_file(copy_file)
# route("/files/delete", methods=["POST"])
def test_delete_file(http_client, create_test_image, list_files):
file_name = create_test_image(auto_delete=False)
response = http_client.post("/files/delete", data={"file_name": file_name})
response = http_client.post(DELETE_ENDPOINT, data={"file_name": file_name})
response_data = response.json()
@ -181,7 +185,6 @@ def test_delete_file(http_client, create_test_image, list_files):
assert file_name not in list_files()
# route("/files/extract_image", methods=["POST"])
@pytest.mark.parametrize(
"archive_file_name,image_file_name",
[
@ -205,7 +208,7 @@ def test_extract_file(
)
http_client.post(
"/files/download_url",
DOWNLOAD_URL_ENDPOINT,
data={
"destination": "disk_images",
"images_subdir": "",
@ -214,7 +217,7 @@ def test_extract_file(
)
response = http_client.post(
"/files/extract_image",
EXTRACT_IMAGE_ENDPOINT,
data={
"archive_file": archive_file_name,
"archive_members": image_file_name,
@ -233,7 +236,6 @@ def test_extract_file(
delete_file(image_file_name)
# route("/files/upload", methods=["POST"])
def test_upload_file(http_client, delete_file):
file_name = f"{uuid.uuid4()}.test"
@ -267,7 +269,7 @@ def test_upload_file(http_client, delete_file):
file_data = {"file": (file_name, file.read(chunk_size))}
response = http_client.post(
"/files/upload",
UPLOAD_ENDPOINT,
data=form_data,
files=file_data,
)
@ -283,11 +285,10 @@ def test_upload_file(http_client, delete_file):
delete_file(file_name)
# route("/files/download_image", methods=["POST"])
def test_download_image(http_client, create_test_image):
file_name = create_test_image()
response = http_client.post("/files/download_image", data={"file": file_name})
response = http_client.post(DOWNLOAD_IMAGE_ENDPOINT, data={"file": file_name})
assert response.status_code == 200
assert response.headers["content-type"] == "application/octet-stream"
@ -295,13 +296,12 @@ def test_download_image(http_client, create_test_image):
assert response.headers["content-length"] == str(FILE_SIZE_1_MIB)
# route("/files/download_config", methods=["POST"])
def test_download_properties(http_client, list_files, delete_file):
file_prefix = str(uuid.uuid4())
file_name = f"{file_prefix}.hds"
response = http_client.post(
"/files/create",
CREATE_ENDPOINT,
data={
"file_name": file_prefix,
"type": "hds",
@ -321,7 +321,7 @@ def test_download_properties(http_client, list_files, delete_file):
)
assert file_name in list_files()
response = http_client.post("/files/download_config", data={"file": f"{file_name}.properties"})
response = http_client.post(DOWNLOAD_CONFIG_ENDPOINT, data={"file": f"{file_name}.properties"})
assert response.status_code == 200
assert response.headers["content-type"] == "application/octet-stream"
@ -331,7 +331,6 @@ def test_download_properties(http_client, list_files, delete_file):
delete_file(file_name)
# route("/files/download_url", methods=["POST"])
def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_file):
file_name = str(uuid.uuid4())
http_path = f"/images/{file_name}"
@ -347,7 +346,7 @@ def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_fi
)
response = http_client.post(
"/files/download_url",
DOWNLOAD_URL_ENDPOINT,
data={
"destination": "disk_images",
"images_subdir": subdir,
@ -369,7 +368,6 @@ def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_fi
delete_file(file_name)
# route("/files/create_iso", methods=["POST"])
def test_create_iso_from_url(
httpserver,
http_client,
@ -392,7 +390,7 @@ def test_create_iso_from_url(
)
response = http_client.post(
"/files/create_iso",
CREATE_ISO_ENDPOINT,
data={
"type": ISO_TYPE,
"url": url,
@ -414,7 +412,6 @@ def test_create_iso_from_url(
delete_file(iso_file_name)
# route("/files/create_iso", methods=["POST"])
def test_create_iso_from_local_file(
http_client,
create_test_image,
@ -427,7 +424,7 @@ def test_create_iso_from_local_file(
ISO_TYPE = "HFS"
response = http_client.post(
"/files/create_iso",
CREATE_ISO_ENDPOINT,
data={
"type": ISO_TYPE,
"file": test_file_name,
@ -449,12 +446,11 @@ def test_create_iso_from_local_file(
delete_file(iso_file_name)
# route("/files/diskinfo", methods=["POST"])
def test_show_diskinfo(http_client, create_test_image):
test_image = create_test_image()
response = http_client.post(
"/files/diskinfo",
DISKINFO_ENDPOINT,
data={
"file_name": test_image,
},

View File

@ -3,10 +3,16 @@ import uuid
from conftest import (
FILE_SIZE_1_MIB,
STATUS_SUCCESS,
ENV_ENDPOINT,
PWA_FAVICON_ENDPOINT,
HEALTHCHECK_ENDPOINT,
DRIVE_LIST_ENDPOINT,
DRIVE_CREATE_ENDPOINT,
DRIVE_CDROM_ENDPOINT,
MANPAGE_ENDPOINT,
)
# route("/")
def test_index(http_client):
response = http_client.get("/")
response_data = response.json()
@ -16,9 +22,8 @@ def test_index(http_client):
assert "devices" in response_data["data"]
# route("/env")
def test_get_env_info(http_client):
response = http_client.get("/env")
response = http_client.get(ENV_ENDPOINT)
response_data = response.json()
assert response.status_code == 200
@ -26,17 +31,15 @@ def test_get_env_info(http_client):
assert "running_env" in response_data["data"]
# route("/pwa/<path:pwa_path>")
def test_pwa_route(http_client):
response = http_client.get("/pwa/favicon.ico")
response = http_client.get(PWA_FAVICON_ENDPOINT)
assert response.status_code == 200
assert response.headers["content-disposition"] == "inline; filename=favicon.ico"
# route("/drive/list", methods=["GET"])
def test_show_named_drive_presets(http_client):
response = http_client.get("/drive/list")
response = http_client.get(DRIVE_LIST_ENDPOINT)
response_data = response.json()
prev_drive = {"name": ""}
@ -57,12 +60,11 @@ def test_show_named_drive_presets(http_client):
assert "files" in response_data["data"]
# route("/drive/cdrom", methods=["POST"])
def test_create_cdrom_properties_file(env, http_client):
file_name = f"{uuid.uuid4()}.iso"
response = http_client.post(
"/drive/cdrom",
DRIVE_CDROM_ENDPOINT,
data={
"drive_name": "Sony CDU-8012",
"file_name": file_name,
@ -78,13 +80,12 @@ def test_create_cdrom_properties_file(env, http_client):
)
# route("/drive/create", methods=["POST"])
def test_create_image_with_properties_file(http_client, delete_file):
file_prefix = str(uuid.uuid4())
file_name = f"{file_prefix}.hds"
response = http_client.post(
"/drive/create",
DRIVE_CREATE_ENDPOINT,
data={
"drive_name": "Miniscribe M8425",
"size": FILE_SIZE_1_MIB,
@ -105,16 +106,14 @@ def test_create_image_with_properties_file(http_client, delete_file):
delete_file(file_name)
# route("/sys/manpage", methods=["POST"])
def test_show_manpage(http_client):
response = http_client.get("/sys/manpage?app=piscsi")
response = http_client.get(MANPAGE_ENDPOINT)
response_data = response.json()
assert response.status_code == 200
assert "piscsi" in response_data["data"]["manpage"]
# route("/healthcheck", methods=["GET"])
def test_healthcheck(http_client):
response = http_client.get("/healthcheck")
response = http_client.get(HEALTHCHECK_ENDPOINT)
assert response.status_code == 200

View File

@ -1,10 +1,20 @@
import pytest
import uuid
from conftest import STATUS_SUCCESS
from conftest import (
STATUS_SUCCESS,
ENV_ENDPOINT,
LANGUAGE_ENDPOINT,
LOG_LEVEL_ENDPOINT,
LOG_SHOW_ENDPOINT,
CONFIG_SAVE_ENDPOINT,
CONFIG_ACTION_ENDPOINT,
THEME_ENDPOINT,
SYS_RENAME_ENDPOINT,
RESERVE_ENDPOINT,
)
# route("/language", methods=["POST"])
@pytest.mark.parametrize(
"locale,confirm_message",
[
@ -18,7 +28,7 @@ from conftest import STATUS_SUCCESS
)
def test_set_language(http_client, locale, confirm_message):
response = http_client.post(
"/language",
LANGUAGE_ENDPOINT,
data={
"locale": locale,
},
@ -31,11 +41,10 @@ def test_set_language(http_client, locale, confirm_message):
assert response_data["messages"][0]["message"] == confirm_message
# route("/logs/level", methods=["POST"])
@pytest.mark.parametrize("level", ["trace", "debug", "info", "warn", "err", "off"])
def test_set_log_level(http_client, level):
response = http_client.post(
"/logs/level",
LOG_LEVEL_ENDPOINT,
data={
"level": level,
},
@ -49,17 +58,16 @@ def test_set_log_level(http_client, level):
# Cleanup
http_client.post(
"/logs/level",
LOG_LEVEL_ENDPOINT,
data={
"level": "debug",
},
)
# route("/logs/show", methods=["POST"])
def test_show_logs(http_client):
response = http_client.post(
"/logs/show",
LOG_SHOW_ENDPOINT,
data={
"lines": 100,
"scope": "piscsi",
@ -73,8 +81,6 @@ def test_show_logs(http_client):
assert response_data["data"]["scope"] == "piscsi"
# route("/config/save", methods=["POST"])
# route("/config/action", methods=["POST"])
def test_save_load_and_delete_configs(env, http_client):
config_name = str(uuid.uuid4())
config_json_file = f"{config_name}.json"
@ -86,7 +92,7 @@ def test_save_load_and_delete_configs(env, http_client):
# Save the initial state to a config
response = http_client.post(
"/config/save",
CONFIG_SAVE_ENDPOINT,
data={
"name": config_name,
},
@ -104,7 +110,7 @@ def test_save_load_and_delete_configs(env, http_client):
# Modify the state
http_client.post(
"/scsi/reserve",
RESERVE_ENDPOINT,
data={
"scsi_id": reserved_scsi_id,
"memo": reservation_memo,
@ -115,7 +121,7 @@ def test_save_load_and_delete_configs(env, http_client):
# Load the saved config
response = http_client.post(
"/config/action",
CONFIG_ACTION_ENDPOINT,
data={
"name": config_json_file,
"load": True,
@ -135,7 +141,7 @@ def test_save_load_and_delete_configs(env, http_client):
# Delete the saved config
response = http_client.post(
"/config/action",
CONFIG_ACTION_ENDPOINT,
data={
"name": config_json_file,
"delete": True,
@ -153,15 +159,13 @@ def test_save_load_and_delete_configs(env, http_client):
assert config_json_file not in http_client.get("/").json()["data"]["config_files"]
# route("/config/save", methods=["POST"])
# route("/config/action", methods=["POST"])
def test_download_configs(env, http_client, delete_file):
def test_download_configs(env, http_client):
config_name = str(uuid.uuid4())
config_json_file = f"{config_name}.json"
# Save the initial state to a config
response = http_client.post(
"/config/save",
CONFIG_SAVE_ENDPOINT,
data={
"name": config_name,
},
@ -172,7 +176,7 @@ def test_download_configs(env, http_client, delete_file):
# Download the saved config
response = http_client.post(
"/config/action",
CONFIG_ACTION_ENDPOINT,
data={
"name": config_json_file,
"send": True,
@ -185,7 +189,7 @@ def test_download_configs(env, http_client, delete_file):
# Delete the saved config
response = http_client.post(
"/config/action",
CONFIG_ACTION_ENDPOINT,
data={
"name": config_json_file,
"delete": True,
@ -196,7 +200,6 @@ def test_download_configs(env, http_client, delete_file):
assert config_json_file not in http_client.get("/").json()["data"]["config_files"]
# route("/theme", methods=["POST"])
@pytest.mark.parametrize(
"theme",
[
@ -206,7 +209,7 @@ def test_download_configs(env, http_client, delete_file):
)
def test_set_theme(http_client, theme):
response = http_client.post(
"/theme",
THEME_ENDPOINT,
data={
"name": theme,
},
@ -219,7 +222,6 @@ def test_set_theme(http_client, theme):
assert response_data["messages"][0]["message"] == f"Theme changed to '{theme}'."
# route("/theme", methods=["GET"])
@pytest.mark.parametrize(
"theme",
[
@ -229,7 +231,7 @@ def test_set_theme(http_client, theme):
)
def test_set_theme_via_query_string(http_client, theme):
response = http_client.get(
"/theme",
THEME_ENDPOINT,
params={
"name": theme,
},
@ -242,17 +244,16 @@ def test_set_theme_via_query_string(http_client, theme):
assert response_data["messages"][0]["message"] == f"Theme changed to '{theme}'."
# route("/sys/rename", methods=["POST"])
def test_rename_system(env, http_client):
new_name = "SYSTEM NAME TEST"
response = http_client.get("/env")
response = http_client.get(ENV_ENDPOINT)
response_data = response.json()
old_name = response_data["data"]["system_name"]
response = http_client.post(
"/sys/rename",
SYS_RENAME_ENDPOINT,
data={
"system_name": new_name,
},
@ -264,13 +265,13 @@ def test_rename_system(env, http_client):
assert response_data["status"] == STATUS_SUCCESS
assert response_data["messages"][0]["message"] == f"System name changed to '{new_name}'."
response = http_client.get("/env")
response = http_client.get(ENV_ENDPOINT)
response_data = response.json()
assert response_data["data"]["system_name"] == new_name
response = http_client.post(
"/sys/rename",
SYS_RENAME_ENDPOINT,
data={
"system_name": old_name,
},
@ -282,7 +283,7 @@ def test_rename_system(env, http_client):
assert response_data["status"] == STATUS_SUCCESS
assert response_data["messages"][0]["message"] == f"System name changed to '{old_name}'."
response = http_client.get("/env")
response = http_client.get(ENV_ENDPOINT)
response_data = response.json()
assert response_data["data"]["system_name"] == old_name