mirror of https://github.com/akuker/RASCSI.git
Merge branch 'develop' into dependabot/pip/python/web/jinja2-3.1.3
This commit is contained in:
commit
83f4feaa21
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -12,6 +12,3 @@ obj
|
|||
bin
|
||||
coverage
|
||||
generated
|
||||
.project
|
||||
.cproject
|
||||
.settings
|
||||
|
|
|
@ -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
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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) \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Copyright (C) 2020 akuker
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) 2001-2006 PI.(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);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Copyright (C) 2020 akuker
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) 2001-2006 PI.(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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 "$@"
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ private:
|
|||
|
||||
PiscsiImage piscsi_image;
|
||||
|
||||
PiscsiResponse response;
|
||||
[[no_unique_address]] PiscsiResponse response;
|
||||
|
||||
PiscsiService service;
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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?";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}",
|
||||
|
|
|
@ -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/*
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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!")
|
||||
|
|
|
@ -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"])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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"
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 |
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue