VBoxServiceToolBox.cpp revision e961f5bfe1727c6816d3dad3805ebe21b6ba1c64
/* $Id$ */
/** @file
* VBoxServiceToolbox - Internal (BusyBox-like) toolbox.
*/
/*
* 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;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <stdio.h>
#include <iprt/buildconfig.h>
#ifndef RT_OS_WINDOWS
#endif
#include <VBox/VBoxGuestLib.h>
#include "VBoxServiceInternal.h"
#include "VBoxServiceUtils.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Options indices for "vbox_cat". */
typedef enum VBOXSERVICETOOLBOXCATOPT
{
/** Options indices for "vbox_ls". */
typedef enum VBOXSERVICETOOLBOXLSOPT
{
/** Options indices for "vbox_stat". */
typedef enum VBOXSERVICETOOLBOXSTATOPT
{
/** Flags for "vbox_ls". */
typedef enum VBOXSERVICETOOLBOXLSFLAG
{
/** Flags for fs object output. */
typedef enum VBOXSERVICETOOLBOXOUTPUTFLAG
{
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** Pointer to a handler function. */
typedef RTEXITCODE (*PFNHANDLER)(int , char **);
/**
*/
typedef struct VBOXSERVICETOOLBOXPATHENTRY
{
/** Our node. */
/** Name of the entry. */
char *pszName;
typedef struct VBOXSERVICETOOLBOXDIRENTRY
{
/** Our node. */
/** The actual entry. */
/**
* Displays a help text to stdout.
*/
static void VBoxServiceToolboxShowUsage(void)
{
RTPrintf("Toolbox Usage:\n"
"cat [FILE] - Concatenate FILE(s), or standard input, to standard output.\n"
"\n"
/** @todo Document options! */
"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");
}
/**
* Displays the program's version number.
*/
static void VBoxServiceToolboxShowVersion(void)
{
}
/**
* Initializes the parseable stream(s).
*
* @return IPRT status code.
*/
static int VBoxServiceToolboxStrmInit(void)
{
/* Set stdout's mode to binary. This is required for outputting all the machine-readable
* data correctly. */
if (RT_FAILURE(rc))
return rc;
}
/**
* Prints a parseable stream header which contains the actual tool
*
* @param pszToolName Name of the tool being used, e.g. "vbt_ls".
* @param uVersion Stream version name. Handy for distinguishing
* different stream versions later.
*/
{
}
/**
* 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.
*
* @return IPRT status code.
* @param pList Pointer to list to destroy.
*/
{
/** @todo use RTListForEachSafe */
while (pNode)
{
? NULL
}
}
/**
*
* @return IPRT status code.
* @param pList Pointer to list to add entry to.
* @param pszName Name of entry to add.
*/
{
int rc = VINF_SUCCESS;
PVBOXSERVICETOOLBOXPATHENTRY pNode = (PVBOXSERVICETOOLBOXPATHENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXPATHENTRY));
if (pNode)
{
}
else
rc = VERR_NO_MEMORY;
return rc;
}
/**
* Performs the actual output operation of "vbox_cat".
*
* @return IPRT status code.
* @param hInput Handle of input file (if any) to use;
* else stdin will be used.
* @param hOutput Handle of output file (if any) to use;
* else stdout will be used.
*/
{
int rc = VINF_SUCCESS;
if (hInput == NIL_RTFILE)
{
if (RT_FAILURE(rc))
}
if (hOutput == NIL_RTFILE)
{
if (RT_FAILURE(rc))
}
if (RT_SUCCESS(rc))
{
for (;;)
{
{
if (RT_FAILURE(rc))
{
break;
}
}
else
{
if (rc == VERR_BROKEN_PIPE)
rc = VINF_SUCCESS;
else if (RT_FAILURE(rc))
break;
}
}
}
return rc;
}
/**
* Main function for tool "vbox_cat".
*
* @return RTEXITCODE.
* @param argc Number of arguments.
* @param argv Pointer to argument array.
*/
{
static const RTGETOPTDEF s_aOptions[] =
{
/* Sorted by short ops. */
};
int ch;
1 /*iFirst*/, 0 /*fFlags*/);
int rc = VINF_SUCCESS;
bool fUsageOK = true;
char szOutput[RTPATH_MAX] = { 0 };
/* Init directory list. */
&& 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",
break;
case 'h':
return RTEXITCODE_SUCCESS;
case 'o':
rc = VERR_NO_MEMORY;
break;
case 'u':
/* Ignored. */
break;
case 'V':
return RTEXITCODE_SUCCESS;
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). */
break;
}
default:
}
}
if (RT_SUCCESS(rc))
{
{
if (RT_FAILURE(rc))
RTMsgError("Could not create output file '%s', rc=%Rrc\n",
}
if (RT_SUCCESS(rc))
{
/* Process each input file. */
{
if (RT_SUCCESS(rc))
{
}
else
{
if (pMsg)
RTMsgError("Could not open input file '%s': %s\n",
else
}
if (RT_FAILURE(rc))
break;
}
/* If not input files were defined, process stdin. */
}
}
if (hOutput != NIL_RTFILE)
}
/**
* 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.
*/
{
char chFileType;
switch (fMode & RTFS_TYPE_MASK)
{
default: chFileType = '?'; break;
}
/** @todo sticy bits++ */
{
{
RTPrintf("ftype=%c%cnode_id=%RU64%cname_len=%RU16%cname=%s%c",
}
else
RTPrintf("%c %#18llx %3d %s\n",
RTPrintf("%c%c", 0, 0);
}
else
{
{
RTPrintf("owner_mask=%c%c%c%c",
RTPrintf("group_mask=%c%c%c%c",
RTPrintf("other_mask=%c%c%c%c",
RTPrintf("dos_mask=%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
char szTimeBirth[256];
char szTimeChange[256];
char szTimeModification[256];
char szTimeAccess[256];
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->cbAllocated, 0,
szTimeBirth, 0,
szTimeChange, 0,
szTimeAccess, 0);
RTPrintf("cname_len=%RU16%cname=%s%c",
/* End of data block. */
RTPrintf("%c%c", 0, 0);
}
else
{
RTPrintf("%c%c%c",
RTPrintf("%c%c%c",
RTPrintf("%c%c%c",
RTPrintf(" %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
RTPrintf(" %d %4d %4d %10lld %10lld %#llx %#llx %#llx %#llx",
}
}
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,
{
else if (uFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE)
if (RT_FAILURE(rc))
{
return rc;
}
if (RT_FAILURE(rc))
{
return rc;
}
/* 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);)
{
if (RT_SUCCESS(rc))
{
PVBOXSERVICETOOLBOXDIRENTRY pNode = (PVBOXSERVICETOOLBOXDIRENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXDIRENTRY));
if (pNode)
{
}
else
rc = VERR_NO_MEMORY;
}
}
if (rc == VERR_NO_MORE_FILES)
rc = VINF_SUCCESS;
if (RT_FAILURE(rc2))
{
RTMsgError("Failed to close dir '%s', rc=%Rrc\n",
if (RT_SUCCESS(rc))
}
if (RT_SUCCESS(rc))
{
{
if (RT_FAILURE(rc))
break;
}
/* If everything went fine we do the second run (if needed) ... */
if ( RT_SUCCESS(rc)
{
/* Process all sub-directories. */
{
switch (fMode & RTFS_TYPE_MASK)
{
case RTFS_TYPE_SYMLINK:
if (!(uFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS))
break;
/* Fall through is intentional. */
case RTFS_TYPE_DIRECTORY:
{
{
/* Skip dot directories. */
continue;
}
char szPath[RTPATH_MAX];
if (RT_SUCCESS(rc))
}
break;
default: /* Ignore the rest. */
break;
}
if (RT_FAILURE(rc))
break;
}
}
}
/* Clean up the mess. */
{
}
return rc;
}
/**
* Main function for tool "vbox_ls".
*
* @return RTEXITCODE.
* @param argc Number of arguments.
* @param argv Pointer to argument array.
*/
{
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
bool fVerbose = false;
/* Init file list. */
&& RT_SUCCESS(rc))
{
/* For options that require an argument, ValueUnion has received the value. */
switch (ch)
{
case 'h':
return RTEXITCODE_SUCCESS;
case 'L': /* Dereference symlinks. */
break;
case 'l': /* Print long format. */
break;
break;
case 'R': /* Recursive processing. */
break;
fVerbose = true;
break;
case 'V':
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). */
/** @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:
}
}
if (RT_SUCCESS(rc))
{
/* If not files given add current directory to list. */
if (RTListIsEmpty(&fileList))
{
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
}
else
}
{
if (RT_FAILURE(rc))
}
{
{
if (RT_FAILURE(rc2))
{
RTMsgError("Cannot access '%s': No such file or directory\n",
/* Do not break here -- process every element in the list
* and keep failing rc. */
}
else
{
&objInfo);
if (RT_FAILURE(rc2))
}
}
else
{
if (RT_FAILURE(rc2))
}
}
}
else if (fVerbose)
}
/**
* Main function for tool "vbox_mkdir".
*
* @return RTEXITCODE.
* @param argc Number of arguments.
* @param argv Pointer to argument array.
*/
{
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
bool fMakeParentDirs = false;
bool fVerbose = false;
int cDirsCreated = 0;
{
/* 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':
return RTMsgErrorExit(RTEXITCODE_SYNTAX,
"Mode flag strings not implemented yet! Use octal numbers instead. (%s)\n",
#ifndef RT_OS_WINDOWS
umask(0); /* RTDirCreate workaround */
#endif
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':
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). */
else
if (RT_FAILURE(rc))
if (fVerbose)
cDirsCreated++;
break;
default:
}
}
if (cDirsCreated == 0)
return RTEXITCODE_SUCCESS;
}
/**
* Main function for tool "vbox_stat".
*
* @return RTEXITCODE.
* @param argc Number of arguments.
* @param argv Pointer to argument array.
*/
{
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
int rc = VINF_SUCCESS;
bool fVerbose = false;
/* Init file list. */
&& RT_SUCCESS(rc))
{
/* For options that require an argument, ValueUnion has received the value. */
switch (ch)
{
case 'f':
case 'L':
break;
break;
case 'v': /** @todo r=bird: There is no verbose option for stat. */
fVerbose = true;
break;
case 'h':
return RTEXITCODE_SUCCESS;
case 'V':
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). */
break;
}
default:
}
}
if (RT_SUCCESS(rc))
{
{
if (RT_FAILURE(rc))
}
{
if (RT_FAILURE(rc2))
{
RTMsgError("Cannot stat for '%s': No such file or directory\n",
/* Do not break here -- process every element in the list
* and keep failing rc. */
}
else
{
&objInfo);
if (RT_FAILURE(rc2))
}
}
if (RTListIsEmpty(&fileList))
RTMsgError("Missing operand\n");
}
else if (fVerbose)
}
/**
* 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 struct
{
const char *pszName;
}
const s_aTools[] =
{
{ "cat", VBoxServiceToolboxCat },
{ "ls", VBoxServiceToolboxLs },
{ "mkdir", VBoxServiceToolboxMkDir },
{ "stat", VBoxServiceToolboxStat },
};
/* Skip optional 'vbox_' prefix. */
if ( pszTool[0] == 'v'
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++)
return s_aTools[i].pfnHandler;
return NULL;
}
/**
* Entry point for internal toolbox.
*
* @return True if an internal tool was handled, false if not.
* @param argc Number of arguments.
* @param argv Pointer to argument array.
* @param prcExit Where to store the exit code when an
* internal toolbox command was handled.
*/
{
/*
* Check if the file named in argv[0] is one of the toolbox programs.
*/
AssertReturn(argc > 0, false);
if (!pfnHandler)
{
/*
* For debugging and testing purposes we also allow toolbox program access
* when the first VBoxService argument is --use-toolbox.
*/
return false;
argc -= 2;
argv += 2;
if (!pfnHandler)
{
return true;
}
}
/*
* Invoke the handler.
*/
return true;
}