mirror of
https://github.com/autc04/Retro68.git
synced 2025-02-18 02:30:48 +00:00
LaunchAPPL: modularize; Executor and MiniVMac backends for now
This commit is contained in:
parent
270cafaab4
commit
5961d1884d
@ -1,20 +1,27 @@
|
|||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
|
set(RETRO68_TEST_CONFIG "-eminivmac" "-t4" "--minivmac-dir=/home/wolfgang/Emulators"
|
||||||
|
"--system-image=/home/wolfgang/Emulators/baresystem.dsk"
|
||||||
|
"--autoquit-image=/home/wolfgang/Emulators/vmac extras/autoquit-1.1.1.dsk")
|
||||||
|
|
||||||
|
|
||||||
add_application(ReallyEmpty ReallyEmpty.c)
|
add_application(ReallyEmpty ReallyEmpty.c)
|
||||||
add_test(NAME ReallyEmpty COMMAND LaunchAPPL -e ReallyEmpty.bin)
|
if(CMAKE_SYSTEM_NAME MATCHES Retro68)
|
||||||
|
set_target_properties(ReallyEmpty PROPERTIES LINK_FLAGS "-Wl,-gc-sections -Wl,--mac-single")
|
||||||
|
endif()
|
||||||
|
add_test(NAME ReallyEmpty COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} ReallyEmpty.bin)
|
||||||
|
|
||||||
add_application(Empty Empty.c)
|
add_application(Empty Empty.c)
|
||||||
add_test(NAME Empty COMMAND LaunchAPPL -e Empty.bin)
|
add_test(NAME Empty COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} Empty.bin)
|
||||||
|
|
||||||
add_application(File File.c)
|
add_application(File File.c)
|
||||||
add_test(NAME File
|
add_test(NAME File
|
||||||
COMMAND LaunchAPPL -e File.bin --logfile=out -t5
|
COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} File.bin
|
||||||
)
|
)
|
||||||
set_tests_properties(File PROPERTIES PASS_REGULAR_EXPRESSION "OK")
|
set_tests_properties(File PROPERTIES PASS_REGULAR_EXPRESSION "OK")
|
||||||
|
|
||||||
add_application(Timeout Timeout.c)
|
add_application(Log Log.c)
|
||||||
add_test(NAME Timeout
|
add_test(NAME Log
|
||||||
COMMAND sh -c "! LaunchAPPL -e Timeout.bin --logfile=out -t2"
|
COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} Log.bin
|
||||||
)
|
)
|
||||||
set_tests_properties(Timeout PROPERTIES PASS_REGULAR_EXPRESSION "One;Two"
|
set_tests_properties(Log PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo\nThree")
|
||||||
FAIL_REGULAR_EXPRESSION "Three")
|
|
||||||
|
@ -4,8 +4,6 @@ int main()
|
|||||||
{
|
{
|
||||||
TEST_LOG_SIZED("One",3);
|
TEST_LOG_SIZED("One",3);
|
||||||
TEST_LOG_SIZED("Two",3);
|
TEST_LOG_SIZED("Two",3);
|
||||||
for(;;)
|
|
||||||
;
|
|
||||||
TEST_LOG_SIZED("Three",5);
|
TEST_LOG_SIZED("Three",5);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
find_package(Boost COMPONENTS filesystem program_options)
|
find_package(Boost COMPONENTS filesystem program_options)
|
||||||
|
|
||||||
add_executable(LaunchAPPL LaunchAPPL.cc bootblock.c)
|
add_executable(LaunchAPPL
|
||||||
|
LaunchAPPL.cc bootblock.c
|
||||||
|
|
||||||
|
LaunchMethod.h LaunchMethod.cc
|
||||||
|
Launcher.h Launcher.cc
|
||||||
|
|
||||||
|
Executor.h Executor.cc
|
||||||
|
MiniVMac.h MiniVMac.cc)
|
||||||
target_include_directories(LaunchAPPL PRIVATE ${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIR})
|
target_include_directories(LaunchAPPL PRIVATE ${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIR})
|
||||||
target_link_libraries(LaunchAPPL ResourceFiles ${Boost_LIBRARIES})
|
target_link_libraries(LaunchAPPL ResourceFiles ${Boost_LIBRARIES})
|
||||||
|
|
||||||
|
48
LaunchAPPL/Executor.cc
Normal file
48
LaunchAPPL/Executor.cc
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include "Executor.h"
|
||||||
|
#include "Launcher.h"
|
||||||
|
|
||||||
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
|
class ExecutorLauncher : public Launcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ExecutorLauncher(po::variables_map& options);
|
||||||
|
virtual ~ExecutorLauncher();
|
||||||
|
|
||||||
|
virtual bool Go(int timeout = 0);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
ExecutorLauncher::ExecutorLauncher(po::variables_map &options)
|
||||||
|
: Launcher(options, ResourceFile::Format::percent_appledouble)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutorLauncher::~ExecutorLauncher()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExecutorLauncher::Go(int timeout)
|
||||||
|
{
|
||||||
|
return ChildProcess(options["executor-path"].as<std::string>(), { appPath.string() }, timeout) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Executor::GetOptions(options_description &desc)
|
||||||
|
{
|
||||||
|
desc.add_options()
|
||||||
|
("executor-path", po::value<std::string>()->default_value("executor"),"path to executor")
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Executor::CheckOptions(variables_map &options)
|
||||||
|
{
|
||||||
|
return options.count("executor-path") != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Launcher> Executor::MakeLauncher(variables_map &options)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<Launcher>(new ExecutorLauncher(options));
|
||||||
|
}
|
16
LaunchAPPL/Executor.h
Normal file
16
LaunchAPPL/Executor.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef EXECUTOR_H
|
||||||
|
#define EXECUTOR_H
|
||||||
|
|
||||||
|
#include "LaunchMethod.h"
|
||||||
|
|
||||||
|
class Executor : public LaunchMethod
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual std::string GetName() { return "executor"; }
|
||||||
|
virtual void GetOptions(options_description& desc);
|
||||||
|
virtual bool CheckOptions(variables_map& options);
|
||||||
|
|
||||||
|
virtual std::unique_ptr<Launcher> MakeLauncher(variables_map& options);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EXECUTOR_H
|
@ -5,18 +5,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include "LaunchMethod.h"
|
||||||
#include <sys/types.h>
|
#include "Launcher.h"
|
||||||
#include <sys/wait.h>
|
|
||||||
|
|
||||||
|
|
||||||
#include "ResourceFork.h"
|
|
||||||
#include "ResourceFile.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "hfs.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#include "Executor.h"
|
||||||
|
#include "MiniVMac.h"
|
||||||
|
|
||||||
namespace po = boost::program_options;
|
namespace po = boost::program_options;
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
@ -34,105 +27,20 @@ static void usage()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int ChildProcess(string program, vector<string> args)
|
|
||||||
{
|
|
||||||
std::vector<const char*> argv;
|
|
||||||
argv.push_back(program.c_str());
|
|
||||||
for(string& s : args)
|
|
||||||
argv.push_back(s.c_str());
|
|
||||||
argv.push_back(NULL);
|
|
||||||
|
|
||||||
int timeout = options.count("timeout") ? options["timeout"].as<int>() : 0;
|
|
||||||
|
|
||||||
pid_t pid = fork();
|
|
||||||
if(pid < 0)
|
|
||||||
{
|
|
||||||
perror("unable to fork");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if(pid == 0)
|
|
||||||
{
|
|
||||||
pid_t worker_pid = timeout ? fork() : 0;
|
|
||||||
if(worker_pid < 0)
|
|
||||||
{
|
|
||||||
perror("unable to fork");
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
if(worker_pid == 0)
|
|
||||||
{
|
|
||||||
execvp(argv[0], const_cast<char* const *> (argv.data()));
|
|
||||||
perror("exec failed");
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t timeout_pid = fork();
|
|
||||||
if(timeout_pid < 0)
|
|
||||||
{
|
|
||||||
perror("unable to fork");
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
if(timeout_pid == 0)
|
|
||||||
{
|
|
||||||
sleep(timeout);
|
|
||||||
_exit(0);
|
|
||||||
}
|
|
||||||
int wstatus;
|
|
||||||
pid_t exited_pid = wait(&wstatus);
|
|
||||||
if(exited_pid == worker_pid)
|
|
||||||
{
|
|
||||||
kill(timeout_pid, SIGKILL);
|
|
||||||
wait(NULL);
|
|
||||||
if(!WIFEXITED(wstatus))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int exitcode = WEXITSTATUS(wstatus);
|
|
||||||
_exit(exitcode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
kill(worker_pid, SIGKILL);
|
|
||||||
wait(NULL);
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int wstatus;
|
|
||||||
int result = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
result = waitpid(pid, &wstatus, 0);
|
|
||||||
} while(result == -1 && errno == EINTR);
|
|
||||||
|
|
||||||
if(!WIFEXITED(wstatus))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int exitcode = WEXITSTATUS(wstatus);
|
|
||||||
return exitcode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
std::vector<LaunchMethod*> methods = {
|
||||||
|
new Executor(), new MiniVMac()
|
||||||
|
};
|
||||||
desc.add_options()
|
desc.add_options()
|
||||||
("help,h", "show this help message")
|
("help,h", "show this help message")
|
||||||
("executor,e", "run using executor")
|
("emulator,e", po::value<std::string>(), "what emulator/environment to use")
|
||||||
("minivmac,m", "run using executor")
|
;
|
||||||
|
for(LaunchMethod *lm : methods)
|
||||||
("executor-path", po::value<std::string>()->default_value("executor"),"path to executor")
|
lm->GetOptions(desc);
|
||||||
("minivmac-path", po::value<std::string>()->default_value("minivmac"),"path to minivmac")
|
|
||||||
("minivmac-dir", po::value<std::string>()->default_value("."),"directory containing vMac.ROM")
|
|
||||||
("system-image", po::value<std::string>(),"path to disk image with system")
|
|
||||||
|
|
||||||
|
desc.add_options()
|
||||||
("timeout,t", po::value<int>(),"abort after timeout")
|
("timeout,t", po::value<int>(),"abort after timeout")
|
||||||
("timeout-ok","timeout counts as success")
|
("timeout-ok","timeout counts as success")
|
||||||
("logfile", po::value<std::string>(), "read log file")
|
("logfile", po::value<std::string>(), "read log file")
|
||||||
@ -142,7 +50,6 @@ int main(int argc, char *argv[])
|
|||||||
("application,a", po::value<std::string>(), "application" )
|
("application,a", po::value<std::string>(), "application" )
|
||||||
;
|
;
|
||||||
alldesc.add(desc).add(hidden);
|
alldesc.add(desc).add(hidden);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto parsed = po::command_line_parser(argc, argv)
|
auto parsed = po::command_line_parser(argc, argv)
|
||||||
@ -162,198 +69,41 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
po::notify(options);
|
po::notify(options);
|
||||||
|
|
||||||
if(options.count("help") || !options.count("application"))
|
if(options.count("help") || !options.count("application") || !options.count("emulator"))
|
||||||
{
|
{
|
||||||
usage();
|
usage();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceFile app(options["application"].as<std::string>());
|
LaunchMethod *method = NULL;
|
||||||
if(!app.read())
|
for(LaunchMethod *lm : methods)
|
||||||
{
|
{
|
||||||
std::cerr << "Could not read application file.\n";
|
if(lm->GetName() == options["emulator"].as<string>())
|
||||||
|
{
|
||||||
|
method = lm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!method)
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: unknown emulator/environment.\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!method->CheckOptions(options))
|
||||||
|
|
||||||
if(options.count("executor"))
|
|
||||||
{
|
{
|
||||||
fs::path tempDir = fs::unique_path();
|
std::cerr << "Missing configuration.\n";
|
||||||
fs::create_directories(tempDir);
|
return 1;
|
||||||
|
|
||||||
fs::path appPath = tempDir / "Application";
|
|
||||||
|
|
||||||
app.assign(appPath.string(), ResourceFile::Format::percent_appledouble);
|
|
||||||
if(!app.write())
|
|
||||||
{
|
|
||||||
std::cerr << "Could not write application file.\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(options.count("logfile"))
|
|
||||||
{
|
|
||||||
fs::ofstream out(tempDir/options["logfile"].as<std::string>());
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = ChildProcess(options["executor-path"].as<std::string>(), { appPath.string() });
|
|
||||||
|
|
||||||
if(options.count("logfile"))
|
|
||||||
{
|
|
||||||
fs::ifstream in(tempDir/options["logfile"].as<std::string>());
|
|
||||||
std::cout << in.rdbuf();
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::remove_all(tempDir);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if(options.count("minivmac"))
|
|
||||||
{
|
|
||||||
assert(options.count("system-image"));
|
|
||||||
fs::path tempDir = fs::unique_path();
|
|
||||||
fs::path path = tempDir / "image.dsk";
|
|
||||||
fs::create_directories(tempDir);
|
|
||||||
|
|
||||||
hfsvol *sysvol = hfs_mount(options["system-image"].as<std::string>().c_str(),
|
|
||||||
0, HFS_MODE_RDONLY);
|
|
||||||
|
|
||||||
int size = 5000*1024;
|
|
||||||
fs::ofstream(path, std::ios::binary | std::ios::trunc).seekp(size-1).put(0);
|
|
||||||
hfs_format(path.string().c_str(), 0, 0, "SysAndApp", 0, NULL);
|
|
||||||
hfsvol *vol = hfs_mount(path.string().c_str(), 0, HFS_MODE_RDWR);
|
|
||||||
|
|
||||||
hfsvolent ent;
|
|
||||||
hfs_vstat(sysvol, &ent);
|
|
||||||
|
|
||||||
hfs_setcwd(sysvol, ent.blessed);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
const char *fn = "System";
|
|
||||||
hfsdirent fileent;
|
|
||||||
hfs_stat(sysvol, fn, &fileent);
|
|
||||||
hfsfile *in = hfs_open(sysvol, fn);
|
|
||||||
hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator);
|
|
||||||
|
|
||||||
std::vector<uint8_t> buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize));
|
|
||||||
hfs_setfork(in, 0);
|
|
||||||
hfs_setfork(out, 0);
|
|
||||||
hfs_read(in, buffer.data(), fileent.u.file.dsize);
|
|
||||||
hfs_write(out, buffer.data(), fileent.u.file.dsize);
|
|
||||||
hfs_setfork(in, 1);
|
|
||||||
hfs_setfork(out, 1);
|
|
||||||
hfs_read(in, buffer.data(), fileent.u.file.rsize);
|
|
||||||
hfs_write(out, buffer.data(), fileent.u.file.rsize);
|
|
||||||
hfs_close(in);
|
|
||||||
hfs_close(out);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const char *fn = "Finder";
|
|
||||||
hfsdirent fileent;
|
|
||||||
hfs_stat(sysvol, fn, &fileent);
|
|
||||||
hfsfile *in = hfs_open(sysvol, fn);
|
|
||||||
hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator);
|
|
||||||
|
|
||||||
std::vector<uint8_t> buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize));
|
|
||||||
hfs_setfork(in, 0);
|
|
||||||
hfs_setfork(out, 0);
|
|
||||||
hfs_read(in, buffer.data(), fileent.u.file.dsize);
|
|
||||||
hfs_write(out, buffer.data(), fileent.u.file.dsize);
|
|
||||||
hfs_setfork(in, 1);
|
|
||||||
hfs_setfork(out, 1);
|
|
||||||
hfs_read(in, buffer.data(), fileent.u.file.rsize);
|
|
||||||
hfs_write(out, buffer.data(), fileent.u.file.rsize);
|
|
||||||
hfs_close(in);
|
|
||||||
hfs_close(out);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const char *fn = "MacsBug";
|
|
||||||
hfsdirent fileent;
|
|
||||||
hfs_stat(sysvol, fn, &fileent);
|
|
||||||
hfsfile *in = hfs_open(sysvol, fn);
|
|
||||||
hfsfile *out = hfs_create(vol, fn, fileent.u.file.type,fileent.u.file.creator);
|
|
||||||
|
|
||||||
std::vector<uint8_t> buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize));
|
|
||||||
hfs_setfork(in, 0);
|
|
||||||
hfs_setfork(out, 0);
|
|
||||||
hfs_read(in, buffer.data(), fileent.u.file.dsize);
|
|
||||||
hfs_write(out, buffer.data(), fileent.u.file.dsize);
|
|
||||||
hfs_setfork(in, 1);
|
|
||||||
hfs_setfork(out, 1);
|
|
||||||
hfs_read(in, buffer.data(), fileent.u.file.rsize);
|
|
||||||
hfs_write(out, buffer.data(), fileent.u.file.rsize);
|
|
||||||
hfs_close(in);
|
|
||||||
hfs_close(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::ostringstream rsrcOut;
|
|
||||||
app.resources.writeFork(rsrcOut);
|
|
||||||
std::string rsrc = rsrcOut.str();
|
|
||||||
std::string& data = app.data;
|
|
||||||
|
|
||||||
hfsfile *file = hfs_create(vol, "App","APPL","????");
|
|
||||||
hfs_setfork(file, 0);
|
|
||||||
hfs_write(file, data.data(), data.size());
|
|
||||||
hfs_setfork(file, 1);
|
|
||||||
hfs_write(file, rsrc.data(), rsrc.size());
|
|
||||||
hfs_close(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
hfsfile *out = hfs_create(vol, "out", "TEXT", "????");
|
|
||||||
hfs_close(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
hfs_vstat(vol, &ent);
|
|
||||||
ent.blessed = hfs_getcwd(vol);
|
|
||||||
std::cout << "blessed: " << ent.blessed << std::endl;
|
|
||||||
hfs_vsetattr(vol, &ent);
|
|
||||||
|
|
||||||
hfs_umount(vol);
|
|
||||||
hfs_umount(sysvol);
|
|
||||||
|
|
||||||
extern unsigned char bootblock[1024];
|
|
||||||
std::vector<unsigned char> bootblock1(bootblock, bootblock+1024);
|
|
||||||
std::fstream out(path.string(), std::ios::in | std::ios::out | std::ios::binary);
|
|
||||||
|
|
||||||
bootblock1[0x5A] = 3;
|
|
||||||
bootblock1[0x5B] = 'A';
|
|
||||||
bootblock1[0x5C] = 'p';
|
|
||||||
bootblock1[0x5D] = 'p';
|
|
||||||
|
|
||||||
out.write((const char*) bootblock1.data(), 1024);
|
|
||||||
|
|
||||||
path = fs::absolute(path);
|
|
||||||
|
|
||||||
fs::path minivmacdir = fs::absolute( options["minivmac-dir"].as<std::string>() );
|
|
||||||
fs::path minivmacpath = fs::absolute( minivmacdir / options["minivmac-path"].as<std::string>() );
|
|
||||||
|
|
||||||
fs::current_path(minivmacdir);
|
|
||||||
|
|
||||||
int result = ChildProcess(minivmacpath.string(), { path.string() });
|
|
||||||
|
|
||||||
std::cerr << "volume at: " << path.string() << std::endl;
|
|
||||||
vol = hfs_mount(path.string().c_str(), 0, HFS_MODE_RDONLY);
|
|
||||||
{
|
|
||||||
hfsfile *out = hfs_open(vol, "out");
|
|
||||||
if(!out)
|
|
||||||
return 1;
|
|
||||||
hfsdirent fileent;
|
|
||||||
int statres = hfs_stat(vol, "out", &fileent);
|
|
||||||
std::cerr << "stat: " << statres << "\n";
|
|
||||||
std::cerr << "out: " << fileent.u.file.dsize << " bytes\n";
|
|
||||||
std::vector<char> buffer(fileent.u.file.dsize);
|
|
||||||
hfs_setfork(out, 0);
|
|
||||||
hfs_read(out, buffer.data(), fileent.u.file.dsize);
|
|
||||||
hfs_close(out);
|
|
||||||
std::cout << string(buffer.begin(), buffer.end());
|
|
||||||
}
|
|
||||||
hfs_umount(vol);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
std::unique_ptr<Launcher> launcher = method->MakeLauncher(options);
|
||||||
|
|
||||||
|
int timeout = options.count("timeout") ? options["timeout"].as<int>() : 0;
|
||||||
|
|
||||||
|
bool result = launcher->Go(timeout);
|
||||||
|
|
||||||
|
launcher->DumpOutput();
|
||||||
|
|
||||||
|
|
||||||
|
return result ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
20
LaunchAPPL/LaunchMethod.cc
Normal file
20
LaunchAPPL/LaunchMethod.cc
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "LaunchMethod.h"
|
||||||
|
|
||||||
|
LaunchMethod::LaunchMethod()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchMethod::~LaunchMethod()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaunchMethod::GetOptions(boost::program_options::options_description &desc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LaunchMethod::CheckOptions(boost::program_options::variables_map &options)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
28
LaunchAPPL/LaunchMethod.h
Normal file
28
LaunchAPPL/LaunchMethod.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef LAUNCHMETHOD_H
|
||||||
|
#define LAUNCHMETHOD_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <boost/program_options/options_description.hpp>
|
||||||
|
#include <boost/program_options/variables_map.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class Launcher;
|
||||||
|
|
||||||
|
class LaunchMethod
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef boost::program_options::options_description options_description;
|
||||||
|
typedef boost::program_options::variables_map variables_map;
|
||||||
|
|
||||||
|
LaunchMethod();
|
||||||
|
virtual ~LaunchMethod();
|
||||||
|
|
||||||
|
virtual std::string GetName() = 0;
|
||||||
|
virtual void GetOptions(options_description& desc);
|
||||||
|
virtual bool CheckOptions(variables_map& options);
|
||||||
|
|
||||||
|
virtual std::unique_ptr<Launcher> MakeLauncher(variables_map& options) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LAUNCHMETHOD_H
|
132
LaunchAPPL/Launcher.cc
Normal file
132
LaunchAPPL/Launcher.cc
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#include "Launcher.h"
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
Launcher::Launcher(boost::program_options::variables_map &options)
|
||||||
|
: options(options)
|
||||||
|
{
|
||||||
|
app.assign(options["application"].as<std::string>());
|
||||||
|
if(!app.read())
|
||||||
|
throw std::runtime_error("Could not load application file.");
|
||||||
|
|
||||||
|
tempDir = fs::absolute(fs::unique_path());
|
||||||
|
fs::create_directories(tempDir);
|
||||||
|
|
||||||
|
appPath = tempDir / "Application";
|
||||||
|
outPath = tempDir / "out";
|
||||||
|
|
||||||
|
fs::ofstream out(outPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Launcher::Launcher(boost::program_options::variables_map &options, ResourceFile::Format f)
|
||||||
|
: Launcher(options)
|
||||||
|
{
|
||||||
|
app.assign(appPath.string(), f);
|
||||||
|
app.write();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Launcher::DumpOutput()
|
||||||
|
{
|
||||||
|
fs::ifstream in(outPath);
|
||||||
|
std::cout << in.rdbuf();
|
||||||
|
}
|
||||||
|
|
||||||
|
Launcher::~Launcher()
|
||||||
|
{
|
||||||
|
fs::remove_all(tempDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int Launcher::ChildProcess(string program, vector<string> args, int timeout)
|
||||||
|
{
|
||||||
|
std::vector<const char*> argv;
|
||||||
|
argv.push_back(program.c_str());
|
||||||
|
for(string& s : args)
|
||||||
|
argv.push_back(s.c_str());
|
||||||
|
argv.push_back(NULL);
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if(pid < 0)
|
||||||
|
{
|
||||||
|
perror("unable to fork");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if(pid == 0)
|
||||||
|
{
|
||||||
|
pid_t worker_pid = timeout ? fork() : 0;
|
||||||
|
if(worker_pid < 0)
|
||||||
|
{
|
||||||
|
perror("unable to fork");
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
if(worker_pid == 0)
|
||||||
|
{
|
||||||
|
execvp(argv[0], const_cast<char* const *> (argv.data()));
|
||||||
|
perror("exec failed");
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t timeout_pid = fork();
|
||||||
|
if(timeout_pid < 0)
|
||||||
|
{
|
||||||
|
perror("unable to fork");
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
if(timeout_pid == 0)
|
||||||
|
{
|
||||||
|
sleep(timeout);
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
int wstatus;
|
||||||
|
pid_t exited_pid = wait(&wstatus);
|
||||||
|
if(exited_pid == worker_pid)
|
||||||
|
{
|
||||||
|
kill(timeout_pid, SIGKILL);
|
||||||
|
wait(NULL);
|
||||||
|
if(!WIFEXITED(wstatus))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int exitcode = WEXITSTATUS(wstatus);
|
||||||
|
_exit(exitcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
kill(worker_pid, SIGKILL);
|
||||||
|
wait(NULL);
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int wstatus;
|
||||||
|
int result = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
result = waitpid(pid, &wstatus, 0);
|
||||||
|
} while(result == -1 && errno == EINTR);
|
||||||
|
|
||||||
|
if(!WIFEXITED(wstatus))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int exitcode = WEXITSTATUS(wstatus);
|
||||||
|
return exitcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
31
LaunchAPPL/Launcher.h
Normal file
31
LaunchAPPL/Launcher.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef LAUNCHER_H
|
||||||
|
#define LAUNCHER_H
|
||||||
|
|
||||||
|
#include <boost/program_options/variables_map.hpp>
|
||||||
|
#include "ResourceFile.h"
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
class Launcher
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
boost::program_options::variables_map& options;
|
||||||
|
|
||||||
|
ResourceFile app;
|
||||||
|
boost::filesystem::path tempDir, appPath, outPath;
|
||||||
|
bool keepTempFiles;
|
||||||
|
|
||||||
|
int ChildProcess(std::string program, std::vector<std::string> args, int timeout);
|
||||||
|
public:
|
||||||
|
Launcher(boost::program_options::variables_map& options);
|
||||||
|
Launcher(boost::program_options::variables_map& options, ResourceFile::Format f);
|
||||||
|
|
||||||
|
void KeepTempFiles() { keepTempFiles = true; }
|
||||||
|
|
||||||
|
virtual bool Go(int timeout = 0) = 0;
|
||||||
|
|
||||||
|
virtual void DumpOutput();
|
||||||
|
|
||||||
|
virtual ~Launcher();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LAUNCHER_H
|
195
LaunchAPPL/MiniVMac.cc
Normal file
195
LaunchAPPL/MiniVMac.cc
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
#include "MiniVMac.h"
|
||||||
|
#include "Launcher.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "hfs.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
namespace po = boost::program_options;
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
class MiniVMacLauncher : public Launcher
|
||||||
|
{
|
||||||
|
fs::path imagePath;
|
||||||
|
fs::path systemImage;
|
||||||
|
|
||||||
|
hfsvol *sysvol;
|
||||||
|
hfsvol *vol;
|
||||||
|
|
||||||
|
void CopySystemFile(const char* fn, const char *dstfn = NULL);
|
||||||
|
public:
|
||||||
|
MiniVMacLauncher(po::variables_map& options);
|
||||||
|
virtual ~MiniVMacLauncher();
|
||||||
|
|
||||||
|
virtual bool Go(int timeout = 0);
|
||||||
|
virtual void DumpOutput();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
MiniVMacLauncher::MiniVMacLauncher(po::variables_map &options)
|
||||||
|
: Launcher(options, ResourceFile::Format::percent_appledouble),
|
||||||
|
sysvol(NULL), vol(NULL)
|
||||||
|
{
|
||||||
|
imagePath = fs::absolute(tempDir / "image.dsk");
|
||||||
|
systemImage = fs::absolute(options["system-image"].as<std::string>());
|
||||||
|
fs::path autoquitImage = fs::absolute(options["autoquit-image"].as<std::string>());
|
||||||
|
|
||||||
|
sysvol = hfs_mount(systemImage.string().c_str(),0, HFS_MODE_RDONLY);
|
||||||
|
assert(sysvol);
|
||||||
|
hfsvolent ent;
|
||||||
|
hfs_vstat(sysvol, &ent);
|
||||||
|
hfs_setcwd(sysvol, ent.blessed);
|
||||||
|
|
||||||
|
|
||||||
|
int size = 5000*1024;
|
||||||
|
|
||||||
|
fs::ofstream(imagePath, std::ios::binary | std::ios::trunc).seekp(size-1).put(0);
|
||||||
|
hfs_format(imagePath.string().c_str(), 0, 0, "SysAndApp", 0, NULL);
|
||||||
|
|
||||||
|
{
|
||||||
|
extern unsigned char bootblock[1024];
|
||||||
|
std::vector<unsigned char> bootblock1(bootblock, bootblock+1024);
|
||||||
|
|
||||||
|
bootblock1[0x1A] = 8;
|
||||||
|
memcpy(&bootblock1[0x1B],"AutoQuit", 8);
|
||||||
|
bootblock1[0x5A] = 3;
|
||||||
|
memcpy(&bootblock1[0x5B],"App", 3);
|
||||||
|
|
||||||
|
std::fstream(imagePath.string(), std::ios::in | std::ios::out | std::ios::binary)
|
||||||
|
.write((const char*) bootblock1.data(), 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vol = hfs_mount(imagePath.string().c_str(), 0, HFS_MODE_RDWR);
|
||||||
|
assert(vol);
|
||||||
|
|
||||||
|
hfs_vstat(vol, &ent);
|
||||||
|
ent.blessed = hfs_getcwd(vol);
|
||||||
|
hfs_vsetattr(vol, &ent);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CopySystemFile("System");
|
||||||
|
CopySystemFile("Finder");
|
||||||
|
CopySystemFile("MacsBug");
|
||||||
|
|
||||||
|
{
|
||||||
|
std::ostringstream rsrcOut;
|
||||||
|
app.resources.writeFork(rsrcOut);
|
||||||
|
std::string rsrc = rsrcOut.str();
|
||||||
|
std::string& data = app.data;
|
||||||
|
|
||||||
|
hfsfile *file = hfs_create(vol, "App","APPL","????");
|
||||||
|
hfs_setfork(file, 0);
|
||||||
|
hfs_write(file, data.data(), data.size());
|
||||||
|
hfs_setfork(file, 1);
|
||||||
|
hfs_write(file, rsrc.data(), rsrc.size());
|
||||||
|
hfs_close(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
hfs_umount(sysvol);
|
||||||
|
sysvol = hfs_mount(autoquitImage.string().c_str(),0, HFS_MODE_RDONLY);
|
||||||
|
assert(sysvol);
|
||||||
|
CopySystemFile("AutoQuit");
|
||||||
|
|
||||||
|
{
|
||||||
|
hfsfile *file = hfs_create(vol, "out", "TEXT", "MPS ");
|
||||||
|
hfs_close(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
hfs_umount(sysvol); sysvol = NULL;
|
||||||
|
hfs_umount(vol); vol = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MiniVMacLauncher::~MiniVMacLauncher()
|
||||||
|
{
|
||||||
|
if(sysvol)
|
||||||
|
hfs_umount(sysvol);
|
||||||
|
if(vol)
|
||||||
|
hfs_umount(vol);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiniVMacLauncher::CopySystemFile(const char *fn, const char *dstfn)
|
||||||
|
{
|
||||||
|
if(!dstfn)
|
||||||
|
dstfn = fn;
|
||||||
|
hfsdirent fileent;
|
||||||
|
if(hfs_stat(sysvol, fn, &fileent) < 0)
|
||||||
|
return;
|
||||||
|
hfsfile *in = hfs_open(sysvol, fn);
|
||||||
|
hfsfile *out = hfs_create(vol, dstfn, fileent.u.file.type,fileent.u.file.creator);
|
||||||
|
|
||||||
|
std::vector<uint8_t> buffer(std::max(fileent.u.file.dsize, fileent.u.file.rsize));
|
||||||
|
hfs_setfork(in, 0);
|
||||||
|
hfs_setfork(out, 0);
|
||||||
|
hfs_read(in, buffer.data(), fileent.u.file.dsize);
|
||||||
|
hfs_write(out, buffer.data(), fileent.u.file.dsize);
|
||||||
|
hfs_setfork(in, 1);
|
||||||
|
hfs_setfork(out, 1);
|
||||||
|
hfs_read(in, buffer.data(), fileent.u.file.rsize);
|
||||||
|
hfs_write(out, buffer.data(), fileent.u.file.rsize);
|
||||||
|
hfs_close(in);
|
||||||
|
hfs_close(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool MiniVMacLauncher::Go(int timeout)
|
||||||
|
{
|
||||||
|
fs::path minivmacdir = fs::absolute( options["minivmac-dir"].as<std::string>() );
|
||||||
|
std::string minivmacpath = options["minivmac-path"].as<std::string>();
|
||||||
|
|
||||||
|
fs::current_path(minivmacdir);
|
||||||
|
|
||||||
|
return ChildProcess(minivmacpath, { imagePath.string() }, timeout) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiniVMacLauncher::DumpOutput()
|
||||||
|
{
|
||||||
|
sleep(1);
|
||||||
|
vol = hfs_mount(imagePath.string().c_str(), 0, HFS_MODE_RDONLY);
|
||||||
|
hfsdirent fileent;
|
||||||
|
int statres = hfs_stat(vol, "out", &fileent);
|
||||||
|
std::cerr << "stat: " << statres << "\n";
|
||||||
|
std::cerr << "out: " << fileent.u.file.dsize << " bytes\n";
|
||||||
|
|
||||||
|
hfsfile *out = hfs_open(vol, "out");
|
||||||
|
if(!out)
|
||||||
|
return;
|
||||||
|
std::vector<char> buffer(fileent.u.file.dsize);
|
||||||
|
hfs_setfork(out, 0);
|
||||||
|
hfs_read(out, buffer.data(), fileent.u.file.dsize);
|
||||||
|
hfs_close(out);
|
||||||
|
std::cout << string(buffer.begin(), buffer.end());
|
||||||
|
hfs_umount(vol); vol = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MiniVMac::GetOptions(options_description &desc)
|
||||||
|
{
|
||||||
|
desc.add_options()
|
||||||
|
("minivmac-dir", po::value<std::string>(),"directory containing vMac.ROM")
|
||||||
|
("minivmac-path", po::value<std::string>()->default_value("./minivmac"),"relative path to minivmac")
|
||||||
|
("system-image", po::value<std::string>(),"path to disk image with system")
|
||||||
|
("autoquit-image", po::value<std::string>(),"path to autoquit disk image, available from the minivmac web site")
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MiniVMac::CheckOptions(variables_map &options)
|
||||||
|
{
|
||||||
|
return options.count("minivmac-path") != 0
|
||||||
|
&& options.count("minivmac-dir") != 0
|
||||||
|
&& options.count("system-image") != 0
|
||||||
|
&& options.count("autoquit-image") != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Launcher> MiniVMac::MakeLauncher(variables_map &options)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<Launcher>(new MiniVMacLauncher(options));
|
||||||
|
}
|
16
LaunchAPPL/MiniVMac.h
Normal file
16
LaunchAPPL/MiniVMac.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef MINIVMAC_H
|
||||||
|
#define MINIVMAC_H
|
||||||
|
|
||||||
|
#include "LaunchMethod.h"
|
||||||
|
|
||||||
|
class MiniVMac : public LaunchMethod
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual std::string GetName() { return "minivmac"; }
|
||||||
|
virtual void GetOptions(options_description& desc);
|
||||||
|
virtual bool CheckOptions(variables_map& options);
|
||||||
|
|
||||||
|
virtual std::unique_ptr<Launcher> MakeLauncher(variables_map& options);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MINIVMAC_H
|
BIN
LaunchAPPL/bootblock
Normal file
BIN
LaunchAPPL/bootblock
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user