1 /* $NetBSD: ntservice.c,v 1.1.1.3 2010/12/12 15:22:09 adam Exp $ */ 2 3 /* OpenLDAP: pkg/ldap/libraries/liblutil/ntservice.c,v 1.31.2.5 2010/04/13 20:23:06 kurt Exp */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2010 The OpenLDAP Foundation. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17 18 /* 19 * NT Service manager utilities for OpenLDAP services 20 */ 21 22 #include "portable.h" 23 24 #ifdef HAVE_NT_SERVICE_MANAGER 25 26 #include <ac/stdlib.h> 27 #include <ac/string.h> 28 29 #include <stdio.h> 30 31 #include <windows.h> 32 #include <winsvc.h> 33 34 #include <ldap.h> 35 36 #include "ldap_log.h" 37 #include "ldap_pvt_thread.h" 38 39 40 #include "ldap_defaults.h" 41 42 #include "slapdmsg.h" 43 44 #define SCM_NOTIFICATION_INTERVAL 5000 45 #define THIRTY_SECONDS (30 * 1000) 46 47 int is_NT_Service; /* is this is an NT service? */ 48 49 SERVICE_STATUS lutil_ServiceStatus; 50 SERVICE_STATUS_HANDLE hlutil_ServiceStatus; 51 52 ldap_pvt_thread_cond_t started_event, stopped_event; 53 ldap_pvt_thread_t start_status_tid, stop_status_tid; 54 55 void (*stopfunc)(int); 56 57 static char *GetLastErrorString( void ); 58 59 int lutil_srv_install(LPCTSTR lpszServiceName, LPCTSTR lpszDisplayName, 60 LPCTSTR lpszBinaryPathName, int auto_start) 61 { 62 HKEY hKey; 63 DWORD dwValue, dwDisposition; 64 SC_HANDLE schSCManager, schService; 65 char *sp = strchr( lpszBinaryPathName, ' '); 66 67 if ( sp ) *sp = '\0'; 68 fprintf( stderr, "The install path is %s.\n", lpszBinaryPathName ); 69 if ( sp ) *sp = ' '; 70 if ((schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE ) ) != NULL ) 71 { 72 if ((schService = CreateService( 73 schSCManager, 74 lpszServiceName, 75 lpszDisplayName, 76 SERVICE_ALL_ACCESS, 77 SERVICE_WIN32_OWN_PROCESS, 78 auto_start ? SERVICE_AUTO_START : SERVICE_DEMAND_START, 79 SERVICE_ERROR_NORMAL, 80 lpszBinaryPathName, 81 NULL, NULL, NULL, NULL, NULL)) != NULL) 82 { 83 char regpath[132]; 84 CloseServiceHandle(schService); 85 CloseServiceHandle(schSCManager); 86 87 snprintf( regpath, sizeof regpath, 88 "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s", 89 lpszServiceName ); 90 /* Create the registry key for event logging to the Windows NT event log. */ 91 if ( RegCreateKeyEx(HKEY_LOCAL_MACHINE, 92 regpath, 0, 93 "REG_SZ", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, 94 &dwDisposition) != ERROR_SUCCESS) 95 { 96 fprintf( stderr, "RegCreateKeyEx() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); 97 RegCloseKey(hKey); 98 return(0); 99 } 100 if ( sp ) *sp = '\0'; 101 if ( RegSetValueEx(hKey, "EventMessageFile", 0, REG_EXPAND_SZ, lpszBinaryPathName, strlen(lpszBinaryPathName) + 1) != ERROR_SUCCESS) 102 { 103 fprintf( stderr, "RegSetValueEx(EventMessageFile) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); 104 RegCloseKey(hKey); 105 return(0); 106 } 107 108 dwValue = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; 109 if ( RegSetValueEx(hKey, "TypesSupported", 0, REG_DWORD, (LPBYTE) &dwValue, sizeof(DWORD)) != ERROR_SUCCESS) 110 { 111 fprintf( stderr, "RegCreateKeyEx(TypesSupported) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); 112 RegCloseKey(hKey); 113 return(0); 114 } 115 RegCloseKey(hKey); 116 return(1); 117 } 118 else 119 { 120 fprintf( stderr, "CreateService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); 121 CloseServiceHandle(schSCManager); 122 return(0); 123 } 124 } 125 else 126 fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); 127 return(0); 128 } 129 130 131 int lutil_srv_remove(LPCTSTR lpszServiceName, LPCTSTR lpszBinaryPathName) 132 { 133 SC_HANDLE schSCManager, schService; 134 135 fprintf( stderr, "The installed path is %s.\n", lpszBinaryPathName ); 136 if ((schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE)) != NULL ) 137 { 138 if ((schService = OpenService(schSCManager, lpszServiceName, DELETE)) != NULL) 139 { 140 if ( DeleteService(schService) == TRUE) 141 { 142 CloseServiceHandle(schService); 143 CloseServiceHandle(schSCManager); 144 return(1); 145 } else { 146 fprintf( stderr, "DeleteService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); 147 fprintf( stderr, "The %s service has not been removed.\n", lpszBinaryPathName); 148 CloseServiceHandle(schService); 149 CloseServiceHandle(schSCManager); 150 return(0); 151 } 152 } else { 153 fprintf( stderr, "OpenService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); 154 CloseServiceHandle(schSCManager); 155 return(0); 156 } 157 } 158 else 159 fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); 160 return(0); 161 } 162 163 164 #if 0 /* unused */ 165 DWORD 166 svc_installed (LPTSTR lpszServiceName, LPTSTR lpszBinaryPathName) 167 { 168 char buf[256]; 169 HKEY key; 170 DWORD rc; 171 DWORD type; 172 long len; 173 174 strcpy(buf, TEXT("SYSTEM\\CurrentControlSet\\Services\\")); 175 strcat(buf, lpszServiceName); 176 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) 177 return(-1); 178 179 rc = 0; 180 if (lpszBinaryPathName) { 181 len = sizeof(buf); 182 if (RegQueryValueEx(key, "ImagePath", NULL, &type, buf, &len) == ERROR_SUCCESS) { 183 if (strcmp(lpszBinaryPathName, buf)) 184 rc = -1; 185 } 186 } 187 RegCloseKey(key); 188 return(rc); 189 } 190 191 192 DWORD 193 svc_running (LPTSTR lpszServiceName) 194 { 195 SC_HANDLE service; 196 SC_HANDLE scm; 197 DWORD rc; 198 SERVICE_STATUS ss; 199 200 if (!(scm = OpenSCManager(NULL, NULL, GENERIC_READ))) 201 return(GetLastError()); 202 203 rc = 1; 204 service = OpenService(scm, lpszServiceName, SERVICE_QUERY_STATUS); 205 if (service) { 206 if (!QueryServiceStatus(service, &ss)) 207 rc = GetLastError(); 208 else if (ss.dwCurrentState != SERVICE_STOPPED) 209 rc = 0; 210 CloseServiceHandle(service); 211 } 212 CloseServiceHandle(scm); 213 return(rc); 214 } 215 #endif 216 217 static void *start_status_routine( void *ptr ) 218 { 219 DWORD wait_result; 220 int done = 0; 221 222 while ( !done ) 223 { 224 wait_result = WaitForSingleObject( started_event, SCM_NOTIFICATION_INTERVAL ); 225 switch ( wait_result ) 226 { 227 case WAIT_ABANDONED: 228 case WAIT_OBJECT_0: 229 /* the object that we were waiting for has been destroyed (ABANDONED) or 230 * signalled (TIMEOUT_0). We can assume that the startup process is 231 * complete and tell the Service Control Manager that we are now runnng */ 232 lutil_ServiceStatus.dwCurrentState = SERVICE_RUNNING; 233 lutil_ServiceStatus.dwWin32ExitCode = NO_ERROR; 234 lutil_ServiceStatus.dwCheckPoint++; 235 lutil_ServiceStatus.dwWaitHint = 1000; 236 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 237 done = 1; 238 break; 239 case WAIT_TIMEOUT: 240 /* We've waited for the required time, so send an update to the Service Control 241 * Manager saying to wait again. */ 242 lutil_ServiceStatus.dwCheckPoint++; 243 lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; 244 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 245 break; 246 case WAIT_FAILED: 247 /* theres been some problem with WaitForSingleObject so tell the Service 248 * Control Manager to wait 30 seconds before deploying its assasin and 249 * then leave the thread. */ 250 lutil_ServiceStatus.dwCheckPoint++; 251 lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; 252 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 253 done = 1; 254 break; 255 } 256 } 257 ldap_pvt_thread_exit(NULL); 258 return NULL; 259 } 260 261 262 263 static void *stop_status_routine( void *ptr ) 264 { 265 DWORD wait_result; 266 int done = 0; 267 268 while ( !done ) 269 { 270 wait_result = WaitForSingleObject( stopped_event, SCM_NOTIFICATION_INTERVAL ); 271 switch ( wait_result ) 272 { 273 case WAIT_ABANDONED: 274 case WAIT_OBJECT_0: 275 /* the object that we were waiting for has been destroyed (ABANDONED) or 276 * signalled (TIMEOUT_0). The shutting down process is therefore complete 277 * and the final SERVICE_STOPPED message will be sent to the service control 278 * manager prior to the process terminating. */ 279 done = 1; 280 break; 281 case WAIT_TIMEOUT: 282 /* We've waited for the required time, so send an update to the Service Control 283 * Manager saying to wait again. */ 284 lutil_ServiceStatus.dwCheckPoint++; 285 lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; 286 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 287 break; 288 case WAIT_FAILED: 289 /* theres been some problem with WaitForSingleObject so tell the Service 290 * Control Manager to wait 30 seconds before deploying its assasin and 291 * then leave the thread. */ 292 lutil_ServiceStatus.dwCheckPoint++; 293 lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; 294 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 295 done = 1; 296 break; 297 } 298 } 299 ldap_pvt_thread_exit(NULL); 300 return NULL; 301 } 302 303 304 305 static void WINAPI lutil_ServiceCtrlHandler( IN DWORD Opcode) 306 { 307 switch (Opcode) 308 { 309 case SERVICE_CONTROL_STOP: 310 case SERVICE_CONTROL_SHUTDOWN: 311 312 lutil_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; 313 lutil_ServiceStatus.dwCheckPoint++; 314 lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; 315 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 316 317 ldap_pvt_thread_cond_init( &stopped_event ); 318 if ( stopped_event == NULL ) 319 { 320 /* the event was not created. We will ask the service control manager for 30 321 * seconds to shutdown */ 322 lutil_ServiceStatus.dwCheckPoint++; 323 lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; 324 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 325 } 326 else 327 { 328 /* start a thread to report the progress to the service control manager 329 * until the stopped_event is fired. */ 330 if ( ldap_pvt_thread_create( &stop_status_tid, 0, stop_status_routine, NULL ) == 0 ) 331 { 332 333 } 334 else { 335 /* failed to create the thread that tells the Service Control Manager that the 336 * service stopping is proceeding. 337 * tell the Service Control Manager to wait another 30 seconds before deploying its 338 * assasin. */ 339 lutil_ServiceStatus.dwCheckPoint++; 340 lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; 341 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 342 } 343 } 344 stopfunc( -1 ); 345 break; 346 347 case SERVICE_CONTROL_INTERROGATE: 348 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 349 break; 350 } 351 return; 352 } 353 354 void *lutil_getRegParam( char *svc, char *value ) 355 { 356 HKEY hkey; 357 char path[255]; 358 DWORD vType; 359 static char vValue[1024]; 360 DWORD valLen = sizeof( vValue ); 361 362 if ( svc != NULL ) 363 snprintf ( path, sizeof path, "SOFTWARE\\%s", svc ); 364 else 365 snprintf ( path, sizeof path, "SOFTWARE\\OpenLDAP\\Parameters" ); 366 367 if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hkey ) != ERROR_SUCCESS ) 368 { 369 return NULL; 370 } 371 372 if ( RegQueryValueEx( hkey, value, NULL, &vType, vValue, &valLen ) != ERROR_SUCCESS ) 373 { 374 RegCloseKey( hkey ); 375 return NULL; 376 } 377 RegCloseKey( hkey ); 378 379 switch ( vType ) 380 { 381 case REG_BINARY: 382 case REG_DWORD: 383 return (void*)&vValue; 384 case REG_SZ: 385 return (void*)&vValue; 386 } 387 return (void*)NULL; 388 } 389 390 void lutil_LogStartedEvent( char *svc, int slap_debug, char *configfile, char *urls ) 391 { 392 char *Inserts[5]; 393 WORD i = 0, j; 394 HANDLE hEventLog; 395 396 hEventLog = RegisterEventSource( NULL, svc ); 397 398 Inserts[i] = (char *)malloc( 20 ); 399 itoa( slap_debug, Inserts[i++], 10 ); 400 Inserts[i++] = strdup( configfile ); 401 Inserts[i++] = strdup( urls ? urls : "ldap:///" ); 402 403 ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0, 404 MSG_SVC_STARTED, NULL, i, 0, (LPCSTR *) Inserts, NULL ); 405 406 for ( j = 0; j < i; j++ ) 407 ldap_memfree( Inserts[j] ); 408 DeregisterEventSource( hEventLog ); 409 } 410 411 412 413 void lutil_LogStoppedEvent( char *svc ) 414 { 415 HANDLE hEventLog; 416 417 hEventLog = RegisterEventSource( NULL, svc ); 418 ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0, 419 MSG_SVC_STOPPED, NULL, 0, 0, NULL, NULL ); 420 DeregisterEventSource( hEventLog ); 421 } 422 423 424 void lutil_CommenceStartupProcessing( char *lpszServiceName, 425 void (*stopper)(int) ) 426 { 427 hlutil_ServiceStatus = RegisterServiceCtrlHandler( lpszServiceName, (LPHANDLER_FUNCTION)lutil_ServiceCtrlHandler); 428 429 stopfunc = stopper; 430 431 /* initialize the Service Status structure */ 432 lutil_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 433 lutil_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 434 lutil_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; 435 lutil_ServiceStatus.dwWin32ExitCode = NO_ERROR; 436 lutil_ServiceStatus.dwServiceSpecificExitCode = 0; 437 lutil_ServiceStatus.dwCheckPoint = 1; 438 lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; 439 440 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 441 442 /* start up a thread to keep sending SERVICE_START_PENDING to the Service Control Manager 443 * until the slapd listener is completed and listening. Only then should we send 444 * SERVICE_RUNNING to the Service Control Manager. */ 445 ldap_pvt_thread_cond_init( &started_event ); 446 if ( started_event == NULL) 447 { 448 /* failed to create the event to determine when the startup process is complete so 449 * tell the Service Control Manager to wait another 30 seconds before deploying its 450 * assasin */ 451 lutil_ServiceStatus.dwCheckPoint++; 452 lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; 453 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 454 } 455 else 456 { 457 /* start a thread to report the progress to the service control manager 458 * until the started_event is fired. */ 459 if ( ldap_pvt_thread_create( &start_status_tid, 0, start_status_routine, NULL ) == 0 ) 460 { 461 462 } 463 else { 464 /* failed to create the thread that tells the Service Control Manager that the 465 * service startup is proceeding. 466 * tell the Service Control Manager to wait another 30 seconds before deploying its 467 * assasin. */ 468 lutil_ServiceStatus.dwCheckPoint++; 469 lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; 470 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 471 } 472 } 473 } 474 475 void lutil_ReportShutdownComplete( ) 476 { 477 if ( is_NT_Service ) 478 { 479 /* stop sending SERVICE_STOP_PENDING messages to the Service Control Manager */ 480 ldap_pvt_thread_cond_signal( &stopped_event ); 481 ldap_pvt_thread_cond_destroy( &stopped_event ); 482 483 /* wait for the thread sending the SERVICE_STOP_PENDING messages to the Service Control Manager to die. 484 * if the wait fails then put ourselves to sleep for half the Service Control Manager update interval */ 485 if (ldap_pvt_thread_join( stop_status_tid, (void *) NULL ) == -1) 486 ldap_pvt_thread_sleep( SCM_NOTIFICATION_INTERVAL / 2 ); 487 488 lutil_ServiceStatus.dwCurrentState = SERVICE_STOPPED; 489 lutil_ServiceStatus.dwCheckPoint++; 490 lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL; 491 SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); 492 } 493 } 494 495 static char *GetErrorString( int err ) 496 { 497 static char msgBuf[1024]; 498 499 FormatMessage( 500 FORMAT_MESSAGE_FROM_SYSTEM, 501 NULL, 502 err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 503 msgBuf, 1024, NULL ); 504 505 return msgBuf; 506 } 507 508 static char *GetLastErrorString( void ) 509 { 510 return GetErrorString( GetLastError() ); 511 } 512 #endif 513