VirtualBox

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

Last change on this file was 98288, checked in by vboxsync, 15 months ago

Main/src-server: rc -> hrc/vrc (partial). bugref:10223

  • 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 98288 2023-01-24 15:32:43Z 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
62 int vrc = RTDBusLoadLib();
63 if (RT_FAILURE(vrc))
64 {
65 LogRel(("HostPowerServiceLinux: DBus library not found. Service not available.\n"));
66 return;
67 }
68 dbus_error_init(&error);
69 /* Connect to the DBus. The connection will be not shared with any other
70 * in-process callers of dbus_bus_get(). This is considered wasteful (see
71 * API documentation) but simplifies our code, specifically shutting down.
72 * The session bus allows up to 100000 connections per user as it "is just
73 * running as the user anyway" (see session.conf.in in the DBus sources). */
74 mpConnection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
75 if (checkDBusError(&error, &mpConnection))
76 return;
77 /* We do not want to exit(1) if the connection is broken. */
78 dbus_connection_set_exit_on_disconnect(mpConnection, FALSE);
79 /* Tell the bus to wait for the sleep signal(s). */
80 /* The current systemd-logind interface. */
81 dbus_bus_add_match(mpConnection, "type='signal',interface='org.freedesktop.login1.Manager'", &error);
82 /* The previous UPower interfaces (2010 - ca 2013). */
83 dbus_bus_add_match(mpConnection, "type='signal',interface='org.freedesktop.UPower'", &error);
84 dbus_connection_flush(mpConnection);
85 if (checkDBusError(&error, &mpConnection))
86 return;
87
88 /* Grab another reference so that both the destruct and thread each has one: */
89 DBusConnection *pForAssert = dbus_connection_ref(mpConnection);
90 Assert(pForAssert == mpConnection); RT_NOREF(pForAssert);
91
92 /* Create the new worker thread. */
93 vrc = RTThreadCreate(&mThread, HostPowerServiceLinux::powerChangeNotificationThread, this, 0 /* cbStack */,
94 RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE, "MainPower");
95 if (RT_FAILURE(vrc))
96 {
97 LogRel(("HostPowerServiceLinux: RTThreadCreate failed with %Rrc\n", vrc));
98 dbus_connection_unref(mpConnection);
99 }
100}
101
102
103HostPowerServiceLinux::~HostPowerServiceLinux()
104{
105 /* Closing the connection should cause the event loop to exit. */
106 LogFunc((": Stopping thread\n"));
107 if (mpConnection)
108 {
109 dbus_connection_close(mpConnection);
110 dbus_connection_unref(mpConnection);
111 mpConnection = NULL;
112 }
113
114 if (mThread != NIL_RTTHREAD)
115 {
116 /* HACK ALERT! This poke call should _not_ be necessary as dbus_connection_close()
117 should close the socket and force the poll/dbus_connection_read_write
118 call to return with POLLHUP/FALSE. It does so when stepping it in the
119 debugger, but not in real life (asan build; dbus-1.12.20-1.fc32; linux 5.8).
120
121 Poking the thread is a crude crude way to wake it up from whatever
122 stuff it's actually blocked on and realize that the connection has
123 been dropped. */
124
125 uint64_t msElapsed = RTTimeMilliTS();
126 int vrc = RTThreadWait(mThread, 10 /*ms*/, NULL);
127 if (RT_FAILURE(vrc))
128 {
129 RTThreadPoke(mThread);
130 vrc = RTThreadWait(mThread, RT_MS_5SEC, NULL);
131 }
132 msElapsed = RTTimeMilliTS() - msElapsed;
133 if (vrc != VINF_SUCCESS)
134 LogRelThisFunc(("RTThreadWait() failed after %llu ms: %Rrc\n", msElapsed, vrc));
135 mThread = NIL_RTTHREAD;
136 }
137}
138
139
140DECLCALLBACK(int) HostPowerServiceLinux::powerChangeNotificationThread(RTTHREAD hThreadSelf, void *pInstance)
141{
142 NOREF(hThreadSelf);
143 HostPowerServiceLinux *pPowerObj = static_cast<HostPowerServiceLinux *>(pInstance);
144 DBusConnection *pConnection = pPowerObj->mpConnection;
145
146 Log(("HostPowerServiceLinux: Thread started\n"));
147 while (dbus_connection_read_write(pConnection, -1))
148 {
149 DBusMessage *pMessage = NULL;
150
151 for (;;)
152 {
153 pMessage = dbus_connection_pop_message(pConnection);
154 if (pMessage == NULL)
155 break;
156
157 /* The systemd-logind interface notification. */
158 DBusMessageIter args;
159 if ( dbus_message_is_signal(pMessage, "org.freedesktop.login1.Manager", "PrepareForSleep")
160 && dbus_message_iter_init(pMessage, &args)
161 && dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_BOOLEAN)
162 {
163 dbus_bool_t fSuspend;
164 dbus_message_iter_get_basic(&args, &fSuspend);
165
166 /* Trinary operator does not work here as Reason_... is an
167 * anonymous enum. */
168 if (fSuspend)
169 pPowerObj->notify(Reason_HostSuspend);
170 else
171 pPowerObj->notify(Reason_HostResume);
172 }
173
174 /* The UPowerd interface notifications. Sleeping is the older one,
175 * NotifySleep the newer. This gives us one second grace before the
176 * suspend triggers. */
177 if ( dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "Sleeping")
178 || dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "NotifySleep"))
179 pPowerObj->notify(Reason_HostSuspend);
180 if ( dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "Resuming")
181 || dbus_message_is_signal(pMessage, "org.freedesktop.UPower", "NotifyResume"))
182 pPowerObj->notify(Reason_HostResume);
183
184 /* Free local resources held for the message. */
185 dbus_message_unref(pMessage);
186 }
187 }
188
189 /* Close the socket or whatever underlying the connection. */
190 dbus_connection_close(pConnection);
191
192 /* Free in-process resources used for the now-closed connection. */
193 dbus_connection_unref(pConnection);
194
195 Log(("HostPowerServiceLinux: Exiting thread\n"));
196 return VINF_SUCCESS;
197}
198
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use