VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 2 weeks 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.8 KB
Line 
1/* $Id: VBoxAutostartStop.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBoxAutostart - VirtualBox Autostart service, stop machines during system shutdown.
4 */
5
6/*
7 * Copyright (C) 2012-2024 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 ComPtr<IMachine> pMachine = machines[i];
138
139 if (pMachine.isNotNull())
140 {
141 Bstr strName;
142 CHECK_ERROR_BREAK(pMachine, COMGETTER(Name)(strName.asOutParam()));
143
144 BOOL fAccessible;
145 CHECK_ERROR_BREAK(pMachine, COMGETTER(Accessible)(&fAccessible));
146 if (!fAccessible)
147 {
148 autostartSvcLogVerbose(1, "Machine '%ls' is not accessible, skipping\n", strName.raw());
149 continue;
150 }
151
152 AUTOSTOPVM autostopVM;
153
154 AutostopType_T enmAutostopType;
155 CHECK_ERROR_BREAK(pMachine, COMGETTER(AutostopType)(&enmAutostopType));
156 if (enmAutostopType != AutostopType_Disabled)
157 {
158 CHECK_ERROR_BREAK(pMachine, COMGETTER(Id)(autostopVM.strId.asOutParam()));
159 autostopVM.enmAutostopType = enmAutostopType;
160
161 listVM.push_back(autostopVM);
162 }
163
164 autostartSvcLogVerbose(1, "Machine '%ls': Autostop type is %#x\n",
165 strName.raw(), autostopVM.enmAutostopType);
166 }
167 }
168
169 if ( SUCCEEDED(hrc)
170 && !listVM.empty())
171 {
172 std::list<AUTOSTOPVM>::iterator it;
173 for (it = listVM.begin(); it != listVM.end(); ++it)
174 {
175 MachineState_T enmMachineState;
176 ComPtr<IMachine> machine;
177
178 CHECK_ERROR_BREAK(g_pVirtualBox, FindMachine((*it).strId.raw(),
179 machine.asOutParam()));
180
181 Bstr strName;
182 CHECK_ERROR_BREAK(machine, COMGETTER(Name)(strName.asOutParam()));
183
184 CHECK_ERROR_BREAK(machine, COMGETTER(State)(&enmMachineState));
185
186 /* Wait until the VM changes from a transient state back. */
187 while ( enmMachineState >= MachineState_FirstTransient
188 && enmMachineState <= MachineState_LastTransient)
189 {
190 RTThreadSleep(1000);
191 CHECK_ERROR_BREAK(machine, COMGETTER(State)(&enmMachineState));
192 }
193
194 /* Only power off running machines. */
195 if ( enmMachineState == MachineState_Running
196 || enmMachineState == MachineState_Paused)
197 {
198 ComPtr<IConsole> console;
199 ComPtr<IProgress> progress;
200
201 /* open a session for the VM */
202 CHECK_ERROR_BREAK(machine, LockMachine(g_pSession, LockType_Shared));
203
204 /* get the associated console */
205 CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam()));
206
207 switch ((*it).enmAutostopType)
208 {
209 case AutostopType_SaveState:
210 {
211 hrc = autostartSaveVMState(console);
212 break;
213 }
214 case AutostopType_PowerOff:
215 {
216 CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));
217
218 hrc = showProgress(progress);
219 CHECK_PROGRESS_ERROR(progress, ("Failed to powering off machine '%ls'", strName.raw()));
220 if (FAILED(hrc))
221 autostartSvcLogError("Powering off machine '%ls' failed with %Rhrc\n", strName.raw(), hrc);
222 break;
223 }
224 case AutostopType_AcpiShutdown:
225 {
226 BOOL fGuestEnteredACPI = false;
227 CHECK_ERROR_BREAK(console, GetGuestEnteredACPIMode(&fGuestEnteredACPI));
228 if ( fGuestEnteredACPI
229 && enmMachineState == MachineState_Running)
230 {
231 CHECK_ERROR_BREAK(console, PowerButton());
232
233 autostartSvcLogVerbose(1, "Waiting for machine '%ls' to power off...\n", strName.raw());
234
235 uint64_t const tsStartMs = RTTimeMilliTS();
236 RTMSINTERVAL const msTimeout = RT_MS_5MIN; /* Should be enough time, shouldn't it? */
237
238 while (RTTimeMilliTS() - tsStartMs <= msTimeout)
239 {
240 CHECK_ERROR_BREAK(machine, COMGETTER(State)(&enmMachineState));
241 if (enmMachineState != MachineState_Running)
242 break;
243 RTThreadSleep(RT_MS_1SEC);
244 }
245
246 if (RTTimeMilliTS() - tsStartMs > msTimeout)
247 autostartSvcLogWarning("Machine '%ls' did not power off via ACPI within time\n", strName.raw());
248 }
249 else
250 {
251 /* Use save state instead and log this to the console. */
252 autostartSvcLogWarning("The guest of machine '%ls' does not support ACPI shutdown or is currently paused, saving state...\n",
253 strName.raw());
254 hrc = autostartSaveVMState(console);
255 }
256 break;
257 }
258 default:
259 autostartSvcLogWarning("Unknown autostop type for machine '%ls', skipping\n", strName.raw());
260 }
261 g_pSession->UnlockMachine();
262 }
263 }
264 }
265 }
266
267 return VINF_SUCCESS; /** @todo r=andy Report back the overall status here. */
268}
269
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