]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/filed/winmain.cpp
Send M_ERROR_TERM and M_ABORT messages to stdout on Windows.
[bacula/bacula] / bacula / src / win32 / filed / winmain.cpp
1 /*
2    Copyright (C) 2000-2006 Kern Sibbald
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
6    version 2 as amended with additional clauses defined in the
7    file LICENSE in the main source directory.
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 
12    the file LICENSE for additional details.
13
14    This file is patterned after the VNC Win32 code by ATT
15   
16 */
17
18 #include <unistd.h>
19 #include <ctype.h>
20
21 #include "bacula.h"
22 #include "winbacula.h"
23 #include "wintray.h"
24 #include "winservice.h"
25 #include <signal.h>
26 #include <pthread.h>
27
28 extern int BaculaMain(int argc, char *argv[]);
29 extern void terminate_filed(int sig);
30 extern DWORD g_error;
31 extern BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
32 extern void d_msg(const char *, int, int, const char *, ...);
33 extern void VSSInit();
34
35 /* Globals */
36 HINSTANCE       hAppInstance;
37 const char      *szAppName = "Bacula-fd";
38 DWORD           mainthreadId;
39 bool            opt_debug = false;
40
41 /* Imported variables */
42 extern DWORD    g_servicethread;
43
44 #define MAX_COMMAND_ARGS 100
45 static char *command_args[MAX_COMMAND_ARGS] = {"bacula-fd", NULL};
46 static int num_command_args = 1;
47 static pid_t main_pid;
48 static pthread_t main_tid;
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    main_tid = pthread_self();
67
68    /*
69     * Funny things happen with the command line if the
70     * execution comes from c:/Program Files/bacula/bacula.exe
71     * We get a command line like: Files/bacula/bacula.exe" options
72     * I.e. someone stops scanning command line on a space, not
73     * realizing that the filename is quoted!!!!!!!!!!
74     * So if first character is not a double quote and
75     * the last character before first space is a double
76     * quote, we throw away the junk.
77     */
78
79    wordPtr = szCmdLine;
80    while (*wordPtr && *wordPtr != ' ')
81       wordPtr++;
82    if (wordPtr > szCmdLine)      /* backup to char before space */
83       wordPtr--;
84    /* if first character is not a quote and last is, junk it */
85    if (*szCmdLine != '"' && *wordPtr == '"') {
86       szCmdLine = wordPtr + 1;
87    }
88
89    /* Build Unix style argc *argv[] */      
90
91    /* Don't NULL command_args[0] !!! */
92    for (i=1;i<MAX_COMMAND_ARGS;i++)
93       command_args[i] = NULL;
94
95    char *pszArgs = bstrdup(szCmdLine);
96    wordPtr = pszArgs;
97    quote = 0;
98    while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
99       wordPtr++;
100    if (*wordPtr == '\"') {
101       quote = 1;
102       wordPtr++;
103    } else if (*wordPtr == '/') {
104       /* Skip Windows options */
105       while (*wordPtr && (*wordPtr != ' ' && *wordPtr != '\t'))
106          wordPtr++;
107       while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
108          wordPtr++;
109    }
110    if (*wordPtr) {
111       while (*wordPtr && num_command_args < MAX_COMMAND_ARGS) {
112          tempPtr = wordPtr;
113          if (quote) {
114             while (*tempPtr && *tempPtr != '\"')
115                tempPtr++;
116             quote = 0;
117          } else {
118             while (*tempPtr && *tempPtr != ' ')
119             tempPtr++;
120          }
121          if (*tempPtr)
122             *(tempPtr++) = '\0';
123          command_args[num_command_args++] = wordPtr;
124          wordPtr = tempPtr;
125          while (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
126             wordPtr++;
127          if (*wordPtr == '\"') {
128             quote = 1;
129             wordPtr++;
130          }
131       }
132    }
133
134    /*
135     * Now process Windows command line options
136     */
137    for (i = 0; i < (int)strlen(szCmdLine); i++) {
138       if (szCmdLine[i] <= ' ') {
139          continue;
140       }
141
142       if (szCmdLine[i] != '/') {
143          break;
144       }
145
146       /* Now check for command-line arguments */
147
148       /* /debug - enable debugging facilities such as console message window */
149       if (strnicmp(&szCmdLine[i], BaculaOptDebug, sizeof(BaculaOptDebug) - 1) == 0) {
150          opt_debug = true;
151          i += sizeof(BaculaOptDebug) - 1;
152          continue;
153       }
154
155       /* /service start service */
156       if (strnicmp(&szCmdLine[i], BaculaRunService, sizeof(BaculaRunService) - 1) == 0) {
157          /* Run Bacula as a service */
158          return bacService::BaculaServiceMain();
159       }
160       /* /run  (this is the default if no command line arguments) */
161       if (strnicmp(&szCmdLine[i], BaculaRunAsUserApp, sizeof(BaculaRunAsUserApp) - 1) == 0) {
162          /* Bacula is being run as a user-level program */
163          return BaculaAppMain();
164       }
165       /* /install */
166       if (strnicmp(&szCmdLine[i], BaculaInstallService, sizeof(BaculaInstallService) - 1) == 0) {
167          /* Install Bacula as a service */
168          return bacService::InstallService(&szCmdLine[i + sizeof(BaculaInstallService) - 1]);
169       }
170       /* /remove */
171       if (strnicmp(&szCmdLine[i], BaculaRemoveService, sizeof(BaculaRemoveService) - 1) == 0) {
172          /* Remove the Bacula service */
173          return bacService::RemoveService();
174       }
175
176       /* /about */
177       if (strnicmp(&szCmdLine[i], BaculaShowAbout, sizeof(BaculaShowAbout) - 1) == 0) {
178          /* Show Bacula's about box */
179          return bacService::ShowAboutBox();
180       }
181
182       /* /status */
183       if (strnicmp(&szCmdLine[i], BaculaShowStatus, sizeof(BaculaShowStatus) - 1) == 0) {
184          /* Show Bacula's status box */                             
185          return bacService::ShowStatus();
186       }
187
188       /* /kill */
189       if (strnicmp(&szCmdLine[i], BaculaKillRunningCopy, sizeof(BaculaKillRunningCopy) - 1) == 0) {
190          /* Kill running copy of Bacula */
191          return bacService::KillRunningCopy();
192       }
193
194       /* /help */
195       if (strnicmp(&szCmdLine[i], BaculaShowHelp, sizeof(BaculaShowHelp) - 1) == 0) {
196          MessageBox(NULL, BaculaUsageText, _("Bacula Usage"), MB_OK|MB_ICONINFORMATION);
197          return 0;
198       }
199       
200       MessageBox(NULL, szCmdLine, _("Bad Command Line Options"), MB_OK);
201
202       /* Show the usage dialog */
203       MessageBox(NULL, BaculaUsageText, _("Bacula Usage"), MB_OK | MB_ICONINFORMATION);
204
205       return 1;
206    }
207
208    return BaculaAppMain();
209 }
210
211
212 /*
213  * Called as a thread from BaculaAppMain()
214  * Here we handle the Windows messages
215  */
216 //DWORD WINAPI Main_Msg_Loop(LPVOID lpwThreadParam)
217 void *Main_Msg_Loop(LPVOID lpwThreadParam) 
218 {
219    DWORD old_servicethread = g_servicethread;
220
221    pthread_detach(pthread_self());
222
223    /* Since we are the only thread with a message loop
224     * mark ourselves as the service thread so that
225     * we can receive all the window events.
226     */
227    g_servicethread = GetCurrentThreadId();
228
229    /* Create tray icon & menu if we're running as an app */
230    bacMenu *menu = new bacMenu();
231    if (menu == NULL) {
232 //    log_error_message("Could not create sys tray menu");
233       PostQuitMessage(0);
234    }
235
236    /* Now enter the Windows message handling loop until told to quit! */
237    MSG msg;
238    while (GetMessage(&msg, NULL, 0,0) ) {
239       TranslateMessage(&msg);
240       DispatchMessage(&msg);
241    }
242
243    if (menu != NULL) {
244       delete menu;
245    }
246
247    if (old_servicethread != 0) { /* started as NT service */
248       /* Mark that we're no longer running */
249       g_servicethread = 0;
250
251       /* Tell the service manager that we've stopped. */
252       ReportStatus(SERVICE_STOPPED, g_error, 0);
253    }  
254    /* Tell main program to go away */
255    terminate_filed(0);
256
257    /* Should not get here */
258    pthread_kill(main_tid, SIGTERM);   /* ask main thread to terminate */
259    sleep(1);
260    kill(main_pid, SIGTERM);           /* ask main thread to terminate */
261    _exit(0);
262 }
263  
264
265 /*
266  * This is the main routine for Bacula when running as an application
267  * (under Windows 95 or Windows NT)
268  * Under NT, Bacula can also run as a service.  The BaculaServerMain routine,
269  * defined in the bacService header, is used instead when running as a service.
270  */
271 int BaculaAppMain()
272 {
273  /* DWORD dwThreadID; */
274    pthread_t tid;
275    DWORD dwCharsWritten;
276
277    InitWinAPIWrapper();
278
279    /* If no arguments were given then just run */
280    if (p_AttachConsole == NULL || !p_AttachConsole(ATTACH_PARENT_PROCESS)) {
281       if (opt_debug) {
282          AllocConsole();
283       }
284    }
285    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), "\r\n", 2, &dwCharsWritten, NULL);
286
287    VSSInit();
288
289    WSA_Init();
290
291    /* Set this process to be the last application to be shut down. */
292    if (p_SetProcessShutdownParameters) {
293       p_SetProcessShutdownParameters(0x100, 0);
294    }
295
296    HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
297    if (hservwnd != NULL) {
298       /* We don't allow multiple instances! */
299       MessageBox(NULL, _("Another instance of Bacula is already running"), szAppName, MB_OK);
300       _exit(0);
301    }
302
303    /* Create a thread to handle the Windows messages */
304    pthread_create(&tid, NULL,  Main_Msg_Loop, (void *)0);
305
306    /* Call the "real" Bacula */
307    BaculaMain(num_command_args, command_args);
308    PostQuitMessage(0);
309    WSACleanup();
310    _exit(0);
311 }