From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Main/src-server/linux/HostPowerLinux.cpp | 198 ++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 src/VBox/Main/src-server/linux/HostPowerLinux.cpp (limited to 'src/VBox/Main/src-server/linux/HostPowerLinux.cpp') diff --git a/src/VBox/Main/src-server/linux/HostPowerLinux.cpp b/src/VBox/Main/src-server/linux/HostPowerLinux.cpp new file mode 100644 index 00000000..c27b149a --- /dev/null +++ b/src/VBox/Main/src-server/linux/HostPowerLinux.cpp @@ -0,0 +1,198 @@ +/* $Id: HostPowerLinux.cpp $ */ +/** @file + * VirtualBox interface to host's power notification service + */ + +/* + * Copyright (C) 2015-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#define LOG_GROUP LOG_GROUP_MAIN_HOST +#include "HostPower.h" +#include "LoggingNew.h" + +#include +#include +#include + +static bool checkDBusError(DBusError *pError, DBusConnection **pConnection) +{ + if (dbus_error_is_set(pError)) + { + LogRel(("HostPowerServiceLinux: DBus connection Error (%s)\n", pError->message)); + dbus_error_free(pError); + if (*pConnection) + { + /* Close the socket or whatever underlying the connection. */ + dbus_connection_close(*pConnection); + /* Free in-process resources used for the now-closed connection. */ + dbus_connection_unref(*pConnection); + *pConnection = NULL; + } + return true; + } + return false; +} + +HostPowerServiceLinux::HostPowerServiceLinux(VirtualBox *aVirtualBox) + : HostPowerService(aVirtualBox) + , mThread(NIL_RTTHREAD) + , mpConnection(NULL) +{ + DBusError error; + + int vrc = RTDBusLoadLib(); + if (RT_FAILURE(vrc)) + { + LogRel(("HostPowerServiceLinux: DBus library not found. Service not available.\n")); + return; + } + dbus_error_init(&error); + /* Connect to the DBus. The connection will be not shared with any other + * in-process callers of dbus_bus_get(). This is considered wasteful (see + * API documentation) but simplifies our code, specifically shutting down. + * The session bus allows up to 100000 connections per user as it "is just + * running as the user anyway" (see session.conf.in in the DBus sources). */ + mpConnection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (checkDBusError(&error, &mpConnection)) + return; + /* We do not want to exit(1) if the connection is broken. */ + dbus_connection_set_exit_on_disconnect(mpConnection, FALSE); + /* Tell the bus to wait for the sleep signal(s). */ + /* The current systemd-logind interface. */ + dbus_bus_add_match(mpConnection, "type='signal',interface='org.freedesktop.login1.Manager'", &error); + /* The previous UPower interfaces (2010 - ca 2013). */ + dbus_bus_add_match(mpConnection, "type='signal',interface='org.freedesktop.UPower'", &error); + dbus_connection_flush(mpConnection); + if (checkDBusError(&error, &mpConnection)) + return; + + /* Grab another reference so that both the destruct and thread each has one: */ + DBusConnection *pForAssert = dbus_connection_ref(mpConnection); + Assert(pForAssert == mpConnection); RT_NOREF(pForAssert); + + /* Create the new worker thread. */ + vrc = RTThreadCreate(&mThread, HostPowerServiceLinux::powerChangeNotificationThread, this, 0 /* cbStack */, + RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE, "MainPower"); + if (RT_FAILURE(vrc)) + { + LogRel(("HostPowerServiceLinux: RTThreadCreate failed with %Rrc\n", vrc)); + dbus_connection_unref(mpConnection); + } +} + + +HostPowerServiceLinux::~HostPowerServiceLinux() +{ + /* Closing the connection should cause the event loop to exit. */ + LogFunc((": Stopping thread\n")); + if (mpConnection) + { + dbus_connection_close(mpConnection); + dbus_connection_unref(mpConnection); + mpConnection = NULL; + } + + if (mThread != NIL_RTTHREAD) + { + /* HACK ALERT! This poke call should _not_ be necessary as dbus_connection_close() + should close the socket and force the poll/dbus_connection_read_write + call to return with POLLHUP/FALSE. It does so when stepping it in the + debugger, but not in real life (asan build; dbus-1.12.20-1.fc32; linux 5.8). + + Poking the thread is a crude crude way to wake it up from whatever + stuff it's actually blocked on and realize that the connection has + been dropped. */ + + uint64_t msElapsed = RTTimeMilliTS(); + int vrc = RTThreadWait(mThread, 10 /*ms*/, NULL); + if (RT_FAILURE(vrc)) + { + RTThreadPoke(mThread); + vrc = RTThreadWait(mThread, RT_MS_5SEC, NULL); + } + msElapsed = RTTimeMilliTS() - msElapsed; + if (vrc != VINF_SUCCESS) + LogRelThisFunc(("RTThreadWait() failed after %llu ms: %Rrc\n", msElapsed, vrc)); + mThread = NIL_RTTHREAD; + } +} + + +DECLCALLBACK(int) HostPowerServiceLinux::powerChangeNotificationThread(RTTHREAD hThreadSelf, void *pInstance) +{ + NOREF(hThreadSelf); + HostPowerServiceLinux *pPowerObj = static_cast(pInstance); + DBusConnection *pConnection = pPowerObj->mpConnection; + + Log(("HostPowerServiceLinux: Thread started\n")); + while (dbus_connection_read_write(pConnection, -1)) + { + DBusMessage *pMessage = NULL; + + for (;;) + { + pMessage = dbus_connection_pop_message(pConnection); + if (pMessage == NULL) + break; + + /* The systemd-logind interface notification. */ + DBusMessageIter args; + if ( dbus_message_is_signal(pMessage, "org.freedesktop.login1.Manager", "PrepareForSleep") + && dbus_message_iter_init(pMessage, &args) + && dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_BOOLEAN) + { + dbus_bool_t fSuspend; + dbus_message_iter_get_basic(&args, &fSuspend); + + /* Trinary operator does not work here as Reason_... is an + * anonymous enum. */ + if (fSuspend) + pPowerObj->notify(Reason_HostSuspend); + else + pPowerObj->notify(Reason_HostResume); + } + + /* The UPowerd interface notifications. Sleeping is the older one, + * NotifySleep the newer. This gives us one second grace before the + * suspend triggers. */ + if ( dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "Sleeping") + || dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "NotifySleep")) + pPowerObj->notify(Reason_HostSuspend); + if ( dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "Resuming") + || dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "NotifyResume")) + pPowerObj->notify(Reason_HostResume); + + /* Free local resources held for the message. */ + dbus_message_unref(pMessage); + } + } + + /* Close the socket or whatever underlying the connection. */ + dbus_connection_close(pConnection); + + /* Free in-process resources used for the now-closed connection. */ + dbus_connection_unref(pConnection); + + Log(("HostPowerServiceLinux: Exiting thread\n")); + return VINF_SUCCESS; +} + -- cgit v1.2.3