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