VirtualBox

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

Last change on this file since 42498 was 42498, checked in by vboxsync, 13 years ago

Additions/common/VBoxService: small duplication elimination.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.1 KB
Line 
1/* $Id: VBoxServiceToolBox.cpp 42498 2012-08-01 09:19:33Z vboxsync $ */
2/** @file
3 * VBoxServiceToolbox - Internal (BusyBox-like) toolbox.
4 */
5
6/*
7 * Copyright (C) 2012 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 <stdio.h>
23
24#include <iprt/assert.h>
25#include <iprt/buildconfig.h>
26#include <iprt/dir.h>
27#include <iprt/file.h>
28#include <iprt/getopt.h>
29#include <iprt/list.h>
30#include <iprt/mem.h>
31#include <iprt/message.h>
32#include <iprt/path.h>
33#include <iprt/string.h>
34#include <iprt/stream.h>
35
36#ifndef RT_OS_WINDOWS
37# include <sys/stat.h> /* need umask */
38#endif
39
40#include <VBox/VBoxGuestLib.h>
41#include <VBox/version.h>
42#include "VBoxServiceInternal.h"
43#include "VBoxServiceUtils.h"
44
45
46/*******************************************************************************
47* Defined Constants And Macros *
48*******************************************************************************/
49
50/** Generic option indices for commands. */
51enum
52{
53 VBOXSERVICETOOLBOXOPT_MACHINE_READABLE = 1000,
54 VBOXSERVICETOOLBOXOPT_VERBOSE
55};
56
57/** Options indices for "vbox_cat". */
58typedef enum VBOXSERVICETOOLBOXCATOPT
59{
60 VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED = 1000
61} VBOXSERVICETOOLBOXCATOPT;
62
63/** Flags for "vbox_ls". */
64typedef enum VBOXSERVICETOOLBOXLSFLAG
65{
66 VBOXSERVICETOOLBOXLSFLAG_NONE = 0x0,
67 VBOXSERVICETOOLBOXLSFLAG_RECURSIVE = 0x1,
68 VBOXSERVICETOOLBOXLSFLAG_SYMLINKS = 0x2
69} VBOXSERVICETOOLBOXLSFLAG;
70
71/** Flags for fs object output. */
72typedef enum VBOXSERVICETOOLBOXOUTPUTFLAG
73{
74 VBOXSERVICETOOLBOXOUTPUTFLAG_NONE = 0x0,
75 VBOXSERVICETOOLBOXOUTPUTFLAG_LONG = 0x1,
76 VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE = 0x2
77} VBOXSERVICETOOLBOXOUTPUTFLAG;
78
79
80/*******************************************************************************
81* Structures and Typedefs *
82*******************************************************************************/
83/** Pointer to a handler function. */
84typedef RTEXITCODE (*PFNHANDLER)(int , char **);
85
86/**
87 * An file/directory entry. Used to cache
88 * file names/paths for later processing.
89 */
90typedef struct VBOXSERVICETOOLBOXPATHENTRY
91{
92 /** Our node. */
93 RTLISTNODE Node;
94 /** Name of the entry. */
95 char *pszName;
96} VBOXSERVICETOOLBOXPATHENTRY, *PVBOXSERVICETOOLBOXPATHENTRY;
97
98typedef struct VBOXSERVICETOOLBOXDIRENTRY
99{
100 /** Our node. */
101 RTLISTNODE Node;
102 /** The actual entry. */
103 RTDIRENTRYEX dirEntry;
104} VBOXSERVICETOOLBOXDIRENTRY, *PVBOXSERVICETOOLBOXDIRENTRY;
105
106
107/**
108 * Displays a help text to stdout.
109 */
110static void VBoxServiceToolboxShowUsage(void)
111{
112 RTPrintf("Toolbox Usage:\n"
113 "cat [FILE] - Concatenate FILE(s), or standard input, to standard output.\n"
114 "\n"
115 /** @todo Document options! */
116 "ls [OPTION]... FILE... - List information about the FILEs (the current directory by default).\n"
117 "\n"
118 /** @todo Document options! */
119 "mkdir [OPTION]... DIRECTORY... - Create the DIRECTORY(ies), if they do not already exist.\n"
120 "\n"
121 /** @todo Document options! */
122 "stat [OPTION]... FILE... - Display file or file system status.\n"
123 "\n"
124 /** @todo Document options! */
125 "\n");
126}
127
128
129/**
130 * Displays the program's version number.
131 */
132static void VBoxServiceToolboxShowVersion(void)
133{
134 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
135}
136
137
138/**
139 * Initializes the parseable stream(s).
140 *
141 * @return IPRT status code.
142 */
143static int VBoxServiceToolboxStrmInit(void)
144{
145 /* Set stdout's mode to binary. This is required for outputting all the machine-readable
146 * data correctly. */
147 int rc = RTStrmSetMode(g_pStdOut, 1 /* Binary mode */, -1 /* Current code set, not changed */);
148 if (RT_FAILURE(rc))
149 RTMsgError("Unable to set stdout to binary mode, rc=%Rrc\n", rc);
150
151 return rc;
152}
153
154
155/**
156 * Prints a parseable stream header which contains the actual tool
157 * which was called/used along with its stream version.
158 *
159 * @param pszToolName Name of the tool being used, e.g. "vbt_ls".
160 * @param uVersion Stream version name. Handy for distinguishing
161 * different stream versions later.
162 */
163static void VBoxServiceToolboxPrintStrmHeader(const char *pszToolName, uint32_t uVersion)
164{
165 AssertPtrReturnVoid(pszToolName);
166 RTPrintf("hdr_id=%s%chdr_ver=%u%c", pszToolName, 0, uVersion, 0);
167}
168
169
170/**
171 * Prints a standardized termination sequence indicating that the
172 * parseable stream just ended.
173 *
174 */
175static void VBoxServiceToolboxPrintStrmTermination()
176{
177 RTPrintf("%c%c%c%c", 0, 0, 0, 0);
178}
179
180
181/**
182 * Destroys a path buffer list.
183 *
184 * @return IPRT status code.
185 * @param pList Pointer to list to destroy.
186 */
187static void VBoxServiceToolboxPathBufDestroy(PRTLISTNODE pList)
188{
189 AssertPtr(pList);
190 /** @todo use RTListForEachSafe */
191 PVBOXSERVICETOOLBOXPATHENTRY pNode = RTListGetFirst(pList, VBOXSERVICETOOLBOXPATHENTRY, Node);
192 while (pNode)
193 {
194 PVBOXSERVICETOOLBOXPATHENTRY pNext = RTListNodeIsLast(pList, &pNode->Node)
195 ? NULL
196 : RTListNodeGetNext(&pNode->Node, VBOXSERVICETOOLBOXPATHENTRY, Node);
197 RTListNodeRemove(&pNode->Node);
198
199 RTStrFree(pNode->pszName);
200
201 RTMemFree(pNode);
202 pNode = pNext;
203 }
204}
205
206
207/**
208 * Adds a path entry (file/directory/whatever) to a given path buffer list.
209 *
210 * @return IPRT status code.
211 * @param pList Pointer to list to add entry to.
212 * @param pszName Name of entry to add.
213 */
214static int VBoxServiceToolboxPathBufAddPathEntry(PRTLISTNODE pList, const char *pszName)
215{
216 AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
217
218 int rc = VINF_SUCCESS;
219 PVBOXSERVICETOOLBOXPATHENTRY pNode = (PVBOXSERVICETOOLBOXPATHENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXPATHENTRY));
220 if (pNode)
221 {
222 pNode->pszName = RTStrDup(pszName);
223 AssertPtr(pNode->pszName);
224
225 /*rc =*/ RTListAppend(pList, &pNode->Node);
226 }
227 else
228 rc = VERR_NO_MEMORY;
229 return rc;
230}
231
232
233/**
234 * Performs the actual output operation of "vbox_cat".
235 *
236 * @return IPRT status code.
237 * @param hInput Handle of input file (if any) to use;
238 * else stdin will be used.
239 * @param hOutput Handle of output file (if any) to use;
240 * else stdout will be used.
241 */
242static int VBoxServiceToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
243{
244 int rc = VINF_SUCCESS;
245 if (hInput == NIL_RTFILE)
246 {
247 rc = RTFileFromNative(&hInput, RTFILE_NATIVE_STDIN);
248 if (RT_FAILURE(rc))
249 RTMsgError("Could not translate input file to native handle, rc=%Rrc\n", rc);
250 }
251
252 if (hOutput == NIL_RTFILE)
253 {
254 rc = RTFileFromNative(&hOutput, RTFILE_NATIVE_STDOUT);
255 if (RT_FAILURE(rc))
256 RTMsgError("Could not translate output file to native handle, rc=%Rrc\n", rc);
257 }
258
259 if (RT_SUCCESS(rc))
260 {
261 uint8_t abBuf[_64K];
262 size_t cbRead;
263 for (;;)
264 {
265 rc = RTFileRead(hInput, abBuf, sizeof(abBuf), &cbRead);
266 if (RT_SUCCESS(rc) && cbRead > 0)
267 {
268 rc = RTFileWrite(hOutput, abBuf, cbRead, NULL /* Try to write all at once! */);
269 if (RT_FAILURE(rc))
270 {
271 RTMsgError("Error while writing output, rc=%Rrc\n", rc);
272 break;
273 }
274 }
275 else
276 {
277 if (rc == VERR_BROKEN_PIPE)
278 rc = VINF_SUCCESS;
279 else if (RT_FAILURE(rc))
280 RTMsgError("Error while reading input, rc=%Rrc\n", rc);
281 break;
282 }
283 }
284 }
285 return rc;
286}
287
288
289/**
290 * Main function for tool "vbox_cat".
291 *
292 * @return RTEXITCODE.
293 * @param argc Number of arguments.
294 * @param argv Pointer to argument array.
295 */
296static RTEXITCODE VBoxServiceToolboxCat(int argc, char **argv)
297{
298 static const RTGETOPTDEF s_aOptions[] =
299 {
300 /* Sorted by short ops. */
301 { "--show-all", 'a', RTGETOPT_REQ_NOTHING },
302 { "--number-nonblank", 'b', RTGETOPT_REQ_NOTHING},
303 { NULL, 'e', RTGETOPT_REQ_NOTHING},
304 { NULL, 'E', RTGETOPT_REQ_NOTHING},
305 { "--flags", 'f', RTGETOPT_REQ_STRING},
306 { "--no-content-indexed", VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED, RTGETOPT_REQ_NOTHING},
307 { "--number", 'n', RTGETOPT_REQ_NOTHING},
308 { "--output", 'o', RTGETOPT_REQ_STRING},
309 { "--squeeze-blank", 's', RTGETOPT_REQ_NOTHING},
310 { NULL, 't', RTGETOPT_REQ_NOTHING},
311 { "--show-tabs", 'T', RTGETOPT_REQ_NOTHING},
312 { NULL, 'u', RTGETOPT_REQ_NOTHING},
313 { "--show-noneprinting", 'v', RTGETOPT_REQ_NOTHING}
314 };
315
316 int ch;
317 RTGETOPTUNION ValueUnion;
318 RTGETOPTSTATE GetState;
319
320 RTGetOptInit(&GetState, argc, argv,
321 s_aOptions, RT_ELEMENTS(s_aOptions),
322 1 /*iFirst*/, 0 /*fFlags*/);
323
324 int rc = VINF_SUCCESS;
325 bool fUsageOK = true;
326
327 char szOutput[RTPATH_MAX] = { 0 };
328 RTFILE hOutput = NIL_RTFILE;
329 uint32_t fFlags = RTFILE_O_CREATE_REPLACE /* Output file flags. */
330 | RTFILE_O_WRITE
331 | RTFILE_O_DENY_WRITE;
332
333 /* Init directory list. */
334 RTLISTANCHOR inputList;
335 RTListInit(&inputList);
336
337 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
338 && RT_SUCCESS(rc))
339 {
340 /* For options that require an argument, ValueUnion has received the value. */
341 switch (ch)
342 {
343 case 'a':
344 case 'b':
345 case 'e':
346 case 'E':
347 case 'n':
348 case 's':
349 case 't':
350 case 'T':
351 case 'v':
352 RTMsgError("Sorry, option '%s' is not implemented yet!\n",
353 ValueUnion.pDef->pszLong);
354 rc = VERR_INVALID_PARAMETER;
355 break;
356
357 case 'h':
358 VBoxServiceToolboxShowUsage();
359 return RTEXITCODE_SUCCESS;
360
361 case 'o':
362 if (!RTStrPrintf(szOutput, sizeof(szOutput), ValueUnion.psz))
363 rc = VERR_NO_MEMORY;
364 break;
365
366 case 'u':
367 /* Ignored. */
368 break;
369
370 case 'V':
371 VBoxServiceToolboxShowVersion();
372 return RTEXITCODE_SUCCESS;
373
374 case VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED:
375 fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
376 break;
377
378 case VINF_GETOPT_NOT_OPTION:
379 {
380 /* Add file(s) to buffer. This enables processing multiple paths
381 * at once.
382 *
383 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
384 * processing this loop it's safe to immediately exit on syntax errors
385 * or showing the help text (see above). */
386 rc = VBoxServiceToolboxPathBufAddPathEntry(&inputList, ValueUnion.psz);
387 break;
388 }
389
390 default:
391 return RTGetOptPrintError(ch, &ValueUnion);
392 }
393 }
394
395 if (RT_SUCCESS(rc))
396 {
397 if (strlen(szOutput))
398 {
399 rc = RTFileOpen(&hOutput, szOutput, fFlags);
400 if (RT_FAILURE(rc))
401 RTMsgError("Could not create output file '%s', rc=%Rrc\n",
402 szOutput, rc);
403 }
404
405 if (RT_SUCCESS(rc))
406 {
407 /* Process each input file. */
408 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
409 RTFILE hInput = NIL_RTFILE;
410 RTListForEach(&inputList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
411 {
412 rc = RTFileOpen(&hInput, pNodeIt->pszName,
413 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
414 if (RT_SUCCESS(rc))
415 {
416 rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
417 RTFileClose(hInput);
418 }
419 else
420 {
421 PCRTSTATUSMSG pMsg = RTErrGet(rc);
422 if (pMsg)
423 RTMsgError("Could not open input file '%s': %s\n",
424 pNodeIt->pszName, pMsg->pszMsgFull);
425 else
426 RTMsgError("Could not open input file '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
427 }
428
429 if (RT_FAILURE(rc))
430 break;
431 }
432
433 /* If not input files were defined, process stdin. */
434 if (RTListNodeIsFirst(&inputList, &inputList))
435 rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
436 }
437 }
438
439 if (hOutput != NIL_RTFILE)
440 RTFileClose(hOutput);
441 VBoxServiceToolboxPathBufDestroy(&inputList);
442
443 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
444}
445
446/**
447 * Prints information (based on given flags) of a file system object (file/directory/...)
448 * to stdout.
449 *
450 * @return IPRT status code.
451 * @param pszName Object name.
452 * @param cbName Size of object name.
453 * @param uOutputFlags Output / handling flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
454 * @param pObjInfo Pointer to object information.
455 */
456static int VBoxServiceToolboxPrintFsInfo(const char *pszName, uint16_t cbName,
457 uint32_t uOutputFlags,
458 PRTFSOBJINFO pObjInfo)
459{
460 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
461 AssertReturn(cbName, VERR_INVALID_PARAMETER);
462 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
463
464 RTFMODE fMode = pObjInfo->Attr.fMode;
465 char chFileType;
466 switch (fMode & RTFS_TYPE_MASK)
467 {
468 case RTFS_TYPE_FIFO: chFileType = 'f'; break;
469 case RTFS_TYPE_DEV_CHAR: chFileType = 'c'; break;
470 case RTFS_TYPE_DIRECTORY: chFileType = 'd'; break;
471 case RTFS_TYPE_DEV_BLOCK: chFileType = 'b'; break;
472 case RTFS_TYPE_FILE: chFileType = '-'; break;
473 case RTFS_TYPE_SYMLINK: chFileType = 'l'; break;
474 case RTFS_TYPE_SOCKET: chFileType = 's'; break;
475 case RTFS_TYPE_WHITEOUT: chFileType = 'w'; break;
476 default: chFileType = '?'; break;
477 }
478 /** @todo sticy bits++ */
479
480 if (!(uOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_LONG))
481 {
482 if (uOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
483 {
484 /** @todo Skip node_id if not present/available! */
485 RTPrintf("ftype=%c%cnode_id=%RU64%cname_len=%RU16%cname=%s%c",
486 chFileType, 0, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, 0,
487 cbName, 0, pszName, 0);
488 }
489 else
490 RTPrintf("%c %#18llx %3d %s\n",
491 chFileType, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, cbName, pszName);
492
493 if (uOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* End of data block. */
494 RTPrintf("%c%c", 0, 0);
495 }
496 else
497 {
498 if (uOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
499 {
500 RTPrintf("ftype=%c%c", chFileType, 0);
501 RTPrintf("owner_mask=%c%c%c%c",
502 fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
503 fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
504 fMode & RTFS_UNIX_IXUSR ? 'x' : '-', 0);
505 RTPrintf("group_mask=%c%c%c%c",
506 fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
507 fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
508 fMode & RTFS_UNIX_IXGRP ? 'x' : '-', 0);
509 RTPrintf("other_mask=%c%c%c%c",
510 fMode & RTFS_UNIX_IROTH ? 'r' : '-',
511 fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
512 fMode & RTFS_UNIX_IXOTH ? 'x' : '-', 0);
513 RTPrintf("dos_mask=%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
514 fMode & RTFS_DOS_READONLY ? 'R' : '-',
515 fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
516 fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
517 fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
518 fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
519 fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
520 fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
521 fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
522 fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
523 fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
524 fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
525 fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
526 fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
527 fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-', 0);
528
529 char szTimeBirth[256];
530 RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth));
531 char szTimeChange[256];
532 RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange));
533 char szTimeModification[256];
534 RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification));
535 char szTimeAccess[256];
536 RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess));
537
538 RTPrintf("hlinks=%RU32%cuid=%RU32%cgid=%RU32%cst_size=%RI64%calloc=%RI64%c"
539 "st_birthtime=%s%cst_ctime=%s%cst_mtime=%s%cst_atime=%s%c",
540 pObjInfo->Attr.u.Unix.cHardlinks, 0,
541 pObjInfo->Attr.u.Unix.uid, 0,
542 pObjInfo->Attr.u.Unix.gid, 0,
543 pObjInfo->cbObject, 0,
544 pObjInfo->cbAllocated, 0,
545 szTimeBirth, 0,
546 szTimeChange, 0,
547 szTimeModification, 0,
548 szTimeAccess, 0);
549 RTPrintf("cname_len=%RU16%cname=%s%c",
550 cbName, 0, pszName, 0);
551
552 /* End of data block. */
553 RTPrintf("%c%c", 0, 0);
554 }
555 else
556 {
557 RTPrintf("%c", chFileType);
558 RTPrintf("%c%c%c",
559 fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
560 fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
561 fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
562 RTPrintf("%c%c%c",
563 fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
564 fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
565 fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
566 RTPrintf("%c%c%c",
567 fMode & RTFS_UNIX_IROTH ? 'r' : '-',
568 fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
569 fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
570 RTPrintf(" %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
571 fMode & RTFS_DOS_READONLY ? 'R' : '-',
572 fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
573 fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
574 fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
575 fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
576 fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
577 fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
578 fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
579 fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
580 fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
581 fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
582 fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
583 fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
584 fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-');
585 RTPrintf(" %d %4d %4d %10lld %10lld %#llx %#llx %#llx %#llx",
586 pObjInfo->Attr.u.Unix.cHardlinks,
587 pObjInfo->Attr.u.Unix.uid,
588 pObjInfo->Attr.u.Unix.gid,
589 pObjInfo->cbObject,
590 pObjInfo->cbAllocated,
591 pObjInfo->BirthTime,
592 pObjInfo->ChangeTime,
593 pObjInfo->ModificationTime,
594 pObjInfo->AccessTime);
595 RTPrintf(" %2d %s\n", cbName, pszName);
596 }
597 }
598
599 return VINF_SUCCESS;
600}
601
602
603/**
604 * Helper routine for ls tool doing the actual parsing and output of
605 * a specified directory.
606 *
607 * @return IPRT status code.
608 * @param pszDir Directory (path) to ouptut.
609 * @param uFlags Flags of type VBOXSERVICETOOLBOXLSFLAG.
610 * @param uOutputFlags Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
611 */
612static int VBoxServiceToolboxLsHandleDir(const char *pszDir,
613 uint32_t uFlags, uint32_t uOutputFlags)
614{
615 AssertPtrReturn(pszDir, VERR_INVALID_PARAMETER);
616
617 if (uFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
618 RTPrintf("dname=%s%c", pszDir, 0);
619 else if (uFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE)
620 RTPrintf("%s:\n", pszDir);
621
622 char szPathAbs[RTPATH_MAX + 1];
623 int rc = RTPathAbs(pszDir, szPathAbs, sizeof(szPathAbs));
624 if (RT_FAILURE(rc))
625 {
626 if (!(uOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
627 RTMsgError("Failed to retrieve absolute path of '%s', rc=%Rrc\n", pszDir, rc);
628 return rc;
629 }
630
631 PRTDIR pDir;
632 rc = RTDirOpen(&pDir, szPathAbs);
633 if (RT_FAILURE(rc))
634 {
635 if (!(uOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
636 RTMsgError("Failed to open directory '%s', rc=%Rrc\n", szPathAbs, rc);
637 return rc;
638 }
639
640 RTLISTANCHOR dirList;
641 RTListInit(&dirList);
642
643 /* To prevent races we need to read in the directory entries once
644 * and process them afterwards: First loop is displaying the current
645 * directory's content and second loop is diving deeper into
646 * sub directories (if wanted). */
647 for (;RT_SUCCESS(rc);)
648 {
649 RTDIRENTRYEX DirEntry;
650 rc = RTDirReadEx(pDir, &DirEntry, NULL, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
651 if (RT_SUCCESS(rc))
652 {
653 PVBOXSERVICETOOLBOXDIRENTRY pNode = (PVBOXSERVICETOOLBOXDIRENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXDIRENTRY));
654 if (pNode)
655 {
656 memcpy(&pNode->dirEntry, &DirEntry, sizeof(RTDIRENTRYEX));
657 /*rc =*/ RTListAppend(&dirList, &pNode->Node);
658 }
659 else
660 rc = VERR_NO_MEMORY;
661 }
662 }
663
664 if (rc == VERR_NO_MORE_FILES)
665 rc = VINF_SUCCESS;
666
667 int rc2 = RTDirClose(pDir);
668 if (RT_FAILURE(rc2))
669 {
670 if (!(uOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
671 RTMsgError("Failed to close dir '%s', rc=%Rrc\n",
672 pszDir, rc2);
673 if (RT_SUCCESS(rc))
674 rc = rc2;
675 }
676
677 if (RT_SUCCESS(rc))
678 {
679 PVBOXSERVICETOOLBOXDIRENTRY pNodeIt;
680 RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXDIRENTRY, Node)
681 {
682 rc = VBoxServiceToolboxPrintFsInfo(pNodeIt->dirEntry.szName, pNodeIt->dirEntry.cbName,
683 uOutputFlags,
684 &pNodeIt->dirEntry.Info);
685 if (RT_FAILURE(rc))
686 break;
687 }
688
689 /* If everything went fine we do the second run (if needed) ... */
690 if ( RT_SUCCESS(rc)
691 && (uFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE))
692 {
693 /* Process all sub-directories. */
694 RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXDIRENTRY, Node)
695 {
696 RTFMODE fMode = pNodeIt->dirEntry.Info.Attr.fMode;
697 switch (fMode & RTFS_TYPE_MASK)
698 {
699 case RTFS_TYPE_SYMLINK:
700 if (!(uFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS))
701 break;
702 /* Fall through is intentional. */
703 case RTFS_TYPE_DIRECTORY:
704 {
705 const char *pszName = pNodeIt->dirEntry.szName;
706 if ( !RTStrICmp(pszName, ".")
707 || !RTStrICmp(pszName, ".."))
708 {
709 /* Skip dot directories. */
710 continue;
711 }
712
713 char szPath[RTPATH_MAX];
714 rc = RTPathJoin(szPath, sizeof(szPath),
715 pszDir, pNodeIt->dirEntry.szName);
716 if (RT_SUCCESS(rc))
717 rc = VBoxServiceToolboxLsHandleDir(szPath,
718 uFlags, uOutputFlags);
719 }
720 break;
721
722 default: /* Ignore the rest. */
723 break;
724 }
725 if (RT_FAILURE(rc))
726 break;
727 }
728 }
729 }
730
731 /* Clean up the mess. */
732 PVBOXSERVICETOOLBOXDIRENTRY pNode, pSafe;
733 RTListForEachSafe(&dirList, pNode, pSafe, VBOXSERVICETOOLBOXDIRENTRY, Node)
734 {
735 RTListNodeRemove(&pNode->Node);
736 RTMemFree(pNode);
737 }
738 return rc;
739}
740
741
742/**
743 * Main function for tool "vbox_ls".
744 *
745 * @return RTEXITCODE.
746 * @param argc Number of arguments.
747 * @param argv Pointer to argument array.
748 */
749static RTEXITCODE VBoxServiceToolboxLs(int argc, char **argv)
750{
751 static const RTGETOPTDEF s_aOptions[] =
752 {
753 { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
754 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
755 { NULL, 'l', RTGETOPT_REQ_NOTHING },
756 { NULL, 'R', RTGETOPT_REQ_NOTHING },
757 { "--verbose", VBOXSERVICETOOLBOXOPT_VERBOSE, RTGETOPT_REQ_NOTHING}
758 };
759
760 int ch;
761 RTGETOPTUNION ValueUnion;
762 RTGETOPTSTATE GetState;
763 int rc = RTGetOptInit(&GetState, argc, argv,
764 s_aOptions, RT_ELEMENTS(s_aOptions),
765 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
766 AssertRCReturn(rc, RTEXITCODE_INIT);
767
768 bool fVerbose = false;
769 uint32_t fFlags = VBOXSERVICETOOLBOXLSFLAG_NONE;
770 uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_NONE;
771
772 /* Init file list. */
773 RTLISTANCHOR fileList;
774 RTListInit(&fileList);
775
776 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
777 && RT_SUCCESS(rc))
778 {
779 /* For options that require an argument, ValueUnion has received the value. */
780 switch (ch)
781 {
782 case 'h':
783 VBoxServiceToolboxShowUsage();
784 return RTEXITCODE_SUCCESS;
785
786 case 'L': /* Dereference symlinks. */
787 fFlags |= VBOXSERVICETOOLBOXLSFLAG_SYMLINKS;
788 break;
789
790 case 'l': /* Print long format. */
791 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_LONG;
792 break;
793
794 case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
795 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
796 break;
797
798 case 'R': /* Recursive processing. */
799 fFlags |= VBOXSERVICETOOLBOXLSFLAG_RECURSIVE;
800 break;
801
802 case VBOXSERVICETOOLBOXOPT_VERBOSE:
803 fVerbose = true;
804 break;
805
806 case 'V':
807 VBoxServiceToolboxShowVersion();
808 return RTEXITCODE_SUCCESS;
809
810 case VINF_GETOPT_NOT_OPTION:
811 /* Add file(s) to buffer. This enables processing multiple files
812 * at once.
813 *
814 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
815 * processing this loop it's safe to immediately exit on syntax errors
816 * or showing the help text (see above). */
817 rc = VBoxServiceToolboxPathBufAddPathEntry(&fileList, ValueUnion.psz);
818 /** @todo r=bird: Nit: creating a list here is not really
819 * necessary since you've got one in argv that's
820 * accessible via RTGetOpt. */
821 break;
822
823 default:
824 return RTGetOptPrintError(ch, &ValueUnion);
825 }
826 }
827
828 if (RT_SUCCESS(rc))
829 {
830 /* If not files given add current directory to list. */
831 if (RTListIsEmpty(&fileList))
832 {
833 char szDirCur[RTPATH_MAX + 1];
834 rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
835 if (RT_SUCCESS(rc))
836 {
837 rc = VBoxServiceToolboxPathBufAddPathEntry(&fileList, szDirCur);
838 if (RT_FAILURE(rc))
839 RTMsgError("Adding current directory failed, rc=%Rrc\n", rc);
840 }
841 else
842 RTMsgError("Getting current directory failed, rc=%Rrc\n", rc);
843 }
844
845 /* Print magic/version. */
846 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
847 {
848 rc = VBoxServiceToolboxStrmInit();
849 if (RT_FAILURE(rc))
850 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
851 VBoxServiceToolboxPrintStrmHeader("vbt_ls", 1 /* Stream version */);
852 }
853
854 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
855 RTListForEach(&fileList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
856 {
857 if (RTFileExists(pNodeIt->pszName))
858 {
859 RTFSOBJINFO objInfo;
860 int rc2 = RTPathQueryInfoEx(pNodeIt->pszName, &objInfo,
861 RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK /* @todo Follow link? */);
862 if (RT_FAILURE(rc2))
863 {
864 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
865 RTMsgError("Cannot access '%s': No such file or directory\n",
866 pNodeIt->pszName);
867 rc = VERR_FILE_NOT_FOUND;
868 /* Do not break here -- process every element in the list
869 * and keep failing rc. */
870 }
871 else
872 {
873 rc2 = VBoxServiceToolboxPrintFsInfo(pNodeIt->pszName,
874 strlen(pNodeIt->pszName) /* cbName */,
875 fOutputFlags,
876 &objInfo);
877 if (RT_FAILURE(rc2))
878 rc = rc2;
879 }
880 }
881 else
882 {
883 int rc2 = VBoxServiceToolboxLsHandleDir(pNodeIt->pszName,
884 fFlags, fOutputFlags);
885 if (RT_FAILURE(rc2))
886 rc = rc2;
887 }
888 }
889
890 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
891 VBoxServiceToolboxPrintStrmTermination();
892 }
893 else if (fVerbose)
894 RTMsgError("Failed with rc=%Rrc\n", rc);
895
896 VBoxServiceToolboxPathBufDestroy(&fileList);
897 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
898}
899
900
901/**
902 * Main function for tool "vbox_mkdir".
903 *
904 * @return RTEXITCODE.
905 * @param argc Number of arguments.
906 * @param argv Pointer to argument array.
907 */
908static RTEXITCODE VBoxServiceToolboxMkDir(int argc, char **argv)
909{
910 static const RTGETOPTDEF s_aOptions[] =
911 {
912 { "--mode", 'm', RTGETOPT_REQ_STRING },
913 { "--parents", 'p', RTGETOPT_REQ_NOTHING},
914 { "--verbose", 'v', RTGETOPT_REQ_NOTHING}
915 };
916
917 int ch;
918 RTGETOPTUNION ValueUnion;
919 RTGETOPTSTATE GetState;
920 int rc = RTGetOptInit(&GetState, argc, argv,
921 s_aOptions, RT_ELEMENTS(s_aOptions),
922 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
923 AssertRCReturn(rc, RTEXITCODE_INIT);
924
925 bool fMakeParentDirs = false;
926 bool fVerbose = false;
927 RTFMODE fDirMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO;
928 int cDirsCreated = 0;
929
930 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
931 {
932 /* For options that require an argument, ValueUnion has received the value. */
933 switch (ch)
934 {
935 case 'p':
936 fMakeParentDirs = true;
937#ifndef RT_OS_WINDOWS
938 umask(0); /* RTDirCreate workaround */
939#endif
940 break;
941
942 case 'm':
943 rc = RTStrToUInt32Ex(ValueUnion.psz, NULL, 8 /* Base */, &fDirMode);
944 if (RT_FAILURE(rc)) /* Only octet based values supported right now! */
945 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
946 "Mode flag strings not implemented yet! Use octal numbers instead. (%s)\n",
947 ValueUnion.psz);
948#ifndef RT_OS_WINDOWS
949 umask(0); /* RTDirCreate workaround */
950#endif
951 break;
952
953 case 'v':
954 fVerbose = true;
955 break;
956
957 case 'h':
958 RTPrintf("Usage: %s [options] dir1 [dir2...]\n"
959 "\n"
960 "Options:\n"
961 " -m,--mode=<mode> The file mode to set (chmod) on the created\n"
962 " directories. Default: a=rwx & umask.\n"
963 " -p,--parents Create parent directories as needed, no\n"
964 " error if the directory already exists.\n"
965 " -v,--verbose Display a message for each created directory.\n"
966 " -V,--version Display the version and exit\n"
967 " -h,--help Display this help text and exit.\n"
968 , argv[0]);
969 return RTEXITCODE_SUCCESS;
970
971 case 'V':
972 VBoxServiceToolboxShowVersion();
973 return RTEXITCODE_SUCCESS;
974
975 case VINF_GETOPT_NOT_OPTION:
976 if (fMakeParentDirs)
977 /** @todo r=bird: If fVerbose is set, we should also show
978 * which directories that get created, parents as well as
979 * omitting existing final dirs. Annoying, but check any
980 * mkdir implementation (try "mkdir -pv asdf/1/2/3/4"
981 * twice). */
982 rc = RTDirCreateFullPath(ValueUnion.psz, fDirMode);
983 else
984 rc = RTDirCreate(ValueUnion.psz, fDirMode, 0);
985 if (RT_FAILURE(rc))
986 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Could not create directory '%s': %Rra\n",
987 ValueUnion.psz, rc);
988 if (fVerbose)
989 RTMsgInfo("Created directory '%s', mode %#RTfmode\n", ValueUnion.psz, fDirMode);
990 cDirsCreated++;
991 break;
992
993 default:
994 return RTGetOptPrintError(ch, &ValueUnion);
995 }
996 }
997 AssertRC(rc);
998
999 if (cDirsCreated == 0)
1000 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No directory argument.");
1001
1002 return RTEXITCODE_SUCCESS;
1003}
1004
1005
1006/**
1007 * Main function for tool "vbox_stat".
1008 *
1009 * @return RTEXITCODE.
1010 * @param argc Number of arguments.
1011 * @param argv Pointer to argument array.
1012 */
1013static RTEXITCODE VBoxServiceToolboxStat(int argc, char **argv)
1014{
1015 static const RTGETOPTDEF s_aOptions[] =
1016 {
1017 { "--file-system", 'f', RTGETOPT_REQ_NOTHING },
1018 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
1019 { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1020 { "--terse", 't', RTGETOPT_REQ_NOTHING },
1021 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
1022 };
1023
1024 int ch;
1025 RTGETOPTUNION ValueUnion;
1026 RTGETOPTSTATE GetState;
1027 RTGetOptInit(&GetState, argc, argv,
1028 s_aOptions, RT_ELEMENTS(s_aOptions),
1029 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1030
1031 int rc = VINF_SUCCESS;
1032 bool fVerbose = false;
1033 uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_LONG; /* Use long mode by default. */
1034
1035 /* Init file list. */
1036 RTLISTANCHOR fileList;
1037 RTListInit(&fileList);
1038
1039 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1040 && RT_SUCCESS(rc))
1041 {
1042 /* For options that require an argument, ValueUnion has received the value. */
1043 switch (ch)
1044 {
1045 case 'f':
1046 case 'L':
1047 RTMsgError("Sorry, option '%s' is not implemented yet!\n", ValueUnion.pDef->pszLong);
1048 rc = VERR_INVALID_PARAMETER;
1049 break;
1050
1051 case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
1052 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
1053 break;
1054
1055 case 'v': /** @todo r=bird: There is no verbose option for stat. */
1056 fVerbose = true;
1057 break;
1058
1059 case 'h':
1060 VBoxServiceToolboxShowUsage();
1061 return RTEXITCODE_SUCCESS;
1062
1063 case 'V':
1064 VBoxServiceToolboxShowVersion();
1065 return RTEXITCODE_SUCCESS;
1066
1067 case VINF_GETOPT_NOT_OPTION:
1068 {
1069 /* Add file(s) to buffer. This enables processing multiple files
1070 * at once.
1071 *
1072 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
1073 * processing this loop it's safe to immediately exit on syntax errors
1074 * or showing the help text (see above). */
1075 rc = VBoxServiceToolboxPathBufAddPathEntry(&fileList, ValueUnion.psz);
1076 break;
1077 }
1078
1079 default:
1080 return RTGetOptPrintError(ch, &ValueUnion);
1081 }
1082 }
1083
1084 if (RT_SUCCESS(rc))
1085 {
1086 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1087 {
1088 rc = VBoxServiceToolboxStrmInit();
1089 if (RT_FAILURE(rc))
1090 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
1091 VBoxServiceToolboxPrintStrmHeader("vbt_stat", 1 /* Stream version */);
1092 }
1093
1094 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
1095 RTListForEach(&fileList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
1096 {
1097 RTFSOBJINFO objInfo;
1098 int rc2 = RTPathQueryInfoEx(pNodeIt->pszName, &objInfo,
1099 RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK /* @todo Follow link? */);
1100 if (RT_FAILURE(rc2))
1101 {
1102 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
1103 RTMsgError("Cannot stat for '%s': No such file or directory\n",
1104 pNodeIt->pszName);
1105 rc = VERR_FILE_NOT_FOUND;
1106 /* Do not break here -- process every element in the list
1107 * and keep failing rc. */
1108 }
1109 else
1110 {
1111 rc2 = VBoxServiceToolboxPrintFsInfo(pNodeIt->pszName,
1112 strlen(pNodeIt->pszName) /* cbName */,
1113 fOutputFlags,
1114 &objInfo);
1115 if (RT_FAILURE(rc2))
1116 rc = rc2;
1117 }
1118 }
1119
1120 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1121 VBoxServiceToolboxPrintStrmTermination();
1122
1123 /* At this point the overall result (success/failure) should be in rc. */
1124
1125 if (RTListIsEmpty(&fileList))
1126 RTMsgError("Missing operand\n");
1127 }
1128 else if (fVerbose)
1129 RTMsgError("Failed with rc=%Rrc\n", rc);
1130
1131 VBoxServiceToolboxPathBufDestroy(&fileList);
1132 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1133}
1134
1135
1136
1137/**
1138 * Looks up the handler for the tool give by @a pszTool.
1139 *
1140 * @returns Pointer to handler function. NULL if not found.
1141 * @param pszTool The name of the tool.
1142 */
1143static PFNHANDLER vboxServiceToolboxLookUpHandler(const char *pszTool)
1144{
1145 static struct
1146 {
1147 const char *pszName;
1148 RTEXITCODE (*pfnHandler)(int argc, char **argv);
1149 }
1150 const s_aTools[] =
1151 {
1152 { "cat", VBoxServiceToolboxCat },
1153 { "ls", VBoxServiceToolboxLs },
1154 { "mkdir", VBoxServiceToolboxMkDir },
1155 { "stat", VBoxServiceToolboxStat },
1156 };
1157
1158 /* Skip optional 'vbox_' prefix. */
1159 if ( pszTool[0] == 'v'
1160 && pszTool[1] == 'b'
1161 && pszTool[2] == 'o'
1162 && pszTool[3] == 'x'
1163 && pszTool[4] == '_')
1164 pszTool += 5;
1165
1166 /* Do a linear search, since we don't have that much stuff in the table. */
1167 for (unsigned i = 0; i < RT_ELEMENTS(s_aTools); i++)
1168 if (!strcmp(s_aTools[i].pszName, pszTool))
1169 return s_aTools[i].pfnHandler;
1170
1171 return NULL;
1172}
1173
1174
1175/**
1176 * Entry point for internal toolbox.
1177 *
1178 * @return True if an internal tool was handled, false if not.
1179 * @param argc Number of arguments.
1180 * @param argv Pointer to argument array.
1181 * @param prcExit Where to store the exit code when an
1182 * internal toolbox command was handled.
1183 */
1184bool VBoxServiceToolboxMain(int argc, char **argv, RTEXITCODE *prcExit)
1185{
1186
1187 /*
1188 * Check if the file named in argv[0] is one of the toolbox programs.
1189 */
1190 AssertReturn(argc > 0, false);
1191 const char *pszTool = RTPathFilename(argv[0]);
1192 PFNHANDLER pfnHandler = vboxServiceToolboxLookUpHandler(pszTool);
1193 if (!pfnHandler)
1194 {
1195 /*
1196 * For debugging and testing purposes we also allow toolbox program access
1197 * when the first VBoxService argument is --use-toolbox.
1198 */
1199 if (argc < 3 || strcmp(argv[1], "--use-toolbox"))
1200 return false;
1201 argc -= 2;
1202 argv += 2;
1203 pszTool = argv[0];
1204 pfnHandler = vboxServiceToolboxLookUpHandler(pszTool);
1205 if (!pfnHandler)
1206 {
1207 *prcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "Toolbox program '%s' does not exist", pszTool);
1208 return true;
1209 }
1210 }
1211
1212 /*
1213 * Invoke the handler.
1214 */
1215 RTMsgSetProgName("VBoxService/%s", pszTool);
1216 *prcExit = pfnHandler(argc, argv);
1217
1218 return true;
1219}
1220
Note: See TracBrowser for help on using the repository browser.

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