VirtualBox

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

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

Additions/common/VBoxService: add vbox_rm.

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