]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/win32/winmain.cpp
4acbb3c2d0480d47662af10fe6d79b7a658870bd
[bacula/bacula] / bacula / src / filed / win32 / winmain.cpp
1 /*
2    Copyright (C) 2000-2003 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 #define HAVE_CYGWIN 1
25
26 #include <unistd.h>
27 #include <lmcons.h>
28 #include <ctype.h>
29 #include "winbacula.h"
30 #include "wintray.h"
31 #include "winservice.h"
32 #include <signal.h>
33 #include <pthread.h>
34 #include "../../findlib/winapi.h"
35
36 extern int BaculaMain(int argc, char **argv);
37 extern int terminate_filed(int sig);
38 extern DWORD g_error;
39 extern BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
40
41 /* Globals */
42 HINSTANCE       hAppInstance;
43 const char      *szAppName = "Bacula";
44 DWORD           mainthreadId;
45
46 /* Imported variables */
47 extern DWORD    g_servicethread;
48
49 #define MAX_COMMAND_ARGS 100
50 static char *command_args[MAX_COMMAND_ARGS] = {"bacula-fd", NULL};
51 static int num_command_args = 1;
52 static pid_t main_pid;
53 static pthread_t main_tid;
54
55 /*
56  * WinMain parses the command line and either calls the main App
57  * routine or, under NT, the main service routine.
58  */
59 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
60                    PSTR CmdLine, int iCmdShow)
61 {
62    char *szCmdLine = CmdLine;
63    char *wordPtr,*tempPtr;
64    int i,quote;
65
66    /* Save the application instance and main thread id */
67    hAppInstance = hInstance;
68    mainthreadId = GetCurrentThreadId();
69
70    main_pid = getpid();
71    main_tid = pthread_self();
72
73    /*
74     * Funny things happen with the command line if the
75     * execution comes from c:/Program Files/bacula/bacula.exe
76     * We get a command line like: Files/bacula/bacula.exe" options
77     * I.e. someone stops scanning command line on a space, not
78     * realizing that the filename is quoted!!!!!!!!!!
79     * So if first character is not a double quote and
80     * the last character before first space is a double
81     * quote, we throw away the junk.
82     */
83    wordPtr = szCmdLine;
84    while (*wordPtr && *wordPtr != ' ')
85       wordPtr++;
86    if (wordPtr > szCmdLine)      /* backup to char before space */
87       wordPtr--;
88    /* if first character is not a quote and last is, junk it */
89    if (*szCmdLine != '"' && *wordPtr == '"')
90       szCmdLine = wordPtr + 1;
91    //      MessageBox(NULL, szCmdLine, "Cmdline", MB_OK);
92
93    /* Build Unix style argc *argv[] */      
94
95    /* Don't NULL command_args[0] !!! */
96    for (i=1;i<MAX_COMMAND_ARGS;i++)
97       command_args[i] = NULL;
98
99    wordPtr = szCmdLine;
100    quote = 0;
101    while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
102       wordPtr++;
103    if (*wordPtr == '\"') {
104       quote = 1;
105       wordPtr++;
106    } else if (*wordPtr == '/') {
107       /* Skip Windows options */
108       while (*wordPtr && (*wordPtr != ' ' && *wordPtr != '\t'))
109          wordPtr++;
110       while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
111          wordPtr++;
112    }
113    if (*wordPtr) {
114       while (*wordPtr && num_command_args < MAX_COMMAND_ARGS) {
115          tempPtr = wordPtr;
116          if (quote) {
117             while (*tempPtr && *tempPtr != '\"')
118             tempPtr++;
119             quote = 0;
120          } else {
121             while (*tempPtr && *tempPtr != ' ')
122             tempPtr++;
123          }
124          if (*tempPtr)
125             *(tempPtr++) = '\0';
126          command_args[num_command_args++] = wordPtr;
127          wordPtr = tempPtr;
128          while (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
129             wordPtr++;
130          if (*wordPtr == '\"') {
131             quote = 1;
132             wordPtr++;
133          }
134       }
135    }
136
137    /*
138     * Now process Windows command line options
139     *   as defined by ATT
140     *
141     * Make the command-line lowercase and parse it
142     */
143    for (i = 0; i < (int)strlen(szCmdLine); i++) {
144       szCmdLine[i] = tolower(szCmdLine[i]);
145    }
146
147    BOOL argfound = FALSE;
148    for (i = 0; i < (int)strlen(szCmdLine); i++) {
149       if (szCmdLine[i] <= ' ') {
150          continue;
151       }
152
153       if (szCmdLine[i] == '-') {
154          while (szCmdLine[i] && szCmdLine[i] != ' ') {
155             i++;
156          }
157          continue;
158       }
159
160       argfound = TRUE;
161
162       // Now check for command-line arguments
163
164       // /servicehelper
165       //  Used on NT to connect to Bacula
166       if (strncmp(&szCmdLine[i], BaculaRunServiceHelper, strlen(BaculaRunServiceHelper)) == 0) {
167          // NB : This flag MUST be parsed BEFORE "-service", otherwise it will match
168          // the wrong option!  (This code should really be replaced with a simple
169          // parser machine and parse-table...)
170
171          // Run the Bacula Service Helper app
172          bacService::PostUserHelperMessage();
173          return 0;
174       }
175       // /service
176       if (strncmp(&szCmdLine[i], BaculaRunService, strlen(BaculaRunService)) == 0) {
177          // Run Bacula as a service
178          return bacService::BaculaServiceMain();
179       }
180       // /run  (this is the default if no command line arguments)
181       if (strncmp(&szCmdLine[i], BaculaRunAsUserApp, strlen(BaculaRunAsUserApp)) == 0) {
182          // Bacula is being run as a user-level program
183          return BaculaAppMain();
184       }
185       // /install
186       if (strncmp(&szCmdLine[i], BaculaInstallService, strlen(BaculaInstallService)) == 0) {
187          // Install Bacula as a service
188          bacService::InstallService();
189          i+=strlen(BaculaInstallService);
190          continue;
191       }
192       // /remove
193       if (strncmp(&szCmdLine[i], BaculaRemoveService, strlen(BaculaRemoveService)) == 0) {
194          // Remove the Bacula service
195          bacService::RemoveService();
196          i+=strlen(BaculaRemoveService);
197          continue;
198       }
199
200       // /about
201       if (strncmp(&szCmdLine[i], BaculaShowAbout, strlen(BaculaShowAbout)) == 0) {
202          // Show the About dialog of an existing instance of Bacula
203          bacService::ShowAboutBox();
204          i+=strlen(BaculaShowAbout);
205          continue;
206       }
207
208       // /status
209       if (strncmp(&szCmdLine[i], BaculaShowStatus, strlen(BaculaShowStatus)) == 0) {
210          // Show the Status dialog of an existing instance of Bacula
211          bacService::ShowStatus();
212          i+=strlen(BaculaShowStatus);
213          continue;
214       }
215
216       // /events
217       if (strncmp(&szCmdLine[i], BaculaShowEvents, strlen(BaculaShowEvents)) == 0) {
218          // Show the Events dialog of an existing instance of Bacula
219          bacService::ShowEvents();
220          i+=strlen(BaculaShowEvents);
221          continue;
222       }
223
224
225       // /kill
226       if (strncmp(&szCmdLine[i], BaculaKillRunningCopy, strlen(BaculaKillRunningCopy)) == 0) {
227          // Kill any already running copy of Bacula
228          bacService::KillRunningCopy();
229          i+=strlen(BaculaKillRunningCopy);
230          continue;
231       }
232
233       // /help
234       if (strncmp(&szCmdLine[i], BaculaShowHelp, strlen(BaculaShowHelp)) == 0) {
235          MessageBox(NULL, BaculaUsageText, "Bacula Usage", MB_OK | MB_ICONINFORMATION);
236          i+=strlen(BaculaShowHelp);
237          continue;
238       }
239       
240       MessageBox(NULL, szCmdLine, "Bad Command Line Options", MB_OK);
241
242       // Show the usage dialog
243       MessageBox(NULL, BaculaUsageText, "Bacula Usage", MB_OK | MB_ICONINFORMATION);
244       break;
245    }
246
247    // If no arguments were given then just run
248    if (!argfound) {
249       BaculaAppMain();
250    }
251    return 0;
252 }
253
254
255 /*
256  * Called as a thread from BaculaAppMain()
257  * Here we handle the Windows messages
258  */
259 //DWORD WINAPI Main_Msg_Loop(LPVOID lpwThreadParam)
260 void *Main_Msg_Loop(LPVOID lpwThreadParam) 
261 {
262    DWORD old_servicethread = g_servicethread;
263
264
265    pthread_detach(pthread_self());
266
267    /* Since we are the only thread with a message loop
268     * mark ourselves as the service thread so that
269     * we can receive all the window events.
270     */
271    g_servicethread = GetCurrentThreadId();
272
273    // Create tray icon & menu if we're running as an app
274    bacMenu *menu = new bacMenu();
275    if (menu == NULL) {
276 //    log_error_message("Could not create sys tray menu");
277       PostQuitMessage(0);
278    }
279
280
281    // Now enter the Windows message handling loop until told to quit!
282    MSG msg;
283    while (GetMessage(&msg, NULL, 0,0) ) {
284       TranslateMessage(&msg);
285       DispatchMessage(&msg);
286    }
287
288    if (menu != NULL) {
289       delete menu;
290    }
291
292    if (old_servicethread != 0) { /* started as NT service */
293       // Mark that we're no longer running
294       g_servicethread = 0;
295
296       // Tell the service manager that we've stopped.
297       ReportStatus(SERVICE_STOPPED, g_error, 0);
298    }   
299    pthread_kill(main_tid, SIGTERM);   /* ask main thread to terminate */
300    sleep(1);
301    kill(main_pid, SIGTERM);           /* ask main thread to terminate */
302    _exit(0);
303 }
304  
305
306 /*
307  * This is the main routine for Bacula when running as an application
308  * (under Windows 95 or Windows NT)
309  * Under NT, Bacula can also run as a service.  The BaculaServerMain routine,
310  * defined in the bacService header, is used instead when running as a service.
311  */
312 int BaculaAppMain()
313 {
314 // DWORD dwThreadID;
315    pthread_t tid;
316
317    HINSTANCE hLib = LoadLibrary("KERNEL32.DLL");
318    if (hLib) {
319       p_GetFileAttributesEx = (t_GetFileAttributesEx)
320           GetProcAddress(hLib, "GetFileAttributesExA");
321       p_SetProcessShutdownParameters = (t_SetProcessShutdownParameters)
322           GetProcAddress(hLib, "SetProcessShutdownParameters");
323       p_BackupRead = (t_BackupRead)
324           GetProcAddress(hLib, "BackupRead");
325       p_BackupWrite = (t_BackupWrite)
326           GetProcAddress(hLib, "BackupWrite");
327       FreeLibrary(hLib);
328     }
329     hLib = LoadLibrary("ADVAPI32.DLL");
330     if (hLib) {
331        p_OpenProcessToken = (t_OpenProcessToken)
332           GetProcAddress(hLib, "OpenProcessToken");
333        p_AdjustTokenPrivileges = (t_AdjustTokenPrivileges)
334           GetProcAddress(hLib, "AdjustTokenPrivileges");
335        p_LookupPrivilegeValue = (t_LookupPrivilegeValue)
336           GetProcAddress(hLib, "LookupPrivilegeValueA");
337        FreeLibrary(hLib);
338     }
339
340 #ifdef debug_xxx
341    char buf[1000];
342    buf[0] = 0;
343    strcat(buf, "GetFileAttributesEx ");
344    if (p_GetFileAttributesEx)
345       strcat(buf, "OK\n");          
346    else 
347       strcat(buf, "NO\n");
348
349    strcat(buf, "SetProcessShutdownParamaters ");
350    if (p_SetProcessShutdownParameters)
351       strcat(buf, "OK\n");          
352    else 
353       strcat(buf, "NO\n");
354
355    strcat(buf, "BackupRead ");
356    if (p_BackupRead)
357       strcat(buf, "OK\n");          
358    else 
359       strcat(buf, "NO\n");
360
361    strcat(buf, "BackupWrite ");
362    if (p_BackupWrite)
363       strcat(buf, "OK\n");          
364    else 
365       strcat(buf, "NO\n");
366
367    strcat(buf, "OpenProcessToken ");
368    if (p_OpenProcessToken)
369       strcat(buf, "OK\n");          
370    else 
371       strcat(buf, "NO\n");
372
373    strcat(buf, "AdjustTokenPrivileges ");
374    if (p_AdjustTokenPrivileges)
375       strcat(buf, "OK\n");          
376    else 
377       strcat(buf, "NO\n");
378
379    strcat(buf, "LookupPrivilegeValue ");
380    if (p_LookupPrivilegeValue)
381       strcat(buf, "OK\n");          
382    else 
383       strcat(buf, "NO\n");
384
385    MessageBox(NULL, buf, "APIs Available", MB_OK); 
386 #endif
387
388    // Set this process to be the last application to be shut down.
389    if (p_SetProcessShutdownParameters) {
390       p_SetProcessShutdownParameters(0x100, 0);
391    }
392
393    HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
394    if (hservwnd != NULL) {
395       // We don't allow multiple instances!
396       MessageBox(NULL, "Another instance of Bacula is already running", szAppName, MB_OK);
397       _exit(0);
398    }
399
400    // Create a thread to handle the Windows messages
401 // (void)CreateThread(NULL, 0, Main_Msg_Loop, NULL, 0, &dwThreadID);
402    pthread_create(&tid, NULL, Main_Msg_Loop, (void *)0);
403
404    // Call the "real" Bacula
405    BaculaMain(num_command_args, command_args);
406    PostQuitMessage(0);
407    _exit(0);
408 }