VirtualBox

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

Last change on this file since 6356 was 6356, checked in by vboxsync, 17 years ago

Integrated VbglR3Daemonize in GuestR3Lib. Linux vboxclient now uses it. Renamed LINUX_CLIPBOARD to VBOX_X11_CLIPBOARD. clipboard-new has the clipboard implemetation using R3 lib, currently used only by Solaris. Note: this change affects OS2, linux, solaris so someone should check for build breaks on OS2.

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