From 569ce05dbbe8a88eb5bb8762c6152c9663083fa6 Mon Sep 17 00:00:00 2001 From: Carl Klemm Date: Fri, 5 Jun 2020 12:14:37 +0200 Subject: [PATCH] Split source into multiple files SIGUSR1 now causes reload of blacklist sigstoped now resumes all processes it stoped upon reciving SIGINT or SIGTERM sigstoped avoids stopping processes that have no top level window left TODO: is this enough? --- CMakeLists.txt | 2 +- debug.h | 10 ++ main.cpp | 328 +++++++++++++++++-------------------------------- process.cpp | 47 +++++++ process.h | 15 +++ split.h | 15 +++ xinstance.cpp | 154 +++++++++++++++++++++++ xinstance.h | 37 ++++++ 8 files changed, 389 insertions(+), 219 deletions(-) create mode 100644 debug.h create mode 100644 process.cpp create mode 100644 process.h create mode 100644 split.h create mode 100644 xinstance.cpp create mode 100644 xinstance.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b51b074..2055edf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.4) project(sigstoped) -set(SRC_FILES main.cpp ) +set(SRC_FILES main.cpp process.cpp xinstance.cpp) set(LIBS -lX11) add_executable(${PROJECT_NAME} ${SRC_FILES}) diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..9bc045f --- /dev/null +++ b/debug.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +constexpr bool DEBUG = false; + +inline void debug(const std::string& in) +{ + if constexpr (DEBUG) std::cout< #include #include -#include #include #include #include -#include #include #include -#include #include #include -#include #include +#include #include +#include +#include +#include -struct Atoms +#include "xinstance.h" +#include "process.h" +#include "split.h" +#include "debug.h" + +Window intraCommesWindow; +XInstance xinstance; +volatile bool stop = false; +volatile bool configStale = false; + +void sigTerm(int dummy) { - Atom netActiveWindow = 0; - Atom netWmPid = 0; - Atom wmClientMachine = 0; -}; - -class XInstance -{ - -public: - - Atoms atoms; - - static constexpr unsigned long MAX_BYTES = 1048576; - - int screen = 0; - Display *display = nullptr; - -private: - unsigned long readProparty(Window wid, Atom atom, unsigned char** prop, int* format) - { - Atom returnedAtom; - unsigned long nitems; - unsigned long bytes_after; - - int ret = XGetWindowProperty( - display, - wid, - atom, - 0, XInstance::MAX_BYTES/4, false, - AnyPropertyType, - &returnedAtom, - format, - &nitems, - &bytes_after, - prop); - if (ret != Success) - { - std::cerr<<"XGetWindowProperty failed!\n"; - return 0; - } - else return std::min((*format)/8*nitems, XInstance::MAX_BYTES); - } - - Atom getAtom(const std::string& atomName) - { - return XInternAtom(display, atomName.c_str(), true); - } - -public: - bool open(const std::string& xDisplayName) - { - display = XOpenDisplay(xDisplayName.c_str()); - if (display == nullptr) - { - std::cerr<<"Can not open display "<(data); - XFree(data); - return wid; - } - pid_t getPid(Window wid) - { - XTextProperty xWidHostNameTextProperty; - bool ret = XGetTextProperty(display, wid, &xWidHostNameTextProperty, atoms.wmClientMachine); - if (!ret) - { - char errorString[1024]; - XGetErrorText(display, ret, errorString, 1024); - - std::cerr<<"XGetWMClientMachine failed! "<(data); - XFree(data); - - } - else - { - std::cout<<"Window "< split(const std::string& in, char delim = '\n' ) -{ - std::stringstream ss(in); - std::vector tokens; - std::string temp_str; - while(std::getline(ss, temp_str, delim)) - { - tokens.push_back(temp_str); - } - return tokens; + stop = true; + XClientMessageEvent dummyEvent; + memset(&dummyEvent, 0, sizeof(XClientMessageEvent)); + dummyEvent.type = ClientMessage; + dummyEvent.window = intraCommesWindow; + dummyEvent.format = 32; + XSendEvent(xinstance.display, intraCommesWindow, 0, 0, (XEvent*)&dummyEvent); + xinstance.flush(); } -class Process +void sigUser1(int dummy) { -public: - pid_t pid = -1; - std::string name; - - bool operator==(const Process& in) - { - return pid == in.pid; - } - bool operator!=(const Process& in) - { - return pid != in.pid; - } - Process(){} - Process(pid_t pidIn) - { - std::fstream statusFile; - std::string statusString; - statusFile.open(std::string("/proc/") + std::to_string(pidIn)+ "/status", std::fstream::in); - if(statusFile.is_open()) - { - std::string statusString((std::istreambuf_iterator(statusFile)), - std::istreambuf_iterator()); - std::vector lines = split(statusString); - if(lines.size() > 0) - { - pid = pidIn; - std::vector tokens = split(lines[0], '\t'); - if(tokens.size() > 1) - { - name = tokens[1]; - } - } - statusFile.close(); - - } - else - { - std::cout<<"cant open "<<"/proc/" + std::to_string(pidIn)+ "/status"< getBlacklist() { - XInstance xinstance; - const char* homeDir = getenv("HOME"); if(homeDir == nullptr) { std::cerr<<"HOME enviroment variable must be set.\n"; - return 1; - } - - std::fstream blacklistFile; - blacklistFile.open(std::string(homeDir)+"/.config/sigstoped/blacklist", std::fstream::in); - std::string blacklistString; - if(!blacklistFile.is_open()) - { - std::cout<(blacklistFile)), - std::istreambuf_iterator()); + const std::string configDir(std::string(homeDir)+"/.config/sigstoped/"); + std::fstream blacklistFile; + blacklistFile.open(configDir+"blacklist", std::fstream::in); + std::string blacklistString; + if(!blacklistFile.is_open()) + { + std::cout<(blacklistFile)), + std::istreambuf_iterator()); + } + return split(blacklistString); } - - std::vector applicationNames = split(blacklistString); + return std::vector(); +} + +int main(int argc, char* argv[]) +{ + std::vector applicationNames = getBlacklist(); + if(applicationNames.size() == 0) std::cout<<"WARNIG: no application names configured.\n"; struct stat sb; if( stat("/proc/version", &sb) != 0) @@ -251,14 +100,25 @@ int main(int argc, char* argv[]) return 1; } + std::list stoppedPids; + if(!xinstance.open(xDisplayName)) exit(1); - XSelectInput(xinstance.display, RootWindow(xinstance.display, xinstance.screen), PropertyChangeMask | StructureNotifyMask); + intraCommesWindow = XCreateSimpleWindow(xinstance.display, + RootWindow(xinstance.display, xinstance.screen), + 10, 10, 10, 10, 0, 0, 0); + XSelectInput(xinstance.display, intraCommesWindow, StructureNotifyMask); + XSelectInput(xinstance.display, RootWindow(xinstance.display, xinstance.screen), PropertyChangeMask | StructureNotifyMask); XEvent event; Process prevProcess; Window prevWindow = 0; - while(true) + + signal(SIGINT, sigTerm); + signal(SIGTERM, sigTerm); + signal(SIGUSR1, sigUser1); + + while(!stop) { XNextEvent(xinstance.display, &event); if (event.type == DestroyNotify) break; @@ -269,28 +129,60 @@ int main(int argc, char* argv[]) { pid_t windowPid = xinstance.getPid(wid); Process process(windowPid); + std::cout<<"Active window: "< 0) { kill(process.pid, SIGCONT); - std::cout<<"Resumeing: "< 0) { - kill(prevProcess.pid, SIGSTOP); - std::cout<<"Stoping: "< tlWindows = xinstance.getTopLevelWindows(); + for(auto& window : tlWindows) + { + if(xinstance.getPid(window) == prevProcess.pid) hasTopLevelWindow = true; + } + if(hasTopLevelWindow) + { + kill(prevProcess.pid, SIGSTOP); + std::cout<<"Stoping wid: "+std::to_string(prevWindow)+" pid: "+std::to_string(prevProcess.pid)+" name: "+prevProcess.name<<'\n'; + stoppedPids.push_back(prevProcess.pid); + } + else std::cout<<"not Stoping wid: "+std::to_string(prevWindow)+" pid: "+std::to_string(prevProcess.pid)+" name: "+prevProcess.name<<'\n'; } } } prevProcess = process; prevWindow = wid; - std::cout<<"Active window: "< +#include +#include +#include +#include "process.h" +#include "split.h" + +bool Process::operator==(const Process& in) +{ + return pid == in.pid; +} + +bool Process::operator!=(const Process& in) +{ + return pid != in.pid; +} + +Process::Process(pid_t pidIn) +{ + if(pidIn > 0) + { + std::fstream statusFile; + std::string statusString; + statusFile.open(std::string("/proc/") + std::to_string(pidIn)+ "/status", std::fstream::in); + if(statusFile.is_open()) + { + std::string statusString((std::istreambuf_iterator(statusFile)), + std::istreambuf_iterator()); + std::vector lines = split(statusString); + if(lines.size() > 0) + { + pid = pidIn; + std::vector tokens = split(lines[0], '\t'); + if(tokens.size() > 1) + { + name = tokens[1]; + } + } + statusFile.close(); + + } + else + { + std::cout<<"cant open "<<"/proc/" + std::to_string(pidIn)+ "/status"< +#include + +class Process +{ +public: + pid_t pid = -1; + std::string name; + + bool operator==(const Process& in); + bool operator!=(const Process& in); + Process(){} + Process(pid_t pidIn); +}; diff --git a/split.h b/split.h new file mode 100644 index 0000000..f4abbca --- /dev/null +++ b/split.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include + +inline std::vector split(const std::string& in, char delim = '\n' ) +{ + std::stringstream ss(in); + std::vector tokens; + std::string temp_str; + while(std::getline(ss, temp_str, delim)) + { + tokens.push_back(temp_str); + } + return tokens; +} diff --git a/xinstance.cpp b/xinstance.cpp new file mode 100644 index 0000000..21fcf3c --- /dev/null +++ b/xinstance.cpp @@ -0,0 +1,154 @@ +#include "xinstance.h" +#include +#include +#include +#include +#include +#include "debug.h" + +unsigned long XInstance::readProparty(Window wid, Atom atom, unsigned char** prop, int* format) +{ + Atom returnedAtom; + unsigned long nitems; + unsigned long bytes_after; + + int ret = XGetWindowProperty( + display, + wid, + atom, + 0, XInstance::MAX_BYTES/4, false, + AnyPropertyType, + &returnedAtom, + format, + &nitems, + &bytes_after, + prop); + if (ret != Success) + { + std::cerr<<"XGetWindowProperty failed!\n"; + return 0; + } + else return std::min((*format)/8*nitems, XInstance::MAX_BYTES); +} + +Atom XInstance::getAtom(const std::string& atomName) +{ + return XInternAtom(display, atomName.c_str(), true); +} + +bool XInstance::open(const std::string& xDisplayName) +{ + display = XOpenDisplay(xDisplayName.c_str()); + if (display == nullptr) + { + std::cerr<<"Can not open display "<(data); + XFree(data); + return wid; +} + +std::vector XInstance::getTopLevelWindows() +{ + Window root_return; + Window parent_return; + Window* windows = nullptr; + unsigned int nwindows; + XQueryTree(display, RootWindow(display, screen), &root_return, &parent_return, &windows, &nwindows); + + std::vector out; + out.reserve(nwindows); + for(unsigned int i; i < nwindows; ++i) + { + out.push_back(windows[i]); + } + return out; +} + +void XInstance::flush() +{ + XFlush(display); +} + +pid_t XInstance::getPid(Window wid) +{ + XTextProperty xWidHostNameTextProperty; + bool ret = XGetTextProperty(display, wid, &xWidHostNameTextProperty, atoms.wmClientMachine); + if (!ret) + { + char errorString[1024]; + XGetErrorText(display, ret, errorString, 1024); + debug("XGetWMClientMachine failed! " + std::string(errorString)); + return -1; + } + char** xWidHostNameStringList; + int nStrings; + ret = XTextPropertyToStringList(&xWidHostNameTextProperty, &xWidHostNameStringList, &nStrings); + if (!ret || nStrings == 0) + { + char errorString[1024]; + XGetErrorText(display, ret, errorString, 1024); + debug("XTextPropertyToStringList failed! " + std::string(errorString)); + return -1; + } + char hostName[HOST_NAME_MAX+1]={0}; + if(gethostname(hostName, HOST_NAME_MAX) != 0) + { + debug("Can't get host name"); + return -1; + } + pid_t pid = -1; + if(strcmp(hostName, xWidHostNameStringList[0]) == 0) + { + unsigned char* data = nullptr; + int format; + unsigned long length = readProparty(wid, atoms.netWmPid, &data, &format); + if(format == 32 && length == 4) pid = *reinterpret_cast(data); + XFree(data); + + } + else + { + debug("Window "+std::to_string(wid)+" is a remote window"); + } + XFreeStringList(xWidHostNameStringList); + return pid; +} + + XInstance::~XInstance() + { + if(display) XCloseDisplay(display); + } diff --git a/xinstance.h b/xinstance.h new file mode 100644 index 0000000..c9b57f2 --- /dev/null +++ b/xinstance.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include + +struct Atoms +{ + Atom netActiveWindow = 0; + Atom netWmPid = 0; + Atom wmClientMachine = 0; +}; + +class XInstance +{ + +public: + + static constexpr unsigned long MAX_BYTES = 1048576; + + Atoms atoms; + int screen = 0; + Display *display = nullptr; + +private: + + unsigned long readProparty(Window wid, Atom atom, unsigned char** prop, int* format); + Atom getAtom(const std::string& atomName); + +public: + + ~XInstance(); + bool open(const std::string& xDisplayName); + Window getActiveWindow(); + pid_t getPid(Window wid); + std::vector getTopLevelWindows(); + void flush(); +};