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