VirtualBox

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

Last change on this file since 93318 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 15.0 KB
Line 
1/* $Id: VBoxManageGuestProp.cpp 93115 2022-01-01 11:31:46Z 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#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
53DECLARE_TRANSLATION_CONTEXT(GuestProp);
54
55
56void usageGuestProperty(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2)
57{
58 RTStrmPrintf(pStrm, "%s guestproperty %s get <uuid|vmname>\n"
59 " <property> [--verbose]\n"
60 "\n", pcszSep1, pcszSep2);
61 RTStrmPrintf(pStrm, "%s guestproperty %s set <uuid|vmname>\n"
62 " <property> [<value> [--flags <flags>]]\n"
63 "\n", pcszSep1, pcszSep2);
64 RTStrmPrintf(pStrm, "%s guestproperty %s delete|unset <uuid|vmname>\n"
65 " <property>\n"
66 "\n", pcszSep1, pcszSep2);
67 RTStrmPrintf(pStrm, "%s guestproperty %s enumerate <uuid|vmname>\n"
68 " [--patterns <patterns>]\n"
69 "\n", pcszSep1, pcszSep2);
70 RTStrmPrintf(pStrm, "%s guestproperty %s wait <uuid|vmname> <patterns>\n"
71 " [--timeout <msec>] [--fail-on-timeout]\n"
72 "\n", pcszSep1, pcszSep2);
73}
74
75#ifndef VBOX_ONLY_DOCS
76
77static RTEXITCODE handleGetGuestProperty(HandlerArg *a)
78{
79 HRESULT rc = S_OK;
80
81 bool verbose = false;
82 if ( a->argc == 3
83 && ( !strcmp(a->argv[2], "--verbose")
84 || !strcmp(a->argv[2], "-verbose")))
85 verbose = true;
86 else if (a->argc != 2)
87 return errorSyntax(USAGE_GUESTPROPERTY, GuestProp::tr("Incorrect parameters"));
88
89 ComPtr<IMachine> machine;
90 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
91 machine.asOutParam()));
92 if (machine)
93 {
94 /* open a session for the VM - new or existing */
95 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
96
97 /* get the mutable session machine */
98 a->session->COMGETTER(Machine)(machine.asOutParam());
99
100 Bstr value;
101 LONG64 i64Timestamp;
102 Bstr flags;
103 CHECK_ERROR(machine, GetGuestProperty(Bstr(a->argv[1]).raw(),
104 value.asOutParam(),
105 &i64Timestamp, flags.asOutParam()));
106 if (value.isEmpty())
107 RTPrintf(GuestProp::tr("No value set!\n"));
108 else
109 RTPrintf(GuestProp::tr("Value: %ls\n"), value.raw());
110 if (!value.isEmpty() && verbose)
111 {
112 RTPrintf(GuestProp::tr("Timestamp: %lld\n"), i64Timestamp);
113 RTPrintf(GuestProp::tr("Flags: %ls\n"), flags.raw());
114 }
115 }
116 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
117}
118
119static RTEXITCODE handleSetGuestProperty(HandlerArg *a)
120{
121 HRESULT rc = S_OK;
122
123 /*
124 * Check the syntax. We can deduce the correct syntax from the number of
125 * arguments.
126 */
127 bool usageOK = true;
128 const char *pszName = NULL;
129 const char *pszValue = NULL;
130 const char *pszFlags = NULL;
131 if (a->argc == 3)
132 pszValue = a->argv[2];
133 else if (a->argc == 4)
134 usageOK = false;
135 else if (a->argc == 5)
136 {
137 pszValue = a->argv[2];
138 if ( strcmp(a->argv[3], "--flags")
139 && strcmp(a->argv[3], "-flags"))
140 usageOK = false;
141 pszFlags = a->argv[4];
142 }
143 else if (a->argc != 2)
144 usageOK = false;
145 if (!usageOK)
146 return errorSyntax(USAGE_GUESTPROPERTY, GuestProp::tr("Incorrect parameters"));
147 /* This is always needed. */
148 pszName = a->argv[1];
149
150 ComPtr<IMachine> machine;
151 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
152 machine.asOutParam()));
153 if (machine)
154 {
155 /* open a session for the VM - new or existing */
156 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
157
158 /* get the mutable session machine */
159 a->session->COMGETTER(Machine)(machine.asOutParam());
160
161 if (!pszFlags)
162 CHECK_ERROR(machine, SetGuestPropertyValue(Bstr(pszName).raw(),
163 Bstr(pszValue).raw()));
164 else
165 CHECK_ERROR(machine, SetGuestProperty(Bstr(pszName).raw(),
166 Bstr(pszValue).raw(),
167 Bstr(pszFlags).raw()));
168
169 if (SUCCEEDED(rc))
170 CHECK_ERROR(machine, SaveSettings());
171
172 a->session->UnlockMachine();
173 }
174 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
175}
176
177static RTEXITCODE handleDeleteGuestProperty(HandlerArg *a)
178{
179 HRESULT rc = S_OK;
180
181 /*
182 * Check the syntax. We can deduce the correct syntax from the number of
183 * arguments.
184 */
185 bool usageOK = true;
186 const char *pszName = NULL;
187 if (a->argc != 2)
188 usageOK = false;
189 if (!usageOK)
190 return errorSyntax(USAGE_GUESTPROPERTY, GuestProp::tr("Incorrect parameters"));
191 /* This is always needed. */
192 pszName = a->argv[1];
193
194 ComPtr<IMachine> machine;
195 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
196 machine.asOutParam()));
197 if (machine)
198 {
199 /* open a session for the VM - new or existing */
200 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
201
202 /* get the mutable session machine */
203 a->session->COMGETTER(Machine)(machine.asOutParam());
204
205 CHECK_ERROR(machine, DeleteGuestProperty(Bstr(pszName).raw()));
206
207 if (SUCCEEDED(rc))
208 CHECK_ERROR(machine, SaveSettings());
209
210 a->session->UnlockMachine();
211 }
212 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
213}
214
215/**
216 * Enumerates the properties in the guest property store.
217 *
218 * @returns 0 on success, 1 on failure
219 * @note see the command line API description for parameters
220 */
221static RTEXITCODE handleEnumGuestProperty(HandlerArg *a)
222{
223 /*
224 * Check the syntax. We can deduce the correct syntax from the number of
225 * arguments.
226 */
227 if ( a->argc < 1
228 || a->argc == 2
229 || ( a->argc > 3
230 && strcmp(a->argv[1], "--patterns")
231 && strcmp(a->argv[1], "-patterns")))
232 return errorSyntax(USAGE_GUESTPROPERTY, GuestProp::tr("Incorrect parameters"));
233
234 /*
235 * Pack the patterns
236 */
237 Utf8Str Utf8Patterns(a->argc > 2 ? a->argv[2] : "");
238 for (int i = 3; i < a->argc; ++i)
239 Utf8Patterns = Utf8StrFmt ("%s,%s", Utf8Patterns.c_str(), a->argv[i]);
240
241 /*
242 * Make the actual call to Main.
243 */
244 ComPtr<IMachine> machine;
245 HRESULT rc;
246 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
247 machine.asOutParam()));
248 if (machine)
249 {
250 /* open a session for the VM - new or existing */
251 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
252
253 /* get the mutable session machine */
254 a->session->COMGETTER(Machine)(machine.asOutParam());
255
256 com::SafeArray<BSTR> names;
257 com::SafeArray<BSTR> values;
258 com::SafeArray<LONG64> timestamps;
259 com::SafeArray<BSTR> flags;
260 CHECK_ERROR(machine, EnumerateGuestProperties(Bstr(Utf8Patterns).raw(),
261 ComSafeArrayAsOutParam(names),
262 ComSafeArrayAsOutParam(values),
263 ComSafeArrayAsOutParam(timestamps),
264 ComSafeArrayAsOutParam(flags)));
265 if (SUCCEEDED(rc))
266 {
267 if (names.size() == 0)
268 RTPrintf(GuestProp::tr("No properties found.\n"));
269 for (unsigned i = 0; i < names.size(); ++i)
270 RTPrintf(GuestProp::tr("Name: %ls, value: %ls, timestamp: %lld, flags: %ls\n"),
271 names[i], values[i], timestamps[i], flags[i]);
272 }
273 }
274 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
275}
276
277/**
278 * Enumerates the properties in the guest property store.
279 *
280 * @returns 0 on success, 1 on failure
281 * @note see the command line API description for parameters
282 */
283static RTEXITCODE handleWaitGuestProperty(HandlerArg *a)
284{
285 /*
286 * Handle arguments
287 */
288 bool fFailOnTimeout = false;
289 const char *pszPatterns = NULL;
290 uint32_t cMsTimeout = RT_INDEFINITE_WAIT;
291 bool usageOK = true;
292 if (a->argc < 2)
293 usageOK = false;
294 else
295 pszPatterns = a->argv[1];
296 ComPtr<IMachine> machine;
297 HRESULT rc;
298 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
299 machine.asOutParam()));
300 if (!machine)
301 usageOK = false;
302 for (int i = 2; usageOK && i < a->argc; ++i)
303 {
304 if ( !strcmp(a->argv[i], "--timeout")
305 || !strcmp(a->argv[i], "-timeout"))
306 {
307 if ( i + 1 >= a->argc
308 || RTStrToUInt32Full(a->argv[i + 1], 10, &cMsTimeout) != VINF_SUCCESS)
309 usageOK = false;
310 else
311 ++i;
312 }
313 else if (!strcmp(a->argv[i], "--fail-on-timeout"))
314 fFailOnTimeout = true;
315 else
316 usageOK = false;
317 }
318 if (!usageOK)
319 return errorSyntax(USAGE_GUESTPROPERTY, GuestProp::tr("Incorrect parameters"));
320
321 /*
322 * Set up the event listener and wait until found match or timeout.
323 */
324 Bstr aMachStrGuid;
325 machine->COMGETTER(Id)(aMachStrGuid.asOutParam());
326 Guid aMachGuid(aMachStrGuid);
327 ComPtr<IEventSource> es;
328 CHECK_ERROR(a->virtualBox, COMGETTER(EventSource)(es.asOutParam()));
329 ComPtr<IEventListener> listener;
330 CHECK_ERROR(es, CreateListener(listener.asOutParam()));
331 com::SafeArray <VBoxEventType_T> eventTypes(1);
332 eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged);
333 CHECK_ERROR(es, RegisterListener(listener, ComSafeArrayAsInParam(eventTypes), false));
334
335 uint64_t u64Started = RTTimeMilliTS();
336 bool fSignalled = false;
337 do
338 {
339 unsigned cMsWait;
340 if (cMsTimeout == RT_INDEFINITE_WAIT)
341 cMsWait = 1000;
342 else
343 {
344 uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
345 if (cMsElapsed >= cMsTimeout)
346 break; /* timed out */
347 cMsWait = RT_MIN(1000, cMsTimeout - (uint32_t)cMsElapsed);
348 }
349
350 ComPtr<IEvent> ev;
351 rc = es->GetEvent(listener, cMsWait, ev.asOutParam());
352 if (ev)
353 {
354 VBoxEventType_T aType;
355 rc = ev->COMGETTER(Type)(&aType);
356 switch (aType)
357 {
358 case VBoxEventType_OnGuestPropertyChanged:
359 {
360 ComPtr<IGuestPropertyChangedEvent> gpcev = ev;
361 Assert(gpcev);
362 Bstr aNextStrGuid;
363 gpcev->COMGETTER(MachineId)(aNextStrGuid.asOutParam());
364 if (aMachGuid != Guid(aNextStrGuid))
365 continue;
366 Bstr aNextName;
367 gpcev->COMGETTER(Name)(aNextName.asOutParam());
368 if (RTStrSimplePatternMultiMatch(pszPatterns, RTSTR_MAX,
369 Utf8Str(aNextName).c_str(), RTSTR_MAX, NULL))
370 {
371 Bstr aNextValue, aNextFlags;
372 gpcev->COMGETTER(Value)(aNextValue.asOutParam());
373 gpcev->COMGETTER(Flags)(aNextFlags.asOutParam());
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(USAGE_GUESTPROPERTY, 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(USAGE_GUESTPROPERTY, GuestProp::tr("Incorrect parameters"));
433}
434
435#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