xref: /reactos/base/services/nfsd/service.c (revision 6295cc50)
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.
6 
7 Copyright (C) Microsoft Corporation.  All rights reserved.
8 
9 MODULE:   service.c
10 
11 PURPOSE:  Implements functions required by all Windows NT services
12 
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   CmdDebugService(int argc, char **argv);
20   ControlHandler ( DWORD dwCtrlType );
21   GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
22 
23   ---------------------------------------------------------------------------*/
24 #include <windows.h>
25 #ifndef STANDALONE_NFSD
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <process.h>
29 #include <tchar.h>
30 
31 #include "service.h"
32 
33 // internal variables
34 SERVICE_STATUS          ssStatus;       // current status of the service
35 SERVICE_STATUS_HANDLE   sshStatusHandle;
36 DWORD                   dwErr = 0;
37 BOOL                    bDebug = FALSE;
38 TCHAR                   szErr[256];
39 
40 // internal function prototypes
41 VOID WINAPI service_ctrl(DWORD dwCtrlCode);
42 VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
43 VOID CmdInstallService();
44 VOID CmdRemoveService();
45 VOID CmdDebugService(int argc, char **argv);
46 BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
47 LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
48 
49 //
50 //  FUNCTION: main
51 //
52 //  PURPOSE: entrypoint for service
53 //
54 //  PARAMETERS:
55 //    argc - number of command line arguments
56 //    argv - array of command line arguments
57 //
58 //  RETURN VALUE:
59 //    none
60 //
61 //  COMMENTS:
62 //    main() either performs the command line task, or
63 //    call StartServiceCtrlDispatcher to register the
64 //    main service thread.  When the this call returns,
65 //    the service has stopped, so exit.
66 //
main(int argc,char ** argv)67 void __cdecl main(int argc, char **argv)
68 {
69    SERVICE_TABLE_ENTRY dispatchTable[] =
70    {
71       { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
72       { NULL, NULL}
73    };
74 
75    if ( (argc > 1) &&
76         ((*argv[1] == '-') || (*argv[1] == '/')) )
77    {
78       if ( _stricmp( "install", argv[1]+1 ) == 0 )
79       {
80          CmdInstallService();
81       }
82       else if ( _stricmp( "remove", argv[1]+1 ) == 0 )
83       {
84          CmdRemoveService();
85       }
86       else if ( _stricmp( "debug", argv[1]+1 ) == 0 )
87       {
88          bDebug = TRUE;
89          CmdDebugService(argc, argv);
90       }
91       else
92       {
93          goto dispatch;
94       }
95       exit(0);
96    }
97 
98    // if it doesn't match any of the above parameters
99    // the service control manager may be starting the service
100    // so we must call StartServiceCtrlDispatcher
101    dispatch:
102    // this is just to be friendly
103    printf( "%s -install          to install the service\n", SZAPPNAME );
104    printf( "%s -remove           to remove the service\n", SZAPPNAME );
105    printf( "%s -debug <params>   to run as a console app for debugging\n", SZAPPNAME );
106    printf( "\nStartServiceCtrlDispatcher being called.\n" );
107    printf( "This may take several seconds.  Please wait.\n" );
108 
109    if (!StartServiceCtrlDispatcher(dispatchTable))
110       AddToMessageLog(TEXT("StartServiceCtrlDispatcher failed."));
111 }
112 
113 
114 
115 //
116 //  FUNCTION: service_main
117 //
118 //  PURPOSE: To perform actual initialization of the service
119 //
120 //  PARAMETERS:
121 //    dwArgc   - number of command line arguments
122 //    lpszArgv - array of command line arguments
123 //
124 //  RETURN VALUE:
125 //    none
126 //
127 //  COMMENTS:
128 //    This routine performs the service initialization and then calls
129 //    the user defined ServiceStart() routine to perform majority
130 //    of the work.
131 //
service_main(DWORD dwArgc,LPTSTR * lpszArgv)132 void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
133 {
134 
135    // register our service control handler:
136    //
137    sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl);
138 
139    if (!sshStatusHandle)
140       goto cleanup;
141 
142    // SERVICE_STATUS members that don't change in example
143    //
144    ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
145    ssStatus.dwServiceSpecificExitCode = 0;
146 
147 
148    // report the status to the service control manager.
149    //
150    if (!ReportStatusToSCMgr(
151                            SERVICE_START_PENDING, // service state
152                            NO_ERROR,              // exit code
153                            3000))                 // wait hint
154       goto cleanup;
155 
156    ServiceStart( dwArgc, lpszArgv );
157 
158    cleanup:
159 
160    // try to report the stopped status to the service control manager.
161    //
162    if (sshStatusHandle)
163       (VOID)ReportStatusToSCMgr(
164                                SERVICE_STOPPED,
165                                dwErr,
166                                0);
167 
168    return;
169 }
170 
171 
172 
173 //
174 //  FUNCTION: service_ctrl
175 //
176 //  PURPOSE: This function is called by the SCM whenever
177 //           ControlService() is called on this service.
178 //
179 //  PARAMETERS:
180 //    dwCtrlCode - type of control requested
181 //
182 //  RETURN VALUE:
183 //    none
184 //
185 //  COMMENTS:
186 //
service_ctrl(DWORD dwCtrlCode)187 VOID WINAPI service_ctrl(DWORD dwCtrlCode)
188 {
189    // Handle the requested control code.
190    //
191 
192    switch (dwCtrlCode)
193    {
194    // Stop the service.
195    //
196    // SERVICE_STOP_PENDING should be reported before
197    // setting the Stop Event - hServerStopEvent - in
198    // ServiceStop().  This avoids a race condition
199    // which may result in a 1053 - The Service did not respond...
200    // error.
201    case SERVICE_CONTROL_STOP:
202       ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
203       ServiceStop();
204       return;
205 
206       // Update the service status.
207       //
208    case SERVICE_CONTROL_INTERROGATE:
209       break;
210 
211       // invalid control code
212       //
213    default:
214       break;
215 
216    }
217 
218    ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
219 }
220 
221 
222 
223 //
224 //  FUNCTION: ReportStatusToSCMgr()
225 //
226 //  PURPOSE: Sets the current status of the service and
227 //           reports it to the Service Control Manager
228 //
229 //  PARAMETERS:
230 //    dwCurrentState - the state of the service
231 //    dwWin32ExitCode - error code to report
232 //    dwWaitHint - worst case estimate to next checkpoint
233 //
234 //  RETURN VALUE:
235 //    TRUE  - success
236 //    FALSE - failure
237 //
238 //  COMMENTS:
239 //
ReportStatusToSCMgr(DWORD dwCurrentState,DWORD dwWin32ExitCode,DWORD dwWaitHint)240 BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
241                          DWORD dwWin32ExitCode,
242                          DWORD dwWaitHint)
243 {
244    static DWORD dwCheckPoint = 1;
245    BOOL fResult = TRUE;
246 
247 
248    if ( !bDebug ) // when debugging we don't report to the SCM
249    {
250       if (dwCurrentState == SERVICE_START_PENDING)
251          ssStatus.dwControlsAccepted = 0;
252       else
253          ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
254 
255       ssStatus.dwCurrentState = dwCurrentState;
256       ssStatus.dwWin32ExitCode = dwWin32ExitCode;
257       ssStatus.dwWaitHint = dwWaitHint;
258 
259       if ( ( dwCurrentState == SERVICE_RUNNING ) ||
260            ( dwCurrentState == SERVICE_STOPPED ) )
261          ssStatus.dwCheckPoint = 0;
262       else
263          ssStatus.dwCheckPoint = dwCheckPoint++;
264 
265 
266       // Report the status of the service to the service control manager.
267       fResult = SetServiceStatus(sshStatusHandle, &ssStatus);
268       if (!fResult)
269          AddToMessageLog(TEXT("SetServiceStatus"));
270    }
271    return fResult;
272 }
273 
274 
275 
276 //
277 //  FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
278 //
279 //  PURPOSE: Allows any thread to log an error message
280 //
281 //  PARAMETERS:
282 //    lpszMsg - text for message
283 //
284 //  RETURN VALUE:
285 //    none
286 //
287 //  COMMENTS:
288 //
AddToMessageLog(LPTSTR lpszMsg)289 VOID AddToMessageLog(LPTSTR lpszMsg)
290 {
291    TCHAR szMsg [(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100 ];
292    HANDLE  hEventSource;
293    LPTSTR  lpszStrings[2];
294 
295    if ( !bDebug )
296    {
297       dwErr = GetLastError();
298 
299       // Use event logging to log the error.
300       //
301       hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
302 
303 #ifndef __REACTOS__
304       _stprintf_s(szMsg,(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
305 #else
306       _sntprintf(szMsg,(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
307 #endif
308       lpszStrings[0] = szMsg;
309       lpszStrings[1] = lpszMsg;
310 
311       if (hEventSource != NULL)
312       {
313          ReportEvent(hEventSource, // handle of event source
314                      EVENTLOG_ERROR_TYPE,  // event type
315                      0,                    // event category
316                      0,                    // event ID
317                      NULL,                 // current user's SID
318                      2,                    // strings in lpszStrings
319                      0,                    // no bytes of raw data
320                      lpszStrings,          // array of error strings
321                      NULL);                // no raw data
322 
323          (VOID) DeregisterEventSource(hEventSource);
324       }
325    }
326 }
327 
328 
329 
330 
331 ///////////////////////////////////////////////////////////////////
332 //
333 //  The following code handles service installation and removal
334 //
335 
336 
337 //
338 //  FUNCTION: CmdInstallService()
339 //
340 //  PURPOSE: Installs the service
341 //
342 //  PARAMETERS:
343 //    none
344 //
345 //  RETURN VALUE:
346 //    none
347 //
348 //  COMMENTS:
349 //
CmdInstallService()350 void CmdInstallService()
351 {
352    SC_HANDLE   schService;
353    SC_HANDLE   schSCManager;
354 
355    TCHAR szPath[512];
356 
357    if ( GetModuleFileName( NULL, szPath, 512 ) == 0 )
358    {
359       _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
360       return;
361    }
362 
363    schSCManager = OpenSCManager(
364                                NULL,                   // machine (NULL == local)
365                                NULL,                   // database (NULL == default)
366                                SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE  // access required
367                                );
368    if ( schSCManager )
369    {
370       schService = CreateService(
371                                 schSCManager,               // SCManager database
372                                 TEXT(SZSERVICENAME),        // name of service
373                                 TEXT(SZSERVICEDISPLAYNAME), // name to display
374                                 SERVICE_QUERY_STATUS,       // desired access
375                                 SERVICE_WIN32_OWN_PROCESS,  // service type
376                                 SERVICE_AUTO_START,         // start type
377                                 SERVICE_ERROR_NORMAL,       // error control type
378                                 szPath,                     // service's binary
379                                 NULL,                       // no load ordering group
380                                 NULL,                       // no tag identifier
381                                 TEXT(SZDEPENDENCIES),       // dependencies
382                                 NULL,                       // LocalSystem account
383                                 NULL);                      // no password
384 
385       if ( schService )
386       {
387          _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
388          CloseServiceHandle(schService);
389       }
390       else
391       {
392          _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
393       }
394 
395       CloseServiceHandle(schSCManager);
396    }
397    else
398       _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
399 }
400 
401 
402 
403 //
404 //  FUNCTION: CmdRemoveService()
405 //
406 //  PURPOSE: Stops and removes the service
407 //
408 //  PARAMETERS:
409 //    none
410 //
411 //  RETURN VALUE:
412 //    none
413 //
414 //  COMMENTS:
415 //
CmdRemoveService()416 void CmdRemoveService()
417 {
418    SC_HANDLE   schService;
419    SC_HANDLE   schSCManager;
420 
421    schSCManager = OpenSCManager(
422                                NULL,                   // machine (NULL == local)
423                                NULL,                   // database (NULL == default)
424                                SC_MANAGER_CONNECT   // access required
425                                );
426    if ( schSCManager )
427    {
428       schService = OpenService(schSCManager, TEXT(SZSERVICENAME), DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
429 
430       if (schService)
431       {
432          // try to stop the service
433          if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
434          {
435             _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
436             Sleep( 1000 );
437 
438             while ( QueryServiceStatus( schService, &ssStatus ) )
439             {
440                if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
441                {
442                   _tprintf(TEXT("."));
443                   Sleep( 1000 );
444                }
445                else
446                   break;
447             }
448 
449             if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
450                _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
451             else
452                _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
453 
454          }
455 
456          // now remove the service
457          if ( DeleteService(schService) )
458             _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
459          else
460             _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
461 
462 
463          CloseServiceHandle(schService);
464       }
465       else
466          _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
467 
468       CloseServiceHandle(schSCManager);
469    }
470    else
471       _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
472 }
473 
474 
475 
476 
477 ///////////////////////////////////////////////////////////////////
478 //
479 //  The following code is for running the service as a console app
480 //
481 
482 
483 //
484 //  FUNCTION: CmdDebugService(int argc, char ** argv)
485 //
486 //  PURPOSE: Runs the service as a console application
487 //
488 //  PARAMETERS:
489 //    argc - number of command line arguments
490 //    argv - array of command line arguments
491 //
492 //  RETURN VALUE:
493 //    none
494 //
495 //  COMMENTS:
496 //
CmdDebugService(int argc,char ** argv)497 void CmdDebugService(int argc, char ** argv)
498 {
499    DWORD dwArgc;
500    LPTSTR *lpszArgv;
501 
502 #ifdef UNICODE
503    lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
504    if (NULL == lpszArgv)
505    {
506        // CommandLineToArvW failed!!
507        _tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n"));
508        return;
509    }
510 #else
511    dwArgc   = (DWORD) argc;
512    lpszArgv = argv;
513 #endif
514 
515    _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
516 
517    SetConsoleCtrlHandler( ControlHandler, TRUE );
518 
519    ServiceStart( dwArgc, lpszArgv );
520 
521 #ifdef UNICODE
522 // Must free memory allocated for arguments
523 
524    GlobalFree(lpszArgv);
525 #endif // UNICODE
526 
527 }
528 
529 
530 //
531 //  FUNCTION: ControlHandler ( DWORD dwCtrlType )
532 //
533 //  PURPOSE: Handled console control events
534 //
535 //  PARAMETERS:
536 //    dwCtrlType - type of control event
537 //
538 //  RETURN VALUE:
539 //    True - handled
540 //    False - unhandled
541 //
542 //  COMMENTS:
543 //
ControlHandler(DWORD dwCtrlType)544 BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
545 {
546    switch ( dwCtrlType )
547    {
548    case CTRL_BREAK_EVENT:  // use Ctrl+C or Ctrl+Break to simulate
549    case CTRL_C_EVENT:      // SERVICE_CONTROL_STOP in debug mode
550       _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
551       ServiceStop();
552       return TRUE;
553       break;
554 
555    }
556    return FALSE;
557 }
558 
559 //
560 //  FUNCTION: GetLastErrorText
561 //
562 //  PURPOSE: copies error message text to string
563 //
564 //  PARAMETERS:
565 //    lpszBuf - destination buffer
566 //    dwSize - size of buffer
567 //
568 //  RETURN VALUE:
569 //    destination buffer
570 //
571 //  COMMENTS:
572 //
GetLastErrorText(LPTSTR lpszBuf,DWORD dwSize)573 LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
574 {
575    DWORD dwRet;
576    LPTSTR lpszTemp = NULL;
577 
578    dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
579                           NULL,
580                           GetLastError(),
581                           LANG_NEUTRAL,
582                           (LPTSTR)&lpszTemp,
583                           0,
584                           NULL );
585 
586    // supplied buffer is not long enough
587    if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
588       lpszBuf[0] = TEXT('\0');
589    else
590    {
591        if (NULL != lpszTemp)
592        {
593            lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0');  //remove cr and newline character
594 #ifndef __REACTOS__
595            _stprintf_s( lpszBuf, dwSize, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
596 #else
597            _sntprintf( lpszBuf, dwSize, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
598 #endif
599        }
600    }
601 
602    if ( NULL != lpszTemp )
603       LocalFree((HLOCAL) lpszTemp );
604 
605    return lpszBuf;
606 }
607 #endif
608