]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/libwin32/main.cpp
moved fdplugins directory to under bin in the winbacula installer
[bacula/bacula] / bacula / src / win32 / libwin32 / main.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27  */
28
29 /* 
30  * 
31  *  Kern Sibbald, August 2007
32  *
33  *  Version $Id$
34  *
35  * Note, some of the original Bacula Windows startup and service handling code
36  *  was derived from VNC code that was used in apcupsd then ported to 
37  *  Bacula.  However, since then the code has been significantly enhanced 
38  *  and largely rewritten.  
39  *
40  * Evidently due to the nature of Windows startup code and service
41  *  handling code, certain similarities remain. Thanks to the original
42  *  VNC authors.
43  *
44  * This is a generic main routine, which is used by all three
45  *  of the daemons. Each one compiles it with slightly different
46  *  #defines.
47  *
48  */
49
50 #include "bacula.h"
51 #include "win32.h"
52 #include <signal.h>
53 #include <pthread.h>
54
55 #undef  _WIN32_IE
56 #define _WIN32_IE 0x0401
57 #undef  _WIN32_WINNT
58 #define _WIN32_WINNT 0x0501
59 #include <commctrl.h>
60
61 /* Globals */
62 HINSTANCE appInstance;
63 DWORD mainthreadId;
64 bool opt_debug = false;
65 bool have_service_api;
66 DWORD service_thread_id = 0;
67
68
69 #define MAX_COMMAND_ARGS 100
70 static char *command_args[MAX_COMMAND_ARGS] = {LC_APP_NAME, NULL};
71 static int num_command_args = 1;
72 static pid_t main_pid;
73 static pthread_t main_tid;
74
75 const char usage[] = APP_NAME "[/debug] [/service] [/run] [/kill] [/install] [/remove] [/help]\n";
76
77 /*
78  *
79  * Main Windows entry point.
80  *
81  * We parse the command line and either calls the main App
82  *   or starts up the service.
83  */
84 int WINAPI WinMain(HINSTANCE Instance, HINSTANCE /*PrevInstance*/, PSTR CmdLine, 
85                    int /*show*/)
86 {
87    char *cmdLine = CmdLine;
88    char *wordPtr, *tempPtr;
89    int i, quote;
90    OSVERSIONINFO osversioninfo;
91    osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
92
93
94    /* Save the application instance and main thread id */
95    appInstance = Instance;
96    mainthreadId = GetCurrentThreadId();
97
98    if (GetVersionEx(&osversioninfo) && 
99        osversioninfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
100       have_service_api = true;
101    }
102
103    main_pid = getpid();
104    main_tid = pthread_self();
105
106    INITCOMMONCONTROLSEX initCC = {
107       sizeof(INITCOMMONCONTROLSEX), 
108       ICC_STANDARD_CLASSES
109    };
110
111    InitCommonControlsEx(&initCC);
112
113    /*
114     * Funny things happen with the command line if the
115     * execution comes from c:/Program Files/bacula/bacula.exe
116     * We get a command line like: Files/bacula/bacula.exe" options
117     * I.e. someone stops scanning command line on a space, not
118     * realizing that the filename is quoted!!!!!!!!!!
119     * So if first character is not a double quote and
120     * the last character before first space is a double
121     * quote, we throw away the junk.
122     */
123
124    wordPtr = cmdLine;
125    while (*wordPtr && *wordPtr != ' ')
126       wordPtr++;
127    if (wordPtr > cmdLine)      /* backup to char before space */
128       wordPtr--;
129    /* if first character is not a quote and last is, junk it */
130    if (*cmdLine != '"' && *wordPtr == '"') {
131       cmdLine = wordPtr + 1;
132    }
133
134    /*
135     * Build Unix style argc *argv[] for the main "Unix" code
136     *  stripping out any Windows options 
137     */
138
139    /* Don't NULL command_args[0] !!! */
140    for (i=1;i<MAX_COMMAND_ARGS;i++) {
141       command_args[i] = NULL;
142    }
143
144    char *pszArgs = bstrdup(cmdLine);
145    wordPtr = pszArgs;
146    quote = 0;
147    while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
148       wordPtr++;
149    if (*wordPtr == '\"') {
150       quote = 1;
151       wordPtr++;
152    } else if (*wordPtr == '/') {
153       /* Skip Windows options */
154       while (*wordPtr && (*wordPtr != ' ' && *wordPtr != '\t'))
155          wordPtr++;
156       while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
157          wordPtr++;
158    }
159    if (*wordPtr) {
160       while (*wordPtr && num_command_args < MAX_COMMAND_ARGS) {
161          tempPtr = wordPtr;
162          if (quote) {
163             while (*tempPtr && *tempPtr != '\"')
164                tempPtr++;
165             quote = 0;
166          } else {
167             while (*tempPtr && *tempPtr != ' ')
168             tempPtr++;
169          }
170          if (*tempPtr)
171             *(tempPtr++) = '\0';
172          command_args[num_command_args++] = wordPtr;
173          wordPtr = tempPtr;
174          while (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
175             wordPtr++;
176          if (*wordPtr == '\"') {
177             quote = 1;
178             wordPtr++;
179          }
180       }
181    }
182
183    /*
184     * Now process Windows command line options. Most of these options
185     *  are single shot -- i.e. we accept one option, do something and
186     *  terminate.
187     */
188    for (i = 0; i < (int)strlen(cmdLine); i++) {
189       char *p = &cmdLine[i];
190
191       if (*p <= ' ') {
192          continue;                    /* toss junk */
193       }
194
195       if (*p != '/') {
196          break;                       /* syntax error */
197       }
198
199       /* Start as a service? */
200       if (strncasecmp(p, "/service", 8) == 0) {
201          return baculaServiceMain();      /* yes, run as a service */
202       }
203
204       /* Stop any running copy? */
205       if (strncasecmp(p, "/kill", 5) == 0) {
206          return stopRunningBacula();
207       }
208
209       /* Run app as user program? */
210       if (strncasecmp(p, "/run", 4) == 0) {
211          return BaculaAppMain();         /* yes, run as a user program */
212       }
213
214       /* Install Bacula in registry? */
215       if (strncasecmp(p, "/install", 8) == 0) {
216          return installService(p+8);    /* Pass command options */
217       }
218
219       /* Remove Bacula registry entry? */
220       if (strncasecmp(p, "/remove", 7) == 0) {
221          return removeService();
222       }
223
224       /* Set debug mode? -- causes more dialogs to be displayed */
225       if (strncasecmp(p, "/debug", 6) == 0) {
226          opt_debug = true;
227          i += 6;                /* skip /debug */
228          continue;
229       }
230
231       /* Display help? -- displays usage */
232       if (strncasecmp(p, "/help", 5) == 0) {
233          MessageBox(NULL, usage, APP_DESC, MB_OK|MB_ICONINFORMATION);
234          return 0;
235       }
236       
237       MessageBox(NULL, cmdLine, _("Bad Command Line Option"), MB_OK);
238
239       /* Show the usage dialog */
240       MessageBox(NULL, usage, APP_DESC, MB_OK | MB_ICONINFORMATION);
241
242       return 1;
243    }
244    return BaculaAppMain();
245 }
246
247 #ifndef HAVE_TRAY_MONITOR
248 /* Minimalist winproc when don't have tray monitor */
249 LRESULT CALLBACK bacWinProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
250 {
251    switch (iMsg) {
252    case WM_DESTROY:
253       PostQuitMessage(0);
254       return 0;
255    }
256    return DefWindowProc(hwnd, iMsg, wParam, lParam);
257 }
258 #endif
259
260
261 /*
262  * Called as a thread from BaculaAppMain()
263  * Here we handle the Windows messages
264  */
265 void *Main_Msg_Loop(LPVOID lpwThreadParam) 
266 {
267    MSG msg;
268
269    pthread_detach(pthread_self());
270
271    /*
272     * Since we are the only thread with a message loop
273     * mark ourselves as the service thread so that
274     * we can receive all the window events.
275     */
276    service_thread_id = GetCurrentThreadId();
277
278 #ifdef HAVE_TRAY_MONITOR
279    /* Create tray icon & menu if we're running as an app */
280    trayMonitor *monitor = new trayMonitor();
281    if (monitor == NULL) {
282       PostQuitMessage(0);
283    }
284
285 #else
286    /* Create a window to handle Windows messages */
287    WNDCLASSEX baclass;
288
289    baclass.cbSize         = sizeof(baclass);
290    baclass.style          = 0;
291    baclass.lpfnWndProc    = bacWinProc;
292    baclass.cbClsExtra     = 0;
293    baclass.cbWndExtra     = 0;
294    baclass.hInstance      = appInstance;
295    baclass.hIcon          = NULL;
296    baclass.hCursor        = NULL;
297    baclass.hbrBackground  = NULL;
298    baclass.lpszMenuName   = NULL;
299    baclass.lpszClassName  = APP_NAME;
300    baclass.hIconSm        = NULL;
301
302    RegisterClassEx(&baclass);
303
304    if (CreateWindow(APP_NAME, APP_NAME, WS_OVERLAPPEDWINDOW,
305                 CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
306                 NULL, NULL, appInstance, NULL) == NULL) {
307       PostQuitMessage(0);
308    }
309 #endif
310
311    /* Now enter the Windows message handling loop until told to quit! */
312    while (GetMessage(&msg, NULL, 0,0) ) {
313       TranslateMessage(&msg);
314       DispatchMessage(&msg);
315    }
316
317    /* If we get here, we are shutting down */
318
319 #ifdef HAVE_TRAY_MONITOR
320    if (monitor != NULL) {
321       delete monitor;
322    }
323 #endif
324
325    if (have_service_api) {
326       /* Mark that we're no longer running */
327       service_thread_id = 0;
328       /* Tell the service manager that we've stopped. */
329       ReportStatus(SERVICE_STOPPED, service_error, 0);
330    }  
331    /* Tell main "Unix" program to go away */
332    terminate_app(0);
333
334    /* Should not get here */
335    pthread_kill(main_tid, SIGTERM);   /* ask main thread to terminate */
336    sleep(1);
337    kill(main_pid, SIGTERM);           /* kill main thread */
338    _exit(0);
339 }
340  
341
342 /*
343  * This is the main routine for Bacula when running as an application,
344  *  or after the service has started up.
345  */
346 int BaculaAppMain()
347 {
348    pthread_t tid;
349    DWORD dwCharsWritten;
350
351    OSDependentInit();
352
353    /* If no arguments were given then just run */
354    if (p_AttachConsole == NULL || !p_AttachConsole(ATTACH_PARENT_PROCESS)) {
355       if (opt_debug) {
356          AllocConsole();
357       }
358    }
359    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), "\r\n", 2, &dwCharsWritten, NULL);
360
361    /* Start up Volume Shadow Copy (only on FD) */
362    VSSInit();
363
364    /* Startup networking */
365    WSA_Init();
366
367    /* Set this process to be the last application to be shut down. */
368    if (p_SetProcessShutdownParameters) {
369       p_SetProcessShutdownParameters(0x100, 0);
370    }
371
372    /* Create a thread to handle the Windows messages */
373    pthread_create(&tid, NULL,  Main_Msg_Loop, (void *)0);
374
375    /* Call the Unix Bacula daemon */
376    BaculaMain(num_command_args, command_args);
377    PostQuitMessage(0);                /* terminate our main message loop */
378
379    WSACleanup();
380    _exit(0);
381 }
382
383
384 void pause_msg(const char *file, const char *func, int line, const char *msg)
385 {
386    char buf[1000];
387    if (msg) {
388       bsnprintf(buf, sizeof(buf), "%s:%s:%d %s", file, func, line, msg);
389    } else {
390       bsnprintf(buf, sizeof(buf), "%s:%s:%d", file, func, line);
391    }
392    MessageBox(NULL, buf, "Pause", MB_OK);
393 }