VirtualBox

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

Last change on this file since 18648 was 18648, checked in by vboxsync, 16 years ago

Guest Additions/common: Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.8 KB
Line 
1/** $Id: VBoxService.cpp 18648 2009-04-02 16:20:02Z vboxsync $ */
2/** @file
3 * VBoxService - Guest Additions Service Skeleton.
4 */
5
6/*
7 * Copyright (C) 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#ifndef _MSC_VER
28# include <unistd.h>
29#endif
30#include <errno.h>
31
32#include <iprt/thread.h>
33#include <iprt/string.h>
34#include <iprt/stream.h>
35#include <iprt/initterm.h>
36#include <iprt/asm.h>
37#include <iprt/path.h>
38#include <VBox/VBoxGuest.h>
39#include "VBoxServiceInternal.h"
40
41/*******************************************************************************
42* Global Variables *
43*******************************************************************************/
44/** The program name (derived from argv[0]). */
45char *g_pszProgName = "";
46/** The current verbosity level. */
47int g_cVerbosity = 0;
48/** The default service interval (the -i | --interval) option). */
49uint32_t g_DefaultInterval = 0;
50/** Shutdown the main thread. (later, for signals). */
51bool volatile g_fShutdown;
52
53/**
54 * The details of the services that has been compiled in.
55 */
56static struct
57{
58 /** Pointer to the service descriptor. */
59 PCVBOXSERVICE pDesc;
60 /** The worker thread. NIL_RTTHREAD if it's the main thread. */
61 RTTHREAD Thread;
62 /** Shutdown indicator. */
63 bool volatile fShutdown;
64 /** Indicator set by the service thread exiting. */
65 bool volatile fStopped;
66 /** Whether the service was started or not. */
67 bool fStarted;
68 /** Whether the service is enabled or not. */
69 bool fEnabled;
70} g_aServices[] =
71{
72#ifdef VBOXSERVICE_CONTROL
73 { &g_Control, NIL_RTTHREAD, false, false, false, true },
74#endif
75#ifdef VBOXSERVICE_TIMESYNC
76 { &g_TimeSync, NIL_RTTHREAD, false, false, false, true },
77#endif
78#ifdef VBOXSERVICE_CLIPBOARD
79 { &g_Clipboard, NIL_RTTHREAD, false, false, false, true },
80#endif
81};
82
83
84/**
85 * Displays the program usage message.
86 *
87 * @returns 1.
88 */
89static int VBoxServiceUsage(void)
90{
91 RTPrintf("usage: %s [-f|--foreground] [-i|--interval <seconds>]"
92 " [--disable-<service>] [--enable-<service>] [-h|-?|--help] [-v|--verbose]", g_pszProgName);
93
94#if defined(RT_OS_WINDOWS)
95 RTPrintf(" [-r|--register] [-u|--unregister]");
96#endif
97
98 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
99 RTPrintf(" %s\n", g_aServices[j].pDesc->pszUsage);
100
101 RTPrintf("\n"
102 "Options:\n"
103#if !defined(RT_OS_WINDOWS)
104 " -h | -? | --help Show this message and exit with status 1.\n"
105#else
106 " -h | -? | /? | --help Show this message and exit with status 1.\n"
107#endif
108 " -i | --interval The default interval.\n"
109 " -f | --foreground Don't daemonzie the program. For debugging.\n"
110#if defined(RT_OS_WINDOWS)
111 " -r | --register Installs the service.\n"
112 " -u | --unregister Uninstall service.\n"
113#endif
114 " -v | --verbose Increment the verbosity level. For debugging.\n");
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 return 1;
168}
169
170
171/**
172 * Displays a verbose message.
173 *
174 * @returns 1
175 * @param pszFormat The message text.
176 * @param ... Format arguments.
177 */
178void VBoxServiceVerbose(int iLevel, const char *pszFormat, ...)
179{
180 if (iLevel <= g_cVerbosity)
181 {
182 RTStrmPrintf(g_pStdOut, "%s: ", g_pszProgName);
183 va_list va;
184 va_start(va, pszFormat);
185 RTStrmPrintfV(g_pStdOut, pszFormat, va);
186 va_end(va);
187 }
188}
189
190
191/**
192 * Gets a 32-bit value argument.
193 *
194 * @returns 0 on success, non-zero exit code on error.
195 * @param argc The argument count.
196 * @param argv The argument vector
197 * @param psz Where in *pi to start looking for the value argument.
198 * @param pi Where to find and perhaps update the argument index.
199 * @param pu32 Where to store the 32-bit value.
200 * @param u32Min The minimum value.
201 * @param u32Max The maximum value.
202 */
203int VBoxServiceArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max)
204{
205 if (*psz == ':' || *psz == '=')
206 psz++;
207 if (!*psz)
208 {
209 if (*pi + 1 >= argc)
210 return VBoxServiceSyntax("Missing value for the '%s' argument\n", argv[*pi]);
211 psz = argv[++*pi];
212 }
213
214 char *pszNext;
215 int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
216 if (RT_FAILURE(rc) || *pszNext)
217 return VBoxServiceSyntax("Failed to convert interval '%s' to a number.\n", psz);
218 if (*pu32 < u32Min || *pu32 > u32Max)
219 return VBoxServiceSyntax("The timesync interval of %RU32 secconds is out of range [%RU32..%RU32].\n",
220 *pu32, u32Min, u32Max);
221 return 0;
222}
223
224
225/**
226 * The service thread.
227 *
228 * @returns Whatever the worker function returns.
229 * @param ThreadSelf My thread handle.
230 * @param pvUser The service index.
231 */
232static DECLCALLBACK(int) VBoxServiceThread(RTTHREAD ThreadSelf, void *pvUser)
233{
234 const unsigned i = (uintptr_t)pvUser;
235 int rc = g_aServices[i].pDesc->pfnWorker(&g_aServices[i].fShutdown);
236 ASMAtomicXchgBool(&g_aServices[i].fShutdown, true);
237 RTThreadUserSignal(ThreadSelf);
238 return rc;
239}
240
241int main(int argc, char **argv)
242{
243 int rc = 0;
244
245#if defined(RT_OS_WINDOWS)
246 /* Do not use a global namespace ("Global\\") for mutex name here, will blow up NT4 compatibility! */
247 HANDLE hMutexAppRunning = CreateMutex (NULL, FALSE, VBOXSERVICE_NAME);
248 if ( (hMutexAppRunning != NULL)
249 && (GetLastError() == ERROR_ALREADY_EXISTS))
250 {
251 VBoxServiceError("%s is already running! Terminating.", g_pszProgName);
252
253 /* Close the mutex for this application instance. */
254 CloseHandle(hMutexAppRunning);
255 hMutexAppRunning = NULL;
256 }
257#endif
258
259 /*
260 * Init globals and such.
261 */
262 RTR3Init();
263 g_pszProgName = RTPathFilename(argv[0]);
264 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
265 {
266 rc = g_aServices[j].pDesc->pfnPreInit();
267 if (RT_FAILURE(rc))
268 return VBoxServiceError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName);
269 }
270
271 /*
272 * Parse the arguments.
273 */
274 bool fDaemonize = true;
275 bool fDaemonzied = false;
276 for (int i = 1; i < argc; i++)
277 {
278 const char *psz = argv[i];
279 if( (*psz != '-')
280#if defined(RT_OS_WINDOWS)
281 && (*psz != '/')
282#endif
283 )
284 return VBoxServiceSyntax("Unknown argument '%s'\n", psz);
285
286 psz++;
287
288 /* translate long argument to short */
289 if (*psz == '-')
290 {
291 psz++;
292 size_t cch = strlen(psz);
293#define MATCHES(strconst) ( cch == sizeof(strconst) - 1 \
294 && !memcmp(psz, strconst, sizeof(strconst) - 1) )
295 if (MATCHES("foreground"))
296 psz = "f";
297 else if (MATCHES("verbose"))
298 psz = "v";
299 else if (MATCHES("help"))
300 psz = "h";
301 else if (MATCHES("interval"))
302 psz = "i";
303#if defined(RT_OS_WINDOWS)
304 else if (MATCHES("register"))
305 psz = "r";
306 else if (MATCHES("unregister"))
307 psz = "u";
308#endif
309 else if (MATCHES("daemonized"))
310 {
311 fDaemonzied = true;
312 continue;
313 }
314 else
315 {
316 bool fFound = false;
317
318 if (cch > sizeof("enable-") && !memcmp(psz, "enable-", sizeof("enable-") - 1))
319 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
320 if ((fFound = !RTStrICmp(psz + sizeof("enable-") - 1, g_aServices[j].pDesc->pszName)))
321 g_aServices[j].fEnabled = true;
322
323 if (cch > sizeof("disable-") && !memcmp(psz, "disable-", sizeof("disable-") - 1))
324 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
325 if ((fFound = !RTStrICmp(psz + sizeof("disable-") - 1, g_aServices[j].pDesc->pszName)))
326 g_aServices[j].fEnabled = false;
327
328 if (!fFound)
329 for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
330 {
331 rc = g_aServices[j].pDesc->pfnOption(NULL, argc, argv, &i);
332 fFound = rc == 0;
333 if (fFound)
334 break;
335 if (rc != -1)
336 return rc;
337 }
338 if (!fFound)
339 return VBoxServiceSyntax("Unknown option '%s'\n", argv[i]);
340 continue;
341 }
342#undef MATCHES
343 }
344
345 /* handle the string of short options. */
346 do
347 {
348 switch (*psz)
349 {
350 case 'i':
351 rc = VBoxServiceArgUInt32(argc, argv, psz + 1, &i,
352 &g_DefaultInterval, 1, (UINT32_MAX / 1000) - 1);
353 if (rc)
354 return rc;
355 psz = NULL;
356 break;
357
358 case 'f':
359 fDaemonize = false;
360 break;
361
362 case 'v':
363 g_cVerbosity++;
364 break;
365
366#if defined(RT_OS_WINDOWS)
367 case 'r':
368 return VBoxServiceWinInstall();
369
370 case 'u':
371 return VBoxServiceWinUninstall();
372#endif
373
374 case 'h':
375#if defined(RT_OS_WINDOWS)
376 case '?':
377#endif
378 return VBoxServiceUsage();
379
380 default:
381 {
382 bool fFound = false;
383 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
384 {
385 rc = g_aServices[j].pDesc->pfnOption(&psz, argc, argv, &i);
386 fFound = rc == 0;
387 if (fFound)
388 break;
389 if (rc != -1)
390 return rc;
391 }
392 if (!fFound)
393 return VBoxServiceSyntax("Unknown option '%c' (%s)\n", *psz, argv[i]);
394 break;
395 }
396 }
397 } while (psz && *++psz);
398 }
399
400 /*
401 * Check that at least one service is enabled.
402 */
403 unsigned iMain = ~0U;
404 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
405 if (g_aServices[j].fEnabled)
406 {
407 iMain = j;
408 break;
409 }
410 if (iMain == ~0U)
411 return VBoxServiceSyntax("At least one service must be enabled.\n");
412
413 /*
414 * Connect to the kernel part before daemonizing so we can fail
415 * and complain if there is some kind of problem.
416 */
417 VBoxServiceVerbose(2, "Calling VbgR3Init()\n");
418 rc = VbglR3Init();
419 if (RT_FAILURE(rc))
420 return VBoxServiceError("VbglR3Init failed with rc=%Rrc.\n", rc);
421
422 /*
423 * Daemonize if requested.
424 */
425 if (fDaemonize && !fDaemonzied)
426 {
427 VBoxServiceVerbose(1, "Daemonizing...\n");
428#if defined(RT_OS_WINDOWS)
429 /** @todo Replace StartServiceCtrlDispatcher() with
430 VbglR3Daemonize() once this has been ported
431 to Windows later. */
432 StartServiceCtrlDispatcher (gs_serviceTable);
433#else
434 rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */);
435 if (RT_FAILURE(rc))
436 return VBoxServiceError("daemon failed: %Rrc\n", rc);
437 /* in-child */
438#endif
439 }
440
441/** @todo Make the main thread responsive to signal so it can shutdown/restart the threads on non-SIGKILL signals. */
442
443 /*
444 * Initialize the services.
445 */
446 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
447 {
448 rc = g_aServices[j].pDesc->pfnInit();
449 if (RT_FAILURE(rc))
450 return VBoxServiceError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName);
451 }
452
453 /*
454 * Start the service(s).
455 */
456 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
457 {
458 if ( !g_aServices[j].fEnabled
459 || j == iMain)
460 continue;
461
462 rc = RTThreadCreate(&g_aServices[j].Thread, VBoxServiceThread, (void *)(uintptr_t)j, 0,
463 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_aServices[j].pDesc->pszName);
464 if (RT_FAILURE(rc))
465 {
466 VBoxServiceError("RTThreadCreate failed, rc=%Rrc\n", rc);
467 break;
468 }
469 g_aServices[j].fStarted = true;
470
471 /* wait for the thread to initialize */
472 RTThreadUserWait(g_aServices[j].Thread, 60 * 1000);
473 if (g_aServices[j].fShutdown)
474 rc = VERR_GENERAL_FAILURE;
475 }
476 if (RT_SUCCESS(rc))
477 {
478 /* The final service runs in the main thread. */
479 VBoxServiceVerbose(1, "starting '%s' in the main thread\n", g_aServices[iMain].pDesc->pszName);
480 rc = g_aServices[iMain].pDesc->pfnWorker(&g_fShutdown);
481 VBoxServiceError("service '%s' stopped unexpected; rc=%Rrc\n", g_aServices[iMain].pDesc->pszName, rc);
482 }
483
484 /*
485 * Stop and terminate the services.
486 */
487 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
488 ASMAtomicXchgBool(&g_aServices[j].fShutdown, true);
489 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
490 if (g_aServices[j].fStarted)
491 g_aServices[j].pDesc->pfnStop();
492 for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
493 {
494 if (g_aServices[j].Thread != NIL_RTTHREAD)
495 {
496 rc = RTThreadWait(g_aServices[j].Thread, 30*1000, NULL);
497 if (RT_FAILURE(rc))
498 VBoxServiceError("service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc);
499 }
500 g_aServices[j].pDesc->pfnTerm();
501 }
502
503 return 0;
504}
505
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