]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/win32/winmain.cpp
Allow restore as non-root
[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 #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 #include <pthread.h>
32
33 extern int BaculaMain(int argc, char **argv);
34 extern int terminate_filed(int sig);
35 extern DWORD g_error;
36 extern BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
37
38
39 HINSTANCE       hAppInstance;
40 const char      *szAppName = "Bacula";
41 DWORD           mainthreadId;
42
43 /* Imported variables */
44 extern DWORD    g_servicethread;
45
46 #define MAX_COMMAND_ARGS 100
47 static char *command_args[MAX_COMMAND_ARGS] = {"bacula-fd", NULL};
48 static int num_command_args = 1;
49 static pid_t main_pid;
50 static pthread_t main_tid;
51
52 /*
53  * WinMain parses the command line and either calls the main App
54  * routine or, under NT, the main service routine.
55  */
56 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
57                    PSTR CmdLine, int iCmdShow)
58 {
59    char *szCmdLine = CmdLine;
60    char *wordPtr,*tempPtr;
61    int i,quote;
62
63    /* Save the application instance and main thread id */
64    hAppInstance = hInstance;
65    mainthreadId = GetCurrentThreadId();
66
67    main_pid = getpid();
68    main_tid = pthread_self();
69
70    /*
71     * Funny things happen with the command line if the
72     * execution comes from c:/Program Files/bacula/bacula.exe
73     * We get a command line like: Files/bacula/bacula.exe" options
74     * I.e. someone stops scanning command line on a space, not
75     * realizing that the filename is quoted!!!!!!!!!!
76     * So if first character is not a double quote and
77     * the last character before first space is a double
78     * quote, we throw away the junk.
79     */
80    wordPtr = szCmdLine;
81    while (*wordPtr && *wordPtr != ' ')
82       wordPtr++;
83    if (wordPtr > szCmdLine)      /* backup to char before space */
84       wordPtr--;
85    /* if first character is not a quote and last is, junk it */
86    if (*szCmdLine != '"' && *wordPtr == '"')
87       szCmdLine = wordPtr + 1;
88    //      MessageBox(NULL, szCmdLine, "Cmdline", MB_OK);
89
90    /* Build Unix style argc *argv[] */      
91
92    /* Don't NULL command_args[0] !!! */
93    for (i=1;i<MAX_COMMAND_ARGS;i++)
94       command_args[i] = NULL;
95
96    wordPtr = szCmdLine;
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     *   as defined by ATT
137     *
138     * Make the command-line lowercase and parse it
139     */
140    for (i = 0; i < (int)strlen(szCmdLine); i++) {
141       szCmdLine[i] = tolower(szCmdLine[i]);
142    }
143
144    BOOL argfound = FALSE;
145    for (i = 0; i < (int)strlen(szCmdLine); i++) {
146       if (szCmdLine[i] <= ' ') {
147          continue;
148       }
149
150       if (szCmdLine[i] == '-') {
151          while (szCmdLine[i] && szCmdLine[i] != ' ') {
152             i++;
153          }
154          continue;
155       }
156
157       argfound = TRUE;
158
159       // Now check for command-line arguments
160
161       // /servicehelper
162       //  Used on NT to connect to Bacula
163       if (strncmp(&szCmdLine[i], BaculaRunServiceHelper, strlen(BaculaRunServiceHelper)) == 0) {
164          // NB : This flag MUST be parsed BEFORE "-service", otherwise it will match
165          // the wrong option!  (This code should really be replaced with a simple
166          // parser machine and parse-table...)
167
168          // Run the Bacula Service Helper app
169          bacService::PostUserHelperMessage();
170          return 0;
171       }
172       // /service
173       if (strncmp(&szCmdLine[i], BaculaRunService, strlen(BaculaRunService)) == 0) {
174          // Run Bacula as a service
175          return bacService::BaculaServiceMain();
176       }
177       // /run  (this is the default if no command line arguments)
178       if (strncmp(&szCmdLine[i], BaculaRunAsUserApp, strlen(BaculaRunAsUserApp)) == 0) {
179          // Bacula is being run as a user-level program
180          return BaculaAppMain();
181       }
182       // /install
183       if (strncmp(&szCmdLine[i], BaculaInstallService, strlen(BaculaInstallService)) == 0) {
184          // Install Bacula as a service
185          bacService::InstallService();
186          i+=strlen(BaculaInstallService);
187          continue;
188       }
189       // /remove
190       if (strncmp(&szCmdLine[i], BaculaRemoveService, strlen(BaculaRemoveService)) == 0) {
191          // Remove the Bacula service
192          bacService::RemoveService();
193          i+=strlen(BaculaRemoveService);
194          continue;
195       }
196
197       // /about
198       if (strncmp(&szCmdLine[i], BaculaShowAbout, strlen(BaculaShowAbout)) == 0) {
199          // Show the About dialog of an existing instance of Bacula
200          bacService::ShowAboutBox();
201          i+=strlen(BaculaShowAbout);
202          continue;
203       }
204
205       // /status
206       if (strncmp(&szCmdLine[i], BaculaShowStatus, strlen(BaculaShowStatus)) == 0) {
207          // Show the Status dialog of an existing instance of Bacula
208          bacService::ShowStatus();
209          i+=strlen(BaculaShowStatus);
210          continue;
211       }
212
213       // /events
214       if (strncmp(&szCmdLine[i], BaculaShowEvents, strlen(BaculaShowEvents)) == 0) {
215          // Show the Events dialog of an existing instance of Bacula
216          bacService::ShowEvents();
217          i+=strlen(BaculaShowEvents);
218          continue;
219       }
220
221
222       // /kill
223       if (strncmp(&szCmdLine[i], BaculaKillRunningCopy, strlen(BaculaKillRunningCopy)) == 0) {
224          // Kill any already running copy of Bacula
225          bacService::KillRunningCopy();
226          i+=strlen(BaculaKillRunningCopy);
227          continue;
228       }
229
230       // /help
231       if (strncmp(&szCmdLine[i], BaculaShowHelp, strlen(BaculaShowHelp)) == 0) {
232          MessageBox(NULL, BaculaUsageText, "Bacula Usage", MB_OK | MB_ICONINFORMATION);
233          i+=strlen(BaculaShowHelp);
234          continue;
235       }
236       
237       MessageBox(NULL, szCmdLine, "Bad Command Line Options", MB_OK);
238
239       // Show the usage dialog
240       MessageBox(NULL, BaculaUsageText, "Bacula Usage", MB_OK | MB_ICONINFORMATION);
241       break;
242    }
243
244    // If no arguments were given then just run
245    if (!argfound) {
246       BaculaAppMain();
247    }
248    return 0;
249 }
250
251
252 /*
253  * Called as a thread from BaculaAppMain()
254  * Here we handle the Windows messages
255  */
256 //DWORD WINAPI Main_Msg_Loop(LPVOID lpwThreadParam)
257 void *Main_Msg_Loop(LPVOID lpwThreadParam) 
258 {
259    DWORD old_servicethread = g_servicethread;
260
261
262    pthread_detach(pthread_self());
263
264    /* Since we are the only thread with a message loop
265     * mark ourselves as the service thread so that
266     * we can receive all the window events.
267     */
268    g_servicethread = GetCurrentThreadId();
269
270    // Create tray icon & menu if we're running as an app
271    bacMenu *menu = new bacMenu();
272    if (menu == NULL) {
273 //    LogErrorMsg("Could not create sys tray menu");
274       PostQuitMessage(0);
275    }
276
277
278    // Now enter the Windows message handling loop until told to quit!
279    MSG msg;
280    while (GetMessage(&msg, NULL, 0,0) ) {
281       TranslateMessage(&msg);
282       DispatchMessage(&msg);
283    }
284
285    if (menu != NULL) {
286       delete menu;
287    }
288
289    if (old_servicethread != 0) { /* started as NT service */
290       // Mark that we're no longer running
291       g_servicethread = 0;
292
293       // Tell the service manager that we've stopped.
294       ReportStatus(SERVICE_STOPPED, g_error, 0);
295    }   
296    pthread_kill(main_tid, SIGTERM);   /* ask main thread to terminate */
297    sleep(1);
298    kill(main_pid, SIGTERM);           /* ask main thread to terminate */
299    _exit(0);
300 }
301  
302
303 /*
304  * This is the main routine for Bacula when running as an application
305  * (under Windows 95 or Windows NT)
306  * Under NT, Bacula can also run as a service.  The BaculaServerMain routine,
307  * defined in the bacService header, is used instead when running as a service.
308  */
309 int BaculaAppMain()
310 {
311 // DWORD dwThreadID;
312    pthread_t tid;
313
314    // Set this process to be the last application to be shut down.
315    SetProcessShutdownParameters(0x100, 0);
316
317    HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
318    if (hservwnd != NULL) {
319       // We don't allow multiple instances!
320       MessageBox(NULL, "Another instance of Bacula is already running", szAppName, MB_OK);
321       _exit(0);
322    }
323
324    // Create a thread to handle the Windows messages
325 // (void)CreateThread(NULL, 0, Main_Msg_Loop, NULL, 0, &dwThreadID);
326    pthread_create(&tid, NULL, Main_Msg_Loop, (void *)0);
327
328    // Call the "real" Bacula
329    BaculaMain(num_command_args, command_args);
330    PostQuitMessage(0);
331    _exit(0);
332 }