VirtualBox

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

Last change on this file since 93842 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette