VirtualBox

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

Last change on this file since 86495 was 86495, checked in by vboxsync, 4 years ago

VBoxSVC/HostPowerServiceLinux: Use RTThreadPoke on the notification thread if it doesn't seem to respond to dbus_connection_close() after 10 ms. Also, grab an extra reference on the connection so both the thread and the destruction have one to drop, otherwise we'd be in trouble if the other side terminates the connection and the thread exits early. bugref:9841

  • 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 86495 2020-10-08 13:04:26Z vboxsync $ */
2/** @file
3 * VirtualBox interface to host's power notification service
4 */
5
6/*
7 * Copyright (C) 2015-2020 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