/* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NDB_PROCESS_HPP #define NDB_PROCESS_HPP #include class NdbProcess { #ifdef _WIN32 typedef DWORD pid_t; #endif pid_t m_pid; BaseString m_name; public: static pid_t getpid() { #ifdef _WIN32 return GetCurrentProcessid(); #else return ::getpid(); #endif } class Args { Vector m_args; public: void add(const char* str) { m_args.push_back(str); } void add(const char* str, const char* str2) { BaseString tmp; tmp.assfmt("%s%s", str, str2); m_args.push_back(tmp); } void add(const char* str, int val) { BaseString tmp; tmp.assfmt("%s%d", str, val); m_args.push_back(tmp); } void add(const Args & args) { for (unsigned i = 0; i < args.m_args.size(); i++) add(args.m_args[i].c_str()); } const Vector& args(void) const { return m_args; } }; static NdbProcess* create(const BaseString& name, const BaseString& path, const BaseString& cwd, const Args& args) { NdbProcess* proc = new NdbProcess(name); if (!proc) { fprintf(stderr, "Failed to allocate memory for new process\n"); return NULL; } // Check existence of cwd if (cwd.c_str() && access(cwd.c_str(), F_OK) != 0) { fprintf(stderr, "The specified working directory '%s' does not exist\n", cwd.c_str()); delete proc; return NULL; } if (!start_process(proc->m_pid, path.c_str(), cwd.c_str(), args)) { fprintf(stderr, "Failed to create process '%s'\n", name.c_str()); delete proc; return NULL; } return proc; } bool stop(void) { int ret = kill(m_pid, 9); if (ret != 0) { fprintf(stderr, "Failed to kill process %d, ret: %d, errno: %d\n", m_pid, ret, errno); return false; } printf("Stopped process %d\n", m_pid); return true; } bool wait(int& ret, int timeout = 0) { int retries = 0; int status; while (true) { pid_t ret_pid = waitpid(m_pid, &status, WNOHANG); if (ret_pid == -1) { fprintf(stderr, "Error occured when waiting for process %d, ret: %d, errno: %d\n", m_pid, status, errno); return false; } if (ret_pid == m_pid) { if (WIFEXITED(status)) ret = WEXITSTATUS(status); else if (WIFSIGNALED(status)) ret = WTERMSIG(status); else ret = 37; // Unknown exit status printf("Got process %d, status: %d, ret: %d\n", m_pid, status, ret); return true; } if (timeout == 0) return false; if (retries++ > timeout*10) { fprintf(stderr, "Timeout when waiting for process %d\n", m_pid); return false; } NdbSleep_MilliSleep(10); } require(false); // Never reached } private: NdbProcess(BaseString name) : m_name(name) { m_pid = -1; } static bool start_process(pid_t& pid, const char* path, const char* cwd, const Args& args) { #ifdef _WIN32 #else int retries = 5; pid_t tmp; while ((tmp = fork()) == -1) { fprintf(stderr, "Warning: 'fork' failed, errno: %d - ", errno); if (retries--) { fprintf(stderr, "retrying in 1 second...\n"); NdbSleep_SecSleep(1); continue; } fprintf(stderr, "giving up...\n"); return false; } if (tmp) { pid = tmp; printf("Started process: %d\n", pid); return true; } require(tmp == 0); if (cwd && chdir(cwd) != 0) { fprintf(stderr, "Failed to change directory to '%s', errno: %d\n", cwd, errno); exit(1); } // Concatenate arguments BaseString args_str; args_str.assign(args.args(), " "); char **argv = BaseString::argify(path, args_str.c_str()); //printf("name: %s\n", path); execv(path, argv); fprintf(stderr, "execv failed, errno: %d\n", errno); exit(1); #endif } }; #endif