mirror of
https://github.com/autc04/Retro68.git
synced 2024-11-23 00:34:22 +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()
|
||||
|
||||
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_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_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_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")
|
||||
|
||||
add_application(Timeout Timeout.c)
|
||||
add_test(NAME Timeout
|
||||
COMMAND sh -c "! LaunchAPPL -e Timeout.bin --logfile=out -t2"
|
||||
add_application(Log Log.c)
|
||||
add_test(NAME Log
|
||||
COMMAND LaunchAPPL ${RETRO68_TEST_CONFIG} Log.bin
|
||||
)
|
||||
set_tests_properties(Timeout PROPERTIES PASS_REGULAR_EXPRESSION "One;Two"
|
||||
FAIL_REGULAR_EXPRESSION "Three")
|
||||
set_tests_properties(Log PROPERTIES PASS_REGULAR_EXPRESSION "One\nTwo\nThree")
|
||||
|
@ -4,8 +4,6 @@ int main()
|
||||
{
|
||||
TEST_LOG_SIZED("One",3);
|
||||
TEST_LOG_SIZED("Two",3);
|
||||
for(;;)
|
||||
;
|
||||
TEST_LOG_SIZED("Three",5);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,6 +1,13 @@
|
||||
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_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 <string>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
|
||||
#include "ResourceFork.h"
|
||||
#include "ResourceFile.h"
|
||||
|
||||
extern "C" {
|
||||
#include "hfs.h"
|
||||
}
|
||||
#include "LaunchMethod.h"
|
||||
#include "Launcher.h"
|
||||
|
||||
#include "Executor.h"
|
||||
#include "MiniVMac.h"
|
||||
|
||||
namespace po = boost::program_options;
|
||||
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[])
|
||||
{
|
||||
std::vector<LaunchMethod*> methods = {
|
||||
new Executor(), new MiniVMac()
|
||||
};
|
||||
desc.add_options()
|
||||
("help,h", "show this help message")
|
||||
("executor,e", "run using executor")
|
||||
("minivmac,m", "run using executor")
|
||||
|
||||
("executor-path", po::value<std::string>()->default_value("executor"),"path to executor")
|
||||
("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")
|
||||
("emulator,e", po::value<std::string>(), "what emulator/environment to use")
|
||||
;
|
||||
for(LaunchMethod *lm : methods)
|
||||
lm->GetOptions(desc);
|
||||
|
||||
desc.add_options()
|
||||
("timeout,t", po::value<int>(),"abort after timeout")
|
||||
("timeout-ok","timeout counts as success")
|
||||
("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" )
|
||||
;
|
||||
alldesc.add(desc).add(hidden);
|
||||
|
||||
try
|
||||
{
|
||||
auto parsed = po::command_line_parser(argc, argv)
|
||||
@ -162,198 +69,41 @@ int main(int argc, char *argv[])
|
||||
|
||||
po::notify(options);
|
||||
|
||||
if(options.count("help") || !options.count("application"))
|
||||
if(options.count("help") || !options.count("application") || !options.count("emulator"))
|
||||
{
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ResourceFile app(options["application"].as<std::string>());
|
||||
if(!app.read())
|
||||
LaunchMethod *method = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(options.count("executor"))
|
||||
if(!method->CheckOptions(options))
|
||||
{
|
||||
fs::path tempDir = fs::unique_path();
|
||||
fs::create_directories(tempDir);
|
||||
|
||||
fs::path appPath = tempDir / "Application";
|
||||
|
||||
app.assign(appPath.string(), ResourceFile::Format::percent_appledouble);
|
||||
if(!app.write())
|
||||
{
|
||||
std::cerr << "Could not write application file.\n";
|
||||
std::cerr << "Missing configuration.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(options.count("logfile"))
|
||||
{
|
||||
fs::ofstream out(tempDir/options["logfile"].as<std::string>());
|
||||
}
|
||||
std::unique_ptr<Launcher> launcher = method->MakeLauncher(options);
|
||||
|
||||
int result = ChildProcess(options["executor-path"].as<std::string>(), { appPath.string() });
|
||||
int timeout = options.count("timeout") ? options["timeout"].as<int>() : 0;
|
||||
|
||||
if(options.count("logfile"))
|
||||
{
|
||||
fs::ifstream in(tempDir/options["logfile"].as<std::string>());
|
||||
std::cout << in.rdbuf();
|
||||
}
|
||||
bool result = launcher->Go(timeout);
|
||||
|
||||
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);
|
||||
launcher->DumpOutput();
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
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;
|
||||
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…
Reference in New Issue
Block a user