VirtualBox

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

Last change on this file since 35060 was 34777, checked in by vboxsync, 14 years ago

Minor help text update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.2 KB
Line 
1/* $Id: VBoxServiceToolBox.cpp 34777 2010-12-07 12:41:49Z vboxsync $ */
2/** @file
3 * VBoxServiceToolBox - Internal (BusyBox-like) toolbox.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <stdio.h>
23
24#include <iprt/assert.h>
25#include <iprt/buildconfig.h>
26#include <iprt/dir.h>
27#include <iprt/file.h>
28#include <iprt/getopt.h>
29#include <iprt/list.h>
30#include <iprt/mem.h>
31#include <iprt/message.h>
32#include <iprt/path.h>
33#include <iprt/string.h>
34#include <iprt/stream.h>
35
36#ifndef RT_OS_WINDOWS
37#include <sys/stat.h>
38#endif
39
40#include <VBox/VBoxGuestLib.h>
41#include <VBox/version.h>
42#include "VBoxServiceInternal.h"
43#include "VBoxServiceUtils.h"
44
45
46#define CAT_OPT_NO_CONTENT_INDEXED 1000
47
48/**
49 * An file/directory entry. Used to cache
50 * file names/paths for later processing.
51 */
52typedef struct VBOXSERVICETOOLBOXPATHENTRY
53{
54 /** Our node. */
55 RTLISTNODE Node;
56 /** Name of the entry. */
57 char *pszName;
58} VBOXSERVICETOOLBOXPATHENTRY, *PVBOXSERVICETOOLBOXPATHENTRY;
59
60
61/**
62 * Displays a help text to stdout.
63 */
64static void VBoxServiceToolboxShowUsage(void)
65{
66 RTPrintf("Toolbox Usage:\n"
67 "cat [FILE] - Concatenate FILE(s), or standard input, to standard output.\n"
68 "\n"
69 /** @todo Document options! */
70 "mkdir [OPTION] DIRECTORY... - Create the DIRECTORY(ies), if they do not already exist.\n"
71 /** @todo Document options! */
72 "\n");
73}
74
75
76/**
77 * Displays the program's version number.
78 */
79static void VBoxServiceToolboxShowVersion(void)
80{
81 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
82}
83
84
85/**
86 * Displays an error message because of syntax error.
87 *
88 * @return VERR_INVALID_PARAMETER
89 * @param pszFormat
90 */
91static int VBoxServiceToolboxErrorSyntax(const char *pszFormat, ...)
92{
93 va_list args;
94
95 va_start(args, pszFormat);
96 RTPrintf("\n"
97 "Syntax error: %N\n", pszFormat, &args);
98 va_end(args);
99 return VERR_INVALID_PARAMETER;
100}
101
102
103/**
104 * Destroys a path buffer list.
105 *
106 * @return IPRT status code.
107 * @param pList Pointer to list to destroy.
108 */
109static void VBoxServiceToolboxPathBufDestroy(PRTLISTNODE pList)
110{
111 AssertPtr(pList);
112 /** @todo use RTListForEachSafe */
113 PVBOXSERVICETOOLBOXPATHENTRY pNode = RTListGetFirst(pList, VBOXSERVICETOOLBOXPATHENTRY, Node);
114 while (pNode)
115 {
116 PVBOXSERVICETOOLBOXPATHENTRY pNext = RTListNodeIsLast(pList, &pNode->Node)
117 ? NULL
118 : RTListNodeGetNext(&pNode->Node, VBOXSERVICETOOLBOXPATHENTRY, Node);
119 RTListNodeRemove(&pNode->Node);
120
121 RTStrFree(pNode->pszName);
122
123 RTMemFree(pNode);
124 pNode = pNext;
125 }
126}
127
128
129/**
130 * Adds a path entry (file/directory/whatever) to a given path buffer list.
131 *
132 * @return IPRT status code.
133 * @param pList Pointer to list to add entry to.
134 * @param pszName Name of entry to add.
135 */
136static int VBoxServiceToolboxPathBufAddPathEntry(PRTLISTNODE pList, const char *pszName)
137{
138 AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
139
140 int rc = VINF_SUCCESS;
141 PVBOXSERVICETOOLBOXPATHENTRY pNode = (PVBOXSERVICETOOLBOXPATHENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXPATHENTRY));
142 if (pNode)
143 {
144 pNode->pszName = RTStrDup(pszName);
145 AssertPtr(pNode->pszName);
146
147 /*rc =*/ RTListAppend(pList, &pNode->Node);
148 }
149 else
150 rc = VERR_NO_MEMORY;
151 return rc;
152}
153
154
155/**
156 * Performs the actual output operation of "vbox_cat".
157 *
158 * @return IPRT status code.
159 * @param hInput Handle of input file (if any) to use;
160 * else stdin will be used.
161 * @param hOutput Handle of output file (if any) to use;
162 * else stdout will be used.
163 */
164static int VBoxServiceToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
165{
166 int rc = VINF_SUCCESS;
167 if (hInput == NIL_RTFILE)
168 {
169 rc = RTFileFromNative(&hInput, RTFILE_NATIVE_STDIN);
170 if (RT_FAILURE(rc))
171 RTMsgError("cat: Could not translate input file to native handle, rc=%Rrc\n", rc);
172 }
173
174 if (hOutput == NIL_RTFILE)
175 {
176 rc = RTFileFromNative(&hOutput, RTFILE_NATIVE_STDOUT);
177 if (RT_FAILURE(rc))
178 RTMsgError("cat: Could not translate output file to native handle, rc=%Rrc\n", rc);
179 }
180
181 if (RT_SUCCESS(rc))
182 {
183 uint8_t abBuf[_64K];
184 size_t cbRead;
185 for (;;)
186 {
187 rc = RTFileRead(hInput, abBuf, sizeof(abBuf), &cbRead);
188 if (RT_SUCCESS(rc) && cbRead)
189 {
190 rc = RTFileWrite(hOutput, abBuf, cbRead, NULL /* Try to write all at once! */);
191 cbRead = 0;
192 }
193 else
194 {
195 if ( cbRead == 0
196 && rc == VERR_BROKEN_PIPE)
197 {
198 rc = VINF_SUCCESS;
199 }
200 break;
201 }
202 }
203 }
204 return rc;
205}
206
207
208/**
209 * Main function for tool "vbox_mkdir".
210 *
211 * @return RTEXITCODE.
212 * @param argc Number of arguments.
213 * @param argv Pointer to argument array.
214 */
215static int VBoxServiceToolboxMkDir(int argc, char **argv)
216{
217 static const RTGETOPTDEF s_aOptions[] =
218 {
219 { "--mode", 'm', RTGETOPT_REQ_STRING },
220 { "--parents", 'p', RTGETOPT_REQ_NOTHING },
221 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
222 };
223
224 int ch;
225 RTGETOPTUNION ValueUnion;
226 RTGETOPTSTATE GetState;
227 RTGetOptInit(&GetState, argc, argv,
228 s_aOptions, RT_ELEMENTS(s_aOptions),
229 1 /* Index of argv to start with. */, RTGETOPTINIT_FLAGS_OPTS_FIRST);
230
231 int rc = VINF_SUCCESS;
232 bool fMakeParentDirs = false;
233 bool fVerbose = false;
234
235 RTFMODE newMode = 0;
236 RTFMODE dirMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO;
237
238 /* Init directory list. */
239 RTLISTNODE dirList;
240 RTListInit(&dirList);
241
242 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
243 && RT_SUCCESS(rc))
244 {
245 /* For options that require an argument, ValueUnion has received the value. */
246 switch (ch)
247 {
248 case 'h':
249 VBoxServiceToolboxShowUsage();
250 return RTEXITCODE_SUCCESS;
251
252 case 'p':
253 fMakeParentDirs = true;
254 break;
255
256 case 'm':
257 rc = RTStrToUInt32Ex(ValueUnion.psz, NULL, 8 /* Base */, &newMode);
258 if (RT_FAILURE(rc)) /* Only octet based values supported right now! */
259 {
260 RTMsgError("mkdir: Mode flag strings not implemented yet! Use octal numbers instead.\n");
261 return RTEXITCODE_SYNTAX;
262 }
263 break;
264
265 case 'v':
266 fVerbose = true;
267 break;
268
269 case 'V':
270 VBoxServiceToolboxShowVersion();
271 return RTEXITCODE_SUCCESS;
272
273 case VINF_GETOPT_NOT_OPTION:
274 {
275 /* Add path(s) to buffer. This enables processing multiple paths
276 * at once.
277 *
278 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
279 * processing this loop it's safe to immediately exit on syntax errors
280 * or showing the help text (see above). */
281 rc = VBoxServiceToolboxPathBufAddPathEntry(&dirList, ValueUnion.psz);
282 break;
283 }
284
285 default:
286 return RTGetOptPrintError(ch, &ValueUnion);
287 }
288 }
289
290 if (RT_SUCCESS(rc))
291 {
292 if (fMakeParentDirs || newMode)
293 {
294#ifndef RT_OS_WINDOWS
295 mode_t umaskMode = umask(0); /* Get current umask. */
296 if (newMode)
297 dirMode = newMode;
298#endif
299 }
300
301 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
302 RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
303 {
304 rc = fMakeParentDirs ?
305 RTDirCreateFullPath(pNodeIt->pszName, dirMode)
306 : RTDirCreate(pNodeIt->pszName, dirMode);
307
308 if (RT_SUCCESS(rc) && fVerbose)
309 RTMsgError("mkdir: Created directory 's', mode %#RTfmode\n", pNodeIt->pszName, dirMode);
310 else if (RT_FAILURE(rc)) /** @todo Add a switch with more helpful error texts! */
311 {
312 PCRTSTATUSMSG pMsg = RTErrGet(rc);
313 if (pMsg)
314 RTMsgError("mkdir: Could not create directory '%s': %s\n",
315 pNodeIt->pszName, pMsg->pszMsgFull);
316 else
317 RTMsgError("mkdir: Could not create directory '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
318 break;
319 }
320 }
321 }
322 else if (fVerbose)
323 RTMsgError("mkdir: Failed with rc=%Rrc\n", rc);
324
325 VBoxServiceToolboxPathBufDestroy(&dirList);
326 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
327}
328
329
330/**
331 * Main function for tool "vbox_cat".
332 *
333 * @return RTEXITCODE.
334 * @param argc Number of arguments.
335 * @param argv Pointer to argument array.
336 */
337static int VBoxServiceToolboxCat(int argc, char **argv)
338{
339 static const RTGETOPTDEF s_aOptions[] =
340 {
341 /* Sorted by short ops. */
342 { "--show-all", 'a', RTGETOPT_REQ_NOTHING },
343 { "--number-nonblank", 'b', RTGETOPT_REQ_NOTHING },
344 { NULL, 'e', RTGETOPT_REQ_NOTHING },
345 { NULL, 'E', RTGETOPT_REQ_NOTHING },
346 { "--flags", 'f', RTGETOPT_REQ_STRING },
347 { "--no-content-indexed", CAT_OPT_NO_CONTENT_INDEXED, RTGETOPT_REQ_NOTHING },
348 { "--number", 'n', RTGETOPT_REQ_NOTHING },
349 { "--output", 'o', RTGETOPT_REQ_STRING },
350 { "--squeeze-blank", 's', RTGETOPT_REQ_NOTHING },
351 { NULL, 't', RTGETOPT_REQ_NOTHING },
352 { "--show-tabs", 'T', RTGETOPT_REQ_NOTHING },
353 { NULL, 'u', RTGETOPT_REQ_NOTHING },
354 { "--show-noneprinting", 'v', RTGETOPT_REQ_NOTHING }
355 };
356
357 int ch;
358 RTGETOPTUNION ValueUnion;
359 RTGETOPTSTATE GetState;
360 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
361
362 int rc = VINF_SUCCESS;
363 bool fUsageOK = true;
364
365 char szOutput[RTPATH_MAX] = { 0 };
366 RTFILE hOutput = NIL_RTFILE;
367 uint32_t fFlags = RTFILE_O_CREATE_REPLACE /* Output file flags. */
368 | RTFILE_O_WRITE
369 | RTFILE_O_DENY_WRITE;
370
371 /* Init directory list. */
372 RTLISTNODE inputList;
373 RTListInit(&inputList);
374
375 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
376 && RT_SUCCESS(rc))
377 {
378 /* For options that require an argument, ValueUnion has received the value. */
379 switch (ch)
380 {
381 case 'a':
382 case 'b':
383 case 'e':
384 case 'E':
385 case 'n':
386 case 's':
387 case 't':
388 case 'T':
389 case 'v':
390 RTMsgError("cat: Sorry, option '%s' is not implemented yet!\n",
391 ValueUnion.pDef->pszLong);
392 rc = VERR_INVALID_PARAMETER;
393 break;
394
395 case 'h':
396 VBoxServiceToolboxShowUsage();
397 return RTEXITCODE_SUCCESS;
398
399 case 'o':
400 if (!RTStrPrintf(szOutput, sizeof(szOutput), ValueUnion.psz))
401 rc = VERR_NO_MEMORY;
402 break;
403
404 case 'u':
405 /* Ignored. */
406 break;
407
408 case 'V':
409 VBoxServiceToolboxShowVersion();
410 return RTEXITCODE_SUCCESS;
411
412 case CAT_OPT_NO_CONTENT_INDEXED:
413 fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
414 break;
415
416 case VINF_GETOPT_NOT_OPTION:
417 {
418 /* Add file(s) to buffer. This enables processing multiple paths
419 * at once.
420 *
421 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
422 * processing this loop it's safe to immediately exit on syntax errors
423 * or showing the help text (see above). */
424 rc = VBoxServiceToolboxPathBufAddPathEntry(&inputList, ValueUnion.psz);
425 break;
426 }
427
428 default:
429 return RTGetOptPrintError(ch, &ValueUnion);
430 }
431 }
432
433 if (RT_SUCCESS(rc))
434 {
435 if (strlen(szOutput))
436 {
437 rc = RTFileOpen(&hOutput, szOutput, fFlags);
438 if (RT_FAILURE(rc))
439 RTMsgError("cat: Could not create output file '%s'! rc=%Rrc\n",
440 szOutput, rc);
441 }
442
443 if (RT_SUCCESS(rc))
444 {
445 /* Process each input file. */
446 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
447 RTFILE hInput = NIL_RTFILE;
448 RTListForEach(&inputList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
449 {
450 rc = RTFileOpen(&hInput, pNodeIt->pszName,
451 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
452 if (RT_SUCCESS(rc))
453 {
454 rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
455 RTFileClose(hInput);
456 }
457 else
458 {
459 PCRTSTATUSMSG pMsg = RTErrGet(rc);
460 if (pMsg)
461 RTMsgError("cat: Could not open input file '%s': %s\n",
462 pNodeIt->pszName, pMsg->pszMsgFull);
463 else
464 RTMsgError("cat: Could not open input file '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
465 }
466
467 if (RT_FAILURE(rc))
468 break;
469 }
470
471 /* If not input files were defined, process stdin. */
472 if (RTListNodeIsFirst(&inputList, &inputList))
473 rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
474 }
475 }
476
477 if (hOutput != NIL_RTFILE)
478 RTFileClose(hOutput);
479 VBoxServiceToolboxPathBufDestroy(&inputList);
480
481 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
482}
483
484
485/**
486 * Entry point for internal toolbox.
487 *
488 * @return True if an internal tool was handled, false if not.
489 * @param argc Number of arguments.
490 * @param argv Pointer to argument array.
491 * @param piExitCode Pointer to receive exit code when internal command
492 * was handled.
493 */
494bool VBoxServiceToolboxMain(int argc, char **argv, int *piExitCode)
495{
496 if (argc > 0) /* Do we have at least a main command? */
497 {
498 if ( !strcmp(argv[0], "cat")
499 || !strcmp(argv[0], "vbox_cat"))
500 {
501 *piExitCode = VBoxServiceToolboxCat(argc, argv);
502 return true;
503 }
504 else if ( !strcmp(argv[0], "mkdir")
505 || !strcmp(argv[0], "vbox_mkdir"))
506 {
507 *piExitCode = VBoxServiceToolboxMkDir(argc, argv);
508 return true;
509 }
510 }
511 return false;
512}
513
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