VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp@ 96399

最後變更 在這個檔案從96399是 96399,由 vboxsync 提交於 3 年 前

/Config.kmk and many other places: Change VBOX_VENDOR to the official copyright holder text, needs follow-up changes and equivalent adjustments elsewhere.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 64.3 KB
 
1/* $Id: VBoxServiceToolBox.cpp 96399 2022-08-22 14:47:39Z vboxsync $ */
2/** @file
3 * VBoxServiceToolbox - Internal (BusyBox-like) toolbox.
4 */
5
6/*
7 * Copyright (C) 2012-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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 <iprt/assert.h>
23#include <iprt/buildconfig.h>
24#include <iprt/dir.h>
25#include <iprt/file.h>
26#include <iprt/getopt.h>
27#include <iprt/list.h>
28#include <iprt/mem.h>
29#include <iprt/message.h>
30#include <iprt/path.h>
31#include <iprt/string.h>
32#include <iprt/stream.h>
33#include <iprt/symlink.h>
34
35#ifndef RT_OS_WINDOWS
36# include <sys/stat.h> /* need umask */
37#endif
38
39#include <VBox/VBoxGuestLib.h>
40#include <VBox/version.h>
41
42#include <VBox/GuestHost/GuestControl.h>
43
44#include "VBoxServiceInternal.h"
45#include "VBoxServiceToolBox.h"
46#include "VBoxServiceUtils.h"
47
48using namespace guestControl;
49
50
51/*********************************************************************************************************************************
52* Defined Constants And Macros *
53*********************************************************************************************************************************/
54
55/** Generic option indices for commands. */
56enum
57{
58 VBOXSERVICETOOLBOXOPT_MACHINE_READABLE = 1000,
59 VBOXSERVICETOOLBOXOPT_VERBOSE
60};
61
62/** Options indices for "vbox_cat". */
63typedef enum VBOXSERVICETOOLBOXCATOPT
64{
65 VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED = 1000
66} VBOXSERVICETOOLBOXCATOPT;
67
68/** Flags for "vbox_ls". */
69typedef enum VBOXSERVICETOOLBOXLSFLAG
70{
71 VBOXSERVICETOOLBOXLSFLAG_NONE,
72 VBOXSERVICETOOLBOXLSFLAG_RECURSIVE,
73 VBOXSERVICETOOLBOXLSFLAG_SYMLINKS
74} VBOXSERVICETOOLBOXLSFLAG;
75
76/** Flags for fs object output. */
77typedef enum VBOXSERVICETOOLBOXOUTPUTFLAG
78{
79 VBOXSERVICETOOLBOXOUTPUTFLAG_NONE,
80 VBOXSERVICETOOLBOXOUTPUTFLAG_LONG,
81 VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE
82} VBOXSERVICETOOLBOXOUTPUTFLAG;
83
84/** The size of the directory entry buffer we're using. */
85#define VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX)
86
87
88/*********************************************************************************************************************************
89* Structures and Typedefs *
90*********************************************************************************************************************************/
91/** Pointer to a tool handler function. */
92typedef RTEXITCODE (*PFNHANDLER)(int , char **);
93
94/** Definition for a specific toolbox tool. */
95typedef struct VBOXSERVICETOOLBOXTOOL
96{
97 /** Friendly name of the tool. */
98 const char *pszName;
99 /** Main handler to be invoked to use the tool. */
100 RTEXITCODE (*pfnHandler)(int argc, char **argv);
101 /** Conversion routine to convert the tool's exit code back to an IPRT rc. Optional.
102 *
103 * @todo r=bird: You better revert this, i.e. having pfnHandler return a VBox
104 * status code and have a routine for converting it to RTEXITCODE.
105 * Unless, what you really want to do here is to get a cached status, in
106 * which case you better call it what it is.
107 */
108 int (*pfnExitCodeConvertToRc)(RTEXITCODE rcExit);
109} VBOXSERVICETOOLBOXTOOL;
110/** Pointer to a const tool definition. */
111typedef VBOXSERVICETOOLBOXTOOL const *PCVBOXSERVICETOOLBOXTOOL;
112
113/**
114 * An file/directory entry. Used to cache
115 * file names/paths for later processing.
116 */
117typedef struct VBOXSERVICETOOLBOXPATHENTRY
118{
119 /** Our node. */
120 RTLISTNODE Node;
121 /** Name of the entry. */
122 char *pszName;
123} VBOXSERVICETOOLBOXPATHENTRY, *PVBOXSERVICETOOLBOXPATHENTRY;
124
125/** ID cache entry. */
126typedef struct VGSVCTOOLBOXUIDENTRY
127{
128 /** The identifier name. */
129 uint32_t id;
130 /** Set if UID, clear if GID. */
131 bool fIsUid;
132 /** The name. */
133 char szName[128 - 4 - 1];
134} VGSVCTOOLBOXUIDENTRY;
135typedef VGSVCTOOLBOXUIDENTRY *PVGSVCTOOLBOXUIDENTRY;
136
137
138/** ID cache. */
139typedef struct VGSVCTOOLBOXIDCACHE
140{
141 /** Number of valid cache entries. */
142 uint32_t cEntries;
143 /** The next entry to replace. */
144 uint32_t iNextReplace;
145 /** The cache entries. */
146 VGSVCTOOLBOXUIDENTRY aEntries[16];
147} VGSVCTOOLBOXIDCACHE;
148typedef VGSVCTOOLBOXIDCACHE *PVGSVCTOOLBOXIDCACHE;
149
150
151/*********************************************************************************************************************************
152* Internal Functions *
153*********************************************************************************************************************************/
154static RTEXITCODE vgsvcToolboxCat(int argc, char **argv);
155static RTEXITCODE vgsvcToolboxLs(int argc, char **argv);
156static RTEXITCODE vgsvcToolboxRm(int argc, char **argv);
157static RTEXITCODE vgsvcToolboxMkTemp(int argc, char **argv);
158static RTEXITCODE vgsvcToolboxMkDir(int argc, char **argv);
159static RTEXITCODE vgsvcToolboxStat(int argc, char **argv);
160
161
162/*********************************************************************************************************************************
163* Global Variables *
164*********************************************************************************************************************************/
165/** Tool definitions. */
166static VBOXSERVICETOOLBOXTOOL const g_aTools[] =
167{
168 { VBOXSERVICE_TOOL_CAT, vgsvcToolboxCat , NULL },
169 { VBOXSERVICE_TOOL_LS, vgsvcToolboxLs , NULL },
170 { VBOXSERVICE_TOOL_RM, vgsvcToolboxRm , NULL },
171 { VBOXSERVICE_TOOL_MKTEMP, vgsvcToolboxMkTemp, NULL },
172 { VBOXSERVICE_TOOL_MKDIR, vgsvcToolboxMkDir , NULL },
173 { VBOXSERVICE_TOOL_STAT, vgsvcToolboxStat , NULL }
174};
175
176
177
178
179/**
180 * Displays a common header for all help text to stdout.
181 */
182static void vgsvcToolboxShowUsageHeader(void)
183{
184 RTPrintf(VBOX_PRODUCT " Guest Toolbox Version "
185 VBOX_VERSION_STRING "\n"
186 "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
187 RTPrintf("Usage:\n\n");
188}
189
190
191/**
192 * Displays a help text to stdout.
193 */
194static void vgsvcToolboxShowUsage(void)
195{
196 vgsvcToolboxShowUsageHeader();
197 RTPrintf(" VBoxService [--use-toolbox] vbox_<command> [<general options>] <parameters>\n\n"
198 "General options:\n\n"
199 " --machinereadable produce all output in machine-readable form\n"
200 " -V print version number and exit\n"
201 "\n"
202 "Commands:\n\n"
203 " vbox_cat [<general options>] <file>...\n"
204 " vbox_ls [<general options>] [--dereference|-L] [-l] [-R]\n"
205 " [--verbose|-v] [<file>...]\n"
206 " vbox_rm [<general options>] [-r|-R] <file>...\n"
207 " vbox_mktemp [<general options>] [--directory|-d] [--mode|-m <mode>]\n"
208 " [--secure|-s] [--tmpdir|-t <path>] <template>\n"
209 " vbox_mkdir [<general options>] [--mode|-m <mode>] [--parents|-p]\n"
210 " [--verbose|-v] <directory>...\n"
211 " vbox_stat [<general options>] [--file-system|-f]\n"
212 " [--dereference|-L] [--terse|-t] [--verbose|-v] <file>...\n"
213 "\n");
214}
215
216
217/**
218 * Displays the program's version number.
219 */
220static void vgsvcToolboxShowVersion(void)
221{
222 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
223}
224
225
226/**
227 * Initializes the parseable stream(s).
228 *
229 * @return IPRT status code.
230 */
231static int vgsvcToolboxStrmInit(void)
232{
233 /* Set stdout's mode to binary. This is required for outputting all the machine-readable
234 * data correctly. */
235 int rc = RTStrmSetMode(g_pStdOut, true /* Binary mode */, -1 /* Current code set, not changed */);
236 if (RT_FAILURE(rc))
237 RTMsgError("Unable to set stdout to binary mode, rc=%Rrc\n", rc);
238
239 return rc;
240}
241
242
243/**
244 * Prints a parseable stream header which contains the actual tool
245 * which was called/used along with its stream version.
246 *
247 * @param pszToolName Name of the tool being used, e.g. "vbt_ls".
248 * @param uVersion Stream version name. Handy for distinguishing
249 * different stream versions later.
250 */
251static void vgsvcToolboxPrintStrmHeader(const char *pszToolName, uint32_t uVersion)
252{
253 AssertPtrReturnVoid(pszToolName);
254 RTPrintf("hdr_id=%s%chdr_ver=%u%c", pszToolName, 0, uVersion, 0);
255}
256
257
258/**
259 * Prints a standardized termination sequence indicating that the
260 * parseable stream just ended.
261 *
262 */
263static void vgsvcToolboxPrintStrmTermination()
264{
265 RTPrintf("%c%c%c%c", 0, 0, 0, 0);
266}
267
268
269/**
270 * Parse a file mode string from the command line (currently octal only)
271 * and print an error message and return an error if necessary.
272 */
273static int vgsvcToolboxParseMode(const char *pcszMode, RTFMODE *pfMode)
274{
275 int rc = RTStrToUInt32Ex(pcszMode, NULL, 8 /* Base */, pfMode);
276 if (RT_FAILURE(rc)) /* Only octet based values supported right now! */
277 RTMsgError("Mode flag strings not implemented yet! Use octal numbers instead. (%s)\n", pcszMode);
278 return rc;
279}
280
281
282/**
283 * Destroys a path buffer list.
284 *
285 * @return IPRT status code.
286 * @param pList Pointer to list to destroy.
287 */
288static void vgsvcToolboxPathBufDestroy(PRTLISTNODE pList)
289{
290 if (!pList)
291 return;
292
293 PVBOXSERVICETOOLBOXPATHENTRY pEntry, pEntryNext;
294 RTListForEachSafe(pList, pEntry, pEntryNext, VBOXSERVICETOOLBOXPATHENTRY, Node)
295 {
296 RTListNodeRemove(&pEntry->Node);
297
298 RTStrFree(pEntry->pszName);
299 RTMemFree(pEntry);
300 }
301}
302
303
304/**
305 * Adds a path entry (file/directory/whatever) to a given path buffer list.
306 *
307 * @return IPRT status code.
308 * @param pList Pointer to list to add entry to.
309 * @param pszName Name of entry to add.
310 */
311static int vgsvcToolboxPathBufAddPathEntry(PRTLISTNODE pList, const char *pszName)
312{
313 AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
314
315 int rc = VINF_SUCCESS;
316 PVBOXSERVICETOOLBOXPATHENTRY pNode = (PVBOXSERVICETOOLBOXPATHENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXPATHENTRY));
317 if (pNode)
318 {
319 pNode->pszName = RTStrDup(pszName);
320 AssertPtr(pNode->pszName);
321
322 RTListAppend(pList, &pNode->Node);
323 }
324 else
325 rc = VERR_NO_MEMORY;
326 return rc;
327}
328
329
330/**
331 * Performs the actual output operation of "vbox_cat".
332 *
333 * @return IPRT status code.
334 * @param hInput Handle of input file (if any) to use;
335 * else stdin will be used.
336 * @param hOutput Handle of output file (if any) to use;
337 * else stdout will be used.
338 */
339static int vgsvcToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
340{
341 int rc = VINF_SUCCESS;
342 if (hInput == NIL_RTFILE)
343 {
344 rc = RTFileFromNative(&hInput, RTFILE_NATIVE_STDIN);
345 if (RT_FAILURE(rc))
346 RTMsgError("Could not translate input file to native handle, rc=%Rrc\n", rc);
347 }
348
349 if (hOutput == NIL_RTFILE)
350 {
351 rc = RTFileFromNative(&hOutput, RTFILE_NATIVE_STDOUT);
352 if (RT_FAILURE(rc))
353 RTMsgError("Could not translate output file to native handle, rc=%Rrc\n", rc);
354 }
355
356 if (RT_SUCCESS(rc))
357 {
358 uint8_t abBuf[_64K];
359 size_t cbRead;
360 for (;;)
361 {
362 rc = RTFileRead(hInput, abBuf, sizeof(abBuf), &cbRead);
363 if (RT_SUCCESS(rc) && cbRead > 0)
364 {
365 rc = RTFileWrite(hOutput, abBuf, cbRead, NULL /* Try to write all at once! */);
366 if (RT_FAILURE(rc))
367 {
368 RTMsgError("Error while writing output, rc=%Rrc\n", rc);
369 break;
370 }
371 }
372 else
373 {
374 if (rc == VERR_BROKEN_PIPE)
375 rc = VINF_SUCCESS;
376 else if (RT_FAILURE(rc))
377 RTMsgError("Error while reading input, rc=%Rrc\n", rc);
378 break;
379 }
380 }
381 }
382 return rc;
383}
384
385
386/** @todo Document options! */
387static char g_paszCatHelp[] =
388 " VBoxService [--use-toolbox] vbox_cat [<general options>] <file>...\n\n"
389 "Concatenate files, or standard input, to standard output.\n"
390 "\n";
391
392
393/**
394 * Main function for tool "vbox_cat".
395 *
396 * @return RTEXITCODE.
397 * @param argc Number of arguments.
398 * @param argv Pointer to argument array.
399 */
400static RTEXITCODE vgsvcToolboxCat(int argc, char **argv)
401{
402 static const RTGETOPTDEF s_aOptions[] =
403 {
404 /* Sorted by short ops. */
405 { "--show-all", 'a', RTGETOPT_REQ_NOTHING },
406 { "--number-nonblank", 'b', RTGETOPT_REQ_NOTHING},
407 { NULL, 'e', RTGETOPT_REQ_NOTHING},
408 { NULL, 'E', RTGETOPT_REQ_NOTHING},
409 { "--flags", 'f', RTGETOPT_REQ_STRING},
410 { "--no-content-indexed", VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED, RTGETOPT_REQ_NOTHING},
411 { "--number", 'n', RTGETOPT_REQ_NOTHING},
412 { "--output", 'o', RTGETOPT_REQ_STRING},
413 { "--squeeze-blank", 's', RTGETOPT_REQ_NOTHING},
414 { NULL, 't', RTGETOPT_REQ_NOTHING},
415 { "--show-tabs", 'T', RTGETOPT_REQ_NOTHING},
416 { NULL, 'u', RTGETOPT_REQ_NOTHING},
417 { "--show-noneprinting", 'v', RTGETOPT_REQ_NOTHING}
418 };
419
420 int ch;
421 RTGETOPTUNION ValueUnion;
422 RTGETOPTSTATE GetState;
423
424 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, 0 /*fFlags*/);
425
426 int rc = VINF_SUCCESS;
427
428 const char *pszOutput = NULL;
429 RTFILE hOutput = NIL_RTFILE;
430 uint32_t fFlags = RTFILE_O_CREATE_REPLACE /* Output file flags. */
431 | RTFILE_O_WRITE
432 | RTFILE_O_DENY_WRITE;
433
434 /* Init directory list. */
435 RTLISTANCHOR inputList;
436 RTListInit(&inputList);
437
438 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
439 && RT_SUCCESS(rc))
440 {
441 /* For options that require an argument, ValueUnion has received the value. */
442 switch (ch)
443 {
444 case 'a':
445 case 'b':
446 case 'e':
447 case 'E':
448 case 'n':
449 case 's':
450 case 't':
451 case 'T':
452 case 'v':
453 RTMsgError("Sorry, option '%s' is not implemented yet!\n",
454 ValueUnion.pDef->pszLong);
455 rc = VERR_INVALID_PARAMETER;
456 break;
457
458 case 'h':
459 vgsvcToolboxShowUsageHeader();
460 RTPrintf("%s", g_paszCatHelp);
461 return RTEXITCODE_SUCCESS;
462
463 case 'o':
464 pszOutput = ValueUnion.psz;
465 break;
466
467 case 'u':
468 /* Ignored. */
469 break;
470
471 case 'V':
472 vgsvcToolboxShowVersion();
473 return RTEXITCODE_SUCCESS;
474
475 case VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED:
476 fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
477 break;
478
479 case VINF_GETOPT_NOT_OPTION:
480 /* Add file(s) to buffer. This enables processing multiple paths
481 * at once.
482 *
483 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
484 * processing this loop it's safe to immediately exit on syntax errors
485 * or showing the help text (see above). */
486 rc = vgsvcToolboxPathBufAddPathEntry(&inputList, ValueUnion.psz);
487 break;
488
489 default:
490 return RTGetOptPrintError(ch, &ValueUnion);
491 }
492 }
493
494 if (RT_SUCCESS(rc))
495 {
496 if (pszOutput)
497 {
498 rc = RTFileOpen(&hOutput, pszOutput, fFlags);
499 if (RT_FAILURE(rc))
500 RTMsgError("Could not create output file '%s', rc=%Rrc\n", pszOutput, rc);
501 }
502
503 if (RT_SUCCESS(rc))
504 {
505 /* Process each input file. */
506 RTFILE hInput = NIL_RTFILE;
507 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
508 RTListForEach(&inputList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
509 {
510 rc = RTFileOpen(&hInput, pNodeIt->pszName,
511 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
512 if (RT_SUCCESS(rc))
513 {
514 rc = vgsvcToolboxCatOutput(hInput, hOutput);
515 RTFileClose(hInput);
516 }
517 else
518 RTMsgError("Could not open input file '%s': %Rrc\n", pNodeIt->pszName, rc);
519 if (RT_FAILURE(rc))
520 break;
521 }
522
523 /* If no input files were defined, process stdin. */
524 if (RTListNodeIsFirst(&inputList, &inputList))
525 rc = vgsvcToolboxCatOutput(hInput, hOutput);
526 }
527 }
528
529 if (hOutput != NIL_RTFILE)
530 RTFileClose(hOutput);
531 vgsvcToolboxPathBufDestroy(&inputList);
532
533 if (RT_FAILURE(rc))
534 {
535 switch (rc)
536 {
537 case VERR_ACCESS_DENIED:
538 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_ACCESS_DENIED;
539
540 case VERR_FILE_NOT_FOUND:
541 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_FILE_NOT_FOUND;
542
543 case VERR_PATH_NOT_FOUND:
544 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_PATH_NOT_FOUND;
545
546 case VERR_SHARING_VIOLATION:
547 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_SHARING_VIOLATION;
548
549 case VERR_IS_A_DIRECTORY:
550 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_IS_A_DIRECTORY;
551
552 default:
553#ifdef DEBUG_andy
554 AssertMsgFailed(("Exit code for %Rrc not implemented\n", rc));
555#endif
556 break;
557 }
558
559 return RTEXITCODE_FAILURE;
560 }
561
562 return RTEXITCODE_SUCCESS;
563}
564
565
566/**
567 * Resolves the UID to a name as best as we can.
568 *
569 * @returns Read-only name string. Only valid till the next cache call.
570 * @param pIdCache The ID cache.
571 * @param uid The UID to resolve.
572 * @param pszEntry The filename of the UID.
573 * @param pszRelativeTo What @a pszEntry is relative to, NULL if absolute.
574 */
575static const char *vgsvcToolboxIdCacheGetUidName(PVGSVCTOOLBOXIDCACHE pIdCache, RTUID uid,
576 const char *pszEntry, const char *pszRelativeTo)
577{
578 /* Check cached entries. */
579 for (uint32_t i = 0; i < pIdCache->cEntries; i++)
580 if ( pIdCache->aEntries[i].id == uid
581 && pIdCache->aEntries[i].fIsUid)
582 return pIdCache->aEntries[i].szName;
583
584 /* Miss. */
585 RTFSOBJINFO ObjInfo;
586 RT_ZERO(ObjInfo); /* shut up msc */
587 int rc;
588 if (!pszRelativeTo)
589 rc = RTPathQueryInfoEx(pszEntry, &ObjInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
590 else
591 {
592 char szPath[RTPATH_MAX];
593 rc = RTPathJoin(szPath, sizeof(szPath), pszRelativeTo, pszEntry);
594 if (RT_SUCCESS(rc))
595 rc = RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
596 }
597
598 if ( RT_SUCCESS(rc)
599 && ObjInfo.Attr.u.UnixOwner.uid == uid)
600 {
601 uint32_t i = pIdCache->cEntries;
602 if (i < RT_ELEMENTS(pIdCache->aEntries))
603 pIdCache->cEntries = i + 1;
604 else
605 i = pIdCache->iNextReplace++ % RT_ELEMENTS(pIdCache->aEntries);
606 pIdCache->aEntries[i].id = uid;
607 pIdCache->aEntries[i].fIsUid = true;
608 RTStrCopy(pIdCache->aEntries[i].szName, sizeof(pIdCache->aEntries[i].szName), ObjInfo.Attr.u.UnixOwner.szName);
609 return pIdCache->aEntries[i].szName;
610 }
611 return "";
612}
613
614
615/**
616 * Resolves the GID to a name as best as we can.
617 *
618 * @returns Read-only name string. Only valid till the next cache call.
619 * @param pIdCache The ID cache.
620 * @param gid The GID to resolve.
621 * @param pszEntry The filename of the GID.
622 * @param pszRelativeTo What @a pszEntry is relative to, NULL if absolute.
623 */
624static const char *vgsvcToolboxIdCacheGetGidName(PVGSVCTOOLBOXIDCACHE pIdCache, RTGID gid,
625 const char *pszEntry, const char *pszRelativeTo)
626{
627 /* Check cached entries. */
628 for (uint32_t i = 0; i < pIdCache->cEntries; i++)
629 if ( pIdCache->aEntries[i].id == gid
630 && !pIdCache->aEntries[i].fIsUid)
631 return pIdCache->aEntries[i].szName;
632
633 /* Miss. */
634 RTFSOBJINFO ObjInfo;
635 RT_ZERO(ObjInfo); /* shut up msc */
636 int rc;
637 if (!pszRelativeTo)
638 rc = RTPathQueryInfoEx(pszEntry, &ObjInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
639 else
640 {
641 char szPath[RTPATH_MAX];
642 rc = RTPathJoin(szPath, sizeof(szPath), pszRelativeTo, pszEntry);
643 if (RT_SUCCESS(rc))
644 rc = RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
645 }
646
647 if ( RT_SUCCESS(rc)
648 && ObjInfo.Attr.u.UnixGroup.gid == gid)
649 {
650 uint32_t i = pIdCache->cEntries;
651 if (i < RT_ELEMENTS(pIdCache->aEntries))
652 pIdCache->cEntries = i + 1;
653 else
654 i = pIdCache->iNextReplace++ % RT_ELEMENTS(pIdCache->aEntries);
655 pIdCache->aEntries[i].id = gid;
656 pIdCache->aEntries[i].fIsUid = false;
657 RTStrCopy(pIdCache->aEntries[i].szName, sizeof(pIdCache->aEntries[i].szName), ObjInfo.Attr.u.UnixGroup.szName);
658 return pIdCache->aEntries[i].szName;
659 }
660 return "";
661}
662
663
664/**
665 * Prints information (based on given flags) of a file system object (file/directory/...)
666 * to stdout.
667 *
668 * @return IPRT status code.
669 * @param pszName Object name.
670 * @param cchName Length of pszName.
671 * @param fOutputFlags Output / handling flags of type
672 * VBOXSERVICETOOLBOXOUTPUTFLAG.
673 * @param pszRelativeTo What pszName is relative to.
674 * @param pIdCache The ID cache.
675 * @param pObjInfo Pointer to object information.
676 */
677static int vgsvcToolboxPrintFsInfo(const char *pszName, size_t cchName, uint32_t fOutputFlags, const char *pszRelativeTo,
678 PVGSVCTOOLBOXIDCACHE pIdCache, PRTFSOBJINFO pObjInfo)
679{
680 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
681 AssertReturn(cchName, VERR_INVALID_PARAMETER);
682 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
683
684 RTFMODE fMode = pObjInfo->Attr.fMode;
685 char chFileType;
686 switch (fMode & RTFS_TYPE_MASK)
687 {
688 case RTFS_TYPE_FIFO: chFileType = 'f'; break;
689 case RTFS_TYPE_DEV_CHAR: chFileType = 'c'; break;
690 case RTFS_TYPE_DIRECTORY: chFileType = 'd'; break;
691 case RTFS_TYPE_DEV_BLOCK: chFileType = 'b'; break;
692 case RTFS_TYPE_FILE: chFileType = '-'; break;
693 case RTFS_TYPE_SYMLINK: chFileType = 'l'; break;
694 case RTFS_TYPE_SOCKET: chFileType = 's'; break;
695 case RTFS_TYPE_WHITEOUT: chFileType = 'w'; break;
696 default: chFileType = '?'; break;
697 }
698 /** @todo sticy bits++ */
699
700/** @todo r=bird: turns out the host doesn't use or need cname_len, so perhaps we could drop it? */
701 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_LONG))
702 {
703 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
704 {
705 RTPrintf("ftype=%c%cnode_id=%RU64%inode_dev=%RU32%ccname_len=%zu%cname=%s%c",
706 chFileType, 0, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, 0,
707 (uint32_t)pObjInfo->Attr.u.Unix.INodeIdDevice, 0, cchName, 0, pszName, 0);
708 RTPrintf("%c%c", 0, 0);
709 }
710 else
711 RTPrintf("%c %#18llx %3zu %s\n", chFileType, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, cchName, pszName);
712 }
713 else
714 {
715 char szTimeBirth[RTTIME_STR_LEN];
716 char szTimeChange[RTTIME_STR_LEN];
717 char szTimeModification[RTTIME_STR_LEN];
718 char szTimeAccess[RTTIME_STR_LEN];
719
720 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
721 {
722 RTPrintf("ftype=%c%c", chFileType, 0);
723 if (pObjInfo->Attr.u.Unix.INodeId || pObjInfo->Attr.u.Unix.INodeIdDevice)
724 RTPrintf("node_id=%RU64%cinode_dev=%RU32%c", (uint64_t)pObjInfo->Attr.u.Unix.INodeId, 0,
725 (uint32_t)pObjInfo->Attr.u.Unix.INodeIdDevice, 0);
726 RTPrintf("owner_mask=%c%c%c%c",
727 fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
728 fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
729 fMode & RTFS_UNIX_IXUSR ? 'x' : '-', 0);
730 RTPrintf("group_mask=%c%c%c%c",
731 fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
732 fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
733 fMode & RTFS_UNIX_IXGRP ? 'x' : '-', 0);
734 RTPrintf("other_mask=%c%c%c%c",
735 fMode & RTFS_UNIX_IROTH ? 'r' : '-',
736 fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
737 fMode & RTFS_UNIX_IXOTH ? 'x' : '-', 0);
738 /** @todo sticky bits. */
739 RTPrintf("dos_mask=%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
740 fMode & RTFS_DOS_READONLY ? 'R' : '-',
741 fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
742 fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
743 fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
744 fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
745 fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
746 fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
747 fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
748 fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
749 fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
750 fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
751 fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
752 fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
753 fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-', 0);
754 RTPrintf("hlinks=%RU32%cst_size=%RI64%calloc=%RI64%c",
755 pObjInfo->Attr.u.Unix.cHardlinks, 0,
756 pObjInfo->cbObject, 0,
757 pObjInfo->cbAllocated, 0);
758 RTPrintf("st_birthtime=%s%cst_ctime=%s%cst_mtime=%s%cst_atime=%s%c",
759 RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth)), 0,
760 RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange)), 0,
761 RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification)), 0,
762 RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess)), 0);
763 if (pObjInfo->Attr.u.Unix.uid != NIL_RTUID)
764 RTPrintf("uid=%RU32%cusername=%s%c", pObjInfo->Attr.u.Unix.uid, 0,
765 vgsvcToolboxIdCacheGetUidName(pIdCache, pObjInfo->Attr.u.Unix.uid, pszName, pszRelativeTo), 0);
766 if (pObjInfo->Attr.u.Unix.gid != NIL_RTGID)
767 RTPrintf("gid=%RU32%cgroupname=%s%c", pObjInfo->Attr.u.Unix.gid, 0,
768 vgsvcToolboxIdCacheGetGidName(pIdCache, pObjInfo->Attr.u.Unix.gid, pszName, pszRelativeTo), 0);
769 if ( (RTFS_IS_DEV_BLOCK(pObjInfo->Attr.fMode) || RTFS_IS_DEV_CHAR(pObjInfo->Attr.fMode))
770 && pObjInfo->Attr.u.Unix.Device)
771 RTPrintf("st_rdev=%RU32%c", pObjInfo->Attr.u.Unix.Device, 0);
772 if (pObjInfo->Attr.u.Unix.GenerationId)
773 RTPrintf("st_gen=%RU32%c", pObjInfo->Attr.u.Unix.GenerationId, 0);
774 if (pObjInfo->Attr.u.Unix.fFlags)
775 RTPrintf("st_flags=%RU32%c", pObjInfo->Attr.u.Unix.fFlags, 0);
776 RTPrintf("cname_len=%zu%cname=%s%c", cchName, 0, pszName, 0);
777 RTPrintf("%c%c", 0, 0); /* End of data block. */
778 }
779 else
780 {
781 RTPrintf("%c", chFileType);
782 RTPrintf("%c%c%c",
783 fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
784 fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
785 fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
786 RTPrintf("%c%c%c",
787 fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
788 fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
789 fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
790 RTPrintf("%c%c%c",
791 fMode & RTFS_UNIX_IROTH ? 'r' : '-',
792 fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
793 fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
794 RTPrintf(" %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
795 fMode & RTFS_DOS_READONLY ? 'R' : '-',
796 fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
797 fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
798 fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
799 fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
800 fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
801 fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
802 fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
803 fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
804 fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
805 fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
806 fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
807 fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
808 fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-');
809 RTPrintf(" %d %4d %4d %10lld %10lld",
810 pObjInfo->Attr.u.Unix.cHardlinks,
811 pObjInfo->Attr.u.Unix.uid,
812 pObjInfo->Attr.u.Unix.gid,
813 pObjInfo->cbObject,
814 pObjInfo->cbAllocated);
815 RTPrintf(" %s %s %s %s",
816 RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth)),
817 RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange)),
818 RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification)),
819 RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess)) );
820 RTPrintf(" %2zu %s\n", cchName, pszName);
821 }
822 }
823
824 return VINF_SUCCESS;
825}
826
827/**
828 * Helper routine for ls tool for handling sub directories.
829 *
830 * @return IPRT status code.
831 * @param pszDir Pointer to the directory buffer.
832 * @param cchDir The length of pszDir in pszDir.
833 * @param pDirEntry Pointer to the directory entry.
834 * @param fFlags Flags of type VBOXSERVICETOOLBOXLSFLAG.
835 * @param fOutputFlags Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
836 * @param pIdCache The ID cache.
837 */
838static int vgsvcToolboxLsHandleDirSub(char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry,
839 uint32_t fFlags, uint32_t fOutputFlags, PVGSVCTOOLBOXIDCACHE pIdCache)
840{
841 Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0');
842
843 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
844 RTPrintf("dname=%s%c", pszDir, 0);
845 else if (fFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE)
846 RTPrintf("%s:\n", pszDir);
847
848 /* Make sure we've got some room in the path, to save us extra work further down. */
849 if (cchDir + 3 >= RTPATH_MAX)
850 {
851 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
852 RTMsgError("Path too long: '%s'\n", pszDir);
853 return VERR_BUFFER_OVERFLOW;
854 }
855
856 /* Open directory. */
857 RTDIR hDir;
858 int rc = RTDirOpen(&hDir, pszDir);
859 if (RT_FAILURE(rc))
860 {
861 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
862 RTMsgError("Failed to open directory '%s', rc=%Rrc\n", pszDir, rc);
863 return rc;
864 }
865
866 /* Ensure we've got a trailing slash (there is space for it see above). */
867 if (!RTPATH_IS_SEP(pszDir[cchDir - 1]))
868 {
869 pszDir[cchDir++] = RTPATH_SLASH;
870 pszDir[cchDir] = '\0';
871 }
872
873 /*
874 * Process the files and subdirs.
875 */
876 for (;;)
877 {
878 /* Get the next directory. */
879 size_t cbDirEntry = VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE;
880 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
881 if (RT_FAILURE(rc))
882 break;
883
884 /* Check length. */
885 if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX)
886 {
887 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
888 RTMsgError("Path too long: '%s' in '%.*s'\n", pDirEntry->szName, cchDir, pszDir);
889 rc = VERR_BUFFER_OVERFLOW;
890 break;
891 }
892
893 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
894 {
895 case RTFS_TYPE_SYMLINK:
896 {
897 if (!(fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS))
898 break;
899 RT_FALL_THRU();
900 }
901 case RTFS_TYPE_DIRECTORY:
902 {
903 rc = vgsvcToolboxPrintFsInfo(pDirEntry->szName, pDirEntry->cbName, fOutputFlags, pszDir,
904 pIdCache, &pDirEntry->Info);
905 if (RT_FAILURE(rc))
906 break;
907
908 if (RTDirEntryExIsStdDotLink(pDirEntry))
909 continue;
910
911 if (!(fFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE))
912 continue;
913
914 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
915 int rc2 = vgsvcToolboxLsHandleDirSub(pszDir, cchDir + pDirEntry->cbName, pDirEntry, fFlags, fOutputFlags, pIdCache);
916 if (RT_SUCCESS(rc))
917 rc = rc2;
918 break;
919 }
920
921 case RTFS_TYPE_FILE:
922 {
923 rc = vgsvcToolboxPrintFsInfo(pDirEntry->szName, pDirEntry->cbName, fOutputFlags, pszDir,
924 pIdCache, &pDirEntry->Info);
925 break;
926 }
927
928 default:
929 {
930 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
931 RTMsgError("Entry '%.*s%s' of mode %#x not supported, skipping",
932 cchDir, pszDir, pDirEntry->szName, pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK);
933 break;
934 }
935 }
936 }
937 if (rc != VERR_NO_MORE_FILES)
938 {
939 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
940 RTMsgError("RTDirReadEx failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
941 }
942
943 rc = RTDirClose(hDir);
944 if (RT_FAILURE(rc))
945 {
946 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
947 RTMsgError("RTDirClose failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
948 }
949
950 return rc;
951}
952
953/**
954 * Helper routine for ls tool doing the actual parsing and output of
955 * a specified directory.
956 *
957 * @return IPRT status code.
958 * @param pszDir Absolute path to directory to ouptut.
959 * @param fFlags Flags of type VBOXSERVICETOOLBOXLSFLAG.
960 * @param fOutputFlags Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
961 * @param pIdCache The ID cache.
962 */
963static int vgsvcToolboxLsHandleDir(const char *pszDir, uint32_t fFlags, uint32_t fOutputFlags, PVGSVCTOOLBOXIDCACHE pIdCache)
964{
965 AssertPtrReturn(pszDir, VERR_INVALID_PARAMETER);
966 AssertPtrReturn(pIdCache, VERR_INVALID_PARAMETER);
967
968 char szPath[RTPATH_MAX];
969 int rc = RTPathAbs(pszDir, szPath, sizeof(szPath));
970 if (RT_FAILURE(rc))
971 {
972 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
973 RTMsgError("RTPathAbs failed on '%s': %Rrc\n", pszDir, rc);
974 return rc;
975 }
976
977 union
978 {
979 uint8_t abPadding[VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE];
980 RTDIRENTRYEX DirEntry;
981 } uBuf;
982 return vgsvcToolboxLsHandleDirSub(szPath, strlen(szPath), &uBuf.DirEntry, fFlags, fOutputFlags, pIdCache);
983}
984
985
986/** @todo Document options! */
987static char g_paszLsHelp[] =
988 " VBoxService [--use-toolbox] vbox_ls [<general options>] [option]...\n"
989 " [<file>...]\n\n"
990 "List information about files (the current directory by default).\n\n"
991 "Options:\n\n"
992 " [--dereference|-L]\n"
993 " [-l][-R]\n"
994 " [--verbose|-v]\n"
995 " [<file>...]\n"
996 "\n";
997
998
999/**
1000 * Main function for tool "vbox_ls".
1001 *
1002 * @return RTEXITCODE.
1003 * @param argc Number of arguments.
1004 * @param argv Pointer to argument array.
1005 */
1006static RTEXITCODE vgsvcToolboxLs(int argc, char **argv)
1007{
1008 static const RTGETOPTDEF s_aOptions[] =
1009 {
1010 { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1011 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
1012 { NULL, 'l', RTGETOPT_REQ_NOTHING },
1013 { NULL, 'R', RTGETOPT_REQ_NOTHING },
1014 { "--verbose", VBOXSERVICETOOLBOXOPT_VERBOSE, RTGETOPT_REQ_NOTHING}
1015 };
1016
1017 int ch;
1018 RTGETOPTUNION ValueUnion;
1019 RTGETOPTSTATE GetState;
1020 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions),
1021 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1022 AssertRCReturn(rc, RTEXITCODE_INIT);
1023
1024 bool fVerbose = false;
1025 uint32_t fFlags = VBOXSERVICETOOLBOXLSFLAG_NONE;
1026 uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_NONE;
1027
1028 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1029 {
1030 /* For options that require an argument, ValueUnion has received the value. */
1031 switch (ch)
1032 {
1033 case 'h':
1034 vgsvcToolboxShowUsageHeader();
1035 RTPrintf("%s", g_paszLsHelp);
1036 return RTEXITCODE_SUCCESS;
1037
1038 case 'L': /* Dereference symlinks. */
1039 fFlags |= VBOXSERVICETOOLBOXLSFLAG_SYMLINKS;
1040 break;
1041
1042 case 'l': /* Print long format. */
1043 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_LONG;
1044 break;
1045
1046 case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
1047 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
1048 break;
1049
1050 case 'R': /* Recursive processing. */
1051 fFlags |= VBOXSERVICETOOLBOXLSFLAG_RECURSIVE;
1052 break;
1053
1054 case VBOXSERVICETOOLBOXOPT_VERBOSE:
1055 fVerbose = true;
1056 break;
1057
1058 case 'V':
1059 vgsvcToolboxShowVersion();
1060 return RTEXITCODE_SUCCESS;
1061
1062 case VINF_GETOPT_NOT_OPTION:
1063 Assert(GetState.iNext);
1064 GetState.iNext--;
1065 break;
1066
1067 default:
1068 return RTGetOptPrintError(ch, &ValueUnion);
1069 }
1070
1071 /* All flags / options processed? Bail out here.
1072 * Processing the file / directory list comes down below. */
1073 if (ch == VINF_GETOPT_NOT_OPTION)
1074 break;
1075 }
1076
1077 /* Print magic/version. */
1078 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
1079 {
1080 rc = vgsvcToolboxStrmInit();
1081 if (RT_FAILURE(rc))
1082 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
1083 vgsvcToolboxPrintStrmHeader("vbt_ls", 1 /* Stream version */);
1084 }
1085
1086 VGSVCTOOLBOXIDCACHE IdCache;
1087 RT_ZERO(IdCache);
1088
1089 char szDirCur[RTPATH_MAX];
1090 rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
1091 if (RT_FAILURE(rc))
1092 {
1093 RTMsgError("Getting current directory failed, rc=%Rrc\n", rc);
1094 return RTEXITCODE_FAILURE;
1095 }
1096
1097 ch = RTGetOpt(&GetState, &ValueUnion);
1098 do
1099 {
1100 char const *pszPath;
1101
1102 if (ch == 0) /* Use current directory if no element specified. */
1103 pszPath = szDirCur;
1104 else
1105 pszPath = ValueUnion.psz;
1106
1107 RTFSOBJINFO objInfo;
1108 int rc2 = RTPathQueryInfoEx(pszPath, &objInfo,
1109 RTFSOBJATTRADD_UNIX,
1110 fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK);
1111 if (RT_SUCCESS(rc2))
1112 {
1113 if ( RTFS_IS_FILE(objInfo.Attr.fMode)
1114 || ( RTFS_IS_SYMLINK(objInfo.Attr.fMode)
1115 && (fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS)))
1116 {
1117 rc2 = vgsvcToolboxPrintFsInfo(pszPath, strlen(pszPath), fOutputFlags, NULL, &IdCache, &objInfo);
1118 if (RT_SUCCESS(rc)) /* Keep initial failing rc. */
1119 rc = rc2;
1120 }
1121 else if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1122 {
1123 rc2 = vgsvcToolboxLsHandleDir(pszPath, fFlags, fOutputFlags, &IdCache);
1124 if (RT_SUCCESS(rc)) /* Keep initial failing rc. */
1125 rc = rc2;
1126 }
1127 }
1128 else
1129 {
1130 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
1131 RTMsgError("Cannot access '%s': No such file or directory\n", pszPath);
1132 if (RT_SUCCESS(rc))
1133 rc = VERR_FILE_NOT_FOUND;
1134 /* Do not break here -- process every element in the list
1135 * and keep failing rc. */
1136 }
1137
1138 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0);
1139
1140 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1141 vgsvcToolboxPrintStrmTermination();
1142
1143 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1144}
1145
1146
1147/* Try using RTPathRmCmd. */
1148static RTEXITCODE vgsvcToolboxRm(int argc, char **argv)
1149{
1150 return RTPathRmCmd(argc, argv);
1151}
1152
1153
1154static char g_paszMkTempHelp[] =
1155 " VBoxService [--use-toolbox] vbox_mktemp [<general options>] [<options>]\n"
1156 " <template>\n\n"
1157 "Create a temporary directory based on the template supplied. The first string\n"
1158 "of consecutive 'X' characters in the template will be replaced to form a unique\n"
1159 "name for the directory. The template may not contain a path. The default\n"
1160 "creation mode is 0600 for files and 0700 for directories. If no path is\n"
1161 "specified the default temporary directory will be used.\n"
1162 "Options:\n\n"
1163 " [--directory|-d] Create a directory instead of a file.\n"
1164 " [--mode|-m <mode>] Create the object with mode <mode>.\n"
1165 " [--secure|-s] Fail if the object cannot be created securely.\n"
1166 " [--tmpdir|-t <path>] Create the object with the absolute path <path>.\n"
1167 "\n";
1168
1169
1170/**
1171 * Report the result of a vbox_mktemp operation.
1172 *
1173 * Either errors to stderr (not machine-readable) or everything to stdout as
1174 * {name}\0{rc}\0 (machine- readable format). The message may optionally
1175 * contain a '%s' for the file name and an %Rrc for the result code in that
1176 * order. In future a "verbose" flag may be added, without which nothing will
1177 * be output in non-machine- readable mode. Sets prc if rc is a non-success
1178 * code.
1179 */
1180static void toolboxMkTempReport(const char *pcszMessage, const char *pcszFile,
1181 bool fActive, int rc, uint32_t fOutputFlags, int *prc)
1182{
1183 if (!fActive)
1184 return;
1185 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
1186 if (RT_SUCCESS(rc))
1187 RTPrintf(pcszMessage, pcszFile, rc);
1188 else
1189 RTMsgError(pcszMessage, pcszFile, rc);
1190 else
1191 RTPrintf("name=%s%crc=%d%c", pcszFile, 0, rc, 0);
1192 if (prc && RT_FAILURE(rc))
1193 *prc = rc;
1194}
1195
1196
1197/**
1198 * Main function for tool "vbox_mktemp".
1199 *
1200 * @return RTEXITCODE.
1201 * @param argc Number of arguments.
1202 * @param argv Pointer to argument array.
1203 */
1204static RTEXITCODE vgsvcToolboxMkTemp(int argc, char **argv)
1205{
1206 static const RTGETOPTDEF s_aOptions[] =
1207 {
1208 { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE,
1209 RTGETOPT_REQ_NOTHING },
1210 { "--directory", 'd', RTGETOPT_REQ_NOTHING },
1211 { "--mode", 'm', RTGETOPT_REQ_STRING },
1212 { "--secure", 's', RTGETOPT_REQ_NOTHING },
1213 { "--tmpdir", 't', RTGETOPT_REQ_STRING },
1214 };
1215
1216 enum
1217 {
1218 /* Isn't that a bit long? s/VBOXSERVICETOOLBOX/VSTB/ ? */
1219 /** Create a temporary directory instead of a temporary file. */
1220 VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY = RT_BIT_32(0),
1221 /** Only create the temporary object if the operation is expected
1222 * to be secure. Not guaranteed to be supported on a particular
1223 * set-up. */
1224 VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE = RT_BIT_32(1)
1225 };
1226
1227 int ch, rc;
1228 RTGETOPTUNION ValueUnion;
1229 RTGETOPTSTATE GetState;
1230 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1231 AssertRCReturn(rc, RTEXITCODE_INIT);
1232
1233 uint32_t fFlags = 0;
1234 uint32_t fOutputFlags = 0;
1235 int cNonOptions = 0;
1236 RTFMODE fMode = 0700;
1237 bool fModeSet = false;
1238 const char *pcszPath = NULL;
1239 const char *pcszTemplate;
1240 char szTemplateWithPath[RTPATH_MAX] = "";
1241
1242 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1243 && RT_SUCCESS(rc))
1244 {
1245 /* For options that require an argument, ValueUnion has received the value. */
1246 switch (ch)
1247 {
1248 case 'h':
1249 vgsvcToolboxShowUsageHeader();
1250 RTPrintf("%s", g_paszMkTempHelp);
1251 return RTEXITCODE_SUCCESS;
1252
1253 case 'V':
1254 vgsvcToolboxShowVersion();
1255 return RTEXITCODE_SUCCESS;
1256
1257 case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
1258 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
1259 break;
1260
1261 case 'd':
1262 fFlags |= VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY;
1263 break;
1264
1265 case 'm':
1266 rc = vgsvcToolboxParseMode(ValueUnion.psz, &fMode);
1267 if (RT_FAILURE(rc))
1268 return RTEXITCODE_SYNTAX;
1269 fModeSet = true;
1270#ifndef RT_OS_WINDOWS
1271 umask(0); /* RTDirCreate workaround */
1272#endif
1273 break;
1274 case 's':
1275 fFlags |= VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE;
1276 break;
1277
1278 case 't':
1279 pcszPath = ValueUnion.psz;
1280 break;
1281
1282 case VINF_GETOPT_NOT_OPTION:
1283 /* RTGetOpt will sort these to the end of the argv vector so
1284 * that we will deal with them afterwards. */
1285 ++cNonOptions;
1286 break;
1287
1288 default:
1289 return RTGetOptPrintError(ch, &ValueUnion);
1290 }
1291 }
1292
1293 /* Print magic/version. */
1294 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
1295 {
1296 rc = vgsvcToolboxStrmInit();
1297 if (RT_FAILURE(rc))
1298 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
1299 vgsvcToolboxPrintStrmHeader("vbt_mktemp", 1 /* Stream version */);
1300 }
1301
1302 if (fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE && fModeSet)
1303 {
1304 toolboxMkTempReport("'-s' and '-m' parameters cannot be used together.\n", "",
1305 true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1306 return RTEXITCODE_SYNTAX;
1307 }
1308
1309 /* We need exactly one template, containing at least one 'X'. */
1310 if (cNonOptions != 1)
1311 {
1312 toolboxMkTempReport("Please specify exactly one template.\n", "", true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1313 return RTEXITCODE_SYNTAX;
1314 }
1315 pcszTemplate = argv[argc - 1];
1316
1317 /* Validate that the template is as IPRT requires (asserted by IPRT). */
1318 if ( RTPathHasPath(pcszTemplate)
1319 || ( !strstr(pcszTemplate, "XXX")
1320 && pcszTemplate[strlen(pcszTemplate) - 1] != 'X'))
1321 {
1322 toolboxMkTempReport("Template '%s' should contain a file name with no path and at least three consecutive 'X' characters or ending in 'X'.\n",
1323 pcszTemplate, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1324 return RTEXITCODE_FAILURE;
1325 }
1326 if (pcszPath && !RTPathStartsWithRoot(pcszPath))
1327 {
1328 toolboxMkTempReport("Path '%s' should be absolute.\n", pcszPath, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1329 return RTEXITCODE_FAILURE;
1330 }
1331 if (pcszPath)
1332 {
1333 rc = RTStrCopy(szTemplateWithPath, sizeof(szTemplateWithPath), pcszPath);
1334 if (RT_FAILURE(rc))
1335 {
1336 toolboxMkTempReport("Path '%s' too long.\n", pcszPath, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1337 return RTEXITCODE_FAILURE;
1338 }
1339 }
1340 else
1341 {
1342 rc = RTPathTemp(szTemplateWithPath, sizeof(szTemplateWithPath));
1343 if (RT_FAILURE(rc))
1344 {
1345 toolboxMkTempReport("Failed to get the temporary directory.\n", "", true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1346 return RTEXITCODE_FAILURE;
1347 }
1348 }
1349 rc = RTPathAppend(szTemplateWithPath, sizeof(szTemplateWithPath), pcszTemplate);
1350 if (RT_FAILURE(rc))
1351 {
1352 toolboxMkTempReport("Template '%s' too long for path.\n", pcszTemplate, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1353 return RTEXITCODE_FAILURE;
1354 }
1355
1356 if (fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY)
1357 {
1358 rc = fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE
1359 ? RTDirCreateTempSecure(szTemplateWithPath)
1360 : RTDirCreateTemp(szTemplateWithPath, fMode);
1361 toolboxMkTempReport("Created temporary directory '%s'.\n",
1362 szTemplateWithPath, RT_SUCCESS(rc), rc,
1363 fOutputFlags, NULL);
1364 /* RTDirCreateTemp[Secure] sets the template to "" on failure. */
1365 toolboxMkTempReport("The following error occurred while creating a temporary directory from template '%s': %Rrc.\n",
1366 pcszTemplate, RT_FAILURE(rc), rc, fOutputFlags, NULL /*prc*/);
1367 }
1368 else
1369 {
1370 rc = fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE
1371 ? RTFileCreateTempSecure(szTemplateWithPath)
1372 : RTFileCreateTemp(szTemplateWithPath, fMode);
1373 toolboxMkTempReport("Created temporary file '%s'.\n",
1374 szTemplateWithPath, RT_SUCCESS(rc), rc,
1375 fOutputFlags, NULL);
1376 /* RTFileCreateTemp[Secure] sets the template to "" on failure. */
1377 toolboxMkTempReport("The following error occurred while creating a temporary file from template '%s': %Rrc.\n",
1378 pcszTemplate, RT_FAILURE(rc), rc, fOutputFlags, NULL /*prc*/);
1379 }
1380 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1381 vgsvcToolboxPrintStrmTermination();
1382 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1383}
1384
1385
1386/** @todo Document options! */
1387static char g_paszMkDirHelp[] =
1388 " VBoxService [--use-toolbox] vbox_mkdir [<general options>] [<options>]\n"
1389 " <directory>...\n\n"
1390 "Options:\n\n"
1391 " [--mode|-m <mode>] The file mode to set (chmod) on the created\n"
1392 " directories. Default: a=rwx & umask.\n"
1393 " [--parents|-p] Create parent directories as needed, no\n"
1394 " error if the directory already exists.\n"
1395 " [--verbose|-v] Display a message for each created directory.\n"
1396 "\n";
1397
1398
1399/**
1400 * Main function for tool "vbox_mkdir".
1401 *
1402 * @return RTEXITCODE.
1403 * @param argc Number of arguments.
1404 * @param argv Pointer to argument array.
1405 */
1406static RTEXITCODE vgsvcToolboxMkDir(int argc, char **argv)
1407{
1408 static const RTGETOPTDEF s_aOptions[] =
1409 {
1410 { "--mode", 'm', RTGETOPT_REQ_STRING },
1411 { "--parents", 'p', RTGETOPT_REQ_NOTHING},
1412 { "--verbose", 'v', RTGETOPT_REQ_NOTHING}
1413 };
1414
1415 int ch;
1416 RTGETOPTUNION ValueUnion;
1417 RTGETOPTSTATE GetState;
1418 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions),
1419 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1420 AssertRCReturn(rc, RTEXITCODE_INIT);
1421
1422 bool fMakeParentDirs = false;
1423 bool fVerbose = false;
1424 RTFMODE fDirMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO;
1425 int cDirsCreated = 0;
1426
1427 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1428 {
1429 /* For options that require an argument, ValueUnion has received the value. */
1430 switch (ch)
1431 {
1432 case 'p':
1433 fMakeParentDirs = true;
1434 break;
1435
1436 case 'm':
1437 rc = vgsvcToolboxParseMode(ValueUnion.psz, &fDirMode);
1438 if (RT_FAILURE(rc))
1439 return RTEXITCODE_SYNTAX;
1440#ifndef RT_OS_WINDOWS
1441 umask(0); /* RTDirCreate workaround */
1442#endif
1443 break;
1444
1445 case 'v':
1446 fVerbose = true;
1447 break;
1448
1449 case 'h':
1450 vgsvcToolboxShowUsageHeader();
1451 RTPrintf("%s", g_paszMkDirHelp);
1452 return RTEXITCODE_SUCCESS;
1453
1454 case 'V':
1455 vgsvcToolboxShowVersion();
1456 return RTEXITCODE_SUCCESS;
1457
1458 case VINF_GETOPT_NOT_OPTION:
1459 if (fMakeParentDirs)
1460 /** @todo r=bird: If fVerbose is set, we should also show
1461 * which directories that get created, parents as well as
1462 * omitting existing final dirs. Annoying, but check any
1463 * mkdir implementation (try "mkdir -pv asdf/1/2/3/4"
1464 * twice). */
1465 rc = RTDirCreateFullPath(ValueUnion.psz, fDirMode);
1466 else
1467 rc = RTDirCreate(ValueUnion.psz, fDirMode, 0);
1468 if (RT_FAILURE(rc))
1469 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Could not create directory '%s': %Rra\n",
1470 ValueUnion.psz, rc);
1471 if (fVerbose)
1472 RTMsgInfo("Created directory '%s', mode %#RTfmode\n", ValueUnion.psz, fDirMode);
1473 cDirsCreated++;
1474 break;
1475
1476 default:
1477 return RTGetOptPrintError(ch, &ValueUnion);
1478 }
1479 }
1480 AssertRC(rc);
1481
1482 if (cDirsCreated == 0)
1483 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No directory argument.");
1484
1485 return RTEXITCODE_SUCCESS;
1486}
1487
1488
1489/** @todo Document options! */
1490static char g_paszStatHelp[] =
1491 " VBoxService [--use-toolbox] vbox_stat [<general options>] [<options>]\n"
1492 " <file>...\n\n"
1493 "Display file or file system status.\n\n"
1494 "Options:\n\n"
1495 " [--file-system|-f]\n"
1496 " [--dereference|-L]\n"
1497 " [--terse|-t]\n"
1498 " [--verbose|-v]\n"
1499 "\n";
1500
1501
1502/**
1503 * Main function for tool "vbox_stat".
1504 *
1505 * @return RTEXITCODE.
1506 * @param argc Number of arguments.
1507 * @param argv Pointer to argument array.
1508 */
1509static RTEXITCODE vgsvcToolboxStat(int argc, char **argv)
1510{
1511 static const RTGETOPTDEF s_aOptions[] =
1512 {
1513 { "--file-system", 'f', RTGETOPT_REQ_NOTHING },
1514 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
1515 { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1516 { "--terse", 't', RTGETOPT_REQ_NOTHING },
1517 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
1518 };
1519
1520 int ch;
1521 RTGETOPTUNION ValueUnion;
1522 RTGETOPTSTATE GetState;
1523 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1524
1525 int rc = VINF_SUCCESS;
1526 uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_LONG; /* Use long mode by default. */
1527 uint32_t fQueryInfoFlags = RTPATH_F_ON_LINK;
1528
1529 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1530 && RT_SUCCESS(rc))
1531 {
1532 /* For options that require an argument, ValueUnion has received the value. */
1533 switch (ch)
1534 {
1535 case 'f':
1536 RTMsgError("Sorry, option '%s' is not implemented yet!\n", ValueUnion.pDef->pszLong);
1537 rc = VERR_INVALID_PARAMETER;
1538 break;
1539
1540 case 'L':
1541 fQueryInfoFlags &= ~RTPATH_F_ON_LINK;
1542 fQueryInfoFlags |= RTPATH_F_FOLLOW_LINK;
1543 break;
1544
1545 case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
1546 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
1547 break;
1548
1549 case 'h':
1550 vgsvcToolboxShowUsageHeader();
1551 RTPrintf("%s", g_paszStatHelp);
1552 return RTEXITCODE_SUCCESS;
1553
1554 case 'V':
1555 vgsvcToolboxShowVersion();
1556 return RTEXITCODE_SUCCESS;
1557
1558 case VINF_GETOPT_NOT_OPTION:
1559 {
1560 Assert(GetState.iNext);
1561 GetState.iNext--;
1562 break;
1563 }
1564
1565 default:
1566 return RTGetOptPrintError(ch, &ValueUnion);
1567 }
1568
1569 /* All flags / options processed? Bail out here.
1570 * Processing the file / directory list comes down below. */
1571 if (ch == VINF_GETOPT_NOT_OPTION)
1572 break;
1573 }
1574
1575 if (RT_SUCCESS(rc))
1576 {
1577 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1578 {
1579 rc = vgsvcToolboxStrmInit();
1580 if (RT_FAILURE(rc))
1581 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
1582 vgsvcToolboxPrintStrmHeader("vbt_stat", 1 /* Stream version */);
1583 }
1584
1585 VGSVCTOOLBOXIDCACHE IdCache;
1586 RT_ZERO(IdCache);
1587
1588 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1589 {
1590 RTFSOBJINFO objInfo;
1591 int rc2 = RTPathQueryInfoEx(ValueUnion.psz, &objInfo, RTFSOBJATTRADD_UNIX, fQueryInfoFlags);
1592 if (RT_FAILURE(rc2))
1593 {
1594 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
1595 RTMsgError("Cannot stat for '%s': %Rrc\n", ValueUnion.psz, rc2);
1596 }
1597 else
1598 rc2 = vgsvcToolboxPrintFsInfo(ValueUnion.psz, strlen(ValueUnion.psz), fOutputFlags, NULL, &IdCache, &objInfo);
1599
1600 if (RT_SUCCESS(rc))
1601 rc = rc2;
1602 /* Do not break here -- process every element in the list
1603 * and keep (initial) failing rc. */
1604 }
1605
1606 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1607 vgsvcToolboxPrintStrmTermination();
1608
1609 /* At this point the overall result (success/failure) should be in rc. */
1610 }
1611 else
1612 RTMsgError("Failed with rc=%Rrc\n", rc);
1613
1614 if (RT_FAILURE(rc))
1615 {
1616 switch (rc)
1617 {
1618 case VERR_ACCESS_DENIED:
1619 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_ACCESS_DENIED;
1620
1621 case VERR_FILE_NOT_FOUND:
1622 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_FILE_NOT_FOUND;
1623
1624 case VERR_PATH_NOT_FOUND:
1625 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_PATH_NOT_FOUND;
1626
1627 case VERR_NET_PATH_NOT_FOUND:
1628 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_NET_PATH_NOT_FOUND;
1629
1630 case VERR_INVALID_NAME:
1631 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_INVALID_NAME;
1632
1633 default:
1634#ifdef DEBUG_andy
1635 AssertMsgFailed(("Exit code for %Rrc not implemented\n", rc));
1636#endif
1637 break;
1638 }
1639
1640 return RTEXITCODE_FAILURE;
1641 }
1642
1643 return RTEXITCODE_SUCCESS;
1644}
1645
1646
1647/**
1648 * Looks up the tool definition entry for the tool give by @a pszTool.
1649 *
1650 * @returns Pointer to the tool definition. NULL if not found.
1651 * @param pszTool The name of the tool.
1652 */
1653static PCVBOXSERVICETOOLBOXTOOL vgsvcToolboxLookUp(const char *pszTool)
1654{
1655 AssertPtrReturn(pszTool, NULL);
1656
1657 /* Do a linear search, since we don't have that much stuff in the table. */
1658 for (unsigned i = 0; i < RT_ELEMENTS(g_aTools); i++)
1659 if (!strcmp(g_aTools[i].pszName, pszTool))
1660 return &g_aTools[i];
1661
1662 return NULL;
1663}
1664
1665
1666/**
1667 * Converts a tool's exit code back to an IPRT error code.
1668 *
1669 * @return Converted IPRT status code.
1670 * @param pszTool Name of the toolbox tool to convert exit code for.
1671 * @param rcExit The tool's exit code to convert.
1672 */
1673int VGSvcToolboxExitCodeConvertToRc(const char *pszTool, RTEXITCODE rcExit)
1674{
1675 AssertPtrReturn(pszTool, VERR_INVALID_POINTER);
1676
1677 PCVBOXSERVICETOOLBOXTOOL pTool = vgsvcToolboxLookUp(pszTool);
1678 if (pTool)
1679 return pTool->pfnExitCodeConvertToRc(rcExit);
1680
1681 AssertMsgFailed(("Tool '%s' not found\n", pszTool));
1682 return VERR_GENERAL_FAILURE; /* Lookup failed, should not happen. */
1683}
1684
1685
1686/**
1687 * Entry point for internal toolbox.
1688 *
1689 * @return True if an internal tool was handled, false if not.
1690 * @param argc Number of arguments.
1691 * @param argv Pointer to argument array.
1692 * @param prcExit Where to store the exit code when an
1693 * internal toolbox command was handled.
1694 */
1695bool VGSvcToolboxMain(int argc, char **argv, RTEXITCODE *prcExit)
1696{
1697
1698 /*
1699 * Check if the file named in argv[0] is one of the toolbox programs.
1700 */
1701 AssertReturn(argc > 0, false);
1702 const char *pszTool = RTPathFilename(argv[0]);
1703 PCVBOXSERVICETOOLBOXTOOL pTool = vgsvcToolboxLookUp(pszTool);
1704 if (!pTool)
1705 {
1706 /*
1707 * For debugging and testing purposes we also allow toolbox program access
1708 * when the first VBoxService argument is --use-toolbox.
1709 */
1710 if (argc < 2 || strcmp(argv[1], "--use-toolbox"))
1711 {
1712 /* We must match vgsvcGstCtrlProcessCreateProcess here and claim
1713 everything starting with "vbox_". */
1714 if (!RTStrStartsWith(pszTool, "vbox_"))
1715 return false;
1716 RTMsgError("Unknown tool: %s\n", pszTool);
1717 *prcExit = RTEXITCODE_SYNTAX;
1718 return true;
1719 }
1720
1721 /* No tool specified? Show toolbox help. */
1722 if (argc < 3)
1723 {
1724 RTMsgError("No tool following --use-toolbox\n");
1725 *prcExit = RTEXITCODE_SYNTAX;
1726 return true;
1727 }
1728
1729 argc -= 2;
1730 argv += 2;
1731 pszTool = argv[0];
1732 pTool = vgsvcToolboxLookUp(pszTool);
1733 if (!pTool)
1734 {
1735 *prcExit = RTEXITCODE_SUCCESS;
1736 if ( !strcmp(pszTool, "-V")
1737 || !strcmp(pszTool, "version"))
1738 vgsvcToolboxShowVersion();
1739 else if ( !strcmp(pszTool, "help")
1740 || !strcmp(pszTool, "--help")
1741 || !strcmp(pszTool, "-h"))
1742 vgsvcToolboxShowUsage();
1743 else
1744 {
1745 RTMsgError("Unknown tool: %s\n", pszTool);
1746 *prcExit = RTEXITCODE_SYNTAX;
1747 }
1748 return true;
1749 }
1750 }
1751
1752 /*
1753 * Invoke the handler.
1754 */
1755 RTMsgSetProgName("VBoxService/%s", pszTool);
1756 AssertPtr(pTool);
1757 *prcExit = pTool->pfnHandler(argc, argv);
1758
1759 return true;
1760}
1761
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette