2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2018 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Kern Sibbald, August 2007
23 * Note, some of the original Bacula Windows startup and service handling code
24 * was derived from VNC code that was used in apcupsd then ported to
25 * Bacula. However, since then the code has been significantly enhanced
26 * and largely rewritten.
28 * Evidently due to the nature of Windows startup code and service
29 * handling code, certain similarities remain. Thanks to the original
32 * This is a generic main routine, which is used by all three
33 * of the daemons. Each one compiles it with slightly different
38 #define LOCKMGR_COMPLIANT
48 HINSTANCE appInstance;
50 bool opt_debug = false;
51 bool have_service_api;
52 DWORD service_thread_id = 0;
55 bool GetWindowsVersionString(LPTSTR osbuf, int maxsiz);
58 #define MAX_COMMAND_ARGS 100
59 static char *command_args[MAX_COMMAND_ARGS] = {(char *)LC_APP_NAME, NULL};
60 static int num_command_args = 1;
61 static pid_t main_pid;
62 static pthread_t main_tid;
64 const char usage[] = APP_NAME "[/debug] [/service] [/run] [/kill] [/install] [/remove] [/help]\n";
68 * Main Windows entry point.
70 * We parse the command line and either calls the main App
71 * or starts up the service.
73 int WINAPI WinMain(HINSTANCE Instance, HINSTANCE /*PrevInstance*/, PSTR CmdLine,
76 char *cmdLine = CmdLine;
77 char *wordPtr, *tempPtr;
79 OSVERSIONINFO osversioninfo;
80 osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
82 /* Save the application instance and main thread id */
83 appInstance = Instance;
84 mainthreadId = GetCurrentThreadId();
86 if (GetVersionEx(&osversioninfo) &&
87 osversioninfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
88 have_service_api = true;
91 GetWindowsVersionString(win_os, sizeof(win_os));
94 main_tid = pthread_self();
96 INITCOMMONCONTROLSEX initCC = {
97 sizeof(INITCOMMONCONTROLSEX),
101 InitCommonControlsEx(&initCC);
104 * Funny things happen with the command line if the
105 * execution comes from c:/Program Files/bacula/bacula.exe
106 * We get a command line like: Files/bacula/bacula.exe" options
107 * I.e. someone stops scanning command line on a space, not
108 * realizing that the filename is quoted!!!!!!!!!!
109 * So if first character is not a double quote and
110 * the last character before first space is a double
111 * quote, we throw away the junk.
115 while (*wordPtr && *wordPtr != ' ')
117 if (wordPtr > cmdLine) /* backup to char before space */
119 /* if first character is not a quote and last is, junk it */
120 if (*cmdLine != '"' && *wordPtr == '"') {
121 cmdLine = wordPtr + 1;
125 * Build Unix style argc *argv[] for the main "Unix" code
126 * stripping out any Windows options
129 /* Don't NULL command_args[0] !!! */
130 for (i=1;i<MAX_COMMAND_ARGS;i++) {
131 command_args[i] = NULL;
134 char *pszArgs = bstrdup(cmdLine);
137 while (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
139 if (*wordPtr == '\"') {
142 } else if (*wordPtr == '/') {
143 /* Skip Windows options */
144 while (*wordPtr && (*wordPtr != ' ' && *wordPtr != '\t'))
146 while (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
150 while (*wordPtr && num_command_args < MAX_COMMAND_ARGS) {
153 while (*tempPtr && *tempPtr != '\"')
157 while (*tempPtr && *tempPtr != ' ')
162 command_args[num_command_args++] = wordPtr;
164 while (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
166 if (*wordPtr == '\"') {
174 * Now process Windows command line options. Most of these options
175 * are single shot -- i.e. we accept one option, do something and
178 for (i = 0; i < (int)strlen(cmdLine); i++) {
179 char *p = &cmdLine[i];
182 continue; /* toss junk */
186 break; /* syntax error */
189 /* Start as a service? */
190 if (strncasecmp(p, "/service", 8) == 0) {
191 return baculaServiceMain(); /* yes, run as a service */
194 /* Stop any running copy? */
195 if (strncasecmp(p, "/kill", 5) == 0) {
196 return stopRunningBacula();
199 /* Run app as user program? */
200 if (strncasecmp(p, "/run", 4) == 0) {
201 return BaculaAppMain(); /* yes, run as a user program */
204 /* Install Bacula in registry? */
205 if (strncasecmp(p, "/install", 8) == 0) {
206 return installService(p+8); /* Pass command options */
209 /* Remove Bacula registry entry? */
210 if (strncasecmp(p, "/remove", 7) == 0) {
211 return removeService();
214 /* Set debug mode? -- causes more dialogs to be displayed */
215 if (strncasecmp(p, "/debug", 6) == 0) {
217 i += 6; /* skip /debug */
221 /* Display help? -- displays usage */
222 if (strncasecmp(p, "/help", 5) == 0) {
223 MessageBox(NULL, usage, APP_DESC, MB_OK|MB_ICONINFORMATION);
227 MessageBox(NULL, cmdLine, _("Bad Command Line Option"), MB_OK);
229 /* Show the usage dialog */
230 MessageBox(NULL, usage, APP_DESC, MB_OK | MB_ICONINFORMATION);
234 return BaculaAppMain();
237 #ifndef HAVE_TRAY_MONITOR
238 /* Minimalist winproc when don't have tray monitor */
239 LRESULT CALLBACK bacWinProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
246 return DefWindowProc(hwnd, iMsg, wParam, lParam);
252 * Called as a thread from BaculaAppMain()
253 * Here we handle the Windows messages
255 void *Main_Msg_Loop(LPVOID lpwThreadParam)
259 pthread_detach(pthread_self());
262 * Since we are the only thread with a message loop
263 * mark ourselves as the service thread so that
264 * we can receive all the window events.
266 service_thread_id = GetCurrentThreadId();
268 #ifdef HAVE_TRAY_MONITOR
269 /* Create tray icon & menu if we're running as an app */
270 trayMonitor *monitor = new trayMonitor();
271 if (monitor == NULL) {
276 /* Create a window to handle Windows messages */
279 baclass.cbSize = sizeof(baclass);
281 baclass.lpfnWndProc = bacWinProc;
282 baclass.cbClsExtra = 0;
283 baclass.cbWndExtra = 0;
284 baclass.hInstance = appInstance;
285 baclass.hIcon = NULL;
286 baclass.hCursor = NULL;
287 baclass.hbrBackground = NULL;
288 baclass.lpszMenuName = NULL;
289 baclass.lpszClassName = APP_NAME;
290 baclass.hIconSm = NULL;
292 RegisterClassEx(&baclass);
294 if (CreateWindow(APP_NAME, APP_NAME, WS_OVERLAPPEDWINDOW,
295 CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
296 NULL, NULL, appInstance, NULL) == NULL) {
301 /* Now enter the Windows message handling loop until told to quit! */
302 while (GetMessage(&msg, NULL, 0,0) ) {
303 TranslateMessage(&msg);
304 DispatchMessage(&msg);
307 /* If we get here, we are shutting down */
309 #ifdef HAVE_TRAY_MONITOR
310 if (monitor != NULL) {
315 if (have_service_api) {
316 /* Mark that we're no longer running */
317 service_thread_id = 0;
318 /* Tell the service manager that we've stopped. */
319 ReportStatus(SERVICE_STOPPED, service_error, 0);
321 /* Tell main "Unix" program to go away */
324 /* Should not get here */
325 pthread_kill(main_tid, SIGTERM); /* ask main thread to terminate */
327 kill(main_pid, SIGTERM); /* kill main thread */
333 * This is the main routine for Bacula when running as an application,
334 * or after the service has started up.
339 DWORD dwCharsWritten;
343 /* If no arguments were given then just run */
344 if (p_AttachConsole == NULL || !p_AttachConsole(ATTACH_PARENT_PROCESS)) {
349 WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), "\r\n", 2, &dwCharsWritten, NULL);
351 /* Startup networking */
354 /* Set this process to be the last application to be shut down. */
355 if (p_SetProcessShutdownParameters) {
356 p_SetProcessShutdownParameters(0x100, 0);
359 /* Create a thread to handle the Windows messages */
360 pthread_create(&tid, NULL, Main_Msg_Loop, (void *)0);
362 /* Call the Unix Bacula daemon */
363 BaculaMain(num_command_args, command_args);
364 PostQuitMessage(0); /* terminate our main message loop */
371 void pause_msg(const char *file, const char *func, int line, const char *msg)
375 bsnprintf(buf, sizeof(buf), "%s:%s:%d %s", file, func, line, msg);
377 bsnprintf(buf, sizeof(buf), "%s:%s:%d", file, func, line);
379 MessageBox(NULL, buf, "Pause", MB_OK);
385 #ifndef PRODUCT_UNLICENSED
386 #define PRODUCT_UNLICENSED 0xABCDABCD
387 #define PRODUCT_BUSINESS 0x00000006
388 #define PRODUCT_BUSINESS_N 0x00000010
389 #define PRODUCT_CLUSTER_SERVER 0x00000012
390 #define PRODUCT_DATACENTER_SERVER 0x00000008
391 #define PRODUCT_DATACENTER_SERVER_CORE 0x0000000C
392 #define PRODUCT_DATACENTER_SERVER_CORE_V 0x00000027
393 #define PRODUCT_DATACENTER_SERVER_V 0x00000025
394 #define PRODUCT_ENTERPRISE 0x00000004
395 #define PRODUCT_ENTERPRISE_E 0x00000046
396 #define PRODUCT_ENTERPRISE_N 0x0000001B
397 #define PRODUCT_ENTERPRISE_SERVER 0x0000000A
398 #define PRODUCT_ENTERPRISE_SERVER_CORE 0x0000000E
399 #define PRODUCT_ENTERPRISE_SERVER_CORE_V 0x00000029
400 #define PRODUCT_ENTERPRISE_SERVER_IA64 0x0000000F
401 #define PRODUCT_ENTERPRISE_SERVER_V 0x00000026
402 #define PRODUCT_HOME_BASIC 0x00000002
403 #define PRODUCT_HOME_BASIC_E 0x00000043
404 #define PRODUCT_HOME_BASIC_N 0x00000005
405 #define PRODUCT_HOME_PREMIUM 0x00000003
406 #define PRODUCT_HOME_PREMIUM_E 0x00000044
407 #define PRODUCT_HOME_PREMIUM_N 0x0000001A
408 #define PRODUCT_HYPERV 0x0000002A
409 #define PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT 0x0000001E
410 #define PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING 0x00000020
411 #define PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY 0x0000001F
412 #define PRODUCT_PROFESSIONAL 0x00000030
413 #define PRODUCT_PROFESSIONAL_E 0x00000045
414 #define PRODUCT_PROFESSIONAL_N 0x00000031
415 #define PRODUCT_SERVER_FOR_SMALLBUSINESS 0x00000018
416 #define PRODUCT_SERVER_FOR_SMALLBUSINESS_V 0x00000023
417 #define PRODUCT_SERVER_FOUNDATION 0x00000021
418 #define PRODUCT_SMALLBUSINESS_SERVER 0x00000009
419 #define PRODUCT_SOLUTION_EMBEDDEDSERVER 0x00000038
420 #define PRODUCT_STANDARD_SERVER 0x00000007
421 #define PRODUCT_STANDARD_SERVER_CORE 0x0000000D
422 #define PRODUCT_STANDARD_SERVER_CORE_V 0x00000028
423 #define PRODUCT_STANDARD_SERVER_V 0x00000024
424 #define PRODUCT_STARTER 0x0000000B
425 #define PRODUCT_STARTER_E 0x00000042
426 #define PRODUCT_STARTER_N 0x0000002F
427 #define PRODUCT_STORAGE_ENTERPRISE_SERVER 0x00000017
428 #define PRODUCT_STORAGE_EXPRESS_SERVER 0x00000014
429 #define PRODUCT_STORAGE_STANDARD_SERVER 0x00000015
430 #define PRODUCT_STORAGE_WORKGROUP_SERVER 0x00000016
431 #define PRODUCT_UNDEFINED 0x00000000
432 #define PRODUCT_ULTIMATE 0x00000001
433 #define PRODUCT_ULTIMATE_E 0x00000047
434 #define PRODUCT_ULTIMATE_N 0x0000001C
435 #define PRODUCT_WEB_SERVER 0x00000011
436 #define PRODUCT_WEB_SERVER_CORE 0x0000001D
438 #define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM 0x19
439 #define SM_SERVERR2 89
440 #define VER_SERVER_NT 0x80000000
444 #ifndef PRODUCT_PROFESSIONAL
445 #define PRODUCT_PROFESSIONAL 0x00000030
447 #ifndef VER_SUITE_STORAGE_SERVER
448 #define VER_SUITE_STORAGE_SERVER 0x00002000
450 #ifndef VER_SUITE_COMPUTE_SERVER
451 #define VER_SUITE_COMPUTE_SERVER 0x00004000
455 #ifndef VER_SUITE_WH_SERVER
456 #define VER_SUITE_WH_SERVER -1
459 typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
460 typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);
463 * Get Windows version display string
465 bool GetWindowsVersionString(LPTSTR osbuf, int maxsiz)
467 OSVERSIONINFOEX osvi;
471 BOOL bOsVersionInfoEx;
474 memset(&si, 0, sizeof(SYSTEM_INFO));
475 memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
477 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
479 if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
482 // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
484 pGNSI = (PGNSI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
485 "GetNativeSystemInfo");
492 if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion > 4) {
493 bstrncpy(osbuf, TEXT("Microsoft "), maxsiz);
495 // Test for the specific product.
497 if (osvi.dwMajorVersion == 6) {
498 if (osvi.dwMinorVersion == 0) {
499 if (osvi.wProductType == VER_NT_WORKSTATION)
500 bstrncat(osbuf, TEXT("Windows Vista "), maxsiz);
502 bstrncat(osbuf, TEXT("Windows Server 2008 " ), maxsiz);
505 if (osvi.dwMinorVersion == 1) {
506 if (osvi.wProductType == VER_NT_WORKSTATION )
507 bstrncat(osbuf, TEXT("Windows 7 "), maxsiz);
509 bstrncat(osbuf, TEXT("Windows Server 2008 R2 " ), maxsiz);
512 pGPI = (PGPI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
516 pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);
518 dwType = PRODUCT_HOME_BASIC;
522 case PRODUCT_ULTIMATE:
523 bstrncat(osbuf, TEXT("Ultimate Edition" ), maxsiz);
525 case PRODUCT_PROFESSIONAL:
526 bstrncat(osbuf, TEXT("Professional" ), maxsiz);
528 case PRODUCT_HOME_PREMIUM:
529 bstrncat(osbuf, TEXT("Home Premium Edition" ), maxsiz);
531 case PRODUCT_HOME_BASIC:
532 bstrncat(osbuf, TEXT("Home Basic Edition" ), maxsiz);
534 case PRODUCT_ENTERPRISE:
535 bstrncat(osbuf, TEXT("Enterprise Edition" ), maxsiz);
537 case PRODUCT_BUSINESS:
538 bstrncat(osbuf, TEXT("Business Edition" ), maxsiz);
540 case PRODUCT_STARTER:
541 bstrncat(osbuf, TEXT("Starter Edition" ), maxsiz);
543 case PRODUCT_CLUSTER_SERVER:
544 bstrncat(osbuf, TEXT("Cluster Server Edition" ), maxsiz);
546 case PRODUCT_DATACENTER_SERVER:
547 bstrncat(osbuf, TEXT("Datacenter Edition" ), maxsiz);
549 case PRODUCT_DATACENTER_SERVER_CORE:
550 bstrncat(osbuf, TEXT("Datacenter Edition (core installation)" ), maxsiz);
552 case PRODUCT_ENTERPRISE_SERVER:
553 bstrncat(osbuf, TEXT("Enterprise Edition" ), maxsiz);
555 case PRODUCT_ENTERPRISE_SERVER_CORE:
556 bstrncat(osbuf, TEXT("Enterprise Edition (core installation)" ), maxsiz);
558 case PRODUCT_ENTERPRISE_SERVER_IA64:
559 bstrncat(osbuf, TEXT("Enterprise Edition for Itanium-based Systems" ), maxsiz);
561 case PRODUCT_SMALLBUSINESS_SERVER:
562 bstrncat(osbuf, TEXT("Small Business Server" ), maxsiz);
564 case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
565 bstrncat(osbuf, TEXT("Small Business Server Premium Edition" ), maxsiz);
567 case PRODUCT_STANDARD_SERVER:
568 bstrncat(osbuf, TEXT("Standard Edition" ), maxsiz);
570 case PRODUCT_STANDARD_SERVER_CORE:
571 bstrncat(osbuf, TEXT("Standard Edition (core installation)" ), maxsiz);
573 case PRODUCT_WEB_SERVER:
574 bstrncat(osbuf, TEXT("Web Server Edition" ), maxsiz);
579 if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
580 if( GetSystemMetrics(SM_SERVERR2) )
581 bstrncat(osbuf, TEXT( "Windows Server 2003 R2 "), maxsiz);
582 else if (osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER)
583 bstrncat(osbuf, TEXT( "Windows Storage Server 2003"), maxsiz);
584 else if (osvi.wSuiteMask & VER_SUITE_WH_SERVER )
585 bstrncat(osbuf, TEXT( "Windows Home Server"), maxsiz);
586 else if (osvi.wProductType == VER_NT_WORKSTATION &&
587 si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
588 bstrncat(osbuf, TEXT( "Windows XP Professional x64 Edition"), maxsiz);
590 bstrncat(osbuf, TEXT("Windows Server 2003 "), maxsiz);
592 // Test for the server type.
593 if (osvi.wProductType != VER_NT_WORKSTATION) {
594 if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64) {
595 if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
596 bstrncat(osbuf, TEXT( "Datacenter Edition for Itanium-based Systems" ), maxsiz);
597 else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
598 bstrncat(osbuf, TEXT( "Enterprise Edition for Itanium-based Systems" ), maxsiz);
601 else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) {
602 if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
603 bstrncat(osbuf, TEXT( "Datacenter x64 Edition" ), maxsiz);
604 else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
605 bstrncat(osbuf, TEXT( "Enterprise x64 Edition" ), maxsiz);
606 else bstrncat(osbuf, TEXT( "Standard x64 Edition" ), maxsiz);
608 if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER )
609 bstrncat(osbuf, TEXT( "Compute Cluster Edition" ), maxsiz);
610 else if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
611 bstrncat(osbuf, TEXT( "Datacenter Edition" ), maxsiz);
612 else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
613 bstrncat(osbuf, TEXT( "Enterprise Edition" ), maxsiz);
614 else if ( osvi.wSuiteMask & VER_SUITE_BLADE )
615 bstrncat(osbuf, TEXT( "Web Edition" ), maxsiz);
616 else bstrncat(osbuf, TEXT( "Standard Edition" ), maxsiz);
621 if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
622 bstrncat(osbuf, TEXT("Windows XP "), maxsiz);
623 if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
624 bstrncat(osbuf, TEXT( "Home Edition" ), maxsiz);
626 bstrncat(osbuf, TEXT( "Professional" ), maxsiz);
629 if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
630 bstrncat(osbuf, TEXT("Windows 2000 "), maxsiz);
631 if ( osvi.wProductType == VER_NT_WORKSTATION ) {
632 bstrncat(osbuf, TEXT( "Professional" ), maxsiz);
634 if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
635 bstrncat(osbuf, TEXT( "Datacenter Server" ), maxsiz);
636 else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
637 bstrncat(osbuf, TEXT( "Advanced Server" ), maxsiz);
638 else bstrncat(osbuf, TEXT( "Server" ), maxsiz);
642 // Include service pack (if any) and build number.
644 if (_tcslen(osvi.szCSDVersion) > 0) {
645 bstrncat(osbuf, TEXT(" ") , maxsiz);
646 bstrncat(osbuf, osvi.szCSDVersion, maxsiz);
651 snprintf(buf, 80, " (build %d)", (int)osvi.dwBuildNumber);
652 bstrncat(osbuf, buf, maxsiz);
654 if (osvi.dwMajorVersion >= 6) {
655 if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
656 bstrncat(osbuf, TEXT( ", 64-bit" ), maxsiz);
657 else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL )
658 bstrncat(osbuf, TEXT(", 32-bit"), maxsiz);
663 bstrncpy(osbuf, "Unknown Windows version.", maxsiz);