summaryrefslogtreecommitdiff
path: root/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp')
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp1248
1 files changed, 962 insertions, 286 deletions
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp
index 8584eef46..3bc0e1e3f 100644
--- a/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp
+++ b/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp
@@ -1,10 +1,10 @@
-/* $Id: VBoxServiceToolBox.cpp $ */
+/* $Id: VBoxServiceToolBox.cpp 38015 2011-07-18 12:49:31Z vboxsync $ */
/** @file
- * VBoxServiceToolBox - Internal (BusyBox-like) toolbox.
+ * VBoxServiceToolbox - Internal (BusyBox-like) toolbox.
*/
/*
- * Copyright (C) 2010 Oracle Corporation
+ * Copyright (C) 2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -34,7 +34,7 @@
#include <iprt/stream.h>
#ifndef RT_OS_WINDOWS
-#include <sys/stat.h>
+# include <sys/stat.h> /* need umask */
#endif
#include <VBox/VBoxGuestLib.h>
@@ -43,7 +43,52 @@
#include "VBoxServiceUtils.h"
-#define CAT_OPT_NO_CONTENT_INDEXED 1000
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+
+/** Options indices for "vbox_cat". */
+typedef enum VBOXSERVICETOOLBOXCATOPT
+{
+ VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED = 1000
+} VBOXSERVICETOOLBOXCATOPT;
+
+/** Options indices for "vbox_ls". */
+typedef enum VBOXSERVICETOOLBOXLSOPT
+{
+ VBOXSERVICETOOLBOXLSOPT_MACHINE_READABLE = 1000,
+ VBOXSERVICETOOLBOXLSOPT_VERBOSE
+} VBOXSERVICETOOLBOXLSOPT;
+
+/** Options indices for "vbox_stat". */
+typedef enum VBOXSERVICETOOLBOXSTATOPT
+{
+ VBOXSERVICETOOLBOXSTATOPT_MACHINE_READABLE = 1000
+} VBOXSERVICETOOLBOXSTATOPT;
+
+
+/** Flags for "vbox_ls". */
+typedef enum VBOXSERVICETOOLBOXLSFLAG
+{
+ VBOXSERVICETOOLBOXLSFLAG_NONE = 0x0,
+ VBOXSERVICETOOLBOXLSFLAG_RECURSIVE = 0x1,
+ VBOXSERVICETOOLBOXLSFLAG_SYMLINKS = 0x2
+} VBOXSERVICETOOLBOXLSFLAG;
+
+/** Flags for fs object output. */
+typedef enum VBOXSERVICETOOLBOXOUTPUTFLAG
+{
+ VBOXSERVICETOOLBOXOUTPUTFLAG_NONE = 0x0,
+ VBOXSERVICETOOLBOXOUTPUTFLAG_LONG = 0x1,
+ VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE = 0x2
+} VBOXSERVICETOOLBOXOUTPUTFLAG;
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/** Pointer to a handler function. */
+typedef RTEXITCODE (*PFNHANDLER)(int , char **);
/**
* An file/directory entry. Used to cache
@@ -57,6 +102,14 @@ typedef struct VBOXSERVICETOOLBOXPATHENTRY
char *pszName;
} VBOXSERVICETOOLBOXPATHENTRY, *PVBOXSERVICETOOLBOXPATHENTRY;
+typedef struct VBOXSERVICETOOLBOXDIRENTRY
+{
+ /** Our node. */
+ RTLISTNODE Node;
+ /** The actual entry. */
+ RTDIRENTRYEX dirEntry;
+} VBOXSERVICETOOLBOXDIRENTRY, *PVBOXSERVICETOOLBOXDIRENTRY;
+
/**
* Displays a help text to stdout.
@@ -67,7 +120,14 @@ static void VBoxServiceToolboxShowUsage(void)
"cat [FILE] - Concatenate FILE(s), or standard input, to standard output.\n"
"\n"
/** @todo Document options! */
- "mkdir [OPTION] DIRECTORY... - Create the DIRECTORY(ies), if they do not already exist.\n"
+ "ls [OPTION]... FILE... - List information about the FILEs (the current directory by default).\n"
+ "\n"
+ /** @todo Document options! */
+ "mkdir [OPTION]... DIRECTORY... - Create the DIRECTORY(ies), if they do not already exist.\n"
+ "\n"
+ /** @todo Document options! */
+ "stat [OPTION]... FILE... - Display file or file system status.\n"
+ "\n"
/** @todo Document options! */
"\n");
}
@@ -83,22 +143,28 @@ static void VBoxServiceToolboxShowVersion(void)
/**
- * Displays an error message because of syntax error.
+ * Prints a parseable stream header which contains the actual tool
+ * which was called/used along with its stream version.
*
- * @return VERR_INVALID_PARAMETER
- * @param pszFormat
+ * @param pszToolName Name of the tool being used, e.g. "vbt_ls".
+ * @param uVersion Stream version name. Handy for distinguishing
+ * different stream versions later.
*/
-static int VBoxServiceToolboxErrorSyntax(const char *pszFormat, ...)
+static void VBoxServiceToolboxPrintStrmHeader(const char *pszToolName, uint32_t uVersion)
{
- va_list args;
-
- va_start(args, pszFormat);
- RTPrintf("\n"
- "Syntax error: %N\n", pszFormat, &args);
- va_end(args);
- return VERR_INVALID_PARAMETER;
+ AssertPtrReturnVoid(pszToolName);
+ RTPrintf("hdr_id=%s%chdr_ver=%u%c", pszToolName, 0, uVersion, 0);
}
+/**
+ * Prints a standardized termination sequence indicating that the
+ * parseable stream just ended.
+ *
+ */
+static void VBoxServiceToolboxPrintStrmTermination()
+{
+ RTPrintf("%c%c%c%c", 0, 0, 0, 0);
+}
/**
* Destroys a path buffer list.
@@ -168,14 +234,14 @@ static int VBoxServiceToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
{
rc = RTFileFromNative(&hInput, RTFILE_NATIVE_STDIN);
if (RT_FAILURE(rc))
- RTMsgError("cat: Could not translate input file to native handle, rc=%Rrc\n", rc);
+ RTMsgError("Could not translate input file to native handle, rc=%Rrc\n", rc);
}
if (hOutput == NIL_RTFILE)
{
rc = RTFileFromNative(&hOutput, RTFILE_NATIVE_STDOUT);
if (RT_FAILURE(rc))
- RTMsgError("cat: Could not translate output file to native handle, rc=%Rrc\n", rc);
+ RTMsgError("Could not translate output file to native handle, rc=%Rrc\n", rc);
}
if (RT_SUCCESS(rc))
@@ -195,16 +261,619 @@ static int VBoxServiceToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
if (rc == VERR_BROKEN_PIPE)
rc = VINF_SUCCESS;
else if (RT_FAILURE(rc))
- RTMsgError("cat: Error while reading input, rc=%Rrc\n", rc);
+ RTMsgError("Error while reading input, rc=%Rrc\n", rc);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Main function for tool "vbox_cat".
+ *
+ * @return RTEXITCODE.
+ * @param argc Number of arguments.
+ * @param argv Pointer to argument array.
+ */
+static RTEXITCODE VBoxServiceToolboxCat(int argc, char **argv)
+{
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ /* Sorted by short ops. */
+ { "--show-all", 'a', RTGETOPT_REQ_NOTHING },
+ { "--number-nonblank", 'b', RTGETOPT_REQ_NOTHING},
+ { NULL, 'e', RTGETOPT_REQ_NOTHING},
+ { NULL, 'E', RTGETOPT_REQ_NOTHING},
+ { "--flags", 'f', RTGETOPT_REQ_STRING},
+ { "--no-content-indexed", VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED, RTGETOPT_REQ_NOTHING},
+ { "--number", 'n', RTGETOPT_REQ_NOTHING},
+ { "--output", 'o', RTGETOPT_REQ_STRING},
+ { "--squeeze-blank", 's', RTGETOPT_REQ_NOTHING},
+ { NULL, 't', RTGETOPT_REQ_NOTHING},
+ { "--show-tabs", 'T', RTGETOPT_REQ_NOTHING},
+ { NULL, 'u', RTGETOPT_REQ_NOTHING},
+ { "--show-noneprinting", 'v', RTGETOPT_REQ_NOTHING}
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+
+ RTGetOptInit(&GetState, argc, argv,
+ s_aOptions, RT_ELEMENTS(s_aOptions),
+ 1 /*iFirst*/, 0 /*fFlags*/);
+
+ int rc = VINF_SUCCESS;
+ bool fUsageOK = true;
+
+ char szOutput[RTPATH_MAX] = { 0 };
+ RTFILE hOutput = NIL_RTFILE;
+ uint32_t fFlags = RTFILE_O_CREATE_REPLACE /* Output file flags. */
+ | RTFILE_O_WRITE
+ | RTFILE_O_DENY_WRITE;
+
+ /* Init directory list. */
+ RTLISTNODE inputList;
+ RTListInit(&inputList);
+
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && RT_SUCCESS(rc))
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ case 'a':
+ case 'b':
+ case 'e':
+ case 'E':
+ case 'n':
+ case 's':
+ case 't':
+ case 'T':
+ case 'v':
+ RTMsgError("Sorry, option '%s' is not implemented yet!\n",
+ ValueUnion.pDef->pszLong);
+ rc = VERR_INVALID_PARAMETER;
+ break;
+
+ case 'h':
+ VBoxServiceToolboxShowUsage();
+ return RTEXITCODE_SUCCESS;
+
+ case 'o':
+ if (!RTStrPrintf(szOutput, sizeof(szOutput), ValueUnion.psz))
+ rc = VERR_NO_MEMORY;
+ break;
+
+ case 'u':
+ /* Ignored. */
+ break;
+
+ case 'V':
+ VBoxServiceToolboxShowVersion();
+ return RTEXITCODE_SUCCESS;
+
+ case VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED:
+ fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ /* Add file(s) to buffer. This enables processing multiple paths
+ * at once.
+ *
+ * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
+ * processing this loop it's safe to immediately exit on syntax errors
+ * or showing the help text (see above). */
+ rc = VBoxServiceToolboxPathBufAddPathEntry(&inputList, ValueUnion.psz);
+ break;
+ }
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (strlen(szOutput))
+ {
+ rc = RTFileOpen(&hOutput, szOutput, fFlags);
+ if (RT_FAILURE(rc))
+ RTMsgError("Could not create output file '%s', rc=%Rrc\n",
+ szOutput, rc);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Process each input file. */
+ PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
+ RTFILE hInput = NIL_RTFILE;
+ RTListForEach(&inputList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
+ {
+ rc = RTFileOpen(&hInput, pNodeIt->pszName,
+ RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
+ RTFileClose(hInput);
+ }
+ else
+ {
+ PCRTSTATUSMSG pMsg = RTErrGet(rc);
+ if (pMsg)
+ RTMsgError("Could not open input file '%s': %s\n",
+ pNodeIt->pszName, pMsg->pszMsgFull);
+ else
+ RTMsgError("Could not open input file '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
+ }
+
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ /* If not input files were defined, process stdin. */
+ if (RTListNodeIsFirst(&inputList, &inputList))
+ rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
+ }
+ }
+
+ if (hOutput != NIL_RTFILE)
+ RTFileClose(hOutput);
+ VBoxServiceToolboxPathBufDestroy(&inputList);
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+/**
+ * Prints information (based on given flags) of a file system object (file/directory/...)
+ * to stdout.
+ *
+ * @return IPRT status code.
+ * @param pszName Object name.
+ * @param cbName Size of object name.
+ * @param uOutputFlags Output / handling flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
+ * @param pObjInfo Pointer to object information.
+ */
+static int VBoxServiceToolboxPrintFsInfo(const char *pszName, uint16_t cbName,
+ uint32_t uOutputFlags,
+ PRTFSOBJINFO pObjInfo)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(cbName, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
+
+ RTFMODE fMode = pObjInfo->Attr.fMode;
+ char chFileType;
+ switch (fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_FIFO: chFileType = 'f'; break;
+ case RTFS_TYPE_DEV_CHAR: chFileType = 'c'; break;
+ case RTFS_TYPE_DIRECTORY: chFileType = 'd'; break;
+ case RTFS_TYPE_DEV_BLOCK: chFileType = 'b'; break;
+ case RTFS_TYPE_FILE: chFileType = '-'; break;
+ case RTFS_TYPE_SYMLINK: chFileType = 'l'; break;
+ case RTFS_TYPE_SOCKET: chFileType = 's'; break;
+ case RTFS_TYPE_WHITEOUT: chFileType = 'w'; break;
+ default: chFileType = '?'; break;
+ }
+ /** @todo sticy bits++ */
+
+ if (!(uOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_LONG))
+ {
+ if (uOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
+ {
+ /** @todo Skip node_id if not present/available! */
+ RTPrintf("ftype=%c%cnode_id=%RU64%cname_len=%RU16%cname=%s%c",
+ chFileType, 0, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, 0,
+ cbName, 0, pszName, 0);
+ }
+ else
+ RTPrintf("%c %#18llx %3d %s\n",
+ chFileType, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, cbName, pszName);
+
+ if (uOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* End of data block. */
+ RTPrintf("%c%c", 0, 0);
+ }
+ else
+ {
+ if (uOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
+ {
+ RTPrintf("ftype=%c%c", chFileType, 0);
+ RTPrintf("owner_mask=%c%c%c%c",
+ fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
+ fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
+ fMode & RTFS_UNIX_IXUSR ? 'x' : '-', 0);
+ RTPrintf("group_mask=%c%c%c%c",
+ fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
+ fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
+ fMode & RTFS_UNIX_IXGRP ? 'x' : '-', 0);
+ RTPrintf("other_mask=%c%c%c%c",
+ fMode & RTFS_UNIX_IROTH ? 'r' : '-',
+ fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
+ fMode & RTFS_UNIX_IXOTH ? 'x' : '-', 0);
+ RTPrintf("dos_mask=%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
+ fMode & RTFS_DOS_READONLY ? 'R' : '-',
+ fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
+ fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
+ fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
+ fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
+ fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
+ fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
+ fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
+ fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
+ fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
+ fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
+ fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
+ fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
+ fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-', 0);
+
+ char szTimeBirth[256];
+ RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth));
+ char szTimeChange[256];
+ RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange));
+ char szTimeModification[256];
+ RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification));
+ char szTimeAccess[256];
+ RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess));
+
+ RTPrintf("hlinks=%RU32%cuid=%RU32%cgid=%RU32%cst_size=%RI64%calloc=%RI64%c"
+ "st_birthtime=%s%cst_ctime=%s%cst_mtime=%s%cst_atime=%s%c",
+ pObjInfo->Attr.u.Unix.cHardlinks, 0,
+ pObjInfo->Attr.u.Unix.uid, 0,
+ pObjInfo->Attr.u.Unix.gid, 0,
+ pObjInfo->cbObject, 0,
+ pObjInfo->cbAllocated, 0,
+ szTimeBirth, 0,
+ szTimeChange, 0,
+ szTimeModification, 0,
+ szTimeAccess, 0);
+ RTPrintf("cname_len=%RU16%cname=%s%c",
+ cbName, 0, pszName, 0);
+
+ /* End of data block. */
+ RTPrintf("%c%c", 0, 0);
+ }
+ else
+ {
+ RTPrintf("%c", chFileType);
+ RTPrintf("%c%c%c",
+ fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
+ fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
+ fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
+ RTPrintf("%c%c%c",
+ fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
+ fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
+ fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
+ RTPrintf("%c%c%c",
+ fMode & RTFS_UNIX_IROTH ? 'r' : '-',
+ fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
+ fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
+ RTPrintf(" %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
+ fMode & RTFS_DOS_READONLY ? 'R' : '-',
+ fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
+ fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
+ fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
+ fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
+ fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
+ fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
+ fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
+ fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
+ fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
+ fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
+ fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
+ fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
+ fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-');
+ RTPrintf(" %d %4d %4d %10lld %10lld %#llx %#llx %#llx %#llx",
+ pObjInfo->Attr.u.Unix.cHardlinks,
+ pObjInfo->Attr.u.Unix.uid,
+ pObjInfo->Attr.u.Unix.gid,
+ pObjInfo->cbObject,
+ pObjInfo->cbAllocated,
+ pObjInfo->BirthTime,
+ pObjInfo->ChangeTime,
+ pObjInfo->ModificationTime,
+ pObjInfo->AccessTime);
+ RTPrintf(" %2d %s\n", cbName, pszName);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Helper routine for ls tool doing the actual parsing and output of
+ * a specified directory.
+ *
+ * @return IPRT status code.
+ * @param pszDir Directory (path) to ouptut.
+ * @param uFlags Flags of type VBOXSERVICETOOLBOXLSFLAG.
+ * @param uOutputFlags Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
+ */
+static int VBoxServiceToolboxLsHandleDir(const char *pszDir,
+ uint32_t uFlags, uint32_t uOutputFlags)
+{
+ AssertPtrReturn(pszDir, VERR_INVALID_PARAMETER);
+
+ if (uFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
+ RTPrintf("dname=%s%c", pszDir, 0);
+ else if (uFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE)
+ RTPrintf("%s:\n", pszDir);
+
+ char szPathAbs[RTPATH_MAX + 1];
+ int rc = RTPathAbs(pszDir, szPathAbs, sizeof(szPathAbs));
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Failed to retrieve absolute path of '%s', rc=%Rrc\n", pszDir, rc);
+ return rc;
+ }
+
+ PRTDIR pDir;
+ rc = RTDirOpen(&pDir, szPathAbs);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Failed to open directory '%s', rc=%Rrc\n", szPathAbs, rc);
+ return rc;
+ }
+
+ RTLISTNODE dirList;
+ RTListInit(&dirList);
+
+ /* To prevent races we need to read in the directory entries once
+ * and process them afterwards: First loop is displaying the current
+ * directory's content and second loop is diving deeper into
+ * sub directories (if wanted). */
+ for (;RT_SUCCESS(rc);)
+ {
+ RTDIRENTRYEX DirEntry;
+ rc = RTDirReadEx(pDir, &DirEntry, NULL, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ {
+ PVBOXSERVICETOOLBOXDIRENTRY pNode = (PVBOXSERVICETOOLBOXDIRENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXDIRENTRY));
+ if (pNode)
+ {
+ memcpy(&pNode->dirEntry, &DirEntry, sizeof(RTDIRENTRYEX));
+ /*rc =*/ RTListAppend(&dirList, &pNode->Node);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ if (rc == VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+
+ int rc2 = RTDirClose(pDir);
+ if (RT_FAILURE(rc2))
+ {
+ RTMsgError("Failed to close dir '%s', rc=%Rrc\n",
+ pszDir, rc2);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ PVBOXSERVICETOOLBOXDIRENTRY pNodeIt;
+ RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXDIRENTRY, Node)
+ {
+ rc = VBoxServiceToolboxPrintFsInfo(pNodeIt->dirEntry.szName, pNodeIt->dirEntry.cbName,
+ uOutputFlags,
+ &pNodeIt->dirEntry.Info);
+ if (RT_FAILURE(rc))
break;
+ }
+
+ /* If everything went fine we do the second run (if needed) ... */
+ if ( RT_SUCCESS(rc)
+ && (uFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE))
+ {
+ /* Process all sub-directories. */
+ RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXDIRENTRY, Node)
+ {
+ RTFMODE fMode = pNodeIt->dirEntry.Info.Attr.fMode;
+ switch (fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_SYMLINK:
+ if (!(uFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS))
+ break;
+ /* Fall through is intentional. */
+ case RTFS_TYPE_DIRECTORY:
+ {
+ const char *pszName = pNodeIt->dirEntry.szName;
+ if ( !RTStrICmp(pszName, ".")
+ || !RTStrICmp(pszName, ".."))
+ {
+ /* Skip dot directories. */
+ continue;
+ }
+
+ char szPath[RTPATH_MAX];
+ rc = RTPathJoin(szPath, sizeof(szPath),
+ pszDir, pNodeIt->dirEntry.szName);
+ if (RT_SUCCESS(rc))
+ rc = VBoxServiceToolboxLsHandleDir(szPath,
+ uFlags, uOutputFlags);
+ }
+ break;
+
+ default: /* Ignore the rest. */
+ break;
+ }
+ if (RT_FAILURE(rc))
+ break;
}
}
}
+
+ /* Clean up the mess. */
+ PVBOXSERVICETOOLBOXDIRENTRY pNode, pSafe;
+ RTListForEachSafe(&dirList, pNode, pSafe, VBOXSERVICETOOLBOXDIRENTRY, Node)
+ {
+ RTListNodeRemove(&pNode->Node);
+ RTMemFree(pNode);
+ }
return rc;
}
/**
+ * Main function for tool "vbox_ls".
+ *
+ * @return RTEXITCODE.
+ * @param argc Number of arguments.
+ * @param argv Pointer to argument array.
+ */
+static RTEXITCODE VBoxServiceToolboxLs(int argc, char **argv)
+{
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--machinereadable", VBOXSERVICETOOLBOXLSOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
+ { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
+ { NULL, 'l', RTGETOPT_REQ_NOTHING },
+ { NULL, 'R', RTGETOPT_REQ_NOTHING },
+ { "--verbose", VBOXSERVICETOOLBOXLSOPT_VERBOSE, RTGETOPT_REQ_NOTHING}
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, argc, argv,
+ s_aOptions, RT_ELEMENTS(s_aOptions),
+ 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_INIT);
+
+ bool fVerbose = false;
+ uint32_t fFlags = VBOXSERVICETOOLBOXLSFLAG_NONE;
+ uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_NONE;
+
+ /* Init file list. */
+ RTLISTNODE fileList;
+ RTListInit(&fileList);
+
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && RT_SUCCESS(rc))
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ case 'h':
+ VBoxServiceToolboxShowUsage();
+ return RTEXITCODE_SUCCESS;
+
+ case 'L': /* Dereference symlinks. */
+ fFlags |= VBOXSERVICETOOLBOXLSFLAG_SYMLINKS;
+ break;
+
+ case 'l': /* Print long format. */
+ fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_LONG;
+ break;
+
+ case VBOXSERVICETOOLBOXLSOPT_MACHINE_READABLE:
+ fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
+ break;
+
+ case 'R': /* Recursive processing. */
+ fFlags |= VBOXSERVICETOOLBOXLSFLAG_RECURSIVE;
+ break;
+
+ case VBOXSERVICETOOLBOXLSOPT_VERBOSE:
+ fVerbose = true;
+ break;
+
+ case 'V':
+ VBoxServiceToolboxShowVersion();
+ return RTEXITCODE_SUCCESS;
+
+ case VINF_GETOPT_NOT_OPTION:
+ /* Add file(s) to buffer. This enables processing multiple files
+ * at once.
+ *
+ * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
+ * processing this loop it's safe to immediately exit on syntax errors
+ * or showing the help text (see above). */
+ rc = VBoxServiceToolboxPathBufAddPathEntry(&fileList, ValueUnion.psz);
+ /** @todo r=bird: Nit: creating a list here is not really
+ * necessary since you've got one in argv that's
+ * accessible via RTGetOpt. */
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* If not files given add current directory to list. */
+ if (RTListIsEmpty(&fileList))
+ {
+ char szDirCur[RTPATH_MAX + 1];
+ rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
+ if (RT_SUCCESS(rc))
+ {
+ rc = VBoxServiceToolboxPathBufAddPathEntry(&fileList, szDirCur);
+ if (RT_FAILURE(rc))
+ RTMsgError("Adding current directory failed, rc=%Rrc\n", rc);
+ }
+ else
+ RTMsgError("Getting current directory failed, rc=%Rrc\n", rc);
+ }
+
+ /* Print magic/version. */
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
+ VBoxServiceToolboxPrintStrmHeader("vbt_ls", 1 /* Stream version */);
+
+ PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
+ RTListForEach(&fileList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
+ {
+ if (RTFileExists(pNodeIt->pszName))
+ {
+ RTFSOBJINFO objInfo;
+ int rc2 = RTPathQueryInfoEx(pNodeIt->pszName, &objInfo,
+ RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK /* @todo Follow link? */);
+ if (RT_FAILURE(rc2))
+ {
+ RTMsgError("Cannot access '%s': No such file or directory\n",
+ pNodeIt->pszName);
+ rc = VERR_FILE_NOT_FOUND;
+ /* Do not break here -- process every element in the list
+ * and keep failing rc. */
+ }
+ else
+ {
+ rc2 = VBoxServiceToolboxPrintFsInfo(pNodeIt->pszName,
+ strlen(pNodeIt->pszName) /* cbName */,
+ fOutputFlags,
+ &objInfo);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
+ }
+ }
+ else
+ {
+ int rc2 = VBoxServiceToolboxLsHandleDir(pNodeIt->pszName,
+ fFlags, fOutputFlags);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
+ }
+ }
+
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
+ VBoxServiceToolboxPrintStrmTermination();
+ }
+ else if (fVerbose)
+ RTMsgError("Failed with rc=%Rrc\n", rc);
+
+ VBoxServiceToolboxPathBufDestroy(&fileList);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
* Main function for tool "vbox_mkdir".
*
* @return RTEXITCODE.
@@ -213,271 +882,262 @@ static int VBoxServiceToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
*/
static RTEXITCODE VBoxServiceToolboxMkDir(int argc, char **argv)
{
- static const RTGETOPTDEF s_aOptions[] =
- {
- { "--mode", 'm', RTGETOPT_REQ_STRING },
- { "--parents", 'p', RTGETOPT_REQ_NOTHING },
- { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
- };
-
- int ch;
- RTGETOPTUNION ValueUnion;
- RTGETOPTSTATE GetState;
- RTGetOptInit(&GetState, argc, argv,
- s_aOptions, RT_ELEMENTS(s_aOptions),
- 1 /* Index of argv to start with. */, RTGETOPTINIT_FLAGS_OPTS_FIRST);
-
- int rc = VINF_SUCCESS;
- bool fMakeParentDirs = false;
- bool fVerbose = false;
-
- RTFMODE newMode = 0;
- RTFMODE dirMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO;
-
- /* Init directory list. */
- RTLISTNODE dirList;
- RTListInit(&dirList);
-
- while ( (ch = RTGetOpt(&GetState, &ValueUnion))
- && RT_SUCCESS(rc))
- {
- /* For options that require an argument, ValueUnion has received the value. */
- switch (ch)
- {
- case 'h':
- VBoxServiceToolboxShowUsage();
- return RTEXITCODE_SUCCESS;
-
- case 'p':
- fMakeParentDirs = true;
- break;
-
- case 'm':
- rc = RTStrToUInt32Ex(ValueUnion.psz, NULL, 8 /* Base */, &newMode);
- if (RT_FAILURE(rc)) /* Only octet based values supported right now! */
- {
- RTMsgError("mkdir: Mode flag strings not implemented yet! Use octal numbers instead.\n");
- return RTEXITCODE_SYNTAX;
- }
- break;
-
- case 'v':
- fVerbose = true;
- break;
-
- case 'V':
- VBoxServiceToolboxShowVersion();
- return RTEXITCODE_SUCCESS;
-
- case VINF_GETOPT_NOT_OPTION:
- {
- /* Add path(s) to buffer. This enables processing multiple paths
- * at once.
- *
- * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
- * processing this loop it's safe to immediately exit on syntax errors
- * or showing the help text (see above). */
- rc = VBoxServiceToolboxPathBufAddPathEntry(&dirList, ValueUnion.psz);
- break;
- }
-
- default:
- return RTGetOptPrintError(ch, &ValueUnion);
- }
- }
-
- if (RT_SUCCESS(rc))
- {
- if (fMakeParentDirs || newMode)
- {
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--mode", 'm', RTGETOPT_REQ_STRING },
+ { "--parents", 'p', RTGETOPT_REQ_NOTHING},
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING}
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, argc, argv,
+ s_aOptions, RT_ELEMENTS(s_aOptions),
+ 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_INIT);
+
+ bool fMakeParentDirs = false;
+ bool fVerbose = false;
+ RTFMODE fDirMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO;
+ int cDirsCreated = 0;
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ case 'p':
+ fMakeParentDirs = true;
+#ifndef RT_OS_WINDOWS
+ umask(0); /* RTDirCreate workaround */
+#endif
+ break;
+
+ case 'm':
+ rc = RTStrToUInt32Ex(ValueUnion.psz, NULL, 8 /* Base */, &fDirMode);
+ if (RT_FAILURE(rc)) /* Only octet based values supported right now! */
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX,
+ "Mode flag strings not implemented yet! Use octal numbers instead. (%s)\n",
+ ValueUnion.psz);
#ifndef RT_OS_WINDOWS
- mode_t umaskMode = umask(0); /* Get current umask. */
- if (newMode)
- dirMode = newMode;
+ umask(0); /* RTDirCreate workaround */
#endif
- }
-
- PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
- RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
- {
- rc = fMakeParentDirs ?
- RTDirCreateFullPath(pNodeIt->pszName, dirMode)
- : RTDirCreate(pNodeIt->pszName, dirMode);
-
- if (RT_SUCCESS(rc) && fVerbose)
- RTMsgError("mkdir: Created directory 's', mode %#RTfmode\n", pNodeIt->pszName, dirMode);
- else if (RT_FAILURE(rc)) /** @todo Add a switch with more helpful error texts! */
- {
- PCRTSTATUSMSG pMsg = RTErrGet(rc);
- if (pMsg)
- RTMsgError("mkdir: Could not create directory '%s': %s\n",
- pNodeIt->pszName, pMsg->pszMsgFull);
- else
- RTMsgError("mkdir: Could not create directory '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
- break;
- }
- }
- }
- else if (fVerbose)
- RTMsgError("mkdir: Failed with rc=%Rrc\n", rc);
-
- VBoxServiceToolboxPathBufDestroy(&dirList);
- return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+ break;
+
+ case 'v':
+ fVerbose = true;
+ break;
+
+ case 'h':
+ RTPrintf("Usage: %s [options] dir1 [dir2...]\n"
+ "\n"
+ "Options:\n"
+ " -m,--mode=<mode> The file mode to set (chmod) on the created\n"
+ " directories. Default: a=rwx & umask.\n"
+ " -p,--parents Create parent directories as needed, no\n"
+ " error if the directory already exists.\n"
+ " -v,--verbose Display a message for each created directory.\n"
+ " -V,--version Display the version and exit\n"
+ " -h,--help Display this help text and exit.\n"
+ , argv[0]);
+ return RTEXITCODE_SUCCESS;
+
+ case 'V':
+ VBoxServiceToolboxShowVersion();
+ return RTEXITCODE_SUCCESS;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (fMakeParentDirs)
+ /** @todo r=bird: If fVerbose is set, we should also show
+ * which directories that get created, parents as well as
+ * omitting existing final dirs. Annoying, but check any
+ * mkdir implementation (try "mkdir -pv asdf/1/2/3/4"
+ * twice). */
+ rc = RTDirCreateFullPath(ValueUnion.psz, fDirMode);
+ else
+ rc = RTDirCreate(ValueUnion.psz, fDirMode);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Could not create directory '%s': %Rra\n",
+ ValueUnion.psz, rc);
+ if (fVerbose)
+ RTMsgInfo("Created directory '%s', mode %#RTfmode\n", ValueUnion.psz, fDirMode);
+ cDirsCreated++;
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ AssertRC(rc);
+
+ if (cDirsCreated == 0)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No directory argument.");
+
+ return RTEXITCODE_SUCCESS;
}
/**
- * Main function for tool "vbox_cat".
+ * Main function for tool "vbox_stat".
*
* @return RTEXITCODE.
* @param argc Number of arguments.
* @param argv Pointer to argument array.
*/
-static RTEXITCODE VBoxServiceToolboxCat(int argc, char **argv)
+static RTEXITCODE VBoxServiceToolboxStat(int argc, char **argv)
{
- static const RTGETOPTDEF s_aOptions[] =
- {
- /* Sorted by short ops. */
- { "--show-all", 'a', RTGETOPT_REQ_NOTHING },
- { "--number-nonblank", 'b', RTGETOPT_REQ_NOTHING },
- { NULL, 'e', RTGETOPT_REQ_NOTHING },
- { NULL, 'E', RTGETOPT_REQ_NOTHING },
- { "--flags", 'f', RTGETOPT_REQ_STRING },
- { "--no-content-indexed", CAT_OPT_NO_CONTENT_INDEXED, RTGETOPT_REQ_NOTHING },
- { "--number", 'n', RTGETOPT_REQ_NOTHING },
- { "--output", 'o', RTGETOPT_REQ_STRING },
- { "--squeeze-blank", 's', RTGETOPT_REQ_NOTHING },
- { NULL, 't', RTGETOPT_REQ_NOTHING },
- { "--show-tabs", 'T', RTGETOPT_REQ_NOTHING },
- { NULL, 'u', RTGETOPT_REQ_NOTHING },
- { "--show-noneprinting", 'v', RTGETOPT_REQ_NOTHING }
- };
-
- int ch;
- RTGETOPTUNION ValueUnion;
- RTGETOPTSTATE GetState;
- RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
-
- int rc = VINF_SUCCESS;
- bool fUsageOK = true;
-
- char szOutput[RTPATH_MAX] = { 0 };
- RTFILE hOutput = NIL_RTFILE;
- uint32_t fFlags = RTFILE_O_CREATE_REPLACE /* Output file flags. */
- | RTFILE_O_WRITE
- | RTFILE_O_DENY_WRITE;
-
- /* Init directory list. */
- RTLISTNODE inputList;
- RTListInit(&inputList);
-
- while ( (ch = RTGetOpt(&GetState, &ValueUnion))
- && RT_SUCCESS(rc))
- {
- /* For options that require an argument, ValueUnion has received the value. */
- switch (ch)
- {
- case 'a':
- case 'b':
- case 'e':
- case 'E':
- case 'n':
- case 's':
- case 't':
- case 'T':
- case 'v':
- RTMsgError("cat: Sorry, option '%s' is not implemented yet!\n",
- ValueUnion.pDef->pszLong);
- rc = VERR_INVALID_PARAMETER;
- break;
-
- case 'h':
- VBoxServiceToolboxShowUsage();
- return RTEXITCODE_SUCCESS;
-
- case 'o':
- if (!RTStrPrintf(szOutput, sizeof(szOutput), ValueUnion.psz))
- rc = VERR_NO_MEMORY;
- break;
-
- case 'u':
- /* Ignored. */
- break;
-
- case 'V':
- VBoxServiceToolboxShowVersion();
- return RTEXITCODE_SUCCESS;
-
- case CAT_OPT_NO_CONTENT_INDEXED:
- fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
- break;
-
- case VINF_GETOPT_NOT_OPTION:
- {
- /* Add file(s) to buffer. This enables processing multiple paths
- * at once.
- *
- * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
- * processing this loop it's safe to immediately exit on syntax errors
- * or showing the help text (see above). */
- rc = VBoxServiceToolboxPathBufAddPathEntry(&inputList, ValueUnion.psz);
- break;
- }
-
- default:
- return RTGetOptPrintError(ch, &ValueUnion);
- }
- }
-
- if (RT_SUCCESS(rc))
- {
- if (strlen(szOutput))
- {
- rc = RTFileOpen(&hOutput, szOutput, fFlags);
- if (RT_FAILURE(rc))
- RTMsgError("cat: Could not create output file '%s'! rc=%Rrc\n",
- szOutput, rc);
- }
-
- if (RT_SUCCESS(rc))
- {
- /* Process each input file. */
- PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
- RTFILE hInput = NIL_RTFILE;
- RTListForEach(&inputList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
- {
- rc = RTFileOpen(&hInput, pNodeIt->pszName,
- RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
- if (RT_SUCCESS(rc))
- {
- rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
- RTFileClose(hInput);
- }
- else
- {
- PCRTSTATUSMSG pMsg = RTErrGet(rc);
- if (pMsg)
- RTMsgError("cat: Could not open input file '%s': %s\n",
- pNodeIt->pszName, pMsg->pszMsgFull);
- else
- RTMsgError("cat: Could not open input file '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
- }
-
- if (RT_FAILURE(rc))
- break;
- }
-
- /* If not input files were defined, process stdin. */
- if (RTListNodeIsFirst(&inputList, &inputList))
- rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
- }
- }
-
- if (hOutput != NIL_RTFILE)
- RTFileClose(hOutput);
- VBoxServiceToolboxPathBufDestroy(&inputList);
-
- return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--file-system", 'f', RTGETOPT_REQ_NOTHING },
+ { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
+ { "--machinereadable", VBOXSERVICETOOLBOXLSOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
+ { "--terse", 't', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv,
+ s_aOptions, RT_ELEMENTS(s_aOptions),
+ 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ int rc = VINF_SUCCESS;
+ bool fVerbose = false;
+ uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_LONG; /* Use long mode by default. */
+
+ /* Init file list. */
+ RTLISTNODE fileList;
+ RTListInit(&fileList);
+
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && RT_SUCCESS(rc))
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ case 'f':
+ case 'L':
+ RTMsgError("Sorry, option '%s' is not implemented yet!\n", ValueUnion.pDef->pszLong);
+ rc = VERR_INVALID_PARAMETER;
+ break;
+
+ case VBOXSERVICETOOLBOXLSOPT_MACHINE_READABLE:
+ fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
+ break;
+
+ case 'v': /** @todo r=bird: There is no verbose option for stat. */
+ fVerbose = true;
+ break;
+
+ case 'h':
+ VBoxServiceToolboxShowUsage();
+ return RTEXITCODE_SUCCESS;
+
+ case 'V':
+ VBoxServiceToolboxShowVersion();
+ return RTEXITCODE_SUCCESS;
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ /* Add file(s) to buffer. This enables processing multiple files
+ * at once.
+ *
+ * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
+ * processing this loop it's safe to immediately exit on syntax errors
+ * or showing the help text (see above). */
+ rc = VBoxServiceToolboxPathBufAddPathEntry(&fileList, ValueUnion.psz);
+ break;
+ }
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
+ VBoxServiceToolboxPrintStrmHeader("vbt_stat", 1 /* Stream version */);
+
+ PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
+ RTListForEach(&fileList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
+ {
+ RTFSOBJINFO objInfo;
+ int rc2 = RTPathQueryInfoEx(pNodeIt->pszName, &objInfo,
+ RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK /* @todo Follow link? */);
+ if (RT_FAILURE(rc2))
+ {
+ RTMsgError("Cannot stat for '%s': No such file or directory\n",
+ pNodeIt->pszName);
+ rc = VERR_FILE_NOT_FOUND;
+ /* Do not break here -- process every element in the list
+ * and keep failing rc. */
+ }
+ else
+ {
+ rc2 = VBoxServiceToolboxPrintFsInfo(pNodeIt->pszName,
+ strlen(pNodeIt->pszName) /* cbName */,
+ fOutputFlags,
+ &objInfo);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
+ }
+ }
+
+ if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
+ VBoxServiceToolboxPrintStrmTermination();
+
+ /* At this point the overall result (success/failure) should be in rc. */
+
+ if (RTListIsEmpty(&fileList))
+ RTMsgError("Missing operand\n");
+ }
+ else if (fVerbose)
+ RTMsgError("Failed with rc=%Rrc\n", rc);
+
+ VBoxServiceToolboxPathBufDestroy(&fileList);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+
+/**
+ * Looks up the handler for the tool give by @a pszTool.
+ *
+ * @returns Pointer to handler function. NULL if not found.
+ * @param pszTool The name of the tool.
+ */
+static PFNHANDLER vboxServiceToolboxLookUpHandler(const char *pszTool)
+{
+ static struct
+ {
+ const char *pszName;
+ RTEXITCODE (*pfnHandler)(int argc, char **argv);
+ }
+ const s_aTools[] =
+ {
+ { "cat", VBoxServiceToolboxCat },
+ { "ls", VBoxServiceToolboxLs },
+ { "mkdir", VBoxServiceToolboxMkDir },
+ { "stat", VBoxServiceToolboxStat },
+ };
+
+ /* Skip optional 'vbox_' prefix. */
+ if ( pszTool[0] == 'v'
+ && pszTool[1] == 'b'
+ && pszTool[2] == 'o'
+ && pszTool[3] == 'x'
+ && pszTool[4] == '_')
+ pszTool += 5;
+
+ /* Do a linear search, since we don't have that much stuff in the table. */
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aTools); i++)
+ if (!strcmp(s_aTools[i].pszName, pszTool))
+ return s_aTools[i].pfnHandler;
+
+ return NULL;
}
@@ -492,22 +1152,38 @@ static RTEXITCODE VBoxServiceToolboxCat(int argc, char **argv)
*/
bool VBoxServiceToolboxMain(int argc, char **argv, RTEXITCODE *prcExit)
{
- if (argc > 0) /* Do we have at least a main command? */
- {
- if ( !strcmp(argv[0], "cat")
- || !strcmp(argv[0], "vbox_cat"))
- {
- *prcExit = VBoxServiceToolboxCat(argc, argv);
- return true;
- }
- if ( !strcmp(argv[0], "mkdir")
- || !strcmp(argv[0], "vbox_mkdir"))
+ /*
+ * Check if the file named in argv[0] is one of the toolbox programs.
+ */
+ AssertReturn(argc > 0, false);
+ const char *pszTool = RTPathFilename(argv[0]);
+ PFNHANDLER pfnHandler = vboxServiceToolboxLookUpHandler(pszTool);
+ if (!pfnHandler)
+ {
+ /*
+ * For debugging and testing purposes we also allow toolbox program access
+ * when the first VBoxService argument is --use-toolbox.
+ */
+ if (argc < 3 || strcmp(argv[1], "--use-toolbox"))
+ return false;
+ argc -= 2;
+ argv += 2;
+ pszTool = argv[0];
+ pfnHandler = vboxServiceToolboxLookUpHandler(pszTool);
+ if (!pfnHandler)
{
- *prcExit = VBoxServiceToolboxMkDir(argc, argv);
- return true;
+ *prcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "Toolbox program '%s' does not exist", pszTool);
+ return true;
}
}
- return false;
+
+ /*
+ * Invoke the handler.
+ */
+ RTMsgSetProgName("VBoxService/%s", pszTool);
+ *prcExit = pfnHandler(argc, argv);
+ return true;
+
}