VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestProp.cpp@ 96588

Last change on this file since 96588 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.6 KB
Line 
1/* $Id: VBoxManageGuestProp.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of guestproperty command.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include "VBoxManage.h"
33
34#include <VBox/com/com.h>
35#include <VBox/com/string.h>
36#include <VBox/com/array.h>
37#include <VBox/com/ErrorInfo.h>
38#include <VBox/com/errorprint.h>
39#include <VBox/com/VirtualBox.h>
40
41#include <VBox/log.h>
42#include <iprt/asm.h>
43#include <iprt/stream.h>
44#include <iprt/string.h>
45#include <iprt/time.h>
46#include <iprt/thread.h>
47
48#ifdef USE_XPCOM_QUEUE
49# include <sys/select.h>
50# include <errno.h>
51#endif
52
53#ifdef RT_OS_DARWIN
54# include <CoreFoundation/CFRunLoop.h>
55#endif
56
57using namespace com;
58
59DECLARE_TRANSLATION_CONTEXT(GuestProp);
60
61
62static RTEXITCODE handleGetGuestProperty(HandlerArg *a)
63{
64 HRESULT hrc = S_OK;
65
66 setCurrentSubcommand(HELP_SCOPE_GUESTPROPERTY_GET);
67
68 bool verbose = false;
69 if ( a->argc == 3
70 && ( !strcmp(a->argv[2], "--verbose")
71 || !strcmp(a->argv[2], "-verbose")))
72 verbose = true;
73 else if (a->argc != 2)
74 return errorSyntax(GuestProp::tr("Incorrect parameters"));
75
76 ComPtr<IMachine> machine;
77 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
78 machine.asOutParam()));
79 if (machine)
80 {
81 /* open a session for the VM - new or existing */
82 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
83
84 /* get the mutable session machine */
85 a->session->COMGETTER(Machine)(machine.asOutParam());
86
87 Bstr value;
88 LONG64 i64Timestamp;
89 Bstr flags;
90 CHECK_ERROR(machine, GetGuestProperty(Bstr(a->argv[1]).raw(),
91 value.asOutParam(),
92 &i64Timestamp, flags.asOutParam()));
93 if (value.isEmpty())
94 RTPrintf(GuestProp::tr("No value set!\n"));
95 else
96 RTPrintf(GuestProp::tr("Value: %ls\n"), value.raw());
97 if (!value.isEmpty() && verbose)
98 {
99 RTPrintf(GuestProp::tr("Timestamp: %lld\n"), i64Timestamp);
100 RTPrintf(GuestProp::tr("Flags: %ls\n"), flags.raw());
101 }
102 }
103 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
104}
105
106static RTEXITCODE handleSetGuestProperty(HandlerArg *a)
107{
108 HRESULT hrc = S_OK;
109
110 setCurrentSubcommand(HELP_SCOPE_GUESTPROPERTY_SET);
111
112 /*
113 * Check the syntax. We can deduce the correct syntax from the number of
114 * arguments.
115 */
116 bool usageOK = true;
117 const char *pszName = NULL;
118 const char *pszValue = NULL;
119 const char *pszFlags = NULL;
120 if (a->argc == 3)
121 pszValue = a->argv[2];
122 else if (a->argc == 4)
123 usageOK = false;
124 else if (a->argc == 5)
125 {
126 pszValue = a->argv[2];
127 if ( strcmp(a->argv[3], "--flags")
128 && strcmp(a->argv[3], "-flags"))
129 usageOK = false;
130 pszFlags = a->argv[4];
131 }
132 else if (a->argc != 2)
133 usageOK = false;
134 if (!usageOK)
135 return errorSyntax(GuestProp::tr("Incorrect parameters"));
136 /* This is always needed. */
137 pszName = a->argv[1];
138
139 ComPtr<IMachine> machine;
140 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
141 machine.asOutParam()));
142 if (machine)
143 {
144 /* open a session for the VM - new or existing */
145 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
146
147 /* get the mutable session machine */
148 a->session->COMGETTER(Machine)(machine.asOutParam());
149
150 if (!pszFlags)
151 CHECK_ERROR(machine, SetGuestPropertyValue(Bstr(pszName).raw(),
152 Bstr(pszValue).raw()));
153 else
154 CHECK_ERROR(machine, SetGuestProperty(Bstr(pszName).raw(),
155 Bstr(pszValue).raw(),
156 Bstr(pszFlags).raw()));
157
158 if (SUCCEEDED(hrc))
159 CHECK_ERROR(machine, SaveSettings());
160
161 a->session->UnlockMachine();
162 }
163 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
164}
165
166static RTEXITCODE handleDeleteGuestProperty(HandlerArg *a)
167{
168 HRESULT hrc = S_OK;
169
170 setCurrentSubcommand(HELP_SCOPE_GUESTPROPERTY_UNSET);
171
172 /*
173 * Check the syntax. We can deduce the correct syntax from the number of
174 * arguments.
175 */
176 bool usageOK = true;
177 const char *pszName = NULL;
178 if (a->argc != 2)
179 usageOK = false;
180 if (!usageOK)
181 return errorSyntax(GuestProp::tr("Incorrect parameters"));
182 /* This is always needed. */
183 pszName = a->argv[1];
184
185 ComPtr<IMachine> machine;
186 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
187 machine.asOutParam()));
188 if (machine)
189 {
190 /* open a session for the VM - new or existing */
191 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
192
193 /* get the mutable session machine */
194 a->session->COMGETTER(Machine)(machine.asOutParam());
195
196 CHECK_ERROR(machine, DeleteGuestProperty(Bstr(pszName).raw()));
197
198 if (SUCCEEDED(hrc))
199 CHECK_ERROR(machine, SaveSettings());
200
201 a->session->UnlockMachine();
202 }
203 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
204}
205
206/**
207 * Enumerates the properties in the guest property store.
208 *
209 * @returns 0 on success, 1 on failure
210 * @note see the command line API description for parameters
211 */
212static RTEXITCODE handleEnumGuestProperty(HandlerArg *a)
213{
214 setCurrentSubcommand(HELP_SCOPE_GUESTPROPERTY_ENUMERATE);
215
216 /*
217 * Check the syntax. We can deduce the correct syntax from the number of
218 * arguments.
219 */
220 if ( a->argc < 1
221 || a->argc == 2
222 || ( a->argc > 3
223 && strcmp(a->argv[1], "--patterns")
224 && strcmp(a->argv[1], "-patterns")))
225 return errorSyntax(GuestProp::tr("Incorrect parameters"));
226
227 /*
228 * Pack the patterns
229 */
230 Utf8Str strPatterns(a->argc > 2 ? a->argv[2] : "");
231 for (int i = 3; i < a->argc; ++i)
232 strPatterns = Utf8StrFmt ("%s,%s", strPatterns.c_str(), a->argv[i]);
233
234 /*
235 * Make the actual call to Main.
236 */
237 ComPtr<IMachine> machine;
238 HRESULT hrc;
239 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
240 machine.asOutParam()));
241 if (machine)
242 {
243 /* open a session for the VM - new or existing */
244 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
245
246 /* get the mutable session machine */
247 a->session->COMGETTER(Machine)(machine.asOutParam());
248
249 com::SafeArray<BSTR> names;
250 com::SafeArray<BSTR> values;
251 com::SafeArray<LONG64> timestamps;
252 com::SafeArray<BSTR> flags;
253 CHECK_ERROR(machine, EnumerateGuestProperties(Bstr(strPatterns).raw(),
254 ComSafeArrayAsOutParam(names),
255 ComSafeArrayAsOutParam(values),
256 ComSafeArrayAsOutParam(timestamps),
257 ComSafeArrayAsOutParam(flags)));
258 if (SUCCEEDED(hrc))
259 {
260 if (names.size() == 0)
261 RTPrintf(GuestProp::tr("No properties found.\n"));
262 for (unsigned i = 0; i < names.size(); ++i)
263 RTPrintf(GuestProp::tr("Name: %ls, value: %ls, timestamp: %lld, flags: %ls\n"),
264 names[i], values[i], timestamps[i], flags[i]);
265 }
266 }
267 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
268}
269
270/**
271 * Enumerates the properties in the guest property store.
272 *
273 * @returns 0 on success, 1 on failure
274 * @note see the command line API description for parameters
275 */
276static RTEXITCODE handleWaitGuestProperty(HandlerArg *a)
277{
278 setCurrentSubcommand(HELP_SCOPE_GUESTPROPERTY_WAIT);
279
280 /*
281 * Handle arguments
282 */
283 bool fFailOnTimeout = false;
284 const char *pszPatterns = NULL;
285 uint32_t cMsTimeout = RT_INDEFINITE_WAIT;
286 bool usageOK = true;
287 if (a->argc < 2)
288 usageOK = false;
289 else
290 pszPatterns = a->argv[1];
291 ComPtr<IMachine> machine;
292 HRESULT hrc;
293 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
294 machine.asOutParam()));
295 if (!machine)
296 usageOK = false;
297 for (int i = 2; usageOK && i < a->argc; ++i)
298 {
299 if ( !strcmp(a->argv[i], "--timeout")
300 || !strcmp(a->argv[i], "-timeout"))
301 {
302 if ( i + 1 >= a->argc
303 || RTStrToUInt32Full(a->argv[i + 1], 10, &cMsTimeout) != VINF_SUCCESS)
304 usageOK = false;
305 else
306 ++i;
307 }
308 else if (!strcmp(a->argv[i], "--fail-on-timeout"))
309 fFailOnTimeout = true;
310 else
311 usageOK = false;
312 }
313 if (!usageOK)
314 return errorSyntax(GuestProp::tr("Incorrect parameters"));
315
316 /*
317 * Set up the event listener and wait until found match or timeout.
318 */
319 Bstr aMachStrGuid;
320 machine->COMGETTER(Id)(aMachStrGuid.asOutParam());
321 Guid aMachGuid(aMachStrGuid);
322 ComPtr<IEventSource> es;
323 CHECK_ERROR(a->virtualBox, COMGETTER(EventSource)(es.asOutParam()));
324 ComPtr<IEventListener> listener;
325 CHECK_ERROR(es, CreateListener(listener.asOutParam()));
326 com::SafeArray <VBoxEventType_T> eventTypes(1);
327 eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged);
328 CHECK_ERROR(es, RegisterListener(listener, ComSafeArrayAsInParam(eventTypes), false));
329
330 uint64_t u64Started = RTTimeMilliTS();
331 bool fSignalled = false;
332 do
333 {
334 unsigned cMsWait;
335 if (cMsTimeout == RT_INDEFINITE_WAIT)
336 cMsWait = 1000;
337 else
338 {
339 uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
340 if (cMsElapsed >= cMsTimeout)
341 break; /* timed out */
342 cMsWait = RT_MIN(1000, cMsTimeout - (uint32_t)cMsElapsed);
343 }
344
345 ComPtr<IEvent> ev;
346 hrc = es->GetEvent(listener, cMsWait, ev.asOutParam());
347 if (ev) /** @todo r=andy Why not using SUCCEEDED(hrc) here? */
348 {
349 VBoxEventType_T aType;
350 hrc = ev->COMGETTER(Type)(&aType);
351 switch (aType)
352 {
353 case VBoxEventType_OnGuestPropertyChanged:
354 {
355 ComPtr<IGuestPropertyChangedEvent> gpcev = ev;
356 Assert(gpcev);
357 Bstr aNextStrGuid;
358 gpcev->COMGETTER(MachineId)(aNextStrGuid.asOutParam());
359 if (aMachGuid != Guid(aNextStrGuid))
360 continue;
361 Bstr aNextName;
362 gpcev->COMGETTER(Name)(aNextName.asOutParam());
363 if (RTStrSimplePatternMultiMatch(pszPatterns, RTSTR_MAX,
364 Utf8Str(aNextName).c_str(), RTSTR_MAX, NULL))
365 {
366 Bstr aNextValue, aNextFlags;
367 BOOL aNextWasDeleted;
368 gpcev->COMGETTER(Value)(aNextValue.asOutParam());
369 gpcev->COMGETTER(Flags)(aNextFlags.asOutParam());
370 gpcev->COMGETTER(FWasDeleted)(&aNextWasDeleted);
371 if (aNextWasDeleted)
372 RTPrintf(GuestProp::tr("Property %ls was deleted\n"), aNextName.raw());
373 else
374 RTPrintf(GuestProp::tr("Name: %ls, value: %ls, flags: %ls\n"),
375 aNextName.raw(), aNextValue.raw(), aNextFlags.raw());
376 fSignalled = true;
377 }
378 break;
379 }
380 default:
381 AssertFailed();
382 }
383 }
384 } while (!fSignalled);
385
386 es->UnregisterListener(listener);
387
388 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
389 if (!fSignalled)
390 {
391 RTMsgError(GuestProp::tr("Time out or interruption while waiting for a notification."));
392 if (fFailOnTimeout)
393 /* Hysterical rasins: We always returned 2 here, which now translates to syntax error... Which is bad. */
394 rcExit = RTEXITCODE_SYNTAX;
395 }
396 return rcExit;
397}
398
399/**
400 * Access the guest property store.
401 *
402 * @returns 0 on success, 1 on failure
403 * @note see the command line API description for parameters
404 */
405RTEXITCODE handleGuestProperty(HandlerArg *a)
406{
407 HandlerArg arg = *a;
408 arg.argc = a->argc - 1;
409 arg.argv = a->argv + 1;
410
411 /** @todo This command does not follow the syntax where the <uuid|vmname>
412 * comes between the command and subcommand. The commands controlvm,
413 * snapshot and debugvm puts it between.
414 */
415
416 if (a->argc == 0)
417 return errorSyntax(GuestProp::tr("Incorrect parameters"));
418
419 /* switch (cmd) */
420 if (strcmp(a->argv[0], "get") == 0)
421 return handleGetGuestProperty(&arg);
422 if (strcmp(a->argv[0], "set") == 0)
423 return handleSetGuestProperty(&arg);
424 if (strcmp(a->argv[0], "delete") == 0 || strcmp(a->argv[0], "unset") == 0)
425 return handleDeleteGuestProperty(&arg);
426 if (strcmp(a->argv[0], "enumerate") == 0)
427 return handleEnumGuestProperty(&arg);
428 if (strcmp(a->argv[0], "wait") == 0)
429 return handleWaitGuestProperty(&arg);
430
431 /* default: */
432 return errorSyntax(GuestProp::tr("Incorrect parameters"));
433}
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