VirtualBox

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

Last change on this file since 94660 was 94234, checked in by vboxsync, 3 years ago

FE/VBoxManage: Remove the now unused VBoxManageHelp build target and the VBOX_ONLY_DOCS #ifdef's in the code, ​bugref:9186

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