diff --git a/CMakeLists.txt b/CMakeLists.txt index 54eff76..a873ff0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 2.4) project(sigstoped) -set(SRC_FILES main.cpp process.cpp xinstance.cpp) -set(LIBS -lX11) +set(SRC_FILES main.cpp process.cpp xinstance.cpp CppTimer.cpp) +set(LIBS -lX11 -lrt) add_executable(${PROJECT_NAME} ${SRC_FILES}) diff --git a/argpopt.h b/argpopt.h index 4ab2569..25788c4 100644 --- a/argpopt.h +++ b/argpopt.h @@ -23,6 +23,7 @@ struct Config { bool ignoreClientMachine = false; + int timeoutSecs = 10; }; const char *argp_program_version = "1.0.4"; @@ -33,6 +34,7 @@ static char args_doc[] = ""; static struct argp_option options[] = { {"ignore-client-machine", 'i', 0, 0, "Also stop programs associated with windows that fail to set WM_CLIENT_MACHINE" }, + {"timout", 't', "seconds", 0, "Timeout to give program to close its last window before stoping it" }, { 0 } }; @@ -45,6 +47,9 @@ error_t parse_opt (int key, char *arg, struct argp_state *state) case 'i': config->ignoreClientMachine = true; break; + case 't': + config->timeoutSecs = atol(arg); + break; default: return ARGP_ERR_UNKNOWN; } diff --git a/main.cpp b/main.cpp index a14b2ef..7404894 100644 --- a/main.cpp +++ b/main.cpp @@ -39,23 +39,27 @@ #include "split.h" #include "debug.h" #include "argpopt.h" +#include "CppTimer.h" Window intraCommesWindow; -XInstance xinstance; -volatile bool stop = false; volatile bool configStale = false; +XInstance xinstance; constexpr char configPrefix[] = "/.config/sigstoped/"; +constexpr char STOP_EVENT = 65; +constexpr char PROC_STOP_EVENT = 1; void sigTerm(int dummy) { - 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); + XClientMessageEvent event; + memset(&event, 0, sizeof(XClientMessageEvent)); + event.type = ClientMessage; + event.window = intraCommesWindow; + event.format = 8; + event.data.b[0] = STOP_EVENT; + XLockDisplay(xinstance.display); + XSendEvent(xinstance.display, intraCommesWindow, 0, 0, (XEvent*)&event); + XUnlockDisplay(xinstance.display); xinstance.flush(); } @@ -121,7 +125,7 @@ bool createPidFile(const std::string& fileName) { std::cerr<<"Only one " < tlWindows = xinstance->getTopLevelWindows(); + for(auto& window : tlWindows) + { + if(xinstance->getPid(window) == process.getPid()) hasTopLevelWindow = true; + } + if(hasTopLevelWindow) + { + process.stop(true); + std::cout<<"Stoping pid: "+std::to_string(process.getPid())+" name: "+process.getName()<<'\n'; + return true; + } + else + { + std::cout<<"not Stoping pid: "+std::to_string(process.getPid())+" name: "+process.getName()<<'\n'; + return false; + } +} + int main(int argc, char* argv[]) { + char* xDisplayName = std::getenv( "DISPLAY" ); + if(xDisplayName == nullptr) + { + std::cerr<<"DISPLAY enviroment variable must be set.\n"; + return 1; + } + + if(!xinstance.open(xDisplayName)) exit(1); + Config config; argp_parse(&argp, argc, argv, 0, 0, &config); - + if(config.ignoreClientMachine) { std::cout<<"WARNING: Ignoring WM_CLIENT_MACHINE is dangerous and may cause sigstoped to stop random pids if remote windows are present"< stoppedProcs; - if(!xinstance.open(xDisplayName)) exit(1); - intraCommesWindow = XCreateSimpleWindow(xinstance.display, RootWindow(xinstance.display, xinstance.screen), 10, 10, 10, 10, 0, 0, 0); @@ -190,6 +230,7 @@ int main(int argc, char* argv[]) XEvent event; Process prevProcess; + Process qeuedToStop; Window prevWindow = 0; signal(SIGINT, sigTerm); @@ -197,11 +238,13 @@ int main(int argc, char* argv[]) signal(SIGHUP, sigTerm); signal(SIGUSR1, sigUser1); - while(!stop) + CppTimer timer; + + while(true) { XNextEvent(xinstance.display, &event); if (event.type == DestroyNotify) break; - if (event.type == PropertyNotify && event.xproperty.atom == xinstance.atoms.netActiveWindow) + else if (event.type == PropertyNotify && event.xproperty.atom == xinstance.atoms.netActiveWindow) { Window wid = xinstance.getActiveWindow(); if(wid != 0 && wid != prevWindow) @@ -223,6 +266,12 @@ int main(int argc, char* argv[]) { if(process.getName() == applicationNames[i] && wid != 0 && process.getPid() > 0 && process.getName() != "") { + if(process == qeuedToStop) + { + std::cout<<"Canceling stop of wid: "+std::to_string(wid)+" pid: "+std::to_string(process.getPid())+" name: "+process.getName()<<'\n'; + timer.stop(); + qeuedToStop = Process(); + } process.resume(true); stoppedProcs.remove(process); std::cout<<"Resumeing wid: "+std::to_string(wid)+" pid: "+std::to_string(process.getPid())+" name: "+process.getName()<<'\n'; @@ -232,20 +281,11 @@ int main(int argc, char* argv[]) prevProcess.getName() != "" && prevProcess.getPid() > 0) { - sleep(5); //give the process some time to close its other windows - bool hasTopLevelWindow = false; - std::vector tlWindows = xinstance.getTopLevelWindows(); - for(auto& window : tlWindows) - { - if(xinstance.getPid(window) == prevProcess.getPid()) hasTopLevelWindow = true; - } - if(hasTopLevelWindow) - { - prevProcess.stop(true); - std::cout<<"Stoping wid: "+std::to_string(prevWindow)+" pid: "+std::to_string(prevProcess.getPid())+" name: "+prevProcess.getName()<<'\n'; - stoppedProcs.push_back(prevProcess); - } - else std::cout<<"not Stoping wid: "+std::to_string(prevWindow)+" pid: "+std::to_string(prevProcess.getPid())+" name: "+prevProcess.getName()<<'\n'; + timer.block(); + std::cout<<"Will stop pid: "<window == intraCommesWindow) + { + XClientMessageEvent* clientEvent = (XClientMessageEvent*)&event; + if (clientEvent->data.b[0] == STOP_EVENT) break; + if (clientEvent->data.b[0] == PROC_STOP_EVENT) + { + stopProcess(qeuedToStop, &xinstance); + qeuedToStop = Process(); + } + } } for(auto& process : stoppedProcs) process.resume(true); std::filesystem::remove(confDir+"pidfile"); diff --git a/process.cpp b/process.cpp index 1dbc56c..2c17bff 100644 --- a/process.cpp +++ b/process.cpp @@ -42,11 +42,14 @@ pid_t Process::getPid() void Process::stop(bool children) { - kill(pid_, SIGSTOP); - if(children) + if(pid_ > 0) { - std::vector children = getChildren(); - for(auto& child : children) child.stop(true); + kill(pid_, SIGSTOP); + if(children) + { + std::vector children = getChildren(); + for(auto& child : children) child.stop(true); + } } } diff --git a/xinstance.cpp b/xinstance.cpp index c785a97..8c024a7 100644 --- a/xinstance.cpp +++ b/xinstance.cpp @@ -30,7 +30,8 @@ unsigned long XInstance::readProparty(Window wid, Atom atom, unsigned char** pro Atom returnedAtom; unsigned long nitems; unsigned long bytes_after; - + + XLockDisplay(display); int ret = XGetWindowProperty( display, wid, @@ -42,6 +43,7 @@ unsigned long XInstance::readProparty(Window wid, Atom atom, unsigned char** pro &nitems, &bytes_after, prop); + XUnlockDisplay(display); if (ret != Success) { std::cerr<<"XGetWindowProperty failed!\n"; @@ -52,11 +54,12 @@ unsigned long XInstance::readProparty(Window wid, Atom atom, unsigned char** pro Atom XInstance::getAtom(const std::string& atomName) { - return XInternAtom(display, atomName.c_str(), true); + return XInternAtom(display, atomName.c_str(), true);; } bool XInstance::open(const std::string& xDisplayName) { + XInitThreads(); display = XOpenDisplay(xDisplayName.c_str()); if (display == nullptr) { @@ -96,7 +99,9 @@ Window XInstance::getActiveWindow() unsigned long length = readProparty(RootWindow(display, screen), atoms.netActiveWindow, &data, &format); Window wid = 0; if(format == 32 && length == 4) wid = *reinterpret_cast(data); + XLockDisplay(display); XFree(data); + XUnlockDisplay(display); return wid; } @@ -106,20 +111,26 @@ std::vector XInstance::getTopLevelWindows() Window parent_return; Window* windows = nullptr; unsigned int nwindows; + XLockDisplay(display); XQueryTree(display, RootWindow(display, screen), &root_return, &parent_return, &windows, &nwindows); + XUnlockDisplay(display); std::vector out; out.reserve(nwindows); for(unsigned int i = 0; i < nwindows; ++i) { out.push_back(windows[i]); } - if(windows != nullptr) XFree(windows); + XLockDisplay(display); + if(windows != nullptr) XFree(windows); + XUnlockDisplay(display); return out; } void XInstance::flush() { - XFlush(display); + XLockDisplay(display); + XFlush(display); + XUnlockDisplay(display); } pid_t XInstance::getPid(Window wid) @@ -127,7 +138,9 @@ pid_t XInstance::getPid(Window wid) defaultHandler = XSetErrorHandler(ignoreErrorHandler); XTextProperty xWidHostNameTextProperty; bool ret; - ret = XGetTextProperty(display, wid, &xWidHostNameTextProperty, atoms.wmClientMachine); + XLockDisplay(display); + ret = XGetTextProperty(display, wid, &xWidHostNameTextProperty, atoms.wmClientMachine); + XUnlockDisplay(display); if (!ret) { char errorString[1024]; @@ -191,5 +204,7 @@ int XInstance::ignoreErrorHandler(Display* display, XErrorEvent* xerror) XInstance::~XInstance() { + XLockDisplay(display); if(display) XCloseDisplay(display); + XUnlockDisplay(display); }