VirtualBox

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

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

Additions: Use VBoxGuestLib.h instead of VBoxGuest.h where applicable.

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