VirtualBox

source: vbox/trunk/src/VBox/Additions/os2/VBoxService/VBoxService.cpp@ 4071

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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