]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/filed/winmain.cpp
Update copyright + fix a few Win32 warnings
[bacula/bacula] / bacula / src / win32 / filed / winmain.cpp
1 /*
2    This file is patterned after the VNC Win32 code by ATT
3 */
4 /*
5    Bacula® - The Network Backup Solution
6
7    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
8
9    The main author of Bacula is Kern Sibbald, with contributions from
10    many others, a complete list can be found in the file AUTHORS.
11    This program is Free Software; you can redistribute it and/or
12    modify it under the terms of version two of the GNU General Public
13    License as published by the Free Software Foundation plus additions
14    that are listed in the file LICENSE.
15
16    This program is distributed in the hope that it will be useful, but
17    WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19    General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24    02110-1301, USA.
25
26    Bacula® is a registered trademark of John Walker.
27    The licensor of Bacula is the Free Software Foundation Europe
28    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
29    Switzerland, email:ftf@fsfeurope.org.
30 */
31
32 #include <unistd.h>
33 #include <ctype.h>
34
35 #include "bacula.h"
36 #include "winbacula.h"
37 #include "wintray.h"
38 #include "winservice.h"
39 #include <signal.h>
40 #include <pthread.h>
41
42 #undef  _WIN32_IE
43 #define _WIN32_IE 0x0401
44 #undef  _WIN32_WINNT
45 #define _WIN32_WINNT 0x0501
46 #include <commctrl.h>
47
48 extern int BaculaMain(int argc, char *argv[]);
49 extern void terminate_filed(int sig);
50 extern DWORD g_error;
51 extern BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
52 extern void d_msg(const char *, int, int, const char *, ...);
53 extern void VSSInit();
54
55 /* Globals */
56 HINSTANCE       hAppInstance;
57 const char      *szAppName = "Bacula-fd";
58 DWORD           mainthreadId;
59 bool            opt_debug = false;
60
61 /* Imported variables */
62 extern DWORD    g_servicethread;
63
64 #define MAX_COMMAND_ARGS 100
65 static char *command_args[MAX_COMMAND_ARGS] = {"bacula-fd", NULL};
66 static int num_command_args = 1;
67 static pid_t main_pid;
68 static pthread_t main_tid;
69
70 /*
71  * WinMain parses the command line and either calls the main App
72  * routine or, under NT, the main service routine.
73  */
74 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
75                    PSTR CmdLine, int iCmdShow)
76 {
77    char *szCmdLine = CmdLine;
78    char *wordPtr, *tempPtr;
79    int i, quote;
80
81    /* Save the application instance and main thread id */
82    hAppInstance = hInstance;
83    mainthreadId = GetCurrentThreadId();
84
85    main_pid = getpid();
86    main_tid = pthread_self();
87
88    INITCOMMONCONTROLSEX    initCC = {
89       sizeof(INITCOMMONCONTROLSEX), 
90       ICC_STANDARD_CLASSES
91    };
92
93    InitCommonControlsEx(&initCC);
94
95    /*
96     * Funny things happen with the command line if the
97     * execution comes from c:/Program Files/bacula/bacula.exe
98     * We get a command line like: Files/bacula/bacula.exe" options
99     * I.e. someone stops scanning command line on a space, not
100     * realizing that the filename is quoted!!!!!!!!!!
101     * So if first character is not a double quote and
102     * the last character before first space is a double
103     * quote, we throw away the junk.
104     */
105
106    wordPtr = szCmdLine;
107    while (*wordPtr && *wordPtr != ' ')
108       wordPtr++;
109    if (wordPtr > szCmdLine)      /* backup to char before space */
110       wordPtr--;
111    /* if first character is not a quote and last is, junk it */
112    if (*szCmdLine != '"' && *wordPtr == '"') {
113       szCmdLine = wordPtr + 1;
114    }
115
116    /* Build Unix style argc *argv[] */      
117
118    /* Don't NULL command_args[0] !!! */
119    for (i=1;i<MAX_COMMAND_ARGS;i++)
120       command_args[i] = NULL;
121
122    char *pszArgs = bstrdup(szCmdLine);
123    wordPtr = pszArgs;
124    quote = 0;
125    while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
126       wordPtr++;
127    if (*wordPtr == '\"') {
128       quote = 1;
129       wordPtr++;
130    } else if (*wordPtr == '/') {
131       /* Skip Windows options */
132       while (*wordPtr && (*wordPtr != ' ' && *wordPtr != '\t'))
133          wordPtr++;
134       while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
135          wordPtr++;
136    }
137    if (*wordPtr) {
138       while (*wordPtr && num_command_args < MAX_COMMAND_ARGS) {
139          tempPtr = wordPtr;
140          if (quote) {
141             while (*tempPtr && *tempPtr != '\"')
142                tempPtr++;
143             quote = 0;
144          } else {
145             while (*tempPtr && *tempPtr != ' ')
146             tempPtr++;
147          }
148          if (*tempPtr)
149             *(tempPtr++) = '\0';
150          command_args[num_command_args++] = wordPtr;
151          wordPtr = tempPtr;
152          while (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
153             wordPtr++;
154          if (*wordPtr == '\"') {
155             quote = 1;
156             wordPtr++;
157          }
158       }
159    }
160
161    /*
162     * Now process Windows command line options
163     */
164    for (i = 0; i < (int)strlen(szCmdLine); i++) {
165       if (szCmdLine[i] <= ' ') {
166          continue;
167       }
168
169       if (szCmdLine[i] != '/') {
170          break;
171       }
172
173       /* Now check for command-line arguments */
174
175       /* /debug - enable debugging facilities such as console message window */
176       if (strnicmp(&szCmdLine[i], BaculaOptDebug, sizeof(BaculaOptDebug) - 1) == 0) {
177          opt_debug = true;
178          i += sizeof(BaculaOptDebug) - 1;
179          continue;
180       }
181
182       /* /service start service */
183       if (strnicmp(&szCmdLine[i], BaculaRunService, sizeof(BaculaRunService) - 1) == 0) {
184          /* Run Bacula as a service */
185          return bacService::BaculaServiceMain();
186       }
187       /* /run  (this is the default if no command line arguments) */
188       if (strnicmp(&szCmdLine[i], BaculaRunAsUserApp, sizeof(BaculaRunAsUserApp) - 1) == 0) {
189          /* Bacula is being run as a user-level program */
190          return BaculaAppMain();
191       }
192       /* /install */
193       if (strnicmp(&szCmdLine[i], BaculaInstallService, sizeof(BaculaInstallService) - 1) == 0) {
194          /* Install Bacula as a service */
195          return bacService::InstallService(&szCmdLine[i + sizeof(BaculaInstallService) - 1]);
196       }
197       /* /remove */
198       if (strnicmp(&szCmdLine[i], BaculaRemoveService, sizeof(BaculaRemoveService) - 1) == 0) {
199          /* Remove the Bacula service */
200          return bacService::RemoveService();
201       }
202
203       /* /about */
204       if (strnicmp(&szCmdLine[i], BaculaShowAbout, sizeof(BaculaShowAbout) - 1) == 0) {
205          /* Show Bacula's about box */
206          return bacService::ShowAboutBox();
207       }
208
209       /* /status */
210       if (strnicmp(&szCmdLine[i], BaculaShowStatus, sizeof(BaculaShowStatus) - 1) == 0) {
211          /* Show Bacula's status box */                             
212          return bacService::ShowStatus();
213       }
214
215       /* /kill */
216       if (strnicmp(&szCmdLine[i], BaculaKillRunningCopy, sizeof(BaculaKillRunningCopy) - 1) == 0) {
217          /* Kill running copy of Bacula */
218          return bacService::KillRunningCopy();
219       }
220
221       /* /help */
222       if (strnicmp(&szCmdLine[i], BaculaShowHelp, sizeof(BaculaShowHelp) - 1) == 0) {
223          MessageBox(NULL, BaculaUsageText, _("Bacula Usage"), MB_OK|MB_ICONINFORMATION);
224          return 0;
225       }
226       
227       MessageBox(NULL, szCmdLine, _("Bad Command Line Options"), MB_OK);
228
229       /* Show the usage dialog */
230       MessageBox(NULL, BaculaUsageText, _("Bacula Usage"), MB_OK | MB_ICONINFORMATION);
231
232       return 1;
233    }
234
235    return BaculaAppMain();
236 }
237
238
239 /*
240  * Called as a thread from BaculaAppMain()
241  * Here we handle the Windows messages
242  */
243 //DWORD WINAPI Main_Msg_Loop(LPVOID lpwThreadParam)
244 void *Main_Msg_Loop(LPVOID lpwThreadParam) 
245 {
246    DWORD old_servicethread = g_servicethread;
247
248    pthread_detach(pthread_self());
249
250    /* Since we are the only thread with a message loop
251     * mark ourselves as the service thread so that
252     * we can receive all the window events.
253     */
254    g_servicethread = GetCurrentThreadId();
255
256    /* Create tray icon & menu if we're running as an app */
257    bacMenu *menu = new bacMenu();
258    if (menu == NULL) {
259 //    log_error_message("Could not create sys tray menu");
260       PostQuitMessage(0);
261    }
262
263    /* Now enter the Windows message handling loop until told to quit! */
264    MSG msg;
265    while (GetMessage(&msg, NULL, 0,0) ) {
266       TranslateMessage(&msg);
267       DispatchMessage(&msg);
268    }
269
270    if (menu != NULL) {
271       delete menu;
272    }
273
274    if (old_servicethread != 0) { /* started as NT service */
275       /* Mark that we're no longer running */
276       g_servicethread = 0;
277
278       /* Tell the service manager that we've stopped. */
279       ReportStatus(SERVICE_STOPPED, g_error, 0);
280    }  
281    /* Tell main program to go away */
282    terminate_filed(0);
283
284    /* Should not get here */
285    pthread_kill(main_tid, SIGTERM);   /* ask main thread to terminate */
286    sleep(1);
287    kill(main_pid, SIGTERM);           /* ask main thread to terminate */
288    _exit(0);
289 }
290  
291
292 /*
293  * This is the main routine for Bacula when running as an application
294  * (under Windows 95 or Windows NT)
295  * Under NT, Bacula can also run as a service.  The BaculaServerMain routine,
296  * defined in the bacService header, is used instead when running as a service.
297  */
298 int BaculaAppMain()
299 {
300  /* DWORD dwThreadID; */
301    pthread_t tid;
302    DWORD dwCharsWritten;
303
304    InitWinAPIWrapper();
305
306    /* If no arguments were given then just run */
307    if (p_AttachConsole == NULL || !p_AttachConsole(ATTACH_PARENT_PROCESS)) {
308       if (opt_debug) {
309          AllocConsole();
310       }
311    }
312    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), "\r\n", 2, &dwCharsWritten, NULL);
313
314    VSSInit();
315
316    WSA_Init();
317
318    /* Set this process to be the last application to be shut down. */
319    if (p_SetProcessShutdownParameters) {
320       p_SetProcessShutdownParameters(0x100, 0);
321    }
322
323    HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
324    if (hservwnd != NULL) {
325       /* We don't allow multiple instances! */
326       MessageBox(NULL, _("Another instance of Bacula is already running"), szAppName, MB_OK);
327       _exit(0);
328    }
329
330    /* Create a thread to handle the Windows messages */
331    pthread_create(&tid, NULL,  Main_Msg_Loop, (void *)0);
332
333    /* Call the "real" Bacula */
334    BaculaMain(num_command_args, command_args);
335    PostQuitMessage(0);
336    WSACleanup();
337    _exit(0);
338 }