diff options
Diffstat (limited to 'src/modules/SystemBackendPosix/SystemBackendPosix.cpp')
-rw-r--r-- | src/modules/SystemBackendPosix/SystemBackendPosix.cpp | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/src/modules/SystemBackendPosix/SystemBackendPosix.cpp b/src/modules/SystemBackendPosix/SystemBackendPosix.cpp new file mode 100644 index 0000000..4969c6d --- /dev/null +++ b/src/modules/SystemBackendPosix/SystemBackendPosix.cpp @@ -0,0 +1,296 @@ +/* + * SystemBackendPosix.cpp + * + * Copyright (C) 2008 Matthias Schiffer <matthias@gamezock.de> + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "SystemBackendPosix.h" + +#include <Net/FdManager.h> +#include <Common/ActionManager.h> + +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <sstream> + +#include <fcntl.h> +#include <signal.h> +#include <spawn.h> +#include <sys/wait.h> +#include <unistd.h> + +#define init SystemBackendPosix_LTX_init +#define deinit SystemBackendPosix_LTX_deinit + +namespace Mad { +namespace Modules { + +SystemBackendPosix *SystemBackendPosix::backend = 0; + +std::map<pid_t, boost::function1<void, int> > SystemBackendPosix::processes; +std::map<pid_t, boost::function2<void, int, const std::string&> > SystemBackendPosix::processesWithOutput; +std::map<pid_t, int> SystemBackendPosix::processesWOHandles; +std::map<pid_t, std::string > SystemBackendPosix::processesWOOutput; + + +void SystemBackendPosix::setChildHandler() { + struct sigaction action; + + action.sa_handler = childHandler; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + + sigaction(SIGCHLD, &action, 0); +} + +SystemBackendPosix::~SystemBackendPosix() { + struct sigaction action; + + action.sa_handler = SIG_DFL; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + + sigaction(SIGCHLD, &action, 0); +} + + +void SystemBackendPosix::fsInfoCallback(int, const std::string &output, const boost::function1<void, const std::vector<Common::SystemManager::FSInfo>& > &callback) { + std::vector<Common::SystemManager::FSInfo> ret; + std::istringstream stream(output); + std::string str; + + std::getline(stream, str); // ignore first line + + while(!stream.eof()) { + std::getline(stream, str); + + char *fsName = new char[str.length()+1]; + char *mountedOn = new char[str.length()+1]; + + Common::SystemManager::FSInfo info; + + if(std::sscanf(str.c_str(), "%s %lld %lld %lld %*d%% %s", fsName, &info.total, &info.used, &info.available, mountedOn) == 5) { + info.fsName = fsName; + info.mountedOn = mountedOn; + + ret.push_back(info); + } + + delete [] fsName; + delete [] mountedOn; + } + + callback(ret); +} + +bool SystemBackendPosix::getFSInfo(const boost::function1<void, const std::vector<Common::SystemManager::FSInfo>& > &callback) { + std::vector<std::string> argv; + + argv.push_back("/bin/df"); + argv.push_back("-P"); + argv.push_back("-k"); + + return execWithOutput(boost::bind(&SystemBackendPosix::fsInfoCallback, this, _1, _2, callback), "/bin/df", argv); +} + + +void SystemBackendPosix::childHandler(int) { + int status; + pid_t pid; + + while((pid = waitpid(-1, &status, WNOHANG)) > 0) { + std::map<pid_t, boost::function1<void, int> >::iterator it = processes.find(pid); + + if(it != processes.end()) { + Common::ActionManager::get()->add(boost::bind(it->second, status)); + processes.erase(it); + } + else { + std::map<pid_t, boost::function2<void, int, const std::string&> >::iterator it2 = processesWithOutput.find(pid); + + if(it2 != processesWithOutput.end()) { + char buffer[1024]; + ssize_t n; + std::string &output = processesWOOutput[pid]; + int handle = processesWOHandles[pid]; + + while((n = read(handle, buffer, sizeof(buffer))) > 0) + output += std::string(buffer, n); + + //Net::FdManager::get()->unregisterFd(handle); + //close(handle); + + //Common::ActionManager::get()->add(boost::bind(it2->second, status, output)); + processesWithOutput.erase(it2); + processesWOHandles.erase(pid); + processesWOOutput.erase(pid); + } + } + } + + setChildHandler(); +} + +void SystemBackendPosix::outputHandler(short events, pid_t pid) { + char buf[1024]; + + if(events & POLLIN) { + sigset_t set, oldset; + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + sigprocmask(SIG_BLOCK, &set, &oldset); + + ssize_t ret; + + std::map<pid_t, int>::iterator handle = processesWOHandles.find(pid); + if(handle == processesWOHandles.end()) + return; + + while((ret = read(handle->second, buf, sizeof(buf))) > 0) + processesWOOutput[pid] += std::string(buf, ret); + + sigprocmask(SIG_SETMASK, &oldset, 0); + } +} + +std::pair<char**, char**> SystemBackendPosix::makeArgs(const std::string &filename, const std::vector<std::string> &argv, const std::vector<std::string> &env) { + char **argvp, **envp; + + if(argv.empty()) { + argvp = new char*[2]; + + argvp[0] = strdup(filename.c_str()); + argvp[1] = 0; + } + else { + argvp = new char*[argv.size() + 1]; + + for(size_t s = 0; s < argv.size(); ++s) { + argvp[s] = strdup(argv[s].c_str()); + } + + argvp[argv.size()] = 0; + } + + if(env.empty()) { + envp = environ; + } + else { + envp = new char*[env.size() + 1]; + + for(size_t s = 0; s < env.size(); ++s) { + envp[0] = strdup(env[s].c_str()); + } + + envp[env.size()] = 0; + } + + return std::make_pair(argvp, envp); +} + +void SystemBackendPosix::destroyArgs(std::pair<char**, char**> args) { + for(char **p = args.first; *p != 0; ++p) + std::free(*p); + + delete [] args.first; + + if(args.second != environ) { + for(char **p = args.second; *p != 0; ++p) + std::free(*p); + + delete [] args.second; + } +} + +bool SystemBackendPosix::exec(const boost::function1<void, int> &resultHandler, const std::string &filename, const std::vector<std::string> &argv, const std::vector<std::string> &env) { + pid_t pid; + std::pair<char**, char**> args = makeArgs(filename, argv, env); + + sigset_t set, oldset; + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + sigprocmask(SIG_BLOCK, &set, &oldset); + + bool ret = (posix_spawnp(&pid, filename.c_str(), 0, 0, args.first, args.second) == 0); + + if(ret) + processes.insert(std::make_pair(pid, resultHandler)); + + sigprocmask(SIG_SETMASK, &oldset, 0); + + destroyArgs(args); + + return ret; +} + +bool SystemBackendPosix::execWithOutput(const boost::function2<void, int, const std::string&> &resultHandler, const std::string &filename, const std::vector<std::string> &argv, const std::vector<std::string> &env) { + pid_t pid; + std::pair<char**, char**> args = makeArgs(filename, argv, env); + + int saveStdout = dup(STDOUT_FILENO); + int pipeHandles[2]; + + pipe(pipeHandles); + + fcntl(pipeHandles[0], F_SETFD, FD_CLOEXEC); + + int flags = fcntl(pipeHandles[0], F_GETFL, 0); + fcntl(pipeHandles[0], F_SETFL, flags | O_NONBLOCK); + + sigset_t set, oldset; + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + sigprocmask(SIG_BLOCK, &set, &oldset); + + dup2(pipeHandles[1], STDOUT_FILENO); // set the new pipe as stdout + close(pipeHandles[1]); + + bool ret = (posix_spawnp(&pid, filename.c_str(), 0, 0, args.first, args.second) == 0); + + if(ret) { + processesWithOutput.insert(std::make_pair(pid, resultHandler)); + processesWOHandles.insert(std::make_pair(pid, pipeHandles[0])); + processesWOOutput.insert(std::make_pair(pid, std::string())); + + Net::FdManager::get()->registerFd(pipeHandles[0], boost::bind(&SystemBackendPosix::outputHandler, _1, pid), POLLIN); + } + + dup2(saveStdout, STDOUT_FILENO); // restore old stdout + close(saveStdout); + + sigprocmask(SIG_SETMASK, &oldset, 0); + + destroyArgs(args); + + return ret; +} + +} +} + +extern "C" { + +void init() { + Mad::Modules::SystemBackendPosix::registerBackend(); +} + +void deinit() { + Mad::Modules::SystemBackendPosix::unregisterBackend(); +} + +} + |