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 float m_waitingTime; immutable int CHECK_COUNT = 4; Connection m_dbusCon; bool m_keepRunning = true; this(float waitingTime) { this.m_waitingTime = waitingTime / CHECK_COUNT; this.m_dbusCon = connectToBus(DBusBusType.DBUS_BUS_SYSTEM); } version(WithRPC) { Client m_client; immutable string m_clientAddress; immutable ushort PORT = 18_002; this(float waitingTime, string clientAddress) { this.m_waitingTime = waitingTime / CHECK_COUNT; this.m_clientAddress = clientAddress; this.m_dbusCon = connectToBus(DBusBusType.DBUS_BUS_SYSTEM); } } int exec() { info ("Moet blokkeren:", moetBlokkeren()); info ("Kan afsluiten:", kanAfsluiten()); 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(); } }); auto taak = 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; int afsluitNakijkTeller = CHECK_COUNT; void werkAfsluitNakijkTellerBij() { if (kanAfsluiten()) { afsluitNakijkTeller--; } else { afsluitNakijkTeller = CHECK_COUNT; } } while(m_keepRunning) { sleep(seconds(cast(long) (m_waitingTime * 60))); // Check if we are already preventing shutdown if (inhibitLock != FileDescriptor.none) { // We zijn op dit moment het afsluiten niet aan het blokkeren. Kijken of dat wel moet. if (moetBlokkeren()) { if (checkCount == 1) { // Laat het slot los releaseLock(inhibitLock); info("Afsluitslot losgelaten."); } else { // Kijk nog 1 of meer maal checkCount--; tracef("Nog %d maal kijken of we het afsluitslot los kunnen laten", checkCount); } } else { // We kunnen niet afsluiten. De nakijkteller herstellen. checkCount = CHECK_COUNT; afsluitNakijkTeller = CHECK_COUNT; tracef("Spelers zijn nog steeds actief. NIet afsluiten."); } werkAfsluitNakijkTellerBij(); } else if (moetBlokkeren()) { try { block(); info("Afsluitslot verkregen."); } catch(DBusException e) { warning("Kon afsluitslot niet verkrijgen: ", e); } werkAfsluitNakijkTellerBij(); } else if (kanAfsluiten()){ if (afsluitNakijkTeller <= 1) { trace("We kunnen afsluiten!"); execute(["shutdown", "+1", "Systeem gaat afsluiten vanwege inactiviteit"]); m_keepRunning = false; exitEventLoop(); } else { afsluitNakijkTeller--; tracef("Nog %s keer, dan kunnen we afsluiten!", afsluitNakijkTeller); } } else { afsluitNakijkTeller = CHECK_COUNT; trace("Niets om te doen."); } } }); int exitCode = runEventLoop(); // cleanup info("Rotzooi opruimen."); return exitCode; } /** * Kijkt of we het afsluiten moeten blokkeren */ bool moetBlokkeren() { auto resultaat = execute(["ss", "-H", "--query=tcp", "state", "established", "sport", "=", ":minecraft"]); return resultaat.output.splitLines().length != 0; } /** * Kijkt of we direct af kunnen sluiten. */ bool kanAfsluiten() { auto resultaat = execute(["who"]); return resultaat.output.splitLines().length != 0; } } int main(string[] args) { version(WithRPC) { scope(failure) stderr.writefln("GEBRUIK: %s WACHTTIJD KLANTADRES", args[0]); if (args.length < 3) { stderr.writefln("GEBRUIK: %s WACHTTIJD KLANTADRES", args[0]); return -1; } } else { scope(failure) stderr.writefln("GEBRUIK: %s WACHTTIJD", args[0]); if (args.length < 2) { stderr.writefln("GEBRUIK: %s WACHTTIJD", args[0]); return -1; } } float waitingTime = to!float(args[1]); version(WithRPC) { return Toep(waitingTime, args[2]).exec(); } else { return Toep(waitingTime).exec(); } }