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