/* * SystemBackendPosix.cpp * * Copyright (C) 2008 Matthias Schiffer * * 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 . */ #include "SystemBackendPosix.h" #include #include #include #include #include #include #include #include #include #include #include #define init SystemBackendPosix_LTX_init #define deinit SystemBackendPosix_LTX_deinit namespace Mad { namespace Modules { SystemBackendPosix *SystemBackendPosix::backend = 0; std::map > SystemBackendPosix::processes; std::map > SystemBackendPosix::processesWithOutput; std::map SystemBackendPosix::processesWOHandles; std::map 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& > &callback) { std::vector 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& > &callback) { std::vector 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 >::iterator it = processes.find(pid); if(it != processes.end()) { Common::ActionManager::get()->add(boost::bind(it->second, status)); processes.erase(it); } else { std::map >::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::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 SystemBackendPosix::makeArgs(const std::string &filename, const std::vector &argv, const std::vector &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 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 &resultHandler, const std::string &filename, const std::vector &argv, const std::vector &env) { pid_t pid; std::pair 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 &resultHandler, const std::string &filename, const std::vector &argv, const std::vector &env) { pid_t pid; std::pair 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(); } }