import std.conv; import std.datetime; import std.experimental.logger; import std.stdio; import std.string; import vibe.core.core; import vibe.core.net; import vibe.core.process; import ddbus; import ddbus.c_lib; import msgpackrpc; string appName = "Automatisch In- en Uitschakelen Ober"; extern (C) void close(int fileNo); /** * Represententeerd de toepassing */ struct Toep { public: immutable ushort PORT = 18_002; immutable int m_waitingTime; immutable string m_clientAddress; immutable int CHECK_COUNT = 2; Connection m_dbusCon; bool m_keepRunning = true; version(WithRPC) Client m_client; this(int waitingTime, string clientAddress) { this.m_waitingTime = waitingTime; this.m_clientAddress = clientAddress; this.m_dbusCon = connectToBus(DBusBusType.DBUS_BUS_SYSTEM); } int exec() { info ("Can shut down: ", canShutdown()); version(WithRPC) { TCPConnection conn = connectTCP(m_clientAddress, PORT); Duration sleepTime = 500.msecs; while(!conn) { infof("Not connected, trying again in %s", sleepTime); sleep(sleepTime); conn = connectTCP(m_clientAddress, PORT); } info("Connected!"); m_client = new Client(conn); writeln("Receive response: '", m_client.call!(string)("GetBootReason"), "'"); } runTask({ import vibe.core.core : yield; while(m_dbusCon.tick() && m_keepRunning) { yield(); } }); runTask({ FileDescriptor inhibitLock = FileDescriptor.none; version(WithRPC) { FileDescriptor sleepLock = FileDescriptor.none; FileDescriptor shutdownLock = FileDescriptor.none; } // Get interfaces BusName loginBus = busName("org.freedesktop.login1"); ObjectPath loginPath = ObjectPath("/org/freedesktop/login1"); InterfaceName loginIFace = interfaceName("org.freedesktop.login1.Manager"); PathIface loginManager = new PathIface(m_dbusCon, loginBus, loginPath, loginIFace); PathIface loginManagerProps = new PathIface(m_dbusCon, loginBus, loginPath, interfaceName("org.freedesktop.DBus.Properties")); void releaseLock(ref FileDescriptor fd) { if (fd != FileDescriptor.none) { close(cast(int) fd); fd = FileDescriptor.none; } } MessageRouter testRouter = new MessageRouter(); MessagePattern desktopChangePattern = MessagePattern(ObjectPath("/VirtualDesktopManager"), interfaceName("org.kde.KWin.VirtualDesktopManager"), "currentChanged", true); dbus_bus_add_match(m_dbusCon.conn, "type='signal',sender='org.kde.KWin',interface='org.kde.KWin.VirtualDesktopManager,path='/VirtualDesktopManager',member='currentChanged'", null); testRouter.setHandler!(void, string)(desktopChangePattern, (string id) { logf("Desktop changed: %s", id); }); registerRouter(m_dbusCon, testRouter); version(WithRPC) { // Register signal listeners // FIXME: this does not work yet. MessageRouter router = new MessageRouter(); MessagePattern sleepPattern = MessagePattern(loginPath, loginIFace, "PrepareForSleep", true); MessagePattern shutdownPattern = MessagePattern(loginPath, loginIFace, "PrepareForShutdown", true); router.setHandler!(void, bool)(sleepPattern, (bool active) { logf("Preparing for sleep: %b", active); if (active) { m_client.notify("NotifySleep"); releaseLock(sleepLock); } else { m_client.notify("NotifyWakeup"); sleepLock = loginManager.Inhibit("sleep", appName, "Systeem op de hoogte brengen dat de ober gaat slapen", "delay").to!FileDescriptor; } }); router.setHandler!(void, bool)(shutdownPattern, (bool active) { logf("Preparing for shutdown: %b", active); if (active) { m_client.notify("NotifyShutdown"); releaseLock(shutdownLock); } }); registerRouter(m_dbusCon, router); // Take the sleep lock sleepLock = loginManager.Inhibit("sleep", appName, "Systeem op de hoogte brengen dat de ober gaat slapen", "delay").to!FileDescriptor; shutdownLock = loginManager.Inhibit("shutdown", appName, "Systeem op de hoogte brengen dat de ober gaat sluiten", "delay").to!FileDescriptor; } void block() { if (inhibitLock != FileDescriptor.none) return; Message mesg = loginManager.Inhibit("shutdown:sleep:idle:handle-suspend-key:handle-power-key", appName, "Er zijn spelers op de server", "block"); inhibitLock = mesg.to!FileDescriptor; } scope (exit) { // Als we om een of andere redenen deze functie verlaten, laat het slot los! releaseLock(inhibitLock); version(WithRPC) { releaseLock(sleepLock); releaseLock(shutdownLock); } } int checkCount = CHECK_COUNT; while(m_keepRunning) { // Check if we are already preventing shutdown if (inhibitLock != FileDescriptor.none) { // We are. Check if we can shutdown (as in, no players are on the server) if (canShutdown()) { if (checkCount == 1) { // Release the lock releaseLock(inhibitLock); info("Stop preventing shutdown"); } else { // Check 1? more time checkCount--; tracef("Checking %d more time(s)", checkCount); } } else { // We cannot shut down. Reset the check counter. checkCount = CHECK_COUNT; tracef("Still players out there. Keeping lock"); } } else if (!canShutdown()) { try { block(); } catch(DBusException e) { warning("Could not take lock and prevent sleep/shutdown: ", e); } info("Start preventing shutdown"); } else { trace("Nothing to do"); } sleep(seconds(m_waitingTime)); } }); int exitCode = runEventLoop(); // cleanup info("Cleanup"); return exitCode; } /** * Checks if the system can shut down. */ bool canShutdown() { auto result = execute(["ss", "-H", "--query=tcp", "state", "established", "sport", "=", ":minecraft"]); return result.output.splitLines().length == 0; } } int main(string[] args) { scope(failure) stderr.writefln("GEBRUIK: %s WACHTTIJD KLANTADRES", args[0]); if (args.length < 3) { stderr.writefln("GEBRUIK: %s WACHTTIJD KLANTADRES", args[0]); return -1; } int waitingTime = to!int(args[1]); return Toep(waitingTime, args[2]).exec(); }