VirtualBox

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

Last change on this file since 86663 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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