VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostartStop.cpp@ 103068

Last change on this file since 103068 was 98103, checked in by vboxsync, 18 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: 9.7 KB
Line 
1/* $Id: VBoxAutostartStop.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBoxAutostart - VirtualBox Autostart service, stop machines during system shutdown.
4 */
5
6/*
7 * Copyright (C) 2012-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#include <iprt/assert.h>
29#include <iprt/log.h>
30#include <iprt/message.h>
31#include <iprt/stream.h>
32#include <iprt/thread.h>
33#include <iprt/time.h>
34
35#include <VBox/com/com.h>
36#include <VBox/com/string.h>
37#include <VBox/com/Guid.h>
38#include <VBox/com/array.h>
39#include <VBox/com/ErrorInfo.h>
40#include <VBox/com/errorprint.h>
41
42#include <list>
43
44#include "VBoxAutostart.h"
45
46using namespace com;
47
48/**
49 * VM list entry.
50 */
51typedef struct AUTOSTOPVM
52{
53 /** ID of the VM to start. */
54 Bstr strId;
55 /** Action to do with the VM. */
56 AutostopType_T enmAutostopType;
57} AUTOSTOPVM;
58
59static HRESULT autostartSaveVMState(ComPtr<IConsole> &console)
60{
61 HRESULT hrc = S_OK;
62 ComPtr<IMachine> machine;
63 ComPtr<IProgress> progress;
64
65 do
66 {
67 /* first pause so we don't trigger a live save which needs more time/resources */
68 bool fPaused = false;
69 hrc = console->Pause();
70 if (FAILED(hrc))
71 {
72 bool fError = true;
73 if (hrc == VBOX_E_INVALID_VM_STATE)
74 {
75 /* check if we are already paused */
76 MachineState_T machineState;
77 CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
78 /* the error code was lost by the previous instruction */
79 hrc = VBOX_E_INVALID_VM_STATE;
80 if (machineState != MachineState_Paused)
81 {
82 RTMsgError("Machine in invalid state %d -- %s\n",
83 machineState, machineStateToName(machineState, false));
84 }
85 else
86 {
87 fError = false;
88 fPaused = true;
89 }
90 }
91 if (fError)
92 break;
93 }
94
95 CHECK_ERROR(console, COMGETTER(Machine)(machine.asOutParam()));
96 CHECK_ERROR(machine, SaveState(progress.asOutParam()));
97 if (FAILED(hrc))
98 {
99 if (!fPaused)
100 console->Resume();
101 break;
102 }
103
104 hrc = showProgress(progress);
105 CHECK_PROGRESS_ERROR(progress, ("Failed to save machine state"));
106 if (FAILED(hrc))
107 {
108 if (!fPaused)
109 console->Resume();
110 }
111 } while (0);
112
113 return hrc;
114}
115
116DECLHIDDEN(int) autostartStopMain(PCFGAST pCfgAst)
117{
118 RT_NOREF(pCfgAst);
119 std::list<AUTOSTOPVM> listVM;
120
121 autostartSvcLogVerbose(1, "Stopping machines ...\n");
122
123 /*
124 * Build a list of all VMs we need to autostop first, apply the overrides
125 * from the configuration and start the VMs afterwards.
126 */
127 com::SafeIfaceArray<IMachine> machines;
128 HRESULT hrc = g_pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines));
129 if (SUCCEEDED(hrc))
130 {
131 /*
132 * Iterate through the collection and construct a list of machines
133 * we have to check.
134 */
135 for (size_t i = 0; i < machines.size(); ++i)
136 {
137 if (machines[i])
138 {
139 Bstr strName;
140 CHECK_ERROR_BREAK(machines[i], COMGETTER(Name)(strName.asOutParam()));
141
142 BOOL fAccessible;
143 CHECK_ERROR_BREAK(machines[i], COMGETTER(Accessible)(&fAccessible));
144 if (!fAccessible)
145 {
146 autostartSvcLogVerbose(1, "Machine '%ls' is not accessible, skipping\n", strName.raw());
147 continue;
148 }
149
150 AUTOSTOPVM autostopVM;
151
152 AutostopType_T enmAutostopType;
153 CHECK_ERROR_BREAK(machines[i], COMGETTER(AutostopType)(&enmAutostopType));
154 if (enmAutostopType != AutostopType_Disabled)
155 {
156 CHECK_ERROR_BREAK(machines[i], COMGETTER(Id)(autostopVM.strId.asOutParam()));
157 autostopVM.enmAutostopType = enmAutostopType;
158
159 listVM.push_back(autostopVM);
160 }
161
162 autostartSvcLogVerbose(1, "Machine '%ls': Autostop type is %#x\n",
163 strName.raw(), autostopVM.enmAutostopType);
164 }
165 }
166
167 if ( SUCCEEDED(hrc)
168 && !listVM.empty())
169 {
170 std::list<AUTOSTOPVM>::iterator it;
171 for (it = listVM.begin(); it != listVM.end(); ++it)
172 {
173 MachineState_T enmMachineState;
174 ComPtr<IMachine> machine;
175
176 CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine((*it).strId.raw(),
177 machine.asOutParam()));
178
179 Bstr strName;
180 CHECK_ERROR_BREAK(machine, COMGETTER(Name)(strName.asOutParam()));
181
182 CHECK_ERROR_BREAK(machine, COMGETTER(State)(&enmMachineState));
183
184 /* Wait until the VM changes from a transient state back. */
185 while ( enmMachineState >= MachineState_FirstTransient
186 && enmMachineState <= MachineState_LastTransient)
187 {
188 RTThreadSleep(1000);
189 CHECK_ERROR_BREAK(machine, COMGETTER(State)(&enmMachineState));
190 }
191
192 /* Only power off running machines. */
193 if ( enmMachineState == MachineState_Running
194 || enmMachineState == MachineState_Paused)
195 {
196 ComPtr<IConsole> console;
197 ComPtr<IProgress> progress;
198
199 /* open a session for the VM */
200 CHECK_ERROR_BREAK(machine, LockMachine(g_pSession, LockType_Shared));
201
202 /* get the associated console */
203 CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam()));
204
205 switch ((*it).enmAutostopType)
206 {
207 case AutostopType_SaveState:
208 {
209 hrc = autostartSaveVMState(console);
210 break;
211 }
212 case AutostopType_PowerOff:
213 {
214 CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));
215
216 hrc = showProgress(progress);
217 CHECK_PROGRESS_ERROR(progress, ("Failed to powering off machine '%ls'", strName.raw()));
218 if (FAILED(hrc))
219 autostartSvcLogError("Powering off machine '%ls' failed with %Rhrc\n", strName.raw(), hrc);
220 break;
221 }
222 case AutostopType_AcpiShutdown:
223 {
224 BOOL fGuestEnteredACPI = false;
225 CHECK_ERROR_BREAK(console, GetGuestEnteredACPIMode(&fGuestEnteredACPI));
226 if ( fGuestEnteredACPI
227 && enmMachineState == MachineState_Running)
228 {
229 CHECK_ERROR_BREAK(console, PowerButton());
230
231 autostartSvcLogVerbose(1, "Waiting for machine '%ls' to power off...\n", strName.raw());
232
233 uint64_t const tsStartMs = RTTimeMilliTS();
234 RTMSINTERVAL const msTimeout = RT_MS_5MIN; /* Should be enough time, shouldn't it? */
235
236 while (RTTimeMilliTS() - tsStartMs <= msTimeout)
237 {
238 CHECK_ERROR_BREAK(machine, COMGETTER(State)(&enmMachineState));
239 if (enmMachineState != MachineState_Running)
240 break;
241 RTThreadSleep(RT_MS_1SEC);
242 }
243
244 if (RTTimeMilliTS() - tsStartMs > msTimeout)
245 autostartSvcLogWarning("Machine '%ls' did not power off via ACPI within time\n", strName.raw());
246 }
247 else
248 {
249 /* Use save state instead and log this to the console. */
250 autostartSvcLogWarning("The guest of machine '%ls' does not support ACPI shutdown or is currently paused, saving state...\n",
251 strName.raw());
252 hrc = autostartSaveVMState(console);
253 }
254 break;
255 }
256 default:
257 autostartSvcLogWarning("Unknown autostop type for machine '%ls', skipping\n", strName.raw());
258 }
259 g_pSession->UnlockMachine();
260 }
261 }
262 }
263 }
264
265 return VINF_SUCCESS; /** @todo r=andy Report back the overall status here. */
266}
267
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use