source: box/trunk/bin/bbackupd/Win32ServiceFunctions.cpp @ 2460

Revision 2460, 8.6 KB checked in by chris, 3 years ago (diff)

Fix tests (hopefully) on Win32 for struct stat ino_t change from 16 to
64 bits.

  • Property svn:eol-style set to native
Line 
1//***************************************************************
2// From the book "Win32 System Services: The Heart of Windows 98
3// and Windows 2000"
4// by Marshall Brain
5// Published by Prentice Hall
6// Copyright 1995 Prentice Hall.
7//
8// This code implements the Windows API Service interface
9// for the Box Backup for Windows native port.
10// Adapted for Box Backup by Nick Knight.
11//***************************************************************
12
13#ifdef WIN32
14
15#include "Box.h"
16
17#ifdef HAVE_UNISTD_H
18        #include <unistd.h>
19#endif
20#ifdef HAVE_PROCESS_H
21        #include <process.h>
22#endif
23
24extern void TerminateService(void);
25extern unsigned int WINAPI RunService(LPVOID lpParameter);
26
27// Global variables
28
29TCHAR* gServiceName = TEXT("Box Backup Service");
30SERVICE_STATUS gServiceStatus;
31SERVICE_STATUS_HANDLE gServiceStatusHandle = 0;
32HANDLE gStopServiceEvent = 0;
33DWORD gServiceReturnCode = 0;
34
35#define SERVICE_NAME "boxbackup"
36
37void ShowMessage(char *s)
38{
39        MessageBox(0, s, "Box Backup Message", 
40                MB_OK | MB_SETFOREGROUND | MB_DEFAULT_DESKTOP_ONLY);
41}
42
43void ErrorHandler(char *s, DWORD err)
44{
45        char buf[256];
46        memset(buf, 0, sizeof(buf));
47        _snprintf(buf, sizeof(buf)-1, "%s: %s", s,
48                GetErrorMessage(err).c_str());
49        BOX_ERROR(buf);
50        MessageBox(0, buf, "Error", 
51                MB_OK | MB_SETFOREGROUND | MB_DEFAULT_DESKTOP_ONLY);
52        ExitProcess(err);
53}
54
55void WINAPI ServiceControlHandler( DWORD controlCode )
56{
57        switch ( controlCode )
58        {
59                case SERVICE_CONTROL_INTERROGATE:
60                        break;
61
62                case SERVICE_CONTROL_SHUTDOWN:
63                case SERVICE_CONTROL_STOP:
64                        Beep(1000,100);
65                        TerminateService();
66                        gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
67                        SetServiceStatus(gServiceStatusHandle, &gServiceStatus);
68
69                        SetEvent(gStopServiceEvent);
70                        return;
71
72                case SERVICE_CONTROL_PAUSE:
73                        break;
74
75                case SERVICE_CONTROL_CONTINUE:
76                        break;
77
78                default:
79                        if ( controlCode >= 128 && controlCode <= 255 )
80                                // user defined control code
81                                break;
82                        else
83                                // unrecognised control code
84                                break;
85        }
86
87        SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
88}
89
90// ServiceMain is called when the SCM wants to
91// start the service. When it returns, the service
92// has stopped. It therefore waits on an event
93// just before the end of the function, and
94// that event gets set when it is time to stop.
95// It also returns on any error because the
96// service cannot start if there is an eror.
97
98static char* spConfigFileName;
99
100VOID ServiceMain(DWORD argc, LPTSTR *argv) 
101{
102        // initialise service status
103        gServiceStatus.dwServiceType = SERVICE_WIN32;
104        gServiceStatus.dwCurrentState = SERVICE_STOPPED;
105        gServiceStatus.dwControlsAccepted = 0;
106        gServiceStatus.dwWin32ExitCode = NO_ERROR;
107        gServiceStatus.dwServiceSpecificExitCode = NO_ERROR;
108        gServiceStatus.dwCheckPoint = 0;
109        gServiceStatus.dwWaitHint = 0;
110
111        gServiceStatusHandle = RegisterServiceCtrlHandler(gServiceName, 
112                ServiceControlHandler);
113
114        if (gServiceStatusHandle)
115        {
116                // service is starting
117                gServiceStatus.dwCurrentState = SERVICE_START_PENDING;
118                SetServiceStatus(gServiceStatusHandle, &gServiceStatus);
119
120                // do initialisation here
121                gStopServiceEvent = CreateEvent(0, TRUE, FALSE, 0);
122                if (!gStopServiceEvent)
123                {
124                        gServiceStatus.dwControlsAccepted &= 
125                                ~(SERVICE_ACCEPT_STOP | 
126                                  SERVICE_ACCEPT_SHUTDOWN);
127                        gServiceStatus.dwCurrentState = SERVICE_STOPPED;
128                        SetServiceStatus(gServiceStatusHandle, &gServiceStatus);
129                        return;
130                }
131
132                HANDLE ourThread = (HANDLE)_beginthreadex(
133                        NULL,
134                        0,
135                        RunService,
136                        spConfigFileName,
137                        CREATE_SUSPENDED,
138                        NULL);
139
140                SetThreadPriority(ourThread, THREAD_PRIORITY_LOWEST);
141                ResumeThread(ourThread);
142
143                // we are now running so tell the SCM
144                gServiceStatus.dwControlsAccepted |= 
145                        (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
146                gServiceStatus.dwCurrentState = SERVICE_RUNNING;
147                SetServiceStatus(gServiceStatusHandle, &gServiceStatus);
148
149                // do cleanup here
150                WaitForSingleObject(gStopServiceEvent, INFINITE);
151                CloseHandle(gStopServiceEvent);
152                gStopServiceEvent = 0;
153
154                // service was stopped
155                gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
156                SetServiceStatus(gServiceStatusHandle, &gServiceStatus);
157
158                // service is now stopped
159                gServiceStatus.dwControlsAccepted &= 
160                        ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
161
162                gServiceStatus.dwCurrentState = SERVICE_STOPPED;
163
164                if (gServiceReturnCode != 0)
165                {
166                        gServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
167                        gServiceStatus.dwServiceSpecificExitCode = gServiceReturnCode;
168                }
169
170                SetServiceStatus(gServiceStatusHandle, &gServiceStatus);
171        }
172}
173
174int OurService(const char* pConfigFileName)
175{
176        spConfigFileName = strdup(pConfigFileName);
177
178        SERVICE_TABLE_ENTRY serviceTable[] = 
179        { 
180                { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain },
181                { NULL, NULL }
182        };
183        BOOL success;
184
185        // Register with the SCM
186        success = StartServiceCtrlDispatcher(serviceTable);
187
188        free(spConfigFileName);
189        spConfigFileName = NULL;
190
191        if (!success)
192        {
193                ErrorHandler("Failed to start service. Did you start "
194                        "Box Backup from the Service Control Manager? "
195                        "(StartServiceCtrlDispatcher)", GetLastError());
196                return 1;
197        }
198
199        return 0;
200}
201
202int InstallService(const char* pConfigFileName, const std::string& rServiceName)
203{
204        if (pConfigFileName != NULL)
205        {
206                EMU_STRUCT_STAT st;
207
208                if (emu_stat(pConfigFileName, &st) != 0)
209                {
210                        BOX_LOG_SYS_ERROR("Failed to open configuration file "
211                                "'" << pConfigFileName << "'");
212                        return 1;
213                }
214
215                if (!(st.st_mode & S_IFREG))
216                {
217       
218                        BOX_ERROR("Failed to open configuration file '" <<
219                                pConfigFileName << "': not a file");
220                        return 1;
221                }
222        }
223
224        SC_HANDLE scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
225
226        if (!scm) 
227        {
228                BOX_ERROR("Failed to open service control manager: " <<
229                        GetErrorMessage(GetLastError()));
230                return 1;
231        }
232
233        char cmd[MAX_PATH];
234        GetModuleFileName(NULL, cmd, sizeof(cmd)-1);
235        cmd[sizeof(cmd)-1] = 0;
236
237        std::string cmdWithArgs(cmd);
238        cmdWithArgs += " -s -S \"" + rServiceName + "\"";
239
240        if (pConfigFileName != NULL)
241        {
242                cmdWithArgs += " \"";
243                cmdWithArgs += pConfigFileName;
244                cmdWithArgs += "\"";
245        }
246
247        std::string serviceDesc = "Box Backup (" + rServiceName + ")";
248
249        SC_HANDLE newService = CreateService(
250                scm, 
251                rServiceName.c_str(),
252                serviceDesc.c_str(),
253                SERVICE_ALL_ACCESS, 
254                SERVICE_WIN32_OWN_PROCESS, 
255                SERVICE_AUTO_START, 
256                SERVICE_ERROR_NORMAL, 
257                cmdWithArgs.c_str(),
258                0,0,0,0,0);
259
260        DWORD err = GetLastError();
261        CloseServiceHandle(scm);
262
263        if (!newService) 
264        {
265                switch (err)
266                {
267                        case ERROR_SERVICE_EXISTS:
268                        {
269                                BOX_ERROR("Failed to create Box Backup "
270                                        "service: it already exists");
271                        }
272                        break;
273
274                        case ERROR_SERVICE_MARKED_FOR_DELETE:
275                        {
276                                BOX_ERROR("Failed to create Box Backup "
277                                        "service: it is waiting to be deleted");
278                        }
279                        break;
280
281                        case ERROR_DUPLICATE_SERVICE_NAME:
282                        {
283                                BOX_ERROR("Failed to create Box Backup "
284                                        "service: a service with this name "
285                                        "already exists");
286                        }
287                        break;
288
289                        default:
290                        {
291                                BOX_ERROR("Failed to create Box Backup "
292                                        "service: error " <<
293                                        GetErrorMessage(GetLastError()));
294                        }
295                }
296
297                return 1;
298        }
299
300        BOX_INFO("Created Box Backup service");
301       
302        SERVICE_DESCRIPTION desc;
303        desc.lpDescription = "Backs up your data files over the Internet";
304       
305        if (!ChangeServiceConfig2(newService, SERVICE_CONFIG_DESCRIPTION,
306                &desc))
307        {
308                BOX_WARNING("Failed to set description for Box Backup "
309                        "service: " << GetErrorMessage(GetLastError()));
310        }
311
312        CloseServiceHandle(newService);
313
314        return 0;
315}
316
317int RemoveService(const std::string& rServiceName)
318{
319        SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
320
321        if (!scm) 
322        {
323                BOX_ERROR("Failed to open service control manager: " <<
324                        GetErrorMessage(GetLastError()));
325                return 1;
326        }
327
328        SC_HANDLE service = OpenService(scm, rServiceName.c_str(), 
329                SERVICE_ALL_ACCESS|DELETE);
330        DWORD err = GetLastError();
331        CloseServiceHandle(scm);
332
333        if (!service)
334        {
335                if (err == ERROR_SERVICE_DOES_NOT_EXIST ||
336                        err == ERROR_IO_PENDING) 
337                        // hello microsoft? anyone home?
338                {
339                        BOX_ERROR("Failed to open Box Backup service: "
340                                "not installed or not found");
341                }
342                else
343                {
344                        BOX_ERROR("Failed to open Box Backup service: " <<
345                                GetErrorMessage(err));
346                }
347                return 1;
348        }
349
350        SERVICE_STATUS status;
351        if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
352        {
353                err = GetLastError();
354                if (err != ERROR_SERVICE_NOT_ACTIVE)
355                {
356                        BOX_WARNING("Failed to stop Box Backup service: " <<
357                                GetErrorMessage(err));
358                }
359        }
360
361        BOOL deleted = DeleteService(service);
362        err = GetLastError();
363        CloseServiceHandle(service);
364
365        if (deleted)
366        {
367                BOX_INFO("Box Backup service deleted");
368                return 0;
369        }
370        else if (err == ERROR_SERVICE_MARKED_FOR_DELETE)
371        {
372                BOX_ERROR("Failed to remove Box Backup service: "
373                        "it is already being deleted");
374        }
375        else
376        {
377                BOX_ERROR("Failed to remove Box Backup service: " <<
378                        GetErrorMessage(err));
379        }
380
381        return 1;
382}
383
384#endif // WIN32
Note: See TracBrowser for help on using the repository browser.