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