diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index 04f5aa6f8d..ca372552f4 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -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}) diff --git a/LaunchAPPL/Carbon.cc b/LaunchAPPL/Carbon.cc index 9c36553c9c..b7e20d4f0f 100644 --- a/LaunchAPPL/Carbon.cc +++ b/LaunchAPPL/Carbon.cc @@ -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"; diff --git a/LaunchAPPL/Executor.cc b/LaunchAPPL/Executor.cc index 42277e587c..64fb65aacc 100644 --- a/LaunchAPPL/Executor.cc +++ b/LaunchAPPL/Executor.cc @@ -1,5 +1,6 @@ #include "Executor.h" #include "Launcher.h" +#include "Utilities.h" namespace po = boost::program_options; diff --git a/LaunchAPPL/LaunchAPPL.cc b/LaunchAPPL/LaunchAPPL.cc index 3948eb7239..9fae7f8eea 100644 --- a/LaunchAPPL/LaunchAPPL.cc +++ b/LaunchAPPL/LaunchAPPL.cc @@ -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. }; diff --git a/LaunchAPPL/LaunchMethod.cc b/LaunchAPPL/LaunchMethod.cc index dfe67cf980..c2ee674c24 100644 --- a/LaunchAPPL/LaunchMethod.cc +++ b/LaunchAPPL/LaunchMethod.cc @@ -1,8 +1,4 @@ #include "LaunchMethod.h" -#include -#include - -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; -} diff --git a/LaunchAPPL/LaunchMethod.h b/LaunchAPPL/LaunchMethod.h index c8606b4bc3..3c4e307b1d 100644 --- a/LaunchAPPL/LaunchMethod.h +++ b/LaunchAPPL/LaunchMethod.h @@ -66,13 +66,6 @@ public: virtual std::unique_ptr 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 diff --git a/LaunchAPPL/Launcher.cc b/LaunchAPPL/Launcher.cc index c2567e5b69..3094b323d9 100644 --- a/LaunchAPPL/Launcher.cc +++ b/LaunchAPPL/Launcher.cc @@ -2,10 +2,6 @@ #include #include #include -#include -#include -#include -#include namespace fs = boost::filesystem; @@ -60,91 +56,3 @@ Launcher::~Launcher() -int Launcher::ChildProcess(string program, vector args, int timeout) -{ - std::vector 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 (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; - } - } - -} diff --git a/LaunchAPPL/Launcher.h b/LaunchAPPL/Launcher.h index 5c8a66fec8..d47cced085 100644 --- a/LaunchAPPL/Launcher.h +++ b/LaunchAPPL/Launcher.h @@ -19,8 +19,6 @@ protected: ResourceFile app; boost::filesystem::path tempDir, appPath, outPath; bool keepTempFiles; - - int ChildProcess(std::string program, std::vector args, int timeout); public: /** * @brief Launcher diff --git a/LaunchAPPL/MiniVMac.cc b/LaunchAPPL/MiniVMac.cc index ac0d2ae422..2773f51382 100644 --- a/LaunchAPPL/MiniVMac.cc +++ b/LaunchAPPL/MiniVMac.cc @@ -1,5 +1,6 @@ #include "MiniVMac.h" #include "Launcher.h" +#include "Utilities.h" extern "C" { #include "hfs.h" diff --git a/LaunchAPPL/SSH.cc b/LaunchAPPL/SSH.cc new file mode 100644 index 0000000000..8286341cd3 --- /dev/null +++ b/LaunchAPPL/SSH.cc @@ -0,0 +1,150 @@ +#include "SSH.h" +#include "Launcher.h" +#include "Utilities.h" + +#include +#include +#include +#include +#include +#include + +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& args, po::variables_map& options, string name) +{ + if(options.count(name)) + { + auto extraArgs = SplitArguments(options[name].as>()); + args.insert(args.end(), extraArgs.begin(), extraArgs.end()); + } +} + +bool SSHLauncher::Go(int timeout) +{ + std::vector args; + args.push_back(options["ssh-host"].as()); + insertArgs(args, options, "ssh-args"); + args.push_back("--"); + args.push_back(options["ssh-remote-path"].as()); + insertArgs(args, options, "ssh-remote-args"); + if(timeout) + { + args.push_back("--timeout"); + args.push_back(boost::lexical_cast(timeout)); + } + args.push_back("-"); + + std::string program = options["ssh-path"].as(); + + std::vector 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 (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()->default_value("ssh"),"ssh command to use") + ("ssh-host", po::value(), + "[username@]address of remote host") + ("ssh-args", po::value>(), + "additional arguments for ssh") + ("ssh-remote-path", po::value()->default_value("LaunchAPPL"), + "path to LaunchAPPL on remote host") + ("ssh-remote-args", po::value>(), + "additional arguments for LaunchAPPL on remote host") + ; + +} + +bool SSH::CheckOptions(variables_map &options) +{ + return options.count("ssh-host") != 0; +} + +std::unique_ptr SSH::MakeLauncher(variables_map &options) +{ + return std::unique_ptr(new SSHLauncher(options)); +} diff --git a/LaunchAPPL/SSH.h b/LaunchAPPL/SSH.h new file mode 100644 index 0000000000..24adf34ef5 --- /dev/null +++ b/LaunchAPPL/SSH.h @@ -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 MakeLauncher(variables_map& options); +}; + +#endif // SSH_H diff --git a/LaunchAPPL/Utilities.cc b/LaunchAPPL/Utilities.cc new file mode 100644 index 0000000000..e9514cdb94 --- /dev/null +++ b/LaunchAPPL/Utilities.cc @@ -0,0 +1,192 @@ +#include "Utilities.h" + +#include +#include +#include +#include +#include +#include + +namespace fs = boost::filesystem; +using std::string; +using std::vector; + +int ChildProcess(string program, vector args, int timeout) +{ + std::vector 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 (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 SplitArguments(std::string str) +{ + bool backslash = false; + bool quote = false; + bool begun = false; + vector 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 SplitArguments(vector strs) +{ + vector args; + for(string str : strs) + { + vector args1 = SplitArguments(str); + args.insert(args.end(), args1.begin(), args1.end()); + } + return args; +} diff --git a/LaunchAPPL/Utilities.h b/LaunchAPPL/Utilities.h new file mode 100644 index 0000000000..41b9157f3b --- /dev/null +++ b/LaunchAPPL/Utilities.h @@ -0,0 +1,21 @@ +#ifndef UTILITIES_H +#define UTILITIES_H + +#include +#include + +int ChildProcess(std::string program, std::vector 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 SplitArguments(std::string str); +std::vector SplitArguments(std::vector strs); + +#endif // UTILITIES_H