]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/win32/winmain.cpp
First cut 1.27 see kes23Oct02
[bacula/bacula] / bacula / src / filed / win32 / winmain.cpp
1 /*
2    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 2 of
7    the License, or (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public
15    License along with this program; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
17    MA 02111-1307, USA.
18
19    This file is patterned after the VNC Win32 code by ATT
20   
21    Copyright (2000) Kern E. Sibbald
22 */
23
24 #include <unistd.h>
25 #include <lmcons.h>
26 #include <ctype.h>
27 #include "winbacula.h"
28 #include "wintray.h"
29 #include "winservice.h"
30 #include <signal.h>
31
32 extern int BaculaMain(int argc, char **argv);
33 extern int terminate_filed(int sig);
34 extern DWORD g_error;
35 extern BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
36
37
38 HINSTANCE       hAppInstance;
39 const char      *szAppName = "Bacula";
40 DWORD           mainthreadId;
41
42 /* Imported variables */
43 extern DWORD    g_servicethread;
44
45 #define MAX_COMMAND_ARGS 100
46 static char *command_args[MAX_COMMAND_ARGS] = {"bacula-fd", NULL};
47 static int num_command_args = 1;
48 static pid_t main_pid;
49
50 /*
51  * WinMain parses the command line and either calls the main App
52  * routine or, under NT, the main service routine.
53  */
54 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
55                    PSTR CmdLine, int iCmdShow)
56 {
57    char *szCmdLine = CmdLine;
58    char *wordPtr,*tempPtr;
59    int i,quote;
60
61    /* Save the application instance and main thread id */
62    hAppInstance = hInstance;
63    mainthreadId = GetCurrentThreadId();
64
65    main_pid = getpid();
66
67    /*
68     * Funny things happen with the command line if the
69     * execution comes from c:/Program Files/bacula/bacula.exe
70     * We get a command line like: Files/bacula/bacula.exe" options
71     * I.e. someone stops scanning command line on a space, not
72     * realizing that the filename is quoted!!!!!!!!!!
73     * So if first character is not a double quote and
74     * the last character before first space is a double
75     * quote, we throw away the junk.
76     */
77    wordPtr = szCmdLine;
78    while (*wordPtr && *wordPtr != ' ')
79       wordPtr++;
80    if (wordPtr > szCmdLine)      /* backup to char before space */
81       wordPtr--;
82    /* if first character is not a quote and last is, junk it */
83    if (*szCmdLine != '"' && *wordPtr == '"')
84       szCmdLine = wordPtr + 1;
85    //      MessageBox(NULL, szCmdLine, "Cmdline", MB_OK);
86
87    /* Build Unix style argc *argv[] */      
88
89    /* Don't NULL command_args[0] !!! */
90    for (i=1;i<MAX_COMMAND_ARGS;i++)
91       command_args[i] = NULL;
92
93    wordPtr = szCmdLine;
94    quote = 0;
95    while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
96       wordPtr++;
97    if (*wordPtr == '\"') {
98       quote = 1;
99       wordPtr++;
100    } else if (*wordPtr == '/') {
101       /* Skip Windows options */
102       while (*wordPtr && (*wordPtr != ' ' && *wordPtr != '\t'))
103          wordPtr++;
104       while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
105          wordPtr++;
106    }
107    if (*wordPtr) {
108       while (*wordPtr && num_command_args < MAX_COMMAND_ARGS) {
109          tempPtr = wordPtr;
110          if (quote) {
111             while (*tempPtr && *tempPtr != '\"')
112             tempPtr++;
113             quote = 0;
114          } else {
115             while (*tempPtr && *tempPtr != ' ')
116             tempPtr++;
117          }
118          if (*tempPtr)
119             *(tempPtr++) = '\0';
120          command_args[num_command_args++] = wordPtr;
121          wordPtr = tempPtr;
122          while (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
123             wordPtr++;
124          if (*wordPtr == '\"') {
125             quote = 1;
126             wordPtr++;
127          }
128       }
129    }
130
131    /*
132     * Now process Windows command line options
133     *   as defined by ATT
134     *
135     * Make the command-line lowercase and parse it
136     */
137    for (i = 0; i < (int)strlen(szCmdLine); i++) {
138       szCmdLine[i] = tolower(szCmdLine[i]);
139    }
140
141    BOOL argfound = FALSE;
142    for (i = 0; i < (int)strlen(szCmdLine); i++) {
143       if (szCmdLine[i] <= ' ')
144               continue;
145
146       if (szCmdLine[i] == '-') {
147          while (szCmdLine[i] && szCmdLine[i] != ' ')
148             i++;
149          continue;
150       }
151
152       argfound = TRUE;
153
154       // Now check for command-line arguments
155
156       // /servicehelper
157       //  Used on NT to connect to Bacula
158       if (strncmp(&szCmdLine[i], BaculaRunServiceHelper, strlen(BaculaRunServiceHelper)) == 0) {
159               // NB : This flag MUST be parsed BEFORE "-service", otherwise it will match
160               // the wrong option!  (This code should really be replaced with a simple
161               // parser machine and parse-table...)
162
163               // Run the Bacula Service Helper app
164               bacService::PostUserHelperMessage();
165               return 0;
166       }
167       // /service
168       if (strncmp(&szCmdLine[i], BaculaRunService, strlen(BaculaRunService)) == 0) {
169               // Run Bacula as a service
170               return bacService::BaculaServiceMain();
171       }
172       // /run  (this is the default if no command line arguments)
173       if (strncmp(&szCmdLine[i], BaculaRunAsUserApp, strlen(BaculaRunAsUserApp)) == 0) {
174               // Bacula is being run as a user-level program
175               return BaculaAppMain();
176       }
177       // /install
178       if (strncmp(&szCmdLine[i], BaculaInstallService, strlen(BaculaInstallService)) == 0) {
179               // Install Bacula as a service
180               bacService::InstallService();
181               i+=strlen(BaculaInstallService);
182               continue;
183       }
184       // /remove
185       if (strncmp(&szCmdLine[i], BaculaRemoveService, strlen(BaculaRemoveService)) == 0) {
186               // Remove the Bacula service
187               bacService::RemoveService();
188               i+=strlen(BaculaRemoveService);
189               continue;
190       }
191
192       // /about
193       if (strncmp(&szCmdLine[i], BaculaShowAbout, strlen(BaculaShowAbout)) == 0) {
194               // Show the About dialog of an existing instance of Bacula
195               bacService::ShowAboutBox();
196               i+=strlen(BaculaShowAbout);
197               continue;
198       }
199
200       // /status
201       if (strncmp(&szCmdLine[i], BaculaShowStatus, strlen(BaculaShowStatus)) == 0) {
202               // Show the Status dialog of an existing instance of Bacula
203               bacService::ShowStatus();
204               i+=strlen(BaculaShowStatus);
205               continue;
206       }
207
208       // /events
209       if (strncmp(&szCmdLine[i], BaculaShowEvents, strlen(BaculaShowEvents)) == 0) {
210               // Show the Events dialog of an existing instance of Bacula
211               bacService::ShowEvents();
212               i+=strlen(BaculaShowEvents);
213               continue;
214       }
215
216
217       // /kill
218       if (strncmp(&szCmdLine[i], BaculaKillRunningCopy, strlen(BaculaKillRunningCopy)) == 0) {
219               // Kill any already running copy of Bacula
220               bacService::KillRunningCopy();
221               i+=strlen(BaculaKillRunningCopy);
222               continue;
223       }
224
225       // /help
226       if (strncmp(&szCmdLine[i], BaculaShowHelp, strlen(BaculaShowHelp)) == 0) {
227          MessageBox(NULL, BaculaUsageText, "Bacula Usage", MB_OK | MB_ICONINFORMATION);
228          i+=strlen(BaculaShowHelp);
229          continue;
230       }
231       
232       MessageBox(NULL, szCmdLine, "Bad Command Line Options", MB_OK);
233
234       // Show the usage dialog
235       MessageBox(NULL, BaculaUsageText, "Bacula Usage", MB_OK | MB_ICONINFORMATION);
236       break;
237    }
238
239    // If no arguments were given then just run
240    if (!argfound) {
241       BaculaAppMain();
242    }
243    return 0;
244 }
245
246
247 /*
248  * Called as a thread from BaculaAppMain()
249  * Here we handle the Windows messages
250  */
251 DWORD WINAPI Main_Msg_Loop(LPVOID lpwThreadParam)
252 {
253    DWORD old_servicethread = g_servicethread;
254
255    /* Since we are the only thread with a message loop
256     * mark ourselves as the service thread so that
257     * we can receive all the window events.
258     */
259    g_servicethread = GetCurrentThreadId();
260
261    // Create tray icon & menu if we're running as an app
262    bacMenu *menu = new bacMenu();
263    if (menu == NULL) {
264       PostQuitMessage(0);
265    }
266
267
268    // Now enter the Windows message handling loop until told to quit!
269    MSG msg;
270    while (GetMessage(&msg, NULL, 0,0) ) {
271       TranslateMessage(&msg);
272       DispatchMessage(&msg);
273    }
274
275    if (menu != NULL)
276       delete menu;
277
278    if (old_servicethread != 0) { /* started as NT service */
279       // Mark that we're no longer running
280       g_servicethread = 0;
281
282       // Tell the service manager that we've stopped.
283       ReportStatus(SERVICE_STOPPED, g_error, 0);
284    }   
285    kill(main_pid, SIGTERM);           /* ask main thread to terminate */
286    _exit(0);
287 }
288  
289
290 /*
291  * This is the main routine for Bacula when running as an application
292  * (under Windows 95 or Windows NT)
293  * Under NT, Bacula can also run as a service.  The BaculaServerMain routine,
294  * defined in the bacService header, is used instead when running as a service.
295  */
296 int BaculaAppMain()
297 {
298    DWORD dwThreadID;
299
300    // Set this process to be the last application to be shut down.
301    SetProcessShutdownParameters(0x100, 0);
302
303    HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
304    if (hservwnd != NULL) {
305       // We don't allow multiple instances!
306       MessageBox(NULL, "Another instance of Bacula is already running", szAppName, MB_OK);
307       _exit(0);
308    }
309
310    // Create a thread to handle the Windows messages
311    (void)CreateThread(NULL, 0, Main_Msg_Loop, NULL, 0, &dwThreadID);
312
313    // Call the "real" Bacula
314    BaculaMain(num_command_args, command_args);
315    PostQuitMessage(0);
316    _exit(0);
317 }