VirtualBox

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

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

*: Use RTLISTANCHOR.

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