cosmetics
[tomato.git] / release / src / router / openvpn / service-win32 / service.c
blobd5211bd20bde181a963e0205dae8bd43a484cd2e
1 /*---------------------------------------------------------------------------
2 THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
3 ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
4 TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
5 PARTICULAR PURPOSE.
7 Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved.
9 MODULE: service.c
11 PURPOSE: Implements functions required by all Windows NT services
13 FUNCTIONS:
14 main(int argc, char **argv);
15 service_ctrl(DWORD dwCtrlCode);
16 service_main(DWORD dwArgc, LPTSTR *lpszArgv);
17 CmdInstallService();
18 CmdRemoveService();
19 CmdStartService();
20 CmdDebugService(int argc, char **argv);
21 ControlHandler ( DWORD dwCtrlType );
22 GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
24 ---------------------------------------------------------------------------*/
26 #include <windows.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <process.h>
30 #include <tchar.h>
32 #include "service.h"
34 // internal variables
35 SERVICE_STATUS ssStatus; // current status of the service
36 SERVICE_STATUS_HANDLE sshStatusHandle;
37 DWORD dwErr = 0;
38 BOOL bDebug = FALSE;
39 TCHAR szErr[256];
41 // internal function prototypes
42 VOID WINAPI service_ctrl(DWORD dwCtrlCode);
43 VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
44 int CmdInstallService();
45 int CmdRemoveService();
46 int CmdStartService();
47 VOID CmdDebugService(int argc, char **argv);
48 BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
49 LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
52 // FUNCTION: main
54 // PURPOSE: entrypoint for service
56 // PARAMETERS:
57 // argc - number of command line arguments
58 // argv - array of command line arguments
60 // RETURN VALUE:
61 // none
63 // COMMENTS:
64 // main() either performs the command line task, or
65 // call StartServiceCtrlDispatcher to register the
66 // main service thread. When the this call returns,
67 // the service has stopped, so exit.
69 int __cdecl main(int argc, char **argv)
71 SERVICE_TABLE_ENTRY dispatchTable[] =
73 { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
74 { NULL, NULL}
77 if ( (argc > 1) &&
78 ((*argv[1] == '-') || (*argv[1] == '/')) )
80 if ( _stricmp( "install", argv[1]+1 ) == 0 )
82 return CmdInstallService();
84 else if ( _stricmp( "remove", argv[1]+1 ) == 0 )
86 return CmdRemoveService();
88 else if ( _stricmp( "start", argv[1]+1 ) == 0)
90 return CmdStartService();
92 else if ( _stricmp( "debug", argv[1]+1 ) == 0 )
94 bDebug = TRUE;
95 CmdDebugService(argc, argv);
97 else
99 goto dispatch;
101 return 0;
104 // if it doesn't match any of the above parameters
105 // the service control manager may be starting the service
106 // so we must call StartServiceCtrlDispatcher
107 dispatch:
108 // this is just to be friendly
109 printf( "%s -install to install the service\n", SZAPPNAME );
110 printf( "%s -start to start the service\n", SZAPPNAME );
111 printf( "%s -remove to remove the service\n", SZAPPNAME );
112 printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME );
113 printf( "\nStartServiceCtrlDispatcher being called.\n" );
114 printf( "This may take several seconds. Please wait.\n" );
116 if (!StartServiceCtrlDispatcher(dispatchTable))
117 AddToMessageLog(MSG_FLAGS_ERROR, TEXT("StartServiceCtrlDispatcher failed."));
119 return 0;
125 // FUNCTION: service_main
127 // PURPOSE: To perform actual initialization of the service
129 // PARAMETERS:
130 // dwArgc - number of command line arguments
131 // lpszArgv - array of command line arguments
133 // RETURN VALUE:
134 // none
136 // COMMENTS:
137 // This routine performs the service initialization and then calls
138 // the user defined ServiceStart() routine to perform majority
139 // of the work.
141 void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
144 // register our service control handler:
146 sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl);
148 if (!sshStatusHandle)
149 goto cleanup;
151 // SERVICE_STATUS members that don't change in example
153 ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
154 ssStatus.dwServiceSpecificExitCode = 0;
157 // report the status to the service control manager.
159 if (!ReportStatusToSCMgr(
160 SERVICE_START_PENDING, // service state
161 NO_ERROR, // exit code
162 3000)) // wait hint
163 goto cleanup;
166 ServiceStart( dwArgc, lpszArgv );
168 cleanup:
170 // try to report the stopped status to the service control manager.
172 if (sshStatusHandle)
173 (VOID)ReportStatusToSCMgr(
174 SERVICE_STOPPED,
175 dwErr,
178 return;
184 // FUNCTION: service_ctrl
186 // PURPOSE: This function is called by the SCM whenever
187 // ControlService() is called on this service.
189 // PARAMETERS:
190 // dwCtrlCode - type of control requested
192 // RETURN VALUE:
193 // none
195 // COMMENTS:
197 VOID WINAPI service_ctrl(DWORD dwCtrlCode)
199 // Handle the requested control code.
201 switch (dwCtrlCode)
203 // Stop the service.
205 // SERVICE_STOP_PENDING should be reported before
206 // setting the Stop Event - hServerStopEvent - in
207 // ServiceStop(). This avoids a race condition
208 // which may result in a 1053 - The Service did not respond...
209 // error.
210 case SERVICE_CONTROL_STOP:
211 ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
212 ServiceStop();
213 return;
215 // Update the service status.
217 case SERVICE_CONTROL_INTERROGATE:
218 break;
220 // invalid control code
222 default:
223 break;
227 ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
233 // FUNCTION: ReportStatusToSCMgr()
235 // PURPOSE: Sets the current status of the service and
236 // reports it to the Service Control Manager
238 // PARAMETERS:
239 // dwCurrentState - the state of the service
240 // dwWin32ExitCode - error code to report
241 // dwWaitHint - worst case estimate to next checkpoint
243 // RETURN VALUE:
244 // TRUE - success
245 // FALSE - failure
247 // COMMENTS:
249 BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
250 DWORD dwWin32ExitCode,
251 DWORD dwWaitHint)
253 static DWORD dwCheckPoint = 1;
254 BOOL fResult = TRUE;
257 if ( !bDebug ) // when debugging we don't report to the SCM
259 if (dwCurrentState == SERVICE_START_PENDING)
260 ssStatus.dwControlsAccepted = 0;
261 else
262 ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
264 ssStatus.dwCurrentState = dwCurrentState;
265 ssStatus.dwWin32ExitCode = dwWin32ExitCode;
266 ssStatus.dwWaitHint = dwWaitHint;
268 if ( ( dwCurrentState == SERVICE_RUNNING ) ||
269 ( dwCurrentState == SERVICE_STOPPED ) )
270 ssStatus.dwCheckPoint = 0;
271 else
272 ssStatus.dwCheckPoint = dwCheckPoint++;
275 // Report the status of the service to the service control manager.
277 if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus)))
279 AddToMessageLog(MSG_FLAGS_ERROR, TEXT("SetServiceStatus"));
282 return fResult;
288 // FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
290 // PURPOSE: Allows any thread to log an error message
292 // PARAMETERS:
293 // lpszMsg - text for message
295 // RETURN VALUE:
296 // none
298 // COMMENTS:
300 void AddToMessageLog(DWORD flags, LPTSTR lpszMsg)
302 TCHAR szMsg [(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100 ];
303 HANDLE hEventSource;
304 LPCSTR lpszStrings[2];
306 if ( !bDebug )
308 if (flags & MSG_FLAGS_SYS_CODE)
309 dwErr = GetLastError();
310 else
311 dwErr = 0;
313 // Use event logging to log the error.
315 hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
317 _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), (int)dwErr);
318 lpszStrings[0] = szMsg;
319 lpszStrings[1] = lpszMsg;
321 if (hEventSource != NULL)
323 ReportEvent(hEventSource, // handle of event source
324 // event type
325 (flags & MSG_FLAGS_ERROR)
326 ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
327 0, // event category
328 0, // event ID
329 NULL, // current user's SID
330 2, // strings in lpszStrings
331 0, // no bytes of raw data
332 lpszStrings, // array of error strings
333 NULL); // no raw data
335 (VOID) DeregisterEventSource(hEventSource);
340 void ResetError (void)
342 dwErr = 0;
345 ///////////////////////////////////////////////////////////////////
347 // The following code handles service installation and removal
352 // FUNCTION: CmdInstallService()
354 // PURPOSE: Installs the service
356 // PARAMETERS:
357 // none
359 // RETURN VALUE:
360 // 0 if success
362 // COMMENTS:
364 int CmdInstallService()
366 SC_HANDLE schService;
367 SC_HANDLE schSCManager;
369 TCHAR szPath[512];
371 int ret = 0;
373 if ( GetModuleFileName( NULL, szPath, 512 ) == 0 )
375 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
376 return 1;
379 schSCManager = OpenSCManager(
380 NULL, // machine (NULL == local)
381 NULL, // database (NULL == default)
382 SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE // access required
384 if ( schSCManager )
386 schService = CreateService(
387 schSCManager, // SCManager database
388 TEXT(SZSERVICENAME), // name of service
389 TEXT(SZSERVICEDISPLAYNAME), // name to display
390 SERVICE_QUERY_STATUS, // desired access
391 SERVICE_WIN32_OWN_PROCESS, // service type
392 SERVICE_DEMAND_START, // start type -- alternative: SERVICE_AUTO_START
393 SERVICE_ERROR_NORMAL, // error control type
394 szPath, // service's binary
395 NULL, // no load ordering group
396 NULL, // no tag identifier
397 TEXT(SZDEPENDENCIES), // dependencies
398 NULL, // LocalSystem account
399 NULL); // no password
401 if ( schService )
403 _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
404 CloseServiceHandle(schService);
406 else
408 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
409 ret = 1;
412 CloseServiceHandle(schSCManager);
414 else
416 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
417 ret = 1;
419 return ret;
423 // FUNCTION: CmdStartService()
425 // PURPOSE: Start the service
427 // PARAMETERS:
428 // none
430 // RETURN VALUE:
431 // 0 if success
433 // COMMENTS:
435 int CmdStartService()
437 int ret = 0;
439 SC_HANDLE schSCManager;
440 SC_HANDLE schService;
443 // Open a handle to the SC Manager database.
444 schSCManager = OpenSCManager(
445 NULL, // local machine
446 NULL, // ServicesActive database
447 SC_MANAGER_ALL_ACCESS); // full access rights
449 if (NULL == schSCManager) {
450 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
451 ret = 1;
454 schService = OpenService(
455 schSCManager, // SCM database
456 SZSERVICENAME, // service name
457 SERVICE_ALL_ACCESS);
459 if (schService == NULL) {
460 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
461 ret = 1;
464 if (!StartService(
465 schService, // handle to service
466 0, // number of arguments
467 NULL) ) // no arguments
469 _tprintf(TEXT("StartService failed - %s\n"), GetLastErrorText(szErr,256));
470 ret = 1;
472 else
474 _tprintf(TEXT("Service Started\n"));
475 ret = 0;
477 CloseServiceHandle(schService);
478 CloseServiceHandle(schSCManager);
479 return ret;
483 // FUNCTION: CmdRemoveService()
485 // PURPOSE: Stops and removes the service
487 // PARAMETERS:
488 // none
490 // RETURN VALUE:
491 // 0 if success
493 // COMMENTS:
495 int CmdRemoveService()
497 SC_HANDLE schService;
498 SC_HANDLE schSCManager;
500 int ret = 0;
502 schSCManager = OpenSCManager(
503 NULL, // machine (NULL == local)
504 NULL, // database (NULL == default)
505 SC_MANAGER_CONNECT // access required
507 if ( schSCManager )
509 schService = OpenService(schSCManager, TEXT(SZSERVICENAME), DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
511 if (schService)
513 // try to stop the service
514 if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
516 _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
517 Sleep( 1000 );
519 while ( QueryServiceStatus( schService, &ssStatus ) )
521 if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
523 _tprintf(TEXT("."));
524 Sleep( 1000 );
526 else
527 break;
530 if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
531 _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
532 else
534 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
535 ret = 1;
540 // now remove the service
541 if ( DeleteService(schService) )
542 _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
543 else
545 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
546 ret = 1;
550 CloseServiceHandle(schService);
552 else
554 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
555 ret = 1;
558 CloseServiceHandle(schSCManager);
560 else
562 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
563 ret = 1;
565 return ret;
571 ///////////////////////////////////////////////////////////////////
573 // The following code is for running the service as a console app
578 // FUNCTION: CmdDebugService(int argc, char ** argv)
580 // PURPOSE: Runs the service as a console application
582 // PARAMETERS:
583 // argc - number of command line arguments
584 // argv - array of command line arguments
586 // RETURN VALUE:
587 // none
589 // COMMENTS:
591 void CmdDebugService(int argc, char ** argv)
593 DWORD dwArgc;
594 LPTSTR *lpszArgv;
596 #ifdef UNICODE
597 lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
598 if (NULL == lpszArgv)
600 // CommandLineToArvW failed!!
601 _tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n"));
602 return;
604 #else
605 dwArgc = (DWORD) argc;
606 lpszArgv = argv;
607 #endif
609 _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
611 SetConsoleCtrlHandler( ControlHandler, TRUE );
613 ServiceStart( dwArgc, lpszArgv );
615 #ifdef UNICODE
616 // Must free memory allocated for arguments
618 GlobalFree(lpszArgv);
619 #endif // UNICODE
625 // FUNCTION: ControlHandler ( DWORD dwCtrlType )
627 // PURPOSE: Handled console control events
629 // PARAMETERS:
630 // dwCtrlType - type of control event
632 // RETURN VALUE:
633 // True - handled
634 // False - unhandled
636 // COMMENTS:
638 BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
640 switch ( dwCtrlType )
642 case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
643 case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
644 _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
645 ServiceStop();
646 return TRUE;
647 break;
650 return FALSE;
654 // FUNCTION: GetLastErrorText
656 // PURPOSE: copies error message text to string
658 // PARAMETERS:
659 // lpszBuf - destination buffer
660 // dwSize - size of buffer
662 // RETURN VALUE:
663 // destination buffer
665 // COMMENTS:
667 LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
669 DWORD dwRet;
670 LPTSTR lpszTemp = NULL;
672 dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
673 NULL,
674 GetLastError(),
675 LANG_NEUTRAL,
676 (LPTSTR)&lpszTemp,
678 NULL );
680 // supplied buffer is not long enough
681 if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
682 lpszBuf[0] = TEXT('\0');
683 else
685 lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
686 _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, (int)GetLastError() );
689 if ( lpszTemp )
690 LocalFree((HLOCAL) lpszTemp );
692 return lpszBuf;