LaunchAPPL: add new method 'SSH' and move utility functions to separate module

This commit is contained in:
Wolfgang Thaller 2017-10-09 15:45:36 +02:00
parent 1657cf599c
commit ed80c2c09f
13 changed files with 388 additions and 142 deletions

View File

@ -3,6 +3,7 @@ find_package(Boost COMPONENTS filesystem program_options)
set(LAUNCHMETHODS
Executor.h Executor.cc
MiniVMac.h MiniVMac.cc
SSH.h SSH.cc
)
if(APPLE)
@ -21,6 +22,8 @@ add_executable(LaunchAPPL
LaunchMethod.h LaunchMethod.cc
Launcher.h Launcher.cc
Utilities.h Utilities.cc
${LAUNCHMETHODS})

View File

@ -1,5 +1,6 @@
#include "Carbon.h"
#include "Launcher.h"
#include "Utilities.h"
const std::string launchCFM =
"/System/Library/Frameworks/Carbon.framework/Versions/A/Support/LaunchCFMApp";

View File

@ -1,5 +1,6 @@
#include "Executor.h"
#include "Launcher.h"
#include "Utilities.h"
namespace po = boost::program_options;

View File

@ -19,6 +19,7 @@
#endif
#include "Executor.h"
#include "MiniVMac.h"
#include "SSH.h"
namespace po = boost::program_options;
namespace fs = boost::filesystem;
@ -40,7 +41,8 @@ static void RegisterLaunchMethods()
# endif
new Carbon(),
#endif
new Executor(), new MiniVMac()
new Executor(), new MiniVMac(),
new SSH()
// #### Add new `LaunchMethod`s here.
};

View File

@ -1,8 +1,4 @@
#include "LaunchMethod.h"
#include <boost/filesystem.hpp>
#include <unistd.h>
namespace fs = boost::filesystem;
LaunchMethod::LaunchMethod()
{
@ -27,39 +23,3 @@ bool LaunchMethod::CheckOptions(boost::program_options::variables_map &options)
{
return true;
}
bool LaunchMethod::CheckExecutable(std::string program)
{
if(access(program.c_str(), X_OK) == 0)
return true;
if(program.find("/") != std::string::npos)
return false;
const char *PATH = getenv("PATH");
if(PATH)
{
bool endFound = false;
do
{
const char *end = strchr(PATH, ':');
if(!end)
{
end = strchr(PATH, '\0');
endFound = true;
}
std::string pathElement(PATH, end);
if(pathElement == "")
pathElement = ".";
fs::path f = fs::path(pathElement) / program;
if(access(f.string().c_str(), X_OK) == 0)
return true;
PATH = end + 1;
} while(!endFound);
}
return false;
}

View File

@ -66,13 +66,6 @@ public:
virtual std::unique_ptr<Launcher> MakeLauncher(variables_map& options) = 0;
protected:
/**
* @brief CheckExecutable
* @param program
* @return true if "program" exists in the $PATH and is executable.
*/
bool CheckExecutable(std::string program);
};
#endif // LAUNCHMETHOD_H

View File

@ -2,10 +2,6 @@
#include <boost/filesystem/fstream.hpp>
#include <iostream>
#include <sstream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
namespace fs = boost::filesystem;
@ -60,91 +56,3 @@ Launcher::~Launcher()
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");
std::cerr << "Tried to execute: " << program;
for(auto a : args)
std::cerr << " " << a;
std::cerr << std::endl;
_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;
}
}
}

View File

