2 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
25 Change History (most recent first):
28 Revision 1.1 2004/01/30 02:58:39 bradley
29 mDNSResponder Windows Service. Provides global Rendezvous support with an IPC interface.
36 #define _WIN32_WINNT 0x0400
38 #include "CommonServices.h"
39 #include "DebugServices.h"
41 #include "DNSSDDirect.h"
42 #include "RMxServer.h"
46 #include "mDNSClientAPI.h"
49 #pragma mark == Constants ==
52 //===========================================================================================================================
54 //===========================================================================================================================
56 #define DEBUG_NAME "[Server] "
57 #define kServiceName "Rendezvous"
58 #define kServiceDependencies "Tcpip\0winmgmt\0\0"
61 #pragma mark == Prototypes ==
64 //===========================================================================================================================
66 //===========================================================================================================================
68 int main( int argc
, char *argv
[] );
69 static void Usage( void );
70 static BOOL WINAPI
ConsoleControlHandler( DWORD inControlEvent
);
71 static OSStatus
InstallService( const char *inName
, const char *inDisplayName
, const char *inDescription
, const char *inPath
);
72 static OSStatus
RemoveService( const char *inName
);
73 static OSStatus
SetServiceDescription( SC_HANDLE inSCM
, const char *inServiceName
, const char *inDescription
);
74 static void ReportStatus( int inType
, const char *inFormat
, ... );
75 static OSStatus
RunDirect( int argc
, char *argv
[] );
77 static void WINAPI
ServiceMain( DWORD argc
, LPSTR argv
[] );
78 static OSStatus
ServiceSetupEventLogging( void );
79 static void WINAPI
ServiceControlHandler( DWORD inControl
);
81 static OSStatus
ServiceRun( int argc
, char *argv
[] );
82 static void ServiceStop( void );
84 static OSStatus
ServiceSpecificInitialize( int argc
, char *argv
[] );
85 static OSStatus
ServiceSpecificRun( int argc
, char *argv
[] );
86 static OSStatus
ServiceSpecificStop( void );
87 static void ServiceSpecificFinalize( int argc
, char *argv
[] );
89 #include "mDNSClientAPI.h"
92 #pragma mark == Globals ==
95 //===========================================================================================================================
97 //===========================================================================================================================
99 DEBUG_LOCAL BOOL gServiceQuietMode
= FALSE
;
100 DEBUG_LOCAL SERVICE_TABLE_ENTRY gServiceDispatchTable
[] =
102 { kServiceName
, ServiceMain
},
105 DEBUG_LOCAL SERVICE_STATUS gServiceStatus
;
106 DEBUG_LOCAL SERVICE_STATUS_HANDLE gServiceStatusHandle
= NULL
;
107 DEBUG_LOCAL HANDLE gServiceEventSource
= NULL
;
108 DEBUG_LOCAL
bool gServiceAllowRemote
= false;
109 DEBUG_LOCAL
int gServiceCacheEntryCount
= 0; // 0 means to use the DNS-SD default.
115 //===========================================================================================================================
117 //===========================================================================================================================
119 int main( int argc
, char *argv
[] )
126 debug_initialize( kDebugOutputTypeMetaConsole
);
127 debug_set_property( kDebugPropertyTagPrintLevel
, kDebugLevelVerbose
);
129 // Install a Console Control Handler to handle things like control-c signals.
131 ok
= SetConsoleCtrlHandler( ConsoleControlHandler
, TRUE
);
132 err
= translate_errno( ok
, (OSStatus
) GetLastError(), kUnknownErr
);
133 require_noerr( err
, exit
);
135 // Default to automatically starting the service dispatcher if no extra arguments are specified.
137 start
= ( argc
<= 1 );
141 for( i
= 1; i
< argc
; ++i
)
143 if( strcmp( argv
[ i
], "-install" ) == 0 ) // Install
148 LoadStringA( GetModuleHandle( NULL
), IDS_SERVICE_DESCRIPTION
, desc
, sizeof( desc
) );
149 err
= InstallService( kServiceName
, kServiceName
, desc
, argv
[ 0 ] );
152 ReportStatus( EVENTLOG_ERROR_TYPE
, "install service failed (%d)\n", err
);
156 else if( strcmp( argv
[ i
], "-remove" ) == 0 ) // Remove
158 err
= RemoveService( kServiceName
);
161 ReportStatus( EVENTLOG_ERROR_TYPE
, "remove service failed (%d)\n", err
);
165 else if( strcmp( argv
[ i
], "-start" ) == 0 ) // Start
169 else if( strcmp( argv
[ i
], "-server" ) == 0 ) // Server
171 err
= RunDirect( argc
, argv
);
174 ReportStatus( EVENTLOG_ERROR_TYPE
, "run service directly failed (%d)\n", err
);
178 else if( strcmp( argv
[ i
], "-q" ) == 0 ) // Quiet Mode (toggle)
180 gServiceQuietMode
= !gServiceQuietMode
;
182 else if( strcmp( argv
[ i
], "-remote" ) == 0 ) // Allow Remote Connections
184 gServiceAllowRemote
= true;
186 else if( strcmp( argv
[ i
], "-cache" ) == 0 ) // Number of mDNS cache entries
190 ReportStatus( EVENTLOG_ERROR_TYPE
, "-cache used, but number of cache entries not specified\n" );
194 gServiceCacheEntryCount
= atoi( argv
[ ++i
] );
196 else if( ( strcmp( argv
[ i
], "-help" ) == 0 ) || // Help
197 ( strcmp( argv
[ i
], "-h" ) == 0 ) )
211 // Start the service dispatcher if requested. This does not return until all services have terminated. If any
212 // global initialization is needed, it should be done before starting the service dispatcher, but only if it
213 // will take less than 30 seconds. Otherwise, use a separate thread for it and start the dispatcher immediately.
217 ok
= StartServiceCtrlDispatcher( gServiceDispatchTable
);
218 err
= translate_errno( ok
, (OSStatus
) GetLastError(), kInUseErr
);
221 ReportStatus( EVENTLOG_ERROR_TYPE
, "start service dispatcher failed (%d)\n", err
);
228 dlog( kDebugLevelTrace
, DEBUG_NAME
"exited (%d %m)\n", err
, err
);
232 //===========================================================================================================================
234 //===========================================================================================================================
236 static void Usage( void )
238 fprintf( stderr
, "\n" );
239 fprintf( stderr
, "mDNSResponder 1.0d1\n" );
240 fprintf( stderr
, "\n" );
241 fprintf( stderr
, " <no args> Runs the service normally\n" );
242 fprintf( stderr
, " -install Creates the service and starts it\n" );
243 fprintf( stderr
, " -remove Stops the service and deletes it\n" );
244 fprintf( stderr
, " -start Starts the service dispatcher after processing all other arguments\n" );
245 fprintf( stderr
, " -server Runs the service directly as a server (for debugging)\n" );
246 fprintf( stderr
, " -q Toggles Quiet Mode (no events or output)\n" );
247 fprintf( stderr
, " -remote Allow remote connections\n" );
248 fprintf( stderr
, " -cache n Number of mDNS cache entries (defaults to %d)\n", kDNSServiceCacheEntryCountDefault
);
249 fprintf( stderr
, " -h[elp] Display Help/Usage\n" );
250 fprintf( stderr
, "\n" );
253 //===========================================================================================================================
254 // ConsoleControlHandler
255 //===========================================================================================================================
257 static BOOL WINAPI
ConsoleControlHandler( DWORD inControlEvent
)
263 switch( inControlEvent
)
266 case CTRL_BREAK_EVENT
:
267 case CTRL_CLOSE_EVENT
:
268 case CTRL_LOGOFF_EVENT
:
269 case CTRL_SHUTDOWN_EVENT
:
270 err
= ServiceSpecificStop();
271 require_noerr( err
, exit
);
284 //===========================================================================================================================
286 //===========================================================================================================================
288 static OSStatus
InstallService( const char *inName
, const char *inDisplayName
, const char *inDescription
, const char *inPath
)
294 TCHAR fullPath
[ MAX_PATH
];
301 // Get a full path to the executable since a relative path may have been specified.
303 size
= GetFullPathName( inPath
, sizeof( fullPath
), fullPath
, &namePtr
);
304 err
= translate_errno( size
> 0, (OSStatus
) GetLastError(), kPathErr
);
305 require_noerr( err
, exit
);
307 // Create the service and start it.
309 scm
= OpenSCManager( NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
310 err
= translate_errno( scm
, (OSStatus
) GetLastError(), kOpenErr
);
311 require_noerr( err
, exit
);
313 service
= CreateService( scm
, inName
, inDisplayName
, SERVICE_ALL_ACCESS
, SERVICE_WIN32_SHARE_PROCESS
,
314 SERVICE_AUTO_START
, SERVICE_ERROR_NORMAL
, inPath
, NULL
, NULL
, kServiceDependencies
,
316 err
= translate_errno( service
, (OSStatus
) GetLastError(), kDuplicateErr
);
317 require_noerr( err
, exit
);
321 err
= SetServiceDescription( scm
, inName
, inDescription
);
325 ok
= StartService( service
, 0, NULL
);
326 err
= translate_errno( ok
, (OSStatus
) GetLastError(), kInUseErr
);
327 require_noerr( err
, exit
);
329 ReportStatus( EVENTLOG_SUCCESS
, "installed service \"%s\"/\"%s\" at \"%s\"\n", inName
, inDisplayName
, inPath
);
335 CloseServiceHandle( service
);
339 CloseServiceHandle( scm
);
344 //===========================================================================================================================
346 //===========================================================================================================================
348 static OSStatus
RemoveService( const char *inName
)
354 SERVICE_STATUS status
;
359 // Open a connection to the service.
361 scm
= OpenSCManager( 0, 0, SC_MANAGER_ALL_ACCESS
);
362 err
= translate_errno( scm
, (OSStatus
) GetLastError(), kOpenErr
);
363 require_noerr( err
, exit
);
365 service
= OpenService( scm
, inName
, SERVICE_STOP
| SERVICE_QUERY_STATUS
| DELETE
);
366 err
= translate_errno( service
, (OSStatus
) GetLastError(), kNotFoundErr
);
367 require_noerr( err
, exit
);
369 // Stop the service, if it is not already stopped, then delete it.
371 ok
= QueryServiceStatus( service
, &status
);
372 err
= translate_errno( ok
, (OSStatus
) GetLastError(), kAuthenticationErr
);
373 require_noerr( err
, exit
);
375 if( status
.dwCurrentState
!= SERVICE_STOPPED
)
377 ok
= ControlService( service
, SERVICE_CONTROL_STOP
, &status
);
378 check_translated_errno( ok
, (OSStatus
) GetLastError(), kAuthenticationErr
);
381 ok
= DeleteService( service
);
382 err
= translate_errno( ok
, (OSStatus
) GetLastError(), kDeletedErr
);
383 require_noerr( err
, exit
);
385 ReportStatus( EVENTLOG_SUCCESS
, "Removed service \"%s\"\n", inName
);
391 CloseServiceHandle( service
);
395 CloseServiceHandle( scm
);
400 //===========================================================================================================================
401 // SetServiceDescription
402 //===========================================================================================================================
404 static OSStatus
SetServiceDescription( SC_HANDLE inSCM
, const char *inServiceName
, const char *inDescription
)
409 SERVICE_DESCRIPTION description
;
412 check( inServiceName
);
413 check( inDescription
);
418 // Open the database (if not provided) and lock it to prevent other access while re-configuring.
422 inSCM
= OpenSCManager( NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
423 err
= translate_errno( inSCM
, (OSStatus
) GetLastError(), kOpenErr
);
424 require_noerr( err
, exit
);
427 lock
= LockServiceDatabase( inSCM
);
428 err
= translate_errno( lock
, (OSStatus
) GetLastError(), kInUseErr
);
429 require_noerr( err
, exit
);
431 // Open a handle to the service.
433 service
= OpenService( inSCM
, inServiceName
, SERVICE_CHANGE_CONFIG
);
434 err
= translate_errno( service
, (OSStatus
) GetLastError(), kNotFoundErr
);
435 require_noerr( err
, exit
);
437 // Change the description.
439 description
.lpDescription
= (char *) inDescription
;
440 ok
= ChangeServiceConfig2( service
, SERVICE_CONFIG_DESCRIPTION
, &description
);
441 err
= translate_errno( ok
, (OSStatus
) GetLastError(), kParamErr
);
442 require_noerr( err
, exit
);
447 // Close the service and release the lock.
451 CloseServiceHandle( service
);
455 UnlockServiceDatabase( lock
);
460 //===========================================================================================================================
462 //===========================================================================================================================
464 static void ReportStatus( int inType
, const char *inFormat
, ... )
466 if( !gServiceQuietMode
)
470 va_start( args
, inFormat
);
471 if( gServiceEventSource
)
475 const char * array
[ 1 ];
477 vsprintf( s
, inFormat
, args
);
479 ok
= ReportEvent( gServiceEventSource
, (WORD
) inType
, 0, 0x20000001L
, NULL
, 1, 0, array
, NULL
);
480 check_translated_errno( ok
, GetLastError(), kUnknownErr
);
486 n
= vfprintf( stderr
, inFormat
, args
);
493 //===========================================================================================================================
495 //===========================================================================================================================
497 static OSStatus
RunDirect( int argc
, char *argv
[] )
504 err
= ServiceSpecificInitialize( argc
, argv
);
505 require_noerr( err
, exit
);
508 // Run the service. This does not return until the service quits or is stopped.
510 ReportStatus( EVENTLOG_SUCCESS
, "Running \"%s\" service directly\n", kServiceName
);
512 err
= ServiceSpecificRun( argc
, argv
);
513 require_noerr( err
, exit
);
520 ServiceSpecificFinalize( argc
, argv
);
529 //===========================================================================================================================
531 //===========================================================================================================================
533 static void WINAPI
ServiceMain( DWORD argc
, LPSTR argv
[] )
539 err
= ServiceSetupEventLogging();
540 require_noerr( err
, exit
);
542 // Initialize the service status and register the service control handler with the name of the service.
544 gServiceStatus
.dwServiceType
= SERVICE_WIN32_SHARE_PROCESS
;
545 gServiceStatus
.dwCurrentState
= 0;
546 gServiceStatus
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
;
547 gServiceStatus
.dwWin32ExitCode
= NO_ERROR
;
548 gServiceStatus
.dwServiceSpecificExitCode
= NO_ERROR
;
549 gServiceStatus
.dwCheckPoint
= 0;
550 gServiceStatus
.dwWaitHint
= 0;
552 gServiceStatusHandle
= RegisterServiceCtrlHandler( argv
[ 0 ], ServiceControlHandler
);
553 err
= translate_errno( gServiceStatusHandle
, (OSStatus
) GetLastError(), kInUseErr
);
554 require_noerr( err
, exit
);
556 // Setup the description. This should be done by the installer, but it doesn't support that yet.
559 LoadStringA( GetModuleHandle( NULL
), IDS_SERVICE_DESCRIPTION
, desc
, sizeof( desc
) );
560 err
= SetServiceDescription( NULL
, kServiceName
, desc
);
563 // Mark the service as starting.
565 gServiceStatus
.dwCurrentState
= SERVICE_START_PENDING
;
566 gServiceStatus
.dwCheckPoint
= 0;
567 gServiceStatus
.dwWaitHint
= 5000; // 5 seconds
568 ok
= SetServiceStatus( gServiceStatusHandle
, &gServiceStatus
);
569 check_translated_errno( ok
, GetLastError(), kParamErr
);
571 // Run the service. This does not return until the service quits or is stopped.
573 err
= ServiceRun( (int) argc
, argv
);
576 gServiceStatus
.dwWin32ExitCode
= ERROR_SERVICE_SPECIFIC_ERROR
;
577 gServiceStatus
.dwServiceSpecificExitCode
= (DWORD
) err
;
580 // Service-specific work is done so mark the service as stopped.
582 gServiceStatus
.dwCurrentState
= SERVICE_STOPPED
;
583 ok
= SetServiceStatus( gServiceStatusHandle
, &gServiceStatus
);
584 check_translated_errno( ok
, GetLastError(), kParamErr
);
586 // Note: The service status handle should not be closed according to Microsoft documentation.
589 if( gServiceEventSource
)
591 ok
= DeregisterEventSource( gServiceEventSource
);
592 check_translated_errno( ok
, GetLastError(), kUnknownErr
);
593 gServiceEventSource
= NULL
;
597 //===========================================================================================================================
598 // ServiceSetupEventLogging
599 //===========================================================================================================================
601 static OSStatus
ServiceSetupEventLogging( void )
606 DWORD typesSupported
;
607 char path
[ MAX_PATH
];
612 // Add/Open source name as a sub-key under the Application key in the EventLog registry key.
614 s
= "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\" kServiceName
;
615 err
= RegCreateKey( HKEY_LOCAL_MACHINE
, s
, &key
);
616 require_noerr( err
, exit
);
618 // Add the name to the EventMessageFile subkey.
621 GetModuleFileName( NULL
, path
, MAX_PATH
);
622 n
= (DWORD
)( strlen( path
) + 1 );
623 err
= RegSetValueEx( key
, "EventMessageFile", 0, REG_EXPAND_SZ
, (const LPBYTE
) path
, n
);
624 require_noerr( err
, exit
);
626 // Set the supported event types in the TypesSupported subkey.
630 | EVENTLOG_ERROR_TYPE
631 | EVENTLOG_WARNING_TYPE
632 | EVENTLOG_INFORMATION_TYPE
633 | EVENTLOG_AUDIT_SUCCESS
634 | EVENTLOG_AUDIT_FAILURE
;
635 err
= RegSetValueEx( key
, "TypesSupported", 0, REG_DWORD
, (const LPBYTE
) &typesSupported
, sizeof( DWORD
) );
636 require_noerr( err
, exit
);
638 // Set up the event source.
640 gServiceEventSource
= RegisterEventSource( NULL
, kServiceName
);
641 err
= translate_errno( gServiceEventSource
, (OSStatus
) GetLastError(), kParamErr
);
642 require_noerr( err
, exit
);
652 //===========================================================================================================================
653 // ServiceControlHandler
654 //===========================================================================================================================
656 static void WINAPI
ServiceControlHandler( DWORD inControl
)
664 case SERVICE_CONTROL_STOP
:
665 dlog( kDebugLevelNotice
, DEBUG_NAME
"ServiceControlHandler: SERVICE_CONTROL_STOP\n" );
672 dlog( kDebugLevelNotice
, DEBUG_NAME
"ServiceControlHandler: event (0x%08X)\n", inControl
);
676 if( setStatus
&& gServiceStatusHandle
)
678 ok
= SetServiceStatus( gServiceStatusHandle
, &gServiceStatus
);
679 check_translated_errno( ok
, GetLastError(), kUnknownErr
);
683 //===========================================================================================================================
685 //===========================================================================================================================
687 static OSStatus
ServiceRun( int argc
, char *argv
[] )
693 DEBUG_UNUSED( argc
);
694 DEBUG_UNUSED( argv
);
698 // Initialize the service-specific stuff and mark the service as running.
700 err
= ServiceSpecificInitialize( argc
, argv
);
701 require_noerr( err
, exit
);
704 gServiceStatus
.dwCurrentState
= SERVICE_RUNNING
;
705 ok
= SetServiceStatus( gServiceStatusHandle
, &gServiceStatus
);
706 check_translated_errno( ok
, GetLastError(), kParamErr
);
708 // Run the service-specific stuff. This does not return until the service quits or is stopped.
710 ReportStatus( EVENTLOG_INFORMATION_TYPE
, "mDNSResponder started\n" );
711 err
= ServiceSpecificRun( argc
, argv
);
712 ReportStatus( EVENTLOG_INFORMATION_TYPE
, "mDNSResponder stopped (%d)\n", err
);
713 require_noerr( err
, exit
);
715 // Service stopped. Clean up and we're done.
720 ServiceSpecificFinalize( argc
, argv
);
725 //===========================================================================================================================
727 //===========================================================================================================================
729 static void ServiceStop( void )
734 // Signal the event to cause the service to exit.
736 if( gServiceStatusHandle
)
738 gServiceStatus
.dwCurrentState
= SERVICE_STOP_PENDING
;
739 ok
= SetServiceStatus( gServiceStatusHandle
, &gServiceStatus
);
740 check_translated_errno( ok
, GetLastError(), kParamErr
);
743 err
= ServiceSpecificStop();
749 #pragma mark == Service Specific ==
752 //===========================================================================================================================
753 // ServiceSpecificInitialize
754 //===========================================================================================================================
756 static OSStatus
ServiceSpecificInitialize( int argc
, char *argv
[] )
759 DNSServiceInitializeFlags initFlags
;
760 RMxServerFlags flags
;
762 DEBUG_UNUSED( argc
);
763 DEBUG_UNUSED( argv
);
765 initFlags
= kDNSServiceInitializeFlagsAdvertise
| kDNSServiceInitializeFlagsNoServerCheck
;
766 err
= DNSServiceInitialize_direct( initFlags
, gServiceCacheEntryCount
);
767 require_noerr( err
, exit
);
770 if( gServiceAllowRemote
)
772 flags
|= kRMxServerFlagsAllowRemote
;
774 err
= RMxServerInitialize( flags
);
775 require_noerr( err
, exit
);
780 ServiceSpecificFinalize( argc
, argv
);
785 //===========================================================================================================================
786 // ServiceSpecificRun
787 //===========================================================================================================================
789 static OSStatus
ServiceSpecificRun( int argc
, char *argv
[] )
793 DEBUG_UNUSED( argc
);
794 DEBUG_UNUSED( argv
);
796 err
= RMxServerRun();
797 require_noerr( err
, exit
);
803 //===========================================================================================================================
804 // ServiceSpecificStop
805 //===========================================================================================================================
807 static OSStatus
ServiceSpecificStop( void )
811 err
= RMxServerStop( kRMxServerStopFlagsNoWait
);
812 require_noerr( err
, exit
);
818 //===========================================================================================================================
819 // ServiceSpecificFinalize
820 //===========================================================================================================================
822 static void ServiceSpecificFinalize( int argc
, char *argv
[] )
824 DEBUG_UNUSED( argc
);
825 DEBUG_UNUSED( argv
);
828 DNSServiceFinalize_direct();