VirtualBox

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

Last change on this file since 22530 was 22530, checked in by vboxsync, 15 years ago

VBoxService: Bugfixes, enabled release mode verbose logging if specified.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.9 KB
Line 
1/* $Id: VBoxService.cpp 22530 2009-08-27 14:32:01Z vboxsync $ */
2/** @file
3 * VBoxService - Guest Additions Service Skeleton.
4 */
5
6/*
7 * Copyright (C) 2007-2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27/** @todo LOG_GROUP*/
28#ifndef _MSC_VER
29# include <unistd.h>
30#endif
31#include <errno.h>
32
33#include <iprt/thread.h>
34#include <iprt/string.h>
35#include <iprt/stream.h>
36#include <iprt/initterm.h>
37#include <iprt/asm.h>
38#include <iprt/path.h>
39#include <VBox/log.h>
40#include <VBox/VBoxGuestLib.h>
41#include "VBoxServiceInternal.h"
42
43
44/*******************************************************************************
45* Global Variables *
46*******************************************************************************/
47/** The program name (derived from argv[0]). */
48char *g_pszProgName = (char *)"";
49/** The current verbosity level. */
50int g_cVerbosity = 0;
51/** The default service interval (the -i | --interval) option). */
52uint32_t g_DefaultInterval = 0;
53/** Shutdown the main thread. (later, for signals.) */
54bool volatile g_fShutdown;
55
56/**
57 * The details of the services that has been compiled in.
58 */
59static struct
60{
61 /** Pointer to the service descriptor. */
62 PCVBOXSERVICE pDesc;
63 /** The worker thread. NIL_RTTHREAD if it's the main thread. */
64 RTTHREAD Thread;
65 /** Shutdown indicator. */
66 bool volatile fShutdown;
67 /** Indicator set by the service thread exiting. */
68 bool volatile fStopped;
69 /** Whether the service was started or not. */
70 bool fStarted;
71 /** Whether the service is enabled or not. */
72 bool fEnabled;
73} g_aServices[] =
74{
75#ifdef VBOXSERVICE_CONTROL
76 { &g_Control, NIL_RTTHREAD, false, false, false, true },
77#endif
78#ifdef VBOXSERVICE_TIMESYNC
79 { &g_TimeSync, NIL_RTTHREAD, false, false, false, true },
80#endif
81#ifdef VBOXSERVICE_CLIPBOARD
82 { &g_Clipboard, NIL_RTTHREAD, false, false, false, true },
83#endif
84#ifdef VBOXSERVICE_VMINFO
85 { &g_VMInfo, NIL_RTTHREAD, false, false, false, true },
86#endif
87#ifdef VBOXSERVICE_VMEXEC
88 { &g_VMExec, NIL_RTTHREAD, false, false, false, true },
89#endif
90};
91
92
93/**
94 * Displays the program usage message.
95 *
96 * @returns 1.
97 */
98static int VBoxServiceUsage(void)
99{
100 RTPrintf("usage: %s [-f|--foreground] [-v|--verbose] [-i|--interval <seconds>]\n"
101 " [--disable-<service>] [--enable-<service>] [-h|-?|--help]\n", g_pszProgName);
102#ifdef RT_OS_WINDOWS
103 RTPrintf(" [-r|--register] [-u|--unregister]\n");
104#endif
105 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
106 RTPrintf(" %s\n", g_aServices[j].pDesc->pszUsage);
107 RTPrintf("\n"
108 "Options:\n"
109 " -i | --interval The default interval.\n"
110 " -f | --foreground Don't daemonzie the program. For debugging.\n"
111 " -v | --verbose Increment the verbosity level. For debugging.\n"
112 " -h | -? | --help Show this message and exit with status 1.\n"
113 );
114#ifdef RT_OS_WINDOWS
115 RTPrintf(" -r | --register Installs the service.\n"
116 " -u | --unregister Uninstall service.\n");
117#endif
118
119 RTPrintf("\n"
120 "Service specific options:\n");
121 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
122 {
123 RTPrintf(" --enable-%-10s Enables the %s service. (default)\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
124 RTPrintf(" --disable-%-9s Disables the %s service.\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
125 RTPrintf("%s", g_aServices[j].pDesc->pszOptions);
126 }
127 RTPrintf("\n"
128 " Copyright (C) 2009 Sun Microsystems, Inc.\n");
129
130 return 1;
131}
132
133
134/**
135 * Displays a syntax error message.
136 *
137 * @returns 1
138 * @param pszFormat The message text.
139 * @param ... Format arguments.
140 */
141int VBoxServiceSyntax(const char *pszFormat, ...)
142{
143 RTStrmPrintf(g_pStdErr, "%s: syntax error: ", g_pszProgName);
144
145 va_list va;
146 va_start(va, pszFormat);
147 RTStrmPrintfV(g_pStdErr, pszFormat, va);
148 va_end(va);
149
150 return 1;
151}
152
153
154/**
155 * Displays an error message.
156 *
157 * @returns 1
158 * @param pszFormat The message text.
159 * @param ... Format arguments.
160 */
161int VBoxServiceError(const char *pszFormat, ...)
162{
163 RTStrmPrintf(g_pStdErr, "%s: error: ", g_pszProgName);
164
165 va_list va;
166 va_start(va, pszFormat);
167 RTStrmPrintfV(g_pStdErr, pszFormat, va);
168 va_end(va);
169
170 va_start(va, pszFormat);
171 LogRel(("%s: Error: %N", g_pszProgName, pszFormat, &va));
172 va_end(va);
173
174 return 1;
175}
176
177
178/**
179 * Displays a verbose message.
180 *
181 * @returns 1
182 * @param pszFormat The message text.
183 * @param ... Format arguments.
184 */
185void VBoxServiceVerbose(int iLevel, const char *pszFormat, ...)
186{
187 if (iLevel <= g_cVerbosity)
188 {
189 RTStrmPrintf(g_pStdOut, "%s: ", g_pszProgName);
190 va_list va;
191 va_start(va, pszFormat);
192 RTStrmPrintfV(g_pStdOut, pszFormat, va);
193 va_end(va);
194
195 va_start(va, pszFormat);
196 LogRel(("%s: %N", g_pszProgName, pszFormat, &va));
197 va_end(va);
198 }
199}
200
201
202/**
203 * Gets a 32-bit value argument.
204 *
205 * @returns 0 on success, non-zero exit code on error.
206 * @param argc The argument count.
207 * @param argv The argument vector
208 * @param psz Where in *pi to start looking for the value argument.
209 * @param pi Where to find and perhaps update the argument index.
210 * @param pu32 Where to store the 32-bit value.
211 * @param u32Min The minimum value.
212 * @param u32Max The maximum value.
213 */
214int VBoxServiceArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max)
215{
216 if (*psz == ':' || *psz == '=')
217 psz++;
218 if (!*psz)
219 {
220 if (*pi + 1 >= argc)
221 return VBoxServiceSyntax("Missing value for the '%s' argument\n", argv[*pi]);
222 psz = argv[++*pi];
223 }
224
225 char *pszNext;
226 int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
227 if (RT_FAILURE(rc) || *pszNext)
228 return VBoxServiceSyntax("Failed to convert interval '%s' to a number.\n", psz);
229 if (*pu32 < u32Min || *pu32 > u32Max)
230 return VBoxServiceSyntax("The timesync interval of %RU32 secconds is out of range [%RU32..%RU32].\n",
231 *pu32, u32Min, u32Max);
232 return 0;
233}
234
235
236/**
237 * The service thread.
238 *
239 * @returns Whatever the worker function returns.
240 * @param ThreadSelf My thread handle.
241 * @param pvUser The service index.
242 */
243static DECLCALLBACK(int) VBoxServiceThread(RTTHREAD ThreadSelf, void *pvUser)
244{
245 const unsigned i = (uintptr_t)pvUser;
246 int rc = g_aServices[i].pDesc->pfnWorker(&g_aServices[i].fShutdown);
247 ASMAtomicXchgBool(&g_aServices[i].fShutdown, true);
248 RTThreadUserSignal(ThreadSelf);
249 return rc;
250}
251
252
253unsigned VBoxServiceGetStartedServices(void)
254{
255 unsigned iMain = ~0U;
256 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
257 if (g_aServices[j].fEnabled)
258 {
259 iMain = j;
260 break;
261 }
262
263 return iMain; /* Return the index of the main service (must always come last!). */
264}
265
266/**
267 * Starts the service.
268 *
269 * @returns VBox status code, errors are fully bitched.
270 *
271 * @param iMain The index of the service that belongs to the main
272 * thread. Pass ~0U if none does.
273 */
274int VBoxServiceStartServices(unsigned iMain)
275{
276 int rc;
277
278 /*
279 * Initialize the services.
280 */
281 VBoxServiceVerbose(2, "Initializing services ...\n");
282 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
283 if (g_aServices[j].fEnabled)
284 {
285
286 rc = g_aServices[j].pDesc->pfnInit();
287 if (RT_FAILURE(rc))
288 {
289 VBoxServiceError("Service '%s' failed to initialize: %Rrc\n",
290 g_aServices[j].pDesc->pszName, rc);
291 return rc;
292 }
293 }
294
295 /*
296 * Start the service(s).
297 */
298 VBoxServiceVerbose(2, "Starting services ...\n");
299 rc = VINF_SUCCESS;
300 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
301 {
302 if ( !g_aServices[j].fEnabled
303 || j == iMain)
304 continue;
305
306 VBoxServiceVerbose(2, "Starting service '%s' ...\n", g_aServices[j].pDesc->pszName);
307 rc = RTThreadCreate(&g_aServices[j].Thread, VBoxServiceThread, (void *)(uintptr_t)j, 0,
308 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_aServices[j].pDesc->pszName);
309 if (RT_FAILURE(rc))
310 {
311 VBoxServiceError("RTThreadCreate failed, rc=%Rrc\n", rc);
312 break;
313 }
314 g_aServices[j].fStarted = true;
315
316 /* wait for the thread to initialize */
317 RTThreadUserWait(g_aServices[j].Thread, 60 * 1000);
318 if (g_aServices[j].fShutdown)
319 rc = VERR_GENERAL_FAILURE;
320 }
321 if (RT_SUCCESS(rc))
322 {
323 /* The final service runs in the main thread. */
324 VBoxServiceVerbose(1, "Starting '%s' in the main thread\n", g_aServices[iMain].pDesc->pszName);
325 rc = g_aServices[iMain].pDesc->pfnWorker(&g_fShutdown);
326 if (rc != VINF_SUCCESS) /* Only complain if service returned an error. Otherwise the service is a one-timer. */
327 {
328 VBoxServiceError("Service '%s' stopped unexpected; rc=%Rrc\n", g_aServices[iMain].pDesc->pszName, rc);
329 }
330 }
331
332 /* Should never get here. */
333 return rc;
334}
335
336
337/**
338 * Stops and terminates the services.
339 *
340 * This should be called even when VBoxServiceStartServices fails so it can
341 * clean up anything that we succeeded in starting.
342 */
343int VBoxServiceStopServices(void)
344{
345 int rc = VINF_SUCCESS;
346
347 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
348 ASMAtomicXchgBool(&g_aServices[j].fShutdown, true);
349 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
350 if (g_aServices[j].fStarted)
351 g_aServices[j].pDesc->pfnStop();
352 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
353 if (g_aServices[j].fEnabled)
354 {
355 if (g_aServices[j].Thread != NIL_RTTHREAD)
356 {
357 int rc = RTThreadWait(g_aServices[j].Thread, 30*1000, NULL);
358 if (RT_FAILURE(rc))
359 VBoxServiceError("Service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc);
360 }
361 g_aServices[j].pDesc->pfnTerm();
362 }
363
364 VBoxServiceVerbose(2, "Stopping services returned: rc=%Rrc\n", rc);
365 return rc;
366}
367
368
369int main(int argc, char **argv)
370{
371 int rc = VINF_SUCCESS;
372
373 /*
374 * Init globals and such.
375 */
376 RTR3Init();
377 g_pszProgName = RTPathFilename(argv[0]);
378 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
379 {
380 rc = g_aServices[j].pDesc->pfnPreInit();
381 if (RT_FAILURE(rc))
382 return VBoxServiceError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName);
383 }
384
385#ifdef RT_OS_WINDOWS
386 /* Make sure only one instance of VBoxService runs at a time. Create a global mutex for that.
387 Do not use a global namespace ("Global\\") for mutex name here, will blow up NT4 compatibility! */
388 HANDLE hMutexAppRunning = CreateMutex (NULL, FALSE, VBOXSERVICE_NAME);
389 if ( hMutexAppRunning != NULL
390 && GetLastError() == ERROR_ALREADY_EXISTS)
391 {
392 VBoxServiceError("%s is already running! Terminating.", g_pszProgName);
393
394 /* Close the mutex for this application instance. */
395 CloseHandle(hMutexAppRunning);
396 hMutexAppRunning = NULL;
397 }
398#endif
399
400 /*
401 * Parse the arguments.
402 */
403 bool fDaemonize = true;
404 bool fDaemonzied = false;
405 for (int i = 1; i < argc; i++)
406 {
407 const char *psz = argv[i];
408 if (*psz != '-')
409 return VBoxServiceSyntax("Unknown argument '%s'\n", psz);
410 psz++;
411
412 /* translate long argument to short */
413 if (*psz == '-')
414 {
415 psz++;
416 size_t cch = strlen(psz);
417#define MATCHES(strconst) ( cch == sizeof(strconst) - 1 \
418 && !memcmp(psz, strconst, sizeof(strconst) - 1) )
419 if (MATCHES("foreground"))
420 psz = "f";
421 else if (MATCHES("verbose"))
422 psz = "v";
423 else if (MATCHES("help"))
424 psz = "h";
425 else if (MATCHES("interval"))
426 psz = "i";
427#ifdef RT_OS_WINDOWS
428 else if (MATCHES("register"))
429 psz = "r";
430 else if (MATCHES("unregister"))
431 psz = "u";
432#endif
433 else if (MATCHES("daemonized"))
434 {
435 fDaemonzied = true;
436 continue;
437 }
438 else
439 {
440 bool fFound = false;
441
442 if (cch > sizeof("enable-") && !memcmp(psz, "enable-", sizeof("enable-") - 1))
443 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
444 if ((fFound = !RTStrICmp(psz + sizeof("enable-") - 1, g_aServices[j].pDesc->pszName)))
445 g_aServices[j].fEnabled = true;
446
447 if (cch > sizeof("disable-") && !memcmp(psz, "disable-", sizeof("disable-") - 1))
448 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
449 if ((fFound = !RTStrICmp(psz + sizeof("disable-") - 1, g_aServices[j].pDesc->pszName)))
450 g_aServices[j].fEnabled = false;
451
452 if (!fFound)
453 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
454 {
455 rc = g_aServices[j].pDesc->pfnOption(NULL, argc, argv, &i);
456 fFound = rc == 0;
457 if (fFound)
458 break;
459 if (rc != -1)
460 return rc;
461 }
462 if (!fFound)
463 return VBoxServiceSyntax("Unknown option '%s'\n", argv[i]);
464 continue;
465 }
466#undef MATCHES
467 }
468
469 /* handle the string of short options. */
470 do
471 {
472 switch (*psz)
473 {
474 case 'i':
475 rc = VBoxServiceArgUInt32(argc, argv, psz + 1, &i,
476 &g_DefaultInterval, 1, (UINT32_MAX / 1000) - 1);
477 if (rc)
478 return rc;
479 psz = NULL;
480 break;
481
482 case 'f':
483 fDaemonize = false;
484 break;
485
486 case 'v':
487 g_cVerbosity++;
488 break;
489
490 case 'h':
491 case '?':
492 return VBoxServiceUsage();
493
494#ifdef RT_OS_WINDOWS
495 case 'r':
496 return VBoxServiceWinInstall();
497
498 case 'u':
499 return VBoxServiceWinUninstall();
500#endif
501
502 default:
503 {
504 bool fFound = false;
505 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
506 {
507 rc = g_aServices[j].pDesc->pfnOption(&psz, argc, argv, &i);
508 fFound = rc == 0;
509 if (fFound)
510 break;
511 if (rc != -1)
512 return rc;
513 }
514 if (!fFound)
515 return VBoxServiceSyntax("Unknown option '%c' (%s)\n", *psz, argv[i]);
516 break;
517 }
518 }
519 } while (psz && *++psz);
520 }
521 /*
522 * Check that at least one service is enabled.
523 */
524 unsigned iMain = VBoxServiceGetStartedServices();
525 if (iMain == ~0U)
526 return VBoxServiceSyntax("At least one service must be enabled.\n");
527
528 /*
529 * Connect to the kernel part before daemonizing so we can fail
530 * and complain if there is some kind of problem.
531 */
532 VBoxServiceVerbose(2, "Calling VbgR3Init()\n");
533 rc = VbglR3Init();
534 if (RT_FAILURE(rc))
535 return VBoxServiceError("VbglR3Init failed with rc=%Rrc.\n", rc);
536
537 VBoxServiceVerbose(0, "Started. Verbose level = %d\n", g_cVerbosity);
538
539 /*
540 * Daemonize if requested.
541 */
542 if (fDaemonize && !fDaemonzied)
543 {
544#ifdef RT_OS_WINDOWS
545 /** @todo Should do something like VBoxSVC here, OR automatically re-register
546 * the service and start it. Involving VbglR3Daemonize isn't an option
547 * here.
548 *
549 * Also, the idea here, IIRC, was to map the sub service to windows
550 * services. The todo below is for mimicking windows services on
551 * non-windows systems. Not sure if this is doable or not, but in anycase
552 * this code can be moved into -win.
553 *
554 * You should return when StartServiceCtrlDispatcher, btw., not
555 * continue.
556 */
557 VBoxServiceVerbose(2, "Starting service dispatcher ...\n");
558 if (!StartServiceCtrlDispatcher(&g_aServiceTable[0]))
559 return VBoxServiceError("StartServiceCtrlDispatcher: %u\n", GetLastError());
560 /* Service now lives in the control dispatcher registered above. */
561#else
562 VBoxServiceVerbose(1, "Daemonizing...\n");
563 rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */);
564 if (RT_FAILURE(rc))
565 return VBoxServiceError("Daemon failed: %Rrc\n", rc);
566 /* in-child */
567#endif
568 }
569#ifdef RT_OS_WINDOWS
570 else
571 {
572 /* Run the app just like a console one if not daemonized. */
573#endif
574 /** @todo Make the main thread responsive to signal so it can shutdown/restart the threads on non-SIGKILL signals. */
575
576 /*
577 * Start the service, enter the main threads run loop and stop them again when it returns.
578 */
579 rc = VBoxServiceStartServices(iMain);
580 VBoxServiceStopServices();
581#ifdef RT_OS_WINDOWS
582 }
583#endif
584
585#ifdef RT_OS_WINDOWS
586 /*
587 * Release instance mutex if we got it.
588 */
589 if (hMutexAppRunning != NULL)
590 {
591 ::CloseHandle(hMutexAppRunning);
592 hMutexAppRunning = NULL;
593 }
594#endif
595
596 VBoxServiceVerbose(0, "Ended.\n");
597 return RT_SUCCESS(rc) ? 0 : 1;
598}
599
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