@ -19,8 +19,6 @@ protected:
ResourceFile app;
boost::filesystem::path tempDir, appPath, outPath;
bool keepTempFiles;
int ChildProcess(std::string program, std::vector<std::string> args, int timeout);
public:
/**
* @brief Launcher

View File

@ -1,5 +1,6 @@
#include "MiniVMac.h"
#include "Launcher.h"
#include "Utilities.h"
extern "C" {
#include "hfs.h"

150
LaunchAPPL/SSH.cc Normal file
View File

@ -0,0 +1,150 @@
#include "SSH.h"
#include "Launcher.h"
#include "Utilities.h"
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <boost/lexical_cast.hpp>
namespace po = boost::program_options;
using std::string;
using std::vector;
class SSHLauncher : public Launcher
{
public:
SSHLauncher(po::variables_map& options);
virtual ~SSHLauncher();
virtual bool Go(int timeout = 0);
};
SSHLauncher::SSHLauncher(po::variables_map &options)
: Launcher(options, ResourceFile::Format::percent_appledouble)
{
}
SSHLauncher::~SSHLauncher()
{
}
static void insertArgs(vector<string>& args, po::variables_map& options, string name)
{
if(options.count(name))
{
auto extraArgs = SplitArguments(options[name].as<vector<string>>());
args.insert(args.end(), extraArgs.begin(), extraArgs.end());
}
}
bool SSHLauncher::Go(int timeout)
{
std::vector<std::string> args;
args.push_back(options["ssh-host"].as<string>());
insertArgs(args, options, "ssh-args");
args.push_back("--");
args.push_back(options["ssh-remote-path"].as<string>());
insertArgs(args, options, "ssh-remote-args");
if(timeout)
{
args.push_back("--timeout");
args.push_back(boost::lexical_cast<std::string>(timeout));
}
args.push_back("-");
std::string program = options["ssh-path"].as<std::string>();
std::vector<const char*> argv;
argv.push_back(program.c_str());
for(std::string& s : args)
argv.push_back(s.c_str());
argv.push_back(NULL);
int fd[2];
pipe(fd);
const int READ_END = 0;
const int WRITE_END = 1;
pid_t pid = fork();
if(pid < 0)
{
perror("unable to fork");
return 1;
}
else if(pid == 0)
{
dup2(fd[READ_END], STDIN_FILENO);
close(fd[WRITE_END]);
close(fd[READ_END]);
execvp(argv[0], const_cast<char* const *> (argv.data()));
perror("exec failed");
std::cerr << "Tried to execute: " << program;
for(auto a : args)
std::cerr << " " << a;
std::cerr << std::endl;
_exit(1);
}
else
{
close(fd[READ_END]);
std::ostringstream tmp;
app.write(tmp, ResourceFile::Format::macbin);
const std::string data = tmp.str();
write(fd[WRITE_END], data.data(), data.size());
close(fd[WRITE_END]);
int wstatus;
int result = 0;
do
{
result = waitpid(pid, &wstatus, 0);
} while(result == -1 && errno == EINTR);
if(!WIFEXITED(wstatus))
{
return false;
}
else
{
//int exitcode = WEXITSTATUS(wstatus);
return true;
}
}
}
void SSH::GetOptions(options_description &desc)
{
desc.add_options()
("ssh-path", po::value<string>()->default_value("ssh"),"ssh command to use")
("ssh-host", po::value<string>(),
"[username@]address of remote host")
("ssh-args", po::value<vector<string>>(),
"additional arguments for ssh")
("ssh-remote-path", po::value<string>()->default_value("LaunchAPPL"),
"path to LaunchAPPL on remote host")
("ssh-remote-args", po::value<vector<string>>(),
"additional arguments for LaunchAPPL on remote host")
;
}
bool SSH::CheckOptions(variables_map &options)
{
return options.count("ssh-host") != 0;
}
std::unique_ptr<Launcher> SSH::MakeLauncher(variables_map &options)
{
return std::unique_ptr<Launcher>(new SSHLauncher(options));
}

16
LaunchAPPL/SSH.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef SSH_H
#define SSH_H
#include "LaunchMethod.h"
class SSH : public LaunchMethod
{
public:
virtual std::string GetName() { return "ssh"; }
virtual void GetOptions(options_description& desc);
virtual bool CheckOptions(variables_map& options);
virtual std::unique_ptr<Launcher> MakeLauncher(variables_map& options);
};
#endif // SSH_H

192
LaunchAPPL/Utilities.cc Normal file
View File

@ -0,0 +1,192 @@
#include "Utilities.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <boost/filesystem.hpp>
#include <iostream>
namespace fs = boost::filesystem;
using std::string;
using std::vector;
int 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");
std::cerr << "Tried to execute: " << program;
for(auto a : args)
std::cerr << " " << a;
std::cerr << std::endl;
_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;
}
}
}
bool CheckExecutable(std::string program)
{
if(access(program.c_str(), X_OK) == 0)
return true;
if(program.find("/") != std::string::npos)
return false;
const char *PATH = getenv("PATH");
if(PATH)
{
bool endFound = false;
do
{
const char *end = strchr(PATH, ':');
if(!end)
{
end = strchr(PATH, '\0');
endFound = true;
}
std::string pathElement(PATH, end);
if(pathElement == "")
pathElement = ".";
fs::path f = fs::path(pathElement) / program;
if(access(f.string().c_str(), X_OK) == 0)
return true;
PATH = end + 1;
} while(!endFound);
}
return false;
}
vector<string> SplitArguments(std::string str)
{
bool backslash = false;
bool quote = false;
bool begun = false;
vector<string> args;
for(char c : str)
{
if(!backslash && !quote && isspace(c))
{
begun = false;
}
else if(!backslash && c == '"')
{
quote = !quote;
if(quote && !begun)
{
args.emplace_back();
begun = true;
}
}
else if(!backslash && c == '\\')
{
backslash = true;
}
else
{
backslash = false;
if(!begun)
{
args.emplace_back();
begun = true;
}
args.back() += c;
}
}
return args;
}
vector<string> SplitArguments(vector<string> strs)
{
vector<string> args;
for(string str : strs)
{
vector<string> args1 = SplitArguments(str);
args.insert(args.end(), args1.begin(), args1.end());
}
return args;
}

21
LaunchAPPL/Utilities.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef UTILITIES_H
#define UTILITIES_H
#include <string>
#include <vector>
int ChildProcess(std::string program, std::vector<std::string> args, int timeout);
/**
* @brief CheckExecutable
* @param program
* @return true if "program" exists in the $PATH and is executable.
*/
bool CheckExecutable(std::string program);
std::vector<std::string> SplitArguments(std::string str);
std::vector<std::string> SplitArguments(std::vector<std::string> strs);
#endif // UTILITIES_H