]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/win32/winmain.cpp
new devel_bacula + fixes to conio/console
[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
84    wordPtr = szCmdLine;
85    while (*wordPtr && *wordPtr != ' ')
86       wordPtr++;
87    if (wordPtr > szCmdLine)      /* backup to char before space */
88       wordPtr--;
89    /* if first character is not a quote and last is, junk it */
90    if (*szCmdLine != '"' && *wordPtr == '"') {
91       szCmdLine = wordPtr + 1;
92    }
93 // MessageBox(NULL, szCmdLine, "Cmdline", MB_OK);
94
95    /* Build Unix style argc *argv[] */      
96
97    /* Don't NULL command_args[0] !!! */
98    for (i=1;i<MAX_COMMAND_ARGS;i++)
99       command_args[i] = NULL;
100
101    wordPtr = szCmdLine;
102    quote = 0;
103    while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
104       wordPtr++;
105    if (*wordPtr == '\"') {
106       quote = 1;
107       wordPtr++;
108    } else if (*wordPtr == '/') {
109       /* Skip Windows options */
110       while (*wordPtr && (*wordPtr != ' ' && *wordPtr != '\t'))
111          wordPtr++;
112       while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
113          wordPtr++;
114    }
115    if (*wordPtr) {
116       while (*wordPtr && num_command_args < MAX_COMMAND_ARGS) {
117          tempPtr = wordPtr;
118          if (quote) {
119             while (*tempPtr && *tempPtr != '\"')
120             tempPtr++;
121             quote = 0;
122          } else {
123             while (*tempPtr && *tempPtr != ' ')
124             tempPtr++;
125          }
126          if (*tempPtr)
127             *(tempPtr++) = '\0';
128          command_args[num_command_args++] = wordPtr;
129          wordPtr = tempPtr;
130          while (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
131             wordPtr++;
132          if (*wordPtr == '\"') {
133             quote = 1;
134             wordPtr++;
135          }
136       }
137    }
138
139    /*
140     * Now process Windows command line options
141     *   as defined by ATT
142     *
143     * Make the command-line lowercase and parse it
144     */
145    for (i = 0; i < (int)strlen(szCmdLine); i++) {
146       szCmdLine[i] = tolower(szCmdLine[i]);
147    }
148
149    BOOL argfound = FALSE;
150    for (i = 0; i < (int)strlen(szCmdLine); i++) {
151       if (szCmdLine[i] <= ' ') {
152          continue;
153       }
154
155       if (szCmdLine[i] == '-') {
156          while (szCmdLine[i] && szCmdLine[i] != ' ') {
157             i++;
158          }
159          continue;
160       }
161
162       argfound = TRUE;
163
164       // Now check for command-line arguments
165
166       // /servicehelper
167       //  Used on NT to connect to Bacula
168       if (strncmp(&szCmdLine[i], BaculaRunServiceHelper, strlen(BaculaRunServiceHelper)) == 0) {
169          // NB : This flag MUST be parsed BEFORE "-service", otherwise it will match
170          // the wrong option!  (This code should really be replaced with a simple
171          // parser machine and parse-table...)
172
173          // Run the Bacula Service Helper app
174          bacService::PostUserHelperMessage();
175          return 0;
176       }
177       // /service
178       if (strncmp(&szCmdLine[i], BaculaRunService, strlen(BaculaRunService)) == 0) {
179          // Run Bacula as a service
180          return bacService::BaculaServiceMain();
181       }
182       // /run  (this is the default if no command line arguments)
183       if (strncmp(&szCmdLine[i], BaculaRunAsUserApp, strlen(BaculaRunAsUserApp)) == 0) {
184          // Bacula is being run as a user-level program
185          return BaculaAppMain();
186       }
187       // /install
188       if (strncmp(&szCmdLine[i], BaculaInstallService, strlen(BaculaInstallService)) == 0) {
189          // Install Bacula as a service
190          bacService::InstallService();
191          i+=strlen(BaculaInstallService);
192          continue;
193       }
194       // /remove
195       if (strncmp(&szCmdLine[i], BaculaRemoveService, strlen(BaculaRemoveService)) == 0) {
196          // Remove the Bacula service
197          bacService::RemoveService();
198          i+=strlen(BaculaRemoveService);
199          continue;
200       }
201
202       // /about
203       if (strncmp(&szCmdLine[i], BaculaShowAbout, strlen(BaculaShowAbout)) == 0) {
204          // Show the About dialog of an existing instance of Bacula
205          bacService::ShowAboutBox();
206          i+=strlen(BaculaShowAbout);
207          continue;
208       }
209
210       // /status
211       if (strncmp(&szCmdLine[i], BaculaShowStatus, strlen(BaculaShowStatus)) == 0) {
212          // Show the Status dialog of an existing instance of Bacula
213          bacService::ShowStatus();
214          i+=strlen(BaculaShowStatus);
215          continue;
216       }
217
218       // /events
219       if (strncmp(&szCmdLine[i], BaculaShowEvents, strlen(BaculaShowEvents)) == 0) {
220          // Show the Events dialog of an existing instance of Bacula
221          bacService::ShowEvents();
222          i+=strlen(BaculaShowEvents);
223          continue;
224       }
225
226
227       // /kill
228       if (strncmp(&szCmdLine[i], BaculaKillRunningCopy, strlen(BaculaKillRunningCopy)) == 0) {
229          // Kill any already running copy of Bacula
230          bacService::KillRunningCopy();
231          i+=strlen(BaculaKillRunningCopy);
232          continue;
233       }
234
235       // /help
236       if (strncmp(&szCmdLine[i], BaculaShowHelp, strlen(BaculaShowHelp)) == 0) {
237          MessageBox(NULL, BaculaUsageText, "Bacula Usage", MB_OK | MB_ICONINFORMATION);
238          i+=strlen(BaculaShowHelp);
239          continue;
240       }
241       
242       MessageBox(NULL, szCmdLine, "Bad Command Line Options", MB_OK);
243
244       // Show the usage dialog
245       MessageBox(NULL, BaculaUsageText, "Bacula Usage", MB_OK | MB_ICONINFORMATION);
246       break;
247    }
248
249    // If no arguments were given then just run
250    if (!argfound) {
251       BaculaAppMain();
252    }
253    return 0;
254 }
255
256
257 /*
258  * Called as a thread from BaculaAppMain()
259  * Here we handle the Windows messages
260  */
261 //DWORD WINAPI Main_Msg_Loop(LPVOID lpwThreadParam)
262 void *Main_Msg_Loop(LPVOID lpwThreadParam) 
263 {
264    DWORD old_servicethread = g_servicethread;
265
266
267    pthread_detach(pthread_self());
268
269    /* Since we are the only thread with a message loop
270     * mark ourselves as the service thread so that
271     * we can receive all the window events.
272     */
273    g_servicethread = GetCurrentThreadId();
274
275    // Create tray icon & menu if we're running as an app
276    bacMenu *menu = new bacMenu();
277    if (menu == NULL) {
278 //    log_error_message("Could not create sys tray menu");
279       PostQuitMessage(0);
280    }
281
282
283    // Now enter the Windows message handling loop until told to quit!
284    MSG msg;
285    while (GetMessage(&msg, NULL, 0,0) ) {
286       TranslateMessage(&msg);
287       DispatchMessage(&msg);
288    }
289
290    if (menu != NULL) {
291       delete menu;
292    }
293
294    if (old_servicethread != 0) { /* started as NT service */
295       // Mark that we're no longer running
296       g_servicethread = 0;
297
298       // Tell the service manager that we've stopped.
299       ReportStatus(SERVICE_STOPPED, g_error, 0);
300    }   
301    pthread_kill(main_tid, SIGTERM);   /* ask main thread to terminate */
302    sleep(1);
303    kill(main_pid, SIGTERM);           /* ask main thread to terminate */
304    _exit(0);
305 }
306  
307
308 /*
309  * This is the main routine for Bacula when running as an application
310  * (under Windows 95 or Windows NT)
311  * Under NT, Bacula can also run as a service.  The BaculaServerMain routine,
312  * defined in the bacService header, is used instead when running as a service.
313  */
314 int BaculaAppMain()
315 {
316 // DWORD dwThreadID;
317    pthread_t tid;
318
319    HINSTANCE hLib = LoadLibrary("KERNEL32.DLL");
320    if (hLib) {
321       p_GetFileAttributesEx = (t_GetFileAttributesEx)
322           GetProcAddress(hLib, "GetFileAttributesExA");
323       p_SetProcessShutdownParameters = (t_SetProcessShutdownParameters)
324           GetProcAddress(hLib, "SetProcessShutdownParameters");
325       p_BackupRead = (t_BackupRead)
326           GetProcAddress(hLib, "BackupRead");
327       p_BackupWrite = (t_BackupWrite)
328           GetProcAddress(hLib, "BackupWrite");
329       FreeLibrary(hLib);
330    }
331    hLib = LoadLibrary("ADVAPI32.DLL");
332    if (hLib) {
333       p_OpenProcessToken = (t_OpenProcessToken)
334          GetProcAddress(hLib, "OpenProcessToken");
335       p_AdjustTokenPrivileges = (t_AdjustTokenPrivileges)
336          GetProcAddress(hLib, "AdjustTokenPrivileges");
337       p_LookupPrivilegeValue = (t_LookupPrivilegeValue)
338          GetProcAddress(hLib, "LookupPrivilegeValueA");
339       FreeLibrary(hLib);
340    }
341    /*  
342     * Even if these are defined, don't use on old 
343     *  platforms.
344     */
345    if (bacService::IsWin95()) {
346       p_BackupRead = NULL;
347       p_BackupWrite = NULL;
348    }
349
350    // Set this process to be the last application to be shut down.
351    if (p_SetProcessShutdownParameters) {
352       p_SetProcessShutdownParameters(0x100, 0);
353    }
354
355    HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
356    if (hservwnd != NULL) {
357       // We don't allow multiple instances!
358       MessageBox(NULL, "Another instance of Bacula is already running", szAppName, MB_OK);
359       _exit(0);
360    }
361
362    // Create a thread to handle the Windows messages
363 // (void)CreateThread(NULL, 0, Main_Msg_Loop, NULL, 0, &dwThreadID);
364    pthread_create(&tid, NULL, Main_Msg_Loop, (void *)0);
365
366    // Call the "real" Bacula
367    BaculaMain(num_command_args, command_args);
368    PostQuitMessage(0);
369    _exit(0);
370 }