Configureable timout for applications to close their windows.
Fix 5sec stall when switching from an application to be stopped to an application that was stopped before. Make Xinstance thread safe
This commit is contained in:
@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 2.4)
|
|||||||
|
|
||||||
project(sigstoped)
|
project(sigstoped)
|
||||||
|
|
||||||
set(SRC_FILES main.cpp process.cpp xinstance.cpp)
|
set(SRC_FILES main.cpp process.cpp xinstance.cpp CppTimer.cpp)
|
||||||
set(LIBS -lX11)
|
set(LIBS -lX11 -lrt)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} ${SRC_FILES})
|
add_executable(${PROJECT_NAME} ${SRC_FILES})
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
struct Config
|
struct Config
|
||||||
{
|
{
|
||||||
bool ignoreClientMachine = false;
|
bool ignoreClientMachine = false;
|
||||||
|
int timeoutSecs = 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *argp_program_version = "1.0.4";
|
const char *argp_program_version = "1.0.4";
|
||||||
@ -33,6 +34,7 @@ static char args_doc[] = "";
|
|||||||
static struct argp_option options[] =
|
static struct argp_option options[] =
|
||||||
{
|
{
|
||||||
{"ignore-client-machine", 'i', 0, 0, "Also stop programs associated with windows that fail to set WM_CLIENT_MACHINE" },
|
{"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 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -45,6 +47,9 @@ error_t parse_opt (int key, char *arg, struct argp_state *state)
|
|||||||
case 'i':
|
case 'i':
|
||||||
config->ignoreClientMachine = true;
|
config->ignoreClientMachine = true;
|
||||||
break;
|
break;
|
||||||
|
case 't':
|
||||||
|
config->timeoutSecs = atol(arg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return ARGP_ERR_UNKNOWN;
|
return ARGP_ERR_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
122
main.cpp
122
main.cpp
@ -39,23 +39,27 @@
|
|||||||
#include "split.h"
|
#include "split.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "argpopt.h"
|
#include "argpopt.h"
|
||||||
|
#include "CppTimer.h"
|
||||||
|
|
||||||
Window intraCommesWindow;
|
Window intraCommesWindow;
|
||||||
XInstance xinstance;
|
|
||||||
volatile bool stop = false;
|
|
||||||
volatile bool configStale = false;
|
volatile bool configStale = false;
|
||||||
|
XInstance xinstance;
|
||||||
|
|
||||||
constexpr char configPrefix[] = "/.config/sigstoped/";
|
constexpr char configPrefix[] = "/.config/sigstoped/";
|
||||||
|
constexpr char STOP_EVENT = 65;
|
||||||
|
constexpr char PROC_STOP_EVENT = 1;
|
||||||
|
|
||||||
void sigTerm(int dummy)
|
void sigTerm(int dummy)
|
||||||
{
|
{
|
||||||
stop = true;
|
XClientMessageEvent event;
|
||||||
XClientMessageEvent dummyEvent;
|
memset(&event, 0, sizeof(XClientMessageEvent));
|
||||||
memset(&dummyEvent, 0, sizeof(XClientMessageEvent));
|
event.type = ClientMessage;
|
||||||
dummyEvent.type = ClientMessage;
|
event.window = intraCommesWindow;
|
||||||
dummyEvent.window = intraCommesWindow;
|
event.format = 8;
|
||||||
dummyEvent.format = 32;
|
event.data.b[0] = STOP_EVENT;
|
||||||
XSendEvent(xinstance.display, intraCommesWindow, 0, 0, (XEvent*)&dummyEvent);
|
XLockDisplay(xinstance.display);
|
||||||
|
XSendEvent(xinstance.display, intraCommesWindow, 0, 0, (XEvent*)&event);
|
||||||
|
XUnlockDisplay(xinstance.display);
|
||||||
xinstance.flush();
|
xinstance.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +125,7 @@ bool createPidFile(const std::string& fileName)
|
|||||||
{
|
{
|
||||||
std::cerr<<"Only one "
|
std::cerr<<"Only one "
|
||||||
<<sigstopedName
|
<<sigstopedName
|
||||||
<<" process exists, either sigstoped died or you have severl diferently named binarys\n";
|
<<" process exists, either sigstoped died or you have several diferently named binarys\n";
|
||||||
|
|
||||||
std::filesystem::remove(fileName);
|
std::filesystem::remove(fileName);
|
||||||
return createPidFile(fileName);
|
return createPidFile(fileName);
|
||||||
@ -145,11 +149,56 @@ bool createPidFile(const std::string& fileName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void sendEventProcStop()
|
||||||
|
{
|
||||||
|
XClientMessageEvent event;
|
||||||
|
memset(&event, 0, sizeof(XClientMessageEvent));
|
||||||
|
event.type = ClientMessage;
|
||||||
|
event.window = intraCommesWindow;
|
||||||
|
event.format = 8;
|
||||||
|
event.data.b[0] = PROC_STOP_EVENT;
|
||||||
|
XLockDisplay(xinstance.display);
|
||||||
|
XSendEvent(xinstance.display, intraCommesWindow, 0, 0, (XEvent*)&event);
|
||||||
|
XUnlockDisplay(xinstance.display);
|
||||||
|
xinstance.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stopProcess(Process process, XInstance* xinstance)
|
||||||
|
{
|
||||||
|
bool hasTopLevelWindow = false;
|
||||||
|
std::vector<Window> 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[])
|
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;
|
Config config;
|
||||||
argp_parse(&argp, argc, argv, 0, 0, &config);
|
argp_parse(&argp, argc, argv, 0, 0, &config);
|
||||||
|
|
||||||
if(config.ignoreClientMachine)
|
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"<<std::endl;
|
std::cout<<"WARNING: Ignoring WM_CLIENT_MACHINE is dangerous and may cause sigstoped to stop random pids if remote windows are present"<<std::endl;
|
||||||
@ -171,17 +220,8 @@ int main(int argc, char* argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* xDisplayName = std::getenv( "DISPLAY" );
|
|
||||||
if(xDisplayName == nullptr)
|
|
||||||
{
|
|
||||||
std::cerr<<"DISPLAY enviroment variable must be set.\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<Process> stoppedProcs;
|
std::list<Process> stoppedProcs;
|
||||||
|
|
||||||
if(!xinstance.open(xDisplayName)) exit(1);
|
|
||||||
|
|
||||||
intraCommesWindow = XCreateSimpleWindow(xinstance.display,
|
intraCommesWindow = XCreateSimpleWindow(xinstance.display,
|
||||||
RootWindow(xinstance.display, xinstance.screen),
|
RootWindow(xinstance.display, xinstance.screen),
|
||||||
10, 10, 10, 10, 0, 0, 0);
|
10, 10, 10, 10, 0, 0, 0);
|
||||||
@ -190,6 +230,7 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
XEvent event;
|
XEvent event;
|
||||||
Process prevProcess;
|
Process prevProcess;
|
||||||
|
Process qeuedToStop;
|
||||||
Window prevWindow = 0;
|
Window prevWindow = 0;
|
||||||
|
|
||||||
signal(SIGINT, sigTerm);
|
signal(SIGINT, sigTerm);
|
||||||
@ -197,11 +238,13 @@ int main(int argc, char* argv[])
|
|||||||
signal(SIGHUP, sigTerm);
|
signal(SIGHUP, sigTerm);
|
||||||
signal(SIGUSR1, sigUser1);
|
signal(SIGUSR1, sigUser1);
|
||||||
|
|
||||||
while(!stop)
|
CppTimer timer;
|
||||||
|
|
||||||
|
while(true)
|
||||||
{
|
{
|
||||||
XNextEvent(xinstance.display, &event);
|
XNextEvent(xinstance.display, &event);
|
||||||
if (event.type == DestroyNotify) break;
|
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();
|
Window wid = xinstance.getActiveWindow();
|
||||||
if(wid != 0 && wid != prevWindow)
|
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.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);
|
process.resume(true);
|
||||||
stoppedProcs.remove(process);
|
stoppedProcs.remove(process);
|
||||||
std::cout<<"Resumeing wid: "+std::to_string(wid)+" pid: "+std::to_string(process.getPid())+" name: "+process.getName()<<'\n';
|
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.getName() != "" &&
|
||||||
prevProcess.getPid() > 0)
|
prevProcess.getPid() > 0)
|
||||||
{
|
{
|
||||||
sleep(5); //give the process some time to close its other windows
|
timer.block();
|
||||||
bool hasTopLevelWindow = false;
|
std::cout<<"Will stop pid: "<<prevProcess.getPid()<<" name: "<<prevProcess.getName()<<'\n';
|
||||||
std::vector<Window> tlWindows = xinstance.getTopLevelWindows();
|
qeuedToStop = prevProcess;
|
||||||
for(auto& window : tlWindows)
|
timer.start(config.timeoutSecs*1000*CppTimer::MS_TO_NS, sendEventProcStop, CppTimer::ONESHOT);
|
||||||
{
|
stoppedProcs.push_back(prevProcess);
|
||||||
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';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,6 +293,16 @@ int main(int argc, char* argv[])
|
|||||||
prevWindow = wid;
|
prevWindow = wid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (event.type == ClientMessage && ((XClientMessageEvent*)&event)->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);
|
for(auto& process : stoppedProcs) process.resume(true);
|
||||||
std::filesystem::remove(confDir+"pidfile");
|
std::filesystem::remove(confDir+"pidfile");
|
||||||
|
11
process.cpp
11
process.cpp
@ -42,11 +42,14 @@ pid_t Process::getPid()
|
|||||||
|
|
||||||
void Process::stop(bool children)
|
void Process::stop(bool children)
|
||||||
{
|
{
|
||||||
kill(pid_, SIGSTOP);
|
if(pid_ > 0)
|
||||||
if(children)
|
|
||||||
{
|
{
|
||||||
std::vector<Process> children = getChildren();
|
kill(pid_, SIGSTOP);
|
||||||
for(auto& child : children) child.stop(true);
|
if(children)
|
||||||
|
{
|
||||||
|
std::vector<Process> children = getChildren();
|
||||||
|
for(auto& child : children) child.stop(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,8 @@ unsigned long XInstance::readProparty(Window wid, Atom atom, unsigned char** pro
|
|||||||
Atom returnedAtom;
|
Atom returnedAtom;
|
||||||
unsigned long nitems;
|
unsigned long nitems;
|
||||||
unsigned long bytes_after;
|
unsigned long bytes_after;
|
||||||
|
|
||||||
|
XLockDisplay(display);
|
||||||
int ret = XGetWindowProperty(
|
int ret = XGetWindowProperty(
|
||||||
display,
|
display,
|
||||||
wid,
|
wid,
|
||||||
@ -42,6 +43,7 @@ unsigned long XInstance::readProparty(Window wid, Atom atom, unsigned char** pro
|
|||||||
&nitems,
|
&nitems,
|
||||||
&bytes_after,
|
&bytes_after,
|
||||||
prop);
|
prop);
|
||||||
|
XUnlockDisplay(display);
|
||||||
if (ret != Success)
|
if (ret != Success)
|
||||||
{
|
{
|
||||||
std::cerr<<"XGetWindowProperty failed!\n";
|
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)
|
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)
|
bool XInstance::open(const std::string& xDisplayName)
|
||||||
{
|
{
|
||||||
|
XInitThreads();
|
||||||
display = XOpenDisplay(xDisplayName.c_str());
|
display = XOpenDisplay(xDisplayName.c_str());
|
||||||
if (display == nullptr)
|
if (display == nullptr)
|
||||||
{
|
{
|
||||||
@ -96,7 +99,9 @@ Window XInstance::getActiveWindow()
|
|||||||
unsigned long length = readProparty(RootWindow(display, screen), atoms.netActiveWindow, &data, &format);
|
unsigned long length = readProparty(RootWindow(display, screen), atoms.netActiveWindow, &data, &format);
|
||||||
Window wid = 0;
|
Window wid = 0;
|
||||||
if(format == 32 && length == 4) wid = *reinterpret_cast<Window*>(data);
|
if(format == 32 && length == 4) wid = *reinterpret_cast<Window*>(data);
|
||||||
|
XLockDisplay(display);
|
||||||
XFree(data);
|
XFree(data);
|
||||||
|
XUnlockDisplay(display);
|
||||||
return wid;
|
return wid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,20 +111,26 @@ std::vector<Window> XInstance::getTopLevelWindows()
|
|||||||
Window parent_return;
|
Window parent_return;
|
||||||
Window* windows = nullptr;
|
Window* windows = nullptr;
|
||||||
unsigned int nwindows;
|
unsigned int nwindows;
|
||||||
|
XLockDisplay(display);
|
||||||
XQueryTree(display, RootWindow(display, screen), &root_return, &parent_return, &windows, &nwindows);
|
XQueryTree(display, RootWindow(display, screen), &root_return, &parent_return, &windows, &nwindows);
|
||||||
|
XUnlockDisplay(display);
|
||||||
std::vector<Window> out;
|
std::vector<Window> out;
|
||||||
out.reserve(nwindows);
|
out.reserve(nwindows);
|
||||||
for(unsigned int i = 0; i < nwindows; ++i)
|
for(unsigned int i = 0; i < nwindows; ++i)
|
||||||
{
|
{
|
||||||
out.push_back(windows[i]);
|
out.push_back(windows[i]);
|
||||||
}
|
}
|
||||||
if(windows != nullptr) XFree(windows);
|
XLockDisplay(display);
|
||||||
|
if(windows != nullptr) XFree(windows);
|
||||||
|
XUnlockDisplay(display);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XInstance::flush()
|
void XInstance::flush()
|
||||||
{
|
{
|
||||||
XFlush(display);
|
XLockDisplay(display);
|
||||||
|
XFlush(display);
|
||||||
|
XUnlockDisplay(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t XInstance::getPid(Window wid)
|
pid_t XInstance::getPid(Window wid)
|
||||||
@ -127,7 +138,9 @@ pid_t XInstance::getPid(Window wid)
|
|||||||
defaultHandler = XSetErrorHandler(ignoreErrorHandler);
|
defaultHandler = XSetErrorHandler(ignoreErrorHandler);
|
||||||
XTextProperty xWidHostNameTextProperty;
|
XTextProperty xWidHostNameTextProperty;
|
||||||
bool ret;
|
bool ret;
|
||||||
ret = XGetTextProperty(display, wid, &xWidHostNameTextProperty, atoms.wmClientMachine);
|
XLockDisplay(display);
|
||||||
|
ret = XGetTextProperty(display, wid, &xWidHostNameTextProperty, atoms.wmClientMachine);
|
||||||
|
XUnlockDisplay(display);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
{
|
{
|
||||||
char errorString[1024];
|
char errorString[1024];
|
||||||
@ -191,5 +204,7 @@ int XInstance::ignoreErrorHandler(Display* display, XErrorEvent* xerror)
|
|||||||
|
|
||||||
XInstance::~XInstance()
|
XInstance::~XInstance()
|
||||||
{
|
{
|
||||||
|
XLockDisplay(display);
|
||||||
if(display) XCloseDisplay(display);
|
if(display) XCloseDisplay(display);
|
||||||
|
XUnlockDisplay(display);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user