VirtualBox

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

Last change on this file since 37194 was 36754, checked in by vboxsync, 14 years ago

VBoxService/Toolbox: Adjust error check.

  • 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 36754 2011-04-20 14:20:41Z 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 > 0)
189 {
190 rc = RTFileWrite(hOutput, abBuf, cbRead, NULL /* Try to write all at once! */);
191 cbRead = 0;
192 }
193 else
194 {
195 if (rc == VERR_BROKEN_PIPE)
196 rc = VINF_SUCCESS;
197 else if (RT_FAILURE(rc))
198 RTMsgError("cat: Error while reading input, rc=%Rrc\n", rc);
199 break;
200 }
201 }
202 }
203 return rc;
204}
205
206
207/**
208 * Main function for tool "vbox_mkdir".
209 *
210 * @return RTEXITCODE.
211 * @param argc Number of arguments.
212 * @param argv Pointer to argument array.
213 */
214static RTEXITCODE VBoxServiceToolboxMkDir(int argc, char **argv)
215{
216 static const RTGETOPTDEF s_aOptions[] =
217 {
218 { "--mode", 'm', RTGETOPT_REQ_STRING },
219 { "--parents", 'p', RTGETOPT_REQ_NOTHING },
220 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
221 };
222
223 int ch;
224 RTGETOPTUNION ValueUnion;
225 RTGETOPTSTATE GetState;
226 RTGetOptInit(&GetState, argc, argv,
227 s_aOptions, RT_ELEMENTS(s_aOptions),
228 1 /* Index of argv to start with. */, RTGETOPTINIT_FLAGS_OPTS_FIRST);
229
230 int rc = VINF_SUCCESS;
231 bool fMakeParentDirs = false;
232 bool fVerbose = false;
233
234 RTFMODE newMode = 0;
235 RTFMODE dirMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO;
236
237 /* Init directory list. */
238 RTLISTNODE dirList;
239 RTListInit(&dirList);
240
241 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
242 && RT_SUCCESS(rc))
243 {
244 /* For options that require an argument, ValueUnion has received the value. */
245 switch (ch)
246 {
247 case 'h':
248 VBoxServiceToolboxShowUsage();
249 return RTEXITCODE_SUCCESS;
250
251 case 'p':
252 fMakeParentDirs = true;
253 break;
254
255 case 'm':
256 rc = RTStrToUInt32Ex(ValueUnion.psz, NULL, 8 /* Base */, &newMode);
257 if (RT_FAILURE(rc)) /* Only octet based values supported right now! */
258 {
259 RTMsgError("mkdir: Mode flag strings not implemented yet! Use octal numbers instead.\n");
260 return RTEXITCODE_SYNTAX;
261 }
262 break;
263
264 case 'v':
265 fVerbose = true;
266 break;
267
268 case 'V':
269 VBoxServiceToolboxShowVersion();
270 return RTEXITCODE_SUCCESS;
271
272 case VINF_GETOPT_NOT_OPTION:
273 {
274 /* Add path(s) to buffer. This enables processing multiple paths
275 * at once.
276 *
277 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
278 * processing this loop it's safe to immediately exit on syntax errors
279 * or showing the help text (see above). */
280 rc = VBoxServiceToolboxPathBufAddPathEntry(&dirList, ValueUnion.psz);
281 break;
282 }
283
284 default:
285 return RTGetOptPrintError(ch, &ValueUnion);
286 }
287 }
288
289 if (RT_SUCCESS(rc))
290 {
291 if (fMakeParentDirs || newMode)
292 {
293#ifndef RT_OS_WINDOWS
294 mode_t umaskMode = umask(0); /* Get current umask. */
295 if (newMode)
296 dirMode = newMode;
297#endif
298 }
299
300 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
301 RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
302 {
303 rc = fMakeParentDirs ?
304 RTDirCreateFullPath(pNodeIt->pszName, dirMode)
305 : RTDirCreate(pNodeIt->pszName, dirMode);
306
307 if (RT_SUCCESS(rc) && fVerbose)
308 RTMsgError("mkdir: Created directory 's', mode %#RTfmode\n", pNodeIt->pszName, dirMode);
309 else if (RT_FAILURE(rc)) /** @todo Add a switch with more helpful error texts! */
310 {
311 PCRTSTATUSMSG pMsg = RTErrGet(rc);
312 if (pMsg)
313 RTMsgError("mkdir: Could not create directory '%s': %s\n",
314 pNodeIt->pszName, pMsg->pszMsgFull);
315 else
316 RTMsgError("mkdir: Could not create directory '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
317 break;
318 }
319 }
320 }
321 else if (fVerbose)
322 RTMsgError("mkdir: Failed with rc=%Rrc\n", rc);
323
324 VBoxServiceToolboxPathBufDestroy(&dirList);
325 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
326}
327
328
329/**
330 * Main function for tool "vbox_cat".
331 *
332 * @return RTEXITCODE.
333 * @param argc Number of arguments.
334 * @param argv Pointer to argument array.
335 */
336static RTEXITCODE VBoxServiceToolboxCat(int argc, char **argv)
337{
338 static const RTGETOPTDEF s_aOptions[] =
339 {
340 /* Sorted by short ops. */
341 { "--show-all", 'a', RTGETOPT_REQ_NOTHING },
342 { "--number-nonblank", 'b', RTGETOPT_REQ_NOTHING },
343 { NULL, 'e', RTGETOPT_REQ_NOTHING },
344 { NULL, 'E', RTGETOPT_REQ_NOTHING },
345 { "--flags", 'f', RTGETOPT_REQ_STRING },
346 { "--no-content-indexed", CAT_OPT_NO_CONTENT_INDEXED, RTGETOPT_REQ_NOTHING },
347 { "--number", 'n', RTGETOPT_REQ_NOTHING },
348 { "--output", 'o', RTGETOPT_REQ_STRING },
349 { "--squeeze-blank", 's', RTGETOPT_REQ_NOTHING },
350 { NULL, 't', RTGETOPT_REQ_NOTHING },
351 { "--show-tabs", 'T', RTGETOPT_REQ_NOTHING },
352 { NULL, 'u', RTGETOPT_REQ_NOTHING },
353 { "--show-noneprinting", 'v', RTGETOPT_REQ_NOTHING }
354 };
355
356 int ch;
357 RTGETOPTUNION ValueUnion;
358 RTGETOPTSTATE GetState;
359 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
360
361 int rc = VINF_SUCCESS;
362 bool fUsageOK = true;
363
364 char szOutput[RTPATH_MAX] = { 0 };
365 RTFILE hOutput = NIL_RTFILE;
366 uint32_t fFlags = RTFILE_O_CREATE_REPLACE /* Output file flags. */
367 | RTFILE_O_WRITE
368 | RTFILE_O_DENY_WRITE;
369
370 /* Init directory list. */
371 RTLISTNODE inputList;
372 RTListInit(&inputList);
373
374 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
375 && RT_SUCCESS(rc))
376 {
377 /* For options that require an argument, ValueUnion has received the value. */
378 switch (ch)
379 {
380 case 'a':
381 case 'b':
382 case 'e':
383 case 'E':
384 case 'n':
385 case 's':
386 case 't':
387 case 'T':
388 case 'v':
389 RTMsgError("cat: Sorry, option '%s' is not implemented yet!\n",
390 ValueUnion.pDef->pszLong);
391 rc = VERR_INVALID_PARAMETER;
392 break;
393
394 case 'h':
395 VBoxServiceToolboxShowUsage();
396 return RTEXITCODE_SUCCESS;
397
398 case 'o':
399 if (!RTStrPrintf(szOutput, sizeof(szOutput), ValueUnion.psz))
400 rc = VERR_NO_MEMORY;
401 break;
402
403 case 'u':
404 /* Ignored. */
405 break;
406
407 case 'V':
408 VBoxServiceToolboxShowVersion();
409 return RTEXITCODE_SUCCESS;
410
411 case CAT_OPT_NO_CONTENT_INDEXED:
412 fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
413 break;
414
415 case VINF_GETOPT_NOT_OPTION:
416 {
417 /* Add file(s) to buffer. This enables processing multiple paths
418 * at once.
419 *
420 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
421 * processing this loop it's safe to immediately exit on syntax errors
422 * or showing the help text (see above). */
423 rc = VBoxServiceToolboxPathBufAddPathEntry(&inputList, ValueUnion.psz);
424 break;
425 }
426
427 default:
428 return RTGetOptPrintError(ch, &ValueUnion);
429 }
430 }
431
432 if (RT_SUCCESS(rc))
433 {
434 if (strlen(szOutput))
435 {
436 rc = RTFileOpen(&hOutput, szOutput, fFlags);
437 if (RT_FAILURE(rc))
438 RTMsgError("cat: Could not create output file '%s'! rc=%Rrc\n",
439 szOutput, rc);
440 }
441
442 if (RT_SUCCESS(rc))
443 {
444 /* Process each input file. */
445 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
446 RTFILE hInput = NIL_RTFILE;
447 RTListForEach(&inputList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
448 {
449 rc = RTFileOpen(&hInput, pNodeIt->pszName,
450 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
451 if (RT_SUCCESS(rc))
452 {
453 rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
454 RTFileClose(hInput);
455 }
456 else
457 {
458 PCRTSTATUSMSG pMsg = RTErrGet(rc);
459 if (pMsg)
460 RTMsgError("cat: Could not open input file '%s': %s\n",
461 pNodeIt->pszName, pMsg->pszMsgFull);
462 else
463 RTMsgError("cat: Could not open input file '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
464 }
465
466 if (RT_FAILURE(rc))
467 break;
468 }
469
470 /* If not input files were defined, process stdin. */
471 if (RTListNodeIsFirst(&inputList, &inputList))
472 rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
473 }
474 }
475
476 if (hOutput != NIL_RTFILE)
477 RTFileClose(hOutput);
478 VBoxServiceToolboxPathBufDestroy(&inputList);
479
480 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
481}
482
483
484/**
485 * Entry point for internal toolbox.
486 *
487 * @return True if an internal tool was handled, false if not.
488 * @param argc Number of arguments.
489 * @param argv Pointer to argument array.
490 * @param prcExit Where to store the exit code when an
491 * internal toolbox command was handled.
492 */
493bool VBoxServiceToolboxMain(int argc, char **argv, RTEXITCODE *prcExit)
494{
495 if (argc > 0) /* Do we have at least a main command? */
496 {
497 if ( !strcmp(argv[0], "cat")
498 || !strcmp(argv[0], "vbox_cat"))
499 {
500 *prcExit = VBoxServiceToolboxCat(argc, argv);
501 return true;
502 }
503
504 if ( !strcmp(argv[0], "mkdir")
505 || !strcmp(argv[0], "vbox_mkdir"))
506 {
507 *prcExit = VBoxServiceToolboxMkDir(argc, argv);
508 return true;
509 }
510 }
511 return false;
512}
513
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette