VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/linux/HostPowerLinux.cpp@ 98103

Last change on this file since 98103 was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 7.5 KB
Line 
1/* $Id: HostPowerLinux.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VirtualBox interface to host's power notification service
4 */
5
6/*
7 * Copyright (C) 2015-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_HOST
29#include "HostPower.h"
30#include "LoggingNew.h"
31
32#include <iprt/asm.h>
33#include <iprt/power.h>
34#include <iprt/time.h>
35
36static bool checkDBusError(DBusError *pError, DBusConnection **pConnection)
37{
38 if (dbus_error_is_set(pError))
39 {
40 LogRel(("HostPowerServiceLinux: DBus connection Error (%s)\n", pError->message));
41 dbus_error_free(pError);
42 if (*pConnection)
43 {
44 /* Close the socket or whatever underlying the connection. */
45 dbus_connection_close(*pConnection);
46 /* Free in-process resources used for the now-closed connection. */
47 dbus_connection_unref(*pConnection);
48 *pConnection = NULL;
49 }
50 return true;
51 }
52 return false;
53}
54
55HostPowerServiceLinux::HostPowerServiceLinux(VirtualBox *aVirtualBox)
56 : HostPowerService(aVirtualBox)
57 , mThread(NIL_RTTHREAD)
58 , mpConnection(NULL)
59{
60 DBusError error;
61 int rc;
62
63 rc = RTDBusLoadLib();
64 if (RT_FAILURE(rc))
65 {
66 LogRel(("HostPowerServiceLinux: DBus library not found. Service not available.\n"));
67 return;
68 }
69 dbus_error_init(&error);
70 /* Connect to the DBus. The connection will be not shared with any other
71 * in-process callers of dbus_bus_get(). This is considered wasteful (see
72 * API documentation) but simplifies our code, specifically shutting down.
73 * The session bus allows up to 100000 connections per user as it "is just
74 * running as the user anyway" (see session.conf.in in the DBus sources). */
75 mpConnection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
76 if (checkDBusError(&error, &mpConnection))
77 return;
78 /* We do not want to exit(1) if the connection is broken. */
79 dbus_connection_set_exit_on_disconnect(mpConnection, FALSE);
80 /* Tell the bus to wait for the sleep signal(s). */
81 /* The current systemd-logind interface. */
82 dbus_bus_add_match(mpConnection, "type='signal',interface='org.freedesktop.login1.Manager'", &error);
83 /* The previous UPower interfaces (2010 - ca 2013). */
84 dbus_bus_add_match(mpConnection, "type='signal',interface='org.freedesktop.UPower'", &error);
85 dbus_connection_flush(mpConnection);
86 if (checkDBusError(&error, &mpConnection))
87 return;
88
89 /* Grab another reference so that both the destruct and thread each has one: */
90 DBusConnection *pForAssert = dbus_connection_ref(mpConnection);
91 Assert(pForAssert == mpConnection); RT_NOREF(pForAssert);
92
93 /* Create the new worker thread. */
94 rc = RTThreadCreate(&mThread, HostPowerServiceLinux::powerChangeNotificationThread, this, 0 /* cbStack */,
95 RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE, "MainPower");
96 if (RT_FAILURE(rc))
97 {
98 LogRel(("HostPowerServiceLinux: RTThreadCreate failed with %Rrc\n", rc));
99 dbus_connection_unref(mpConnection);
100 }
101}
102
103
104HostPowerServiceLinux::~HostPowerServiceLinux()
105{
106 /* Closing the connection should cause the event loop to exit. */
107 LogFunc((": Stopping thread\n"));
108 if (mpConnection)
109 {
110 dbus_connection_close(mpConnection);
111 dbus_connection_unref(mpConnection);
112 mpConnection = NULL;
113 }
114
115 if (mThread != NIL_RTTHREAD)
116 {
117 /* HACK ALERT! This poke call should _not_ be necessary as dbus_connection_close()
118 should close the socket and force the poll/dbus_connection_read_write
119 call to return with POLLHUP/FALSE. It does so when stepping it in the
120 debugger, but not in real life (asan build; dbus-1.12.20-1.fc32; linux 5.8).
121
122 Poking the thread is a crude crude way to wake it up from whatever
123 stuff it's actually blocked on and realize that the connection has
124 been dropped. */
125
126 uint64_t msElapsed = RTTimeMilliTS();
127 int vrc = RTThreadWait(mThread, 10 /*ms*/, NULL);
128 if (RT_FAILURE(vrc))
129 {
130 RTThreadPoke(mThread);
131 vrc = RTThreadWait(mThread, RT_MS_5SEC, NULL);
132 }
133 msElapsed = RTTimeMilliTS() - msElapsed;
134 if (vrc != VINF_SUCCESS)
135 LogRelThisFunc(("RTThreadWait() failed after %llu ms: %Rrc\n", msElapsed, vrc));
136 mThread = NIL_RTTHREAD;
137 }
138}
139
140
141DECLCALLBACK(int) HostPowerServiceLinux::powerChangeNotificationThread(RTTHREAD hThreadSelf, void *pInstance)
142{
143 NOREF(hThreadSelf);
144 HostPowerServiceLinux *pPowerObj = static_cast<HostPowerServiceLinux *>(pInstance);
145 DBusConnection *pConnection = pPowerObj->mpConnection;
146
147 Log(("HostPowerServiceLinux: Thread started\n"));
148 while (dbus_connection_read_write(pConnection, -1))
149 {
150 DBusMessage *pMessage = NULL;
151
152 for (;;)
153 {
154 pMessage = dbus_connection_pop_message(pConnection);
155 if (pMessage == NULL)
156 break;
157
158 /* The systemd-logind interface notification. */
159 DBusMessageIter args;
160 if ( dbus_message_is_signal(pMessage, "org.freedesktop.login1.Manager", "PrepareForSleep")
161 && dbus_message_iter_init(pMessage, &args)
162 && dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_BOOLEAN)
163 {
164 dbus_bool_t fSuspend;
165 dbus_message_iter_get_basic(&args, &fSuspend);
166
167 /* Trinary operator does not work here as Reason_... is an
168 * anonymous enum. */
169 if (fSuspend)
170 pPowerObj->notify(Reason_HostSuspend);
171 else
172 pPowerObj->notify(Reason_HostResume);
173 }
174
175 /* The UPowerd interface notifications. Sleeping is the older one,
176 * NotifySleep the newer. This gives us one second grace before the
177 * suspend triggers. */
178 if ( dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "Sleeping")
179 || dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "NotifySleep"))
180 pPowerObj->notify(Reason_HostSuspend);
181 if ( dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "Resuming")
182 || dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "NotifyResume"))
183 pPowerObj->notify(Reason_HostResume);
184
185 /* Free local resources held for the message. */
186 dbus_message_unref(pMessage);
187 }
188 }
189
190 /* Close the socket or whatever underlying the connection. */
191 dbus_connection_close(pConnection);
192
193 /* Free in-process resources used for the now-closed connection. */
194 dbus_connection_unref(pConnection);
195
196 Log(("HostPowerServiceLinux: Exiting thread\n"));
197 return VINF_SUCCESS;
198}
199
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use