VBoxServiceToolBox.cpp revision 5ac3a2fbc96993f1cd3ecdabdcd79cc653a6410d
/* $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"
#define CAT_OPT_NO_CONTENT_INDEXED 1000
#define LS_OPT_MACHINE_READABLE 1000
typedef enum VBOXSERVICETOOLBOXLSFLAG
{
VBOXSERVICETOOLBOXLSFLAG_RECURSIVE = 0x00000001,
VBOXSERVICETOOLBOXLSFLAG_LONG = 0x00000002,
VBOXSERVICETOOLBOXLSFLAG_PARSEABLE = 0x00000004,
VBOXSERVICETOOLBOXLSFLAG_SYMLINKS = 0x00000008
* commandos directly from command line, e.g. VBoxService vbox_cat [args] */
#ifdef DEBUG
//# define VBOXSERVICE_TOOLBOX_DEBUG
#endif
/**
*/
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)
{
}
/**
* Displays an error message because of syntax error.
*
* @return VERR_INVALID_PARAMETER
* @param pszFormat
*/
static int VBoxServiceToolboxErrorSyntax(const char *pszFormat, ...)
{
RTPrintf("\n"
return VERR_INVALID_PARAMETER;
}
/**
* 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 (;;)
{
{
cbRead = 0;
}
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;
/* Index of argv to start with. */
#ifdef VBOXSERVICE_TOOLBOX_DEBUG
2,
#else
1,
#endif
0);
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("cat: 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("cat: 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("cat: 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 uFlags Output / handling flags.
* @param pObjInfo Pointer to object information.
*/
{
char cFileType;
switch (fMode & RTFS_TYPE_MASK)
{
default:
cFileType = '?';
break;
}
/** @todo sticy bits++ */
if (!(uFlags & VBOXSERVICETOOLBOXLSFLAG_LONG))
{
{
RTPrintf("ftype=%c%cnode_id=%RU64%cname_len=%RU16%cname=%s%c",
}
else
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.
*/
{
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("ls: 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;
/* Index of argv to start with. */
#ifdef VBOXSERVICE_TOOLBOX_DEBUG
2,
#else
1,
#endif
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 'h':
return RTEXITCODE_SUCCESS;
case 'L': /* Dereference symlinks. */
break;
case 'l': /* Print long format. */
break;
case LS_OPT_MACHINE_READABLE:
break;
case 'R': /* Recursive processing. */
break;
case 'v':
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). */
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_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
}
else
RTMsgError("ls: Unable to query information for '%s', rc=%Rrc\n",
}
else
RTMsgError("ls: Failed opening '%s', rc=%Rrc\n",
}
else
if (RT_FAILURE(rc))
RTMsgError("ls: Failed while enumerating '%s', rc=%Rrc\n",
}
RTPrintf("%c%c%c%c", 0, 0, 0, 0);
}
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;
/* Index of argv to start with. */
#ifdef VBOXSERVICE_TOOLBOX_DEBUG
2,
#else
1,
#endif
int rc = VINF_SUCCESS;
bool fMakeParentDirs = false;
bool fVerbose = false;
/* Init directory list. */
&& RT_SUCCESS(rc))
{
/* For options that require an argument, ValueUnion has received the value. */
switch (ch)
{
case 'h':
return RTEXITCODE_SUCCESS;
case 'p':
fMakeParentDirs = true;
break;
case 'm':
{
RTMsgError("mkdir: Mode flag strings not implemented yet! Use octal numbers instead.\n");
return RTEXITCODE_SYNTAX;
}
break;
case 'v':
fVerbose = true;
break;
case 'V':
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). */
break;
}
default:
}
}
if (RT_SUCCESS(rc))
{
if (fMakeParentDirs || newMode)
{
#ifndef RT_OS_WINDOWS
if (newMode)
#endif
}
{
rc = fMakeParentDirs ?
{
if (pMsg)
RTMsgError("mkdir: Could not create directory '%s': %s\n",
else
break;
}
}
}
else if (fVerbose)
}
/**
* 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;
/* Index of argv to start with. */
#ifdef VBOXSERVICE_TOOLBOX_DEBUG
2,
#else
1,
#endif
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 'h':
return RTEXITCODE_SUCCESS;
case 'f':
case 'L':
RTMsgError("stat: Sorry, option '%s' is not implemented yet!\n",
break;
case 'v':
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). */
break;
}
default:
}
}
if (RT_SUCCESS(rc))
{
{
/* Only check for file existence for now. */
{
/** @todo Do some more work (query size etc.) here later.
* Not needed for now. */
}
else
{
RTMsgError("stat: Cannot stat for '%s': No such file or directory\n",
/* Do not break here -- process every file in the list
* and keep failing rc. */
}
}
if (RTListIsEmpty(&fileList))
RTMsgError("stat: Missing operand\n");
}
else if (fVerbose)
}
/**
* 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.
*/
{
if (argc > 0) /* Do we have at least a main command? */
{
int iCmdIdx = 0;
#ifdef VBOXSERVICE_TOOLBOX_DEBUG
iCmdIdx = 1;
#endif
{
return true;
}
{
return true;
}
{
return true;
}
{
return true;
}
}
return false;
}