summaryrefslogtreecommitdiffstats
path: root/src/modules/SystemBackendPosix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/SystemBackendPosix.cpp')
-rw-r--r--src/modules/SystemBackendPosix.cpp299
1 files changed, 299 insertions, 0 deletions
diff --git a/src/modules/SystemBackendPosix.cpp b/src/modules/SystemBackendPosix.cpp
new file mode 100644
index 0000000..d34b2c8
--- /dev/null
+++ b/src/modules/SystemBackendPosix.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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 <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <sstream>
+
+#include <fcntl.h>
+#include <signal.h>
+#include <spawn.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <sigc++/bind.h>
+
+#define init SystemBackendPosix_LTX_init
+#define deinit SystemBackendPosix_LTX_deinit
+
+namespace Mad {
+namespace Modules {
+
+SystemBackendPosix *SystemBackendPosix::backend = 0;
+
+std::map<pid_t, sigc::slot<void, int> > SystemBackendPosix::processes;
+std::map<pid_t, sigc::slot<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 sigc::slot<void, const std::vector<FSInfo>& > &callback) {
+ std::vector<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];
+
+ 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::fsInfo(const sigc::slot<void, const std::vector<FSInfo>& > &callback) {
+ std::vector<std::string> argv;
+
+ argv.push_back("/bin/df");
+ argv.push_back("-P");
+ argv.push_back("-k");
+
+ return execWithOutput(sigc::bind(sigc::mem_fun(this, &SystemBackendPosix::fsInfoCallback), callback), "/bin/df", argv);
+}
+
+
+void SystemBackendPosix::childHandler(int) {
+ int status;
+ pid_t pid;
+
+ // TODO Don't call callbacks directly
+
+ while((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ std::map<pid_t, sigc::slot<void, int> >::iterator it = processes.find(pid);
+
+ if(it != processes.end()) {
+ it->second(status);
+ processes.erase(it);
+ }
+ else {
+ std::map<pid_t, sigc::slot<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);
+
+ 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 sigc::slot<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 sigc::slot<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], sigc::bind(sigc::ptr_fun(&SystemBackendPosix::outputHandler), 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();
+}
+
+}
+