]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/dird/winmain.cpp
Fix Windows' daemons so that messages print to stdout if not running as a service.
[bacula/bacula] / bacula / src / win32 / dird / 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 "winservice.h"
24 #include <signal.h>
25 #include <pthread.h>
26
27 extern int BaculaMain(int argc, char *argv[]);
28 extern void terminate_dird(int sig);
29 extern DWORD g_error;
30 extern BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
31 extern void d_msg(const char *, int, int, const char *, ...);
32
33 /* Globals */
34 HINSTANCE       hAppInstance;
35 const char      *szAppName = "Bacula-dir";
36 DWORD           mainthreadId;
37 bool            silent = false;
38
39 /* Imported variables */
40 extern DWORD    g_servicethread;
41
42 #define MAX_COMMAND_ARGS 100
43 static char *command_args[MAX_COMMAND_ARGS] = {"bacula-dir", NULL};
44 static int num_command_args = 1;
45 static pid_t main_pid;
46 static pthread_t main_tid;
47
48 /*
49  * WinMain parses the command line and either calls the main App
50  * routine or, under NT, the main service routine.
51  */
52 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
53                    PSTR CmdLine, int iCmdShow)
54 {
55    char *szCmdLine = CmdLine;
56    char *wordPtr, *tempPtr;
57    int i, quote;
58    DWORD dwCharsWritten;
59
60    /* Save the application instance and main thread id */
61    hAppInstance = hInstance;
62    mainthreadId = GetCurrentThreadId();
63
64    main_pid = getpid();
65    main_tid = pthread_self();
66
67    /*
68     * Funny things happen with the command line if the
69     * execution comes from c:/Program Files/bacula/bacula.exe
70     * We get a command line like: Files/bacula/bacula.exe" options
71     * I.e. someone stops scanning command line on a space, not
72     * realizing that the filename is quoted!!!!!!!!!!
73     * So if first character is not a double quote and
74     * the last character before first space is a double
75     * quote, we throw away the junk.
76     */
77
78    wordPtr = szCmdLine;
79    while (*wordPtr && *wordPtr != ' ')
80       wordPtr++;
81    if (wordPtr > szCmdLine)      /* backup to char before space */
82       wordPtr--;
83    /* if first character is not a quote and last is, junk it */
84    if (*szCmdLine != '"' && *wordPtr == '"') {
85       szCmdLine = wordPtr + 1;
86    }
87
88    /* Build Unix style argc *argv[] */      
89
90    /* Don't NULL command_args[0] !!! */
91    for (i=1;i<MAX_COMMAND_ARGS;i++)
92       command_args[i] = NULL;
93
94    char *pszArgs = bstrdup(szCmdLine);
95    wordPtr = pszArgs;
96    quote = 0;
97    while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
98       wordPtr++;
99    if (*wordPtr == '\"') {
100       quote = 1;
101       wordPtr++;
102    } else if (*wordPtr == '/') {
103       /* Skip Windows options */
104       while (*wordPtr && (*wordPtr != ' ' && *wordPtr != '\t'))
105          wordPtr++;
106       while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
107          wordPtr++;
108    }
109    if (*wordPtr) {
110       while (*wordPtr && num_command_args < MAX_COMMAND_ARGS) {
111          tempPtr = wordPtr;
112          if (quote) {
113             while (*tempPtr && *tempPtr != '\"')
114                tempPtr++;
115             quote = 0;
116          } else {
117             while (*tempPtr && *tempPtr != ' ')
118             tempPtr++;
119          }
120          if (*tempPtr)
121             *(tempPtr++) = '\0';
122          command_args[num_command_args++] = wordPtr;
123          wordPtr = tempPtr;
124          while (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
125             wordPtr++;
126          if (*wordPtr == '\"') {
127             quote = 1;
128             wordPtr++;
129          }
130       }
131    }
132
133    /*
134     * Now process Windows command line options
135     */
136    for (i = 0; i < (int)strlen(szCmdLine); i++) {
137       if (szCmdLine[i] <= ' ') {
138          continue;
139       }
140
141       if (szCmdLine[i] != '/') {
142          break;
143       }
144
145       /* Now check for command-line arguments */
146
147       /* /silent install quietly -- no prompts */
148       if (strnicmp(&szCmdLine[i], BaculaSilent, sizeof(BaculaSilent) - 1) == 0) {
149          silent = true;
150          i += sizeof(BaculaSilent) - 1;
151          continue;
152       }
153
154       /* /service start service */
155       if (strnicmp(&szCmdLine[i], BaculaRunService, sizeof(BaculaRunService) - 1) == 0) {
156          /* Run Bacula as a service */
157          return bacService::BaculaServiceMain();
158       }
159       /* /run  (this is the default if no command line arguments) */
160       if (strnicmp(&szCmdLine[i], BaculaRunAsUserApp, sizeof(BaculaRunAsUserApp) - 1) == 0) {
161          /* Bacula is being run as a user-level program */
162          if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
163             AllocConsole();
164          }
165          WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), "\r\n", 2, &dwCharsWritten, NULL);
166
167          return BaculaAppMain();
168       }
169       /* /install */
170       if (strnicmp(&szCmdLine[i], BaculaInstallService, sizeof(BaculaInstallService) - 1) == 0) {
171          /* Install Bacula as a service */
172          return bacService::InstallService(&szCmdLine[i + sizeof(BaculaInstallService) - 1]);
173       }
174       /* /remove */
175       if (strnicmp(&szCmdLine[i], BaculaRemoveService, sizeof(BaculaRemoveService) - 1) == 0) {
176          /* Remove the Bacula service */
177          return bacService::RemoveService();
178       }
179
180       /* /kill */
181       if (strnicmp(&szCmdLine[i], BaculaKillRunningCopy, sizeof(BaculaKillRunningCopy) - 1) == 0) {
182          /* Kill running copy of Bacula */
183          return bacService::KillRunningCopy() ? 0 : 1;
184       }
185
186       /* /help */
187       if (strnicmp(&szCmdLine[i], BaculaShowHelp, sizeof(BaculaShowHelp) - 1) == 0) {
188          MessageBox(NULL, BaculaUsageText, _("Bacula Usage"), MB_OK|MB_ICONINFORMATION);
189          return 0;
190       }
191       
192       MessageBox(NULL, szCmdLine, _("Bad Command Line Options"), MB_OK);
193
194       /* Show the usage dialog */
195       MessageBox(NULL, BaculaUsageText, _("Bacula Usage"), MB_OK | MB_ICONINFORMATION);
196       return 1;
197    }
198
199    /* If no arguments were given then just run */
200    if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
201       AllocConsole();
202    }
203    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), "\r\n", 2, &dwCharsWritten, NULL);
204
205    return BaculaAppMain();
206 }
207
208
209 /*
210  * Called as a thread from BaculaAppMain()
211  * Here we handle the Windows messages
212  */
213 //DWORD WINAPI Main_Msg_Loop(LPVOID lpwThreadParam)
214 void *Main_Msg_Loop(LPVOID lpwThreadParam) 
215 {
216    DWORD old_servicethread = g_servicethread;
217
218    pthread_detach(pthread_self());
219
220    /* Since we are the only thread with a message loop
221     * mark ourselves as the service thread so that
222     * we can receive all the window events.
223     */
224    g_servicethread = GetCurrentThreadId();
225
226    /* Now enter the Windows message handling loop until told to quit! */
227    MSG msg;
228    while (GetMessage(&msg, NULL, 0,0) ) {
229       TranslateMessage(&msg);
230       DispatchMessage(&msg);
231    }
232
233    if (old_servicethread != 0) { /* started as NT service */
234       /* Mark that we're no longer running */
235       g_servicethread = 0;
236
237       /* Tell the service manager that we've stopped. */
238       ReportStatus(SERVICE_STOPPED, g_error, 0);
239    }  
240    /* Tell main program to go away */
241    terminate_dird(0);
242
243    /* Should not get here */
244    pthread_kill(main_tid, SIGTERM);   /* ask main thread to terminate */
245    sleep(1);
246    kill(main_pid, SIGTERM);           /* ask main thread to terminate */
247    _exit(0);
248 }
249  
250
251 /*
252  * This is the main routine for Bacula when running as an application
253  * (under Windows 95 or Windows NT)
254  * Under NT, Bacula can also run as a service.  The BaculaServerMain routine,
255  * defined in the bacService header, is used instead when running as a service.
256  */
257 int BaculaAppMain()
258 {
259  /* DWORD dwThreadID; */
260    pthread_t tid;
261
262    InitWinAPIWrapper();
263
264    WSA_Init();
265
266    /* Set this process to be the last application to be shut down. */
267    if (p_SetProcessShutdownParameters) {
268       p_SetProcessShutdownParameters(0x100, 0);
269    }
270
271    /* Create a thread to handle the Windows messages */
272    pthread_create(&tid, NULL,  Main_Msg_Loop, (void *)0);
273
274    /* Call the "real" Bacula */
275    BaculaMain(num_command_args, command_args);
276    PostQuitMessage(0);
277    WSACleanup();
278    _exit(0);
279 }