Bearsampp 2026.7.11
Loading...
Searching...
No Matches
Win32Service Class Reference

Public Member Functions

 __construct ($name)
 create ()
 delete ()
 ensureReset ()
 fastServiceCheck ()
 getBinPath ()
 getDisplayName ()
 getError ()
 getErrorControl ()
 getLatestError ()
 getLatestStatus ()
 getName ()
 getNssm ()
 getParams ()
 getStartType ()
 infos ()
 isInstalled ()
 isPaused ()
 isPending ($status)
 isRunning ()
 isStopped ()
 reset ()
 restart ()
 setBinPath ($binPath)
 setDisplayName ($displayName)
 setErrorControl ($errorControl)
 setName ($name)
 setNssm ($nssm)
 setParams ($params)
 setStartType ($startType)
 start ()
 status ($timeout=true)
 stop ()
 waitForServiceDeletion ($maxWaitTime=30)

Static Public Member Functions

static getServices ($forceRefresh=false)
static getVbsKeys ()

Data Fields

const PENDING_TIMEOUT = 20
const SERVER_ERROR_IGNORE = '0'
const SERVER_ERROR_NORMAL = '1'
const SERVICE_AUTO_START = '2'
const SERVICE_DEMAND_START = '3'
const SERVICE_DISABLED = '4'
const SERVICE_STATE = 'State'
const SLEEP_TIME = 100000
const VBS_DESCRIPTION = 'Description'
const VBS_DISPLAY_NAME = 'DisplayName'
const VBS_NAME = 'Name'
const VBS_PATH_NAME = 'PathName'
const WIN32_ERROR_ACCESS_DENIED = '5'
const WIN32_ERROR_CIRCULAR_DEPENDENCY = '423'
const WIN32_ERROR_DATABASE_DOES_NOT_EXIST = '429'
const WIN32_ERROR_DEPENDENT_SERVICES_RUNNING = '41B'
const WIN32_ERROR_DUPLICATE_SERVICE_NAME = '436'
const WIN32_ERROR_FAILED_SERVICE_CONTROLLER_CONNECT = '427'
const WIN32_ERROR_INSUFFICIENT_BUFFER = '7A'
const WIN32_ERROR_INVALID_DATA = 'D'
const WIN32_ERROR_INVALID_HANDLE = '6'
const WIN32_ERROR_INVALID_LEVEL = '7C'
const WIN32_ERROR_INVALID_NAME = '7B'
const WIN32_ERROR_INVALID_PARAMETER = '57'
const WIN32_ERROR_INVALID_SERVICE_ACCOUNT = '421'
const WIN32_ERROR_INVALID_SERVICE_CONTROL = '41C'
const WIN32_ERROR_PATH_NOT_FOUND = '3'
const WIN32_ERROR_SERVICE_ALREADY_RUNNING = '420'
const WIN32_ERROR_SERVICE_CANNOT_ACCEPT_CTRL = '425'
const WIN32_ERROR_SERVICE_DATABASE_LOCKED = '41F'
const WIN32_ERROR_SERVICE_DEPENDENCY_DELETED = '433'
const WIN32_ERROR_SERVICE_DEPENDENCY_FAIL = '42C'
const WIN32_ERROR_SERVICE_DISABLED = '422'
const WIN32_ERROR_SERVICE_DOES_NOT_EXIST = '424'
const WIN32_ERROR_SERVICE_EXISTS = '431'
const WIN32_ERROR_SERVICE_LOGON_FAILED = '42D'
const WIN32_ERROR_SERVICE_MARKED_FOR_DELETE = '430'
const WIN32_ERROR_SERVICE_NO_THREAD = '41E'
const WIN32_ERROR_SERVICE_NOT_ACTIVE = '426'
const WIN32_ERROR_SERVICE_REQUEST_TIMEOUT = '41D'
const WIN32_ERROR_SHUTDOWN_IN_PROGRESS = '45B'
const WIN32_NO_ERROR = '0'
const WIN32_SERVICE_CONTINUE_PENDING = '5'
const WIN32_SERVICE_NA = '0'
const WIN32_SERVICE_PAUSE_PENDING = '6'
const WIN32_SERVICE_PAUSED = '7'
const WIN32_SERVICE_RUNNING = '4'
const WIN32_SERVICE_START_PENDING = '2'
const WIN32_SERVICE_STOP_PENDING = '3'
const WIN32_SERVICE_STOPPED = '1'

Private Member Functions

 callWin32Service ($function, $param, $checkError=false)
 getWin32ErrorCodeDesc ($code)
 getWin32ServiceStatusDesc ($status)
 writeLog ($log)

Private Attributes

 $binPath
 $displayName
 $errorControl
 $latestError
 $latestStatus
 $name
 $nssm
 $params
 $startType

Static Private Attributes

static $loggedFunctions = array()
static $serviceListCache = null

Detailed Description

Class Win32Service

This class provides an interface to manage Windows services. It includes methods to create, delete, start, stop, and query the status of services. It also handles logging and error reporting for service operations.

Definition at line 17 of file class.win32service.php.

Constructor & Destructor Documentation

◆ __construct()

__construct ( $name)

Constructor for the Win32Service class.

Parameters
string$nameThe name of the service.

Definition at line 99 of file class.win32service.php.

100 {
101 Log::initClass( $this );
102 $this->name = $name;
103 }
static initClass($classInstance)

References $name, and Log\initClass().

Here is the call graph for this function:

Member Function Documentation

◆ callWin32Service()

callWin32Service ( $function,
$param,
$checkError = false )
private

Calls a Win32 service function.

Parameters
string$functionThe function name.
mixed$paramThe parameter to pass to the function.
bool$checkErrorWhether to check for errors.
Returns
mixed The result of the function call.

Definition at line 170 of file class.win32service.php.

170 : mixed
171 {
172 $result = false;
173 if ( function_exists( $function ) ) {
174 if (!isset(self::$loggedFunctions[$function])) {
175 Log::trace('Win32 function: ' . $function . ' exists');
176 self::$loggedFunctions[$function] = true;
177 }
178
179 // Special handling for win32_query_service_status to prevent hanging
180 if ($function === 'win32_query_service_status') {
181 Log::trace("Using enhanced handling for win32_query_service_status");
182
183 // Set a shorter timeout for this specific function
184 $originalTimeout = ini_get('max_execution_time');
185 set_time_limit(5); // 5 seconds timeout
186
187 try {
188 // Ensure proper parameter handling for PHP 8.5.7 compatibility
189 $result = call_user_func($function, $param);
190
191 // Reset the timeout
192 set_time_limit($originalTimeout);
193
194 if ($checkError && $result !== null) {
195 // Convert to int before using dechex for PHP 8.5.7 compatibility
196 $resultInt = is_numeric($result) ? (int)$result : 0;
197 if (dechex($resultInt) != self::WIN32_NO_ERROR) {
198 $this->latestError = dechex($resultInt);
199 }
200 }
201 } catch (\Win32ServiceException $e) {
202 // Reset the timeout
203 set_time_limit($originalTimeout);
204
205 Log::trace("Win32ServiceException caught: " . $e->getMessage());
206
207 // Handle "service does not exist" exception
208 if (strpos($e->getMessage(), 'service does not exist') !== false) {
209 Log::trace("Service does not exist exception handled for: " . $param);
210 // Return the appropriate error code for "service does not exist"
211 $result = hexdec(self::WIN32_ERROR_SERVICE_DOES_NOT_EXIST);
212 } else {
213 // For other exceptions, log and return false
214 Log::trace("Unhandled Win32ServiceException: " . $e->getMessage());
215 $result = false;
216 }
217 } catch (\Exception $e) {
218 // Reset the timeout
219 set_time_limit($originalTimeout);
220
221 // Catch any other exceptions to prevent application freeze
222 Log::trace("Exception caught in callWin32Service: " . $e->getMessage());
223 $result = false;
224 } catch (\Throwable $e) {
225 // Reset the timeout
226 set_time_limit($originalTimeout);
227
228 // Catch any other throwable (PHP 7+) to prevent application freeze
229 Log::trace("Throwable caught in callWin32Service: " . $e->getMessage());
230 $result = false;
231 }
232 } else {
233 // Standard handling for other functions
234 try {
235 // Ensure proper parameter handling for PHP 8.5.7 compatibility
236 $result = call_user_func($function, $param);
237 if ($checkError && $result !== null) {
238 // Convert to int before using dechex for PHP 8.5.7 compatibility
239 $resultInt = is_numeric($result) ? (int)$result : 0;
240 if (dechex($resultInt) != self::WIN32_NO_ERROR) {
241 $this->latestError = dechex($resultInt);
242 }
243 }
244 } catch (\Win32ServiceException $e) {
245 Log::trace("Win32ServiceException caught: " . $e->getMessage());
246
247 // Handle "service does not exist" exception
248 if (strpos($e->getMessage(), 'service does not exist') !== false) {
249 Log::trace("Service does not exist exception handled for: " . $param);
250 // Return the appropriate error code for "service does not exist"
251 $result = hexdec(self::WIN32_ERROR_SERVICE_DOES_NOT_EXIST);
252 } else {
253 // For other exceptions, log and return false
254 Log::trace("Unhandled Win32ServiceException: " . $e->getMessage());
255 $result = false;
256 }
257 } catch (\Exception $e) {
258 // Catch any other exceptions to prevent application freeze
259 Log::trace("Exception caught in callWin32Service: " . $e->getMessage());
260 $result = false;
261 } catch (\Throwable $e) {
262 // Catch any other throwable (PHP 7+) to prevent application freeze
263 Log::trace("Throwable caught in callWin32Service: " . $e->getMessage());
264 $result = false;
265 }
266 }
267 } else {
268 if (!isset(self::$loggedFunctions[$function])) {
269 Log::trace('Win32 function: ' . $function . ' missing');
270 self::$loggedFunctions[$function] = true;
271 }
272 }
273 return $result;
274 }
$result
static trace($data, $file=null)

References $result, and Log\trace().

Referenced by create(), delete(), start(), status(), and stop().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ create()

create ( )

Creates the service.

Returns
bool True if the service was created successfully, false otherwise.

Definition at line 382 of file class.win32service.php.

382 : bool
383 {
384 global $bearsamppBins;
385
386 Log::trace("Starting Win32Service::create for service: " . $this->getName());
387
388 if ( $this->getName() == BinPostgresql::SERVICE_NAME ) {
389 Log::trace("PostgreSQL service detected - using specialized installation");
390 $bearsamppBins->getPostgresql()->rebuildConf();
391 Log::trace("PostgreSQL configuration rebuilt");
392
393 $bearsamppBins->getPostgresql()->initData();
394 Log::trace("PostgreSQL data initialized");
395
397 Log::trace("PostgreSQL service installation " . ($result ? "succeeded" : "failed"));
398 return $result;
399 }
400
401 if ( $this->getNssm() instanceof Nssm ) {
402 Log::trace("Using NSSM for service installation");
403
404 // Ensure Tools are loaded before building environment paths
405 global $bearsamppTools;
406 if (!isset($bearsamppTools)) {
407 Log::trace("Tools not loaded, forcing synchronous load");
409 }
410
411 global $bearsamppRegistry;
412 $nssmEnvPath = $bearsamppRegistry->getAppBinsRegKey( false );
413 Log::trace("NSSM environment path (bins): " . $nssmEnvPath);
414
415 $nssmEnvPath .= Path::getNssmEnvPaths();
416 Log::trace("NSSM environment path (with additional paths): " . $nssmEnvPath);
417
418 $nssmEnvPath .= '%SystemRoot%/system32;';
419 $nssmEnvPath .= '%SystemRoot%;';
420 $nssmEnvPath .= '%SystemRoot%/system32/Wbem;';
421 $nssmEnvPath .= '%SystemRoot%/system32/WindowsPowerShell/v1.0';
422 Log::trace("NSSM final environment PATH: " . $nssmEnvPath);
423
424 $this->getNssm()->setEnvironmentExtra( 'PATH=' . $nssmEnvPath );
425 Log::trace("NSSM service parameters:");
426 Log::trace("-> Name: " . $this->getNssm()->getName());
427 Log::trace("-> DisplayName: " . $this->getNssm()->getDisplayName());
428 Log::trace("-> BinPath: " . $this->getNssm()->getBinPath());
429 Log::trace("-> Params: " . $this->getNssm()->getParams());
430 Log::trace("-> Start: " . $this->getNssm()->getStart());
431 Log::trace("-> Stdout: " . $this->getNssm()->getStdout());
432 Log::trace("-> Stderr: " . $this->getNssm()->getStderr());
433
434 $result = $this->getNssm()->create();
435 Log::trace("NSSM service creation " . ($result ? "succeeded" : "failed"));
436 if (!$result) {
437 Log::trace("NSSM error: " . $this->getNssm()->getLatestError());
438 }
439 return $result;
440 }
441
442 Log::trace("Using win32_create_service for service installation");
443 $serviceParams = array(
444 'service' => $this->getName(),
445 'display' => $this->getDisplayName(),
446 'description' => $this->getDisplayName(),
447 'path' => $this->getBinPath(),
448 'params' => $this->getParams(),
449 'start_type' => $this->getStartType() != null ? $this->getStartType() : self::SERVICE_DEMAND_START,
450 'error_control' => $this->getErrorControl() != null ? $this->getErrorControl() : self::SERVER_ERROR_NORMAL,
451 );
452
453 Log::trace("win32_create_service parameters:");
454 foreach ($serviceParams as $key => $value) {
455 Log::trace("-> $key: $value");
456 }
457
458 $result = $this->callWin32Service( 'win32_create_service', $serviceParams, true );
459 // Ensure proper type conversion for PHP 8.5.7 compatibility
460 $resultInt = is_numeric($result) ? (int)$result : 0;
461 $create = $result !== null ? dechex( $resultInt ) : '0';
462 Log::trace("win32_create_service result code: " . $create);
463
464 // Retry once if the SCM has the service marked for deletion from a recent delete()
465 if ( $create == self::WIN32_ERROR_SERVICE_MARKED_FOR_DELETE ) {
466 Log::trace("Service marked for delete, waiting 2s before retry: " . $this->getName());
467 usleep( 2000000 );
468 $result = $this->callWin32Service( 'win32_create_service', $serviceParams, true );
469 $resultInt = is_numeric($result) ? (int)$result : 0;
470 $create = $result !== null ? dechex( $resultInt ) : '0';
471 Log::trace("win32_create_service retry result code: " . $create);
472 }
473
474 $this->writeLog( 'Create service: ' . $create . ' (status: ' . $this->status() . ')' );
475 $this->writeLog( '-> service: ' . $this->getName() );
476 $this->writeLog( '-> display: ' . $this->getDisplayName() );
477 $this->writeLog( '-> description: ' . $this->getDisplayName() );
478 $this->writeLog( '-> path: ' . $this->getBinPath() );
479 $this->writeLog( '-> params: ' . $this->getParams() );
480 $this->writeLog( '-> start_type: ' . ($this->getStartType() != null ? $this->getStartType() : self::SERVICE_DEMAND_START) );
481 $this->writeLog( '-> service: ' . ($this->getErrorControl() != null ? $this->getErrorControl() : self::SERVER_ERROR_NORMAL) );
482
483 if ( $create != self::WIN32_NO_ERROR ) {
484 Log::trace("Service creation failed with error code: " . $create);
485 return false;
486 }
487 elseif ( !$this->isInstalled() ) {
488 Log::trace("Service created but not found as installed");
489 $this->latestError = self::WIN32_NO_ERROR;
490 return false;
491 }
492
493 Log::trace("Service created successfully: " . $this->getName());
494 return true;
495 }
global $bearsamppBins
static installPostgresqlService()
static getNssmEnvPaths()
static loadTools()
callWin32Service($function, $param, $checkError=false)
status($timeout=true)

References $bearsamppBins, $result, callWin32Service(), getBinPath(), getDisplayName(), getErrorControl(), getLatestError(), getName(), getNssm(), Path\getNssmEnvPaths(), getParams(), getStartType(), Batch\installPostgresqlService(), isInstalled(), Root\loadTools(), BinPostgresql\SERVICE_NAME, status(), Log\trace(), and writeLog().

Referenced by reset().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ delete()

delete ( )

Deletes the service.

Returns
bool True if the service was deleted successfully, false otherwise.

Definition at line 502 of file class.win32service.php.

502 : bool
503 {
504 Log::trace("Starting Win32Service::delete for service: " . $this->getName());
505 Log::trace("Checking if service is installed: " . $this->getName());
506
507 if ( !$this->isInstalled() ) {
508 Log::trace("Service is not installed, skipping deletion: " . $this->getName());
509 return true;
510 }
511
512 Log::trace("Stopping service before deletion: " . $this->getName());
513 $this->stop();
514
515 if ( $this->getNssm() instanceof Nssm ) {
516 $childExe = basename( $this->getNssm()->getBinPath() );
517 Log::trace("Killing NSSM child process after stop: " . $childExe);
518 Win32Ps::killBins( [$childExe] );
519 }
520
521 if ( $this->getName() == BinPostgresql::SERVICE_NAME ) {
522 Log::trace("PostgreSQL service detected - using specialized uninstallation");
524 Log::trace("PostgreSQL service uninstallation " . ($result ? "succeeded" : "failed"));
525 return $result;
526 }
527
528 Log::trace("Calling win32_delete_service for service: " . $this->getName());
529 $result = $this->callWin32Service( 'win32_delete_service', $this->getName(), true );
530 // Ensure proper type conversion for PHP 8.5.7 compatibility
531 $resultInt = is_numeric($result) ? (int)$result : 0;
532 $delete = $result !== null ? dechex( $resultInt ) : '0';
533 Log::trace("Delete service result code: " . $delete);
534 $this->writeLog( 'Delete service ' . $this->getName() . ': ' . $delete . ' (status: ' . $this->status() . ')' );
535
536 if ( $delete != self::WIN32_NO_ERROR && $delete != self::WIN32_ERROR_SERVICE_DOES_NOT_EXIST ) {
537 return false;
538 }
539 elseif ( $this->isInstalled() ) {
540 $this->latestError = self::WIN32_NO_ERROR;
541
542 return false;
543 }
544
545 return true;
546 }
static uninstallPostgresqlService()
static killBins($refreshProcs=false)

References $result, callWin32Service(), getBinPath(), getName(), getNssm(), isInstalled(), Win32Ps\killBins(), BinPostgresql\SERVICE_NAME, status(), stop(), Log\trace(), Batch\uninstallPostgresqlService(), and writeLog().

Here is the call graph for this function:

◆ ensureReset()

ensureReset ( )

Ensures the service is properly reset (deleted and verified deleted). This is more robust than just calling delete() as it waits for confirmation.

Returns
bool True if service was successfully reset, false otherwise

Definition at line 1281 of file class.win32service.php.

1281 : bool
1282 {
1283 Log::trace("Starting ensureReset for service: " . $this->getName());
1284
1285 // First, make sure service is stopped
1286 if ($this->isRunning()) {
1287 Log::trace("Service is still running, stopping it first");
1288 if (!$this->stop()) {
1289 Log::trace("Failed to stop service during ensureReset");
1290 return false;
1291 }
1292 usleep(1000000); // 1 second wait after stop
1293 }
1294
1295 // Delete the service
1296 Log::trace("Deleting service");
1297 if (!$this->delete()) {
1298 Log::trace("Service deletion failed, but continuing with wait");
1299 }
1300
1301 // Wait for the service to be completely removed
1302 if (!$this->waitForServiceDeletion(30)) {
1303 Log::trace("Service deletion did not complete within timeout, but continuing");
1304 // Even if we timeout, give it a moment and try to create anyway
1305 usleep(2000000); // 2 seconds
1306 }
1307
1308 Log::trace("ensureReset completed for service: " . $this->getName());
1309 return true;
1310 }
waitForServiceDeletion($maxWaitTime=30)

References getName(), isRunning(), stop(), Log\trace(), and waitForServiceDeletion().

Here is the call graph for this function:

◆ fastServiceCheck()

fastServiceCheck ( )

Fast service check using sc.exe (Windows Service Control utility). This is much faster than WMI/VBS queries and less prone to hanging.

Returns
array|false Service information array or false if service doesn't exist

Definition at line 722 of file class.win32service.php.

723 {
724 Log::trace("Starting fastServiceCheck for service: " . $this->getName());
725
726 $startTime = microtime(true);
727
728 // Use sc.exe to query service - this is very fast and reliable
729 // Execute with hidden window to prevent command prompt flash
730 Log::trace("Executing: sc query " . $this->getName());
731
732 $output = CommandRunner::execCombined('sc', ['query', $this->getName()]);
733 $duration = round(microtime(true) - $startTime, 3);
734
735 Log::trace("sc.exe query completed in " . $duration . "s");
736
737 if ($output === null || $output === false) {
738 Log::trace("sc.exe returned null/false, service likely doesn't exist");
739 return false;
740 }
741
742 // Check if service doesn't exist
743 if (stripos($output, 'does not exist') !== false ||
744 stripos($output, 'FAILED') !== false ||
745 stripos($output, '1060') !== false) { // Error code 1060 = service doesn't exist
746 Log::trace("Service doesn't exist: " . $this->getName());
747 return false;
748 }
749
750 // Service exists - parse basic info
751 $serviceInfo = [];
752
753 // Extract service name
754 if (preg_match('/SERVICE_NAME:\s*(.+)/i', $output, $matches)) {
755 $serviceInfo[self::VBS_NAME] = trim($matches[1]);
756 }
757
758 // Extract display name
759 if (preg_match('/DISPLAY_NAME:\s*(.+)/i', $output, $matches)) {
760 $serviceInfo[self::VBS_DISPLAY_NAME] = trim($matches[1]);
761 }
762
763 // Extract state
764 if (preg_match('/STATE\s*:\s*\d+\s+(\w+)/i', $output, $matches)) {
765 $state = trim($matches[1]);
766 $serviceInfo[self::SERVICE_STATE] = $state;
767 Log::trace("Service state: " . $state);
768 }
769
770 // If we have basic info, service exists - get full details if needed
771 if (!empty($serviceInfo)) {
772 Log::trace("Service exists, getting full details");
773
774 // Use sc qc to get configuration details (including path)
775 $configOutput = CommandRunner::execCombined('sc', ['qc', $this->getName()]);
776
777 if ($configOutput !== null && $configOutput !== false && preg_match('/BINARY_PATH_NAME\s*:\s*(.+)/i', $configOutput, $matches)) {
778 $serviceInfo[self::VBS_PATH_NAME] = trim($matches[1]);
779 Log::trace("Service path: " . $serviceInfo[self::VBS_PATH_NAME]);
780 }
781
782 // Get description if available
783 if ($configOutput !== null && $configOutput !== false && preg_match('/DISPLAY_NAME\s*:\s*(.+)/i', $configOutput, $matches)) {
784 $serviceInfo[self::VBS_DESCRIPTION] = trim($matches[1]);
785 }
786
787 Log::trace("Fast service check successful for: " . $this->getName());
788 return $serviceInfo;
789 }
790
791 Log::trace("Could not parse service info from sc.exe output");
792 return false;
793 }
static execCombined(string $executable, array $args=[])

References CommandRunner\execCombined(), getName(), and Log\trace().

Referenced by infos().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getBinPath()

getBinPath ( )

Gets the binary path of the service.

Returns
string The binary path of the service.

Definition at line 1091 of file class.win32service.php.

1091 : string
1092 {
1093 return $this->binPath;
1094 }

References $binPath.

Referenced by create(), and delete().

Here is the caller graph for this function:

◆ getDisplayName()

getDisplayName ( )

Gets the display name of the service.

Returns
string The display name of the service.

Definition at line 1071 of file class.win32service.php.

1071 : string
1072 {
1073 return $this->displayName;
1074 }

References $displayName.

Referenced by create().

Here is the caller graph for this function:

◆ getError()

getError ( )

Gets a detailed error message for the latest error encountered by the service.

Returns
string|null The detailed error message, or null if no error.

Definition at line 1217 of file class.win32service.php.

1218 {
1219 global $bearsamppLang;
1220 if ( $this->latestError != self::WIN32_NO_ERROR ) {
1221 // Ensure proper type conversion for PHP 8.5.7 compatibility
1222 $errorInt = is_numeric($this->latestError) ? hexdec( $this->latestError ) : 0;
1223 return $bearsamppLang->getValue( Lang::ERROR ) . ' ' .
1224 $this->latestError . ' (' . $errorInt . ' : ' . $this->getWin32ErrorCodeDesc( $this->latestError ) . ')';
1225 }
1226 elseif ( $this->latestStatus != self::WIN32_SERVICE_NA ) {
1227 // Ensure proper type conversion for PHP 8.5.7 compatibility
1228 $statusInt = is_numeric($this->latestStatus) ? hexdec( $this->latestStatus ) : 0;
1229 return $bearsamppLang->getValue( Lang::STATUS ) . ' ' .
1230 $this->latestStatus . ' (' . $statusInt . ' : ' . $this->getWin32ServiceStatusDesc( $this->latestStatus ) . ')';
1231 }
1232
1233 return null;
1234 }
global $bearsamppLang
const ERROR
const STATUS
getWin32ServiceStatusDesc($status)

References $bearsamppLang, Lang\ERROR, getWin32ErrorCodeDesc(), getWin32ServiceStatusDesc(), and Lang\STATUS.

Here is the call graph for this function:

◆ getErrorControl()

getErrorControl ( )

Gets the error control setting of the service.

Returns
string The error control setting of the service.

Definition at line 1151 of file class.win32service.php.

1151 : string
1152 {
1153 return $this->errorControl;
1154 }

References $errorControl.

Referenced by create().

Here is the caller graph for this function:

◆ getLatestError()

getLatestError ( )

Gets the latest error encountered by the service.

Returns
string The latest error encountered by the service.

Definition at line 1207 of file class.win32service.php.

1208 {
1209 return $this->latestError;
1210 }

References $latestError.

Referenced by create().

Here is the caller graph for this function:

◆ getLatestStatus()

getLatestStatus ( )

Gets the latest status of the service.

Returns
string The latest status of the service.

Definition at line 1197 of file class.win32service.php.

1198 {
1199 return $this->latestStatus;
1200 }

References $latestStatus.

◆ getName()

getName ( )

Gets the name of the service.

Returns
string The name of the service.

Definition at line 1051 of file class.win32service.php.

1051 : string
1052 {
1053 return $this->name;
1054 }

References $name.

Referenced by create(), delete(), ensureReset(), fastServiceCheck(), infos(), isInstalled(), isPaused(), isRunning(), isStopped(), start(), status(), stop(), and waitForServiceDeletion().

Here is the caller graph for this function:

◆ getNssm()

getNssm ( )

Gets the NSSM instance associated with the service.

Returns
Nssm The NSSM instance.

Definition at line 1171 of file class.win32service.php.

1172 {
1173 return $this->nssm;
1174 }

References $nssm.

Referenced by create(), delete(), and infos().

Here is the caller graph for this function:

◆ getParams()

getParams ( )

Gets the parameters for the service.

Returns
string The parameters for the service.

Definition at line 1111 of file class.win32service.php.

1111 : string
1112 {
1113 return $this->params;
1114 }

References $params.

Referenced by create().

Here is the caller graph for this function:

◆ getServices()

getServices ( $forceRefresh = false)
static

Retrieves all Windows services. Use $forceRefresh to ignore the cache.

Parameters
bool$forceRefreshWhether to force a refresh of the service list.
Returns
array Array of services.

Definition at line 139 of file class.win32service.php.

140 {
141 if (self::$serviceListCache === null || $forceRefresh) {
142 Log::trace('Fetching service list from Windows (COM/WMI)');
143 $startTime = microtime(true);
144 $services = Win32Native::listServices(self::getVbsKeys());
145
146 self::$serviceListCache = [];
147 if (is_array($services)) {
148 foreach ($services as $service) {
149 if (isset($service[self::VBS_NAME])) {
150 self::$serviceListCache[$service[self::VBS_NAME]] = $service;
151 }
152 }
153 }
154 $duration = round(microtime(true) - $startTime, 3);
155 Log::trace('Service list fetched in ' . $duration . 's');
156 }
157
158 return self::$serviceListCache;
159 }
static listServices($properties=[])

References Win32Native\listServices(), and Log\trace().

Referenced by ActionStartup\checkApacheServiceWithTimeout(), ActionStartup\checkMySQLServiceWithTimeout(), and ActionStartup\installServicesSequential().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getStartType()

getStartType ( )

Gets the start type of the service.

Returns
string The start type of the service.

Definition at line 1131 of file class.win32service.php.

1131 : string
1132 {
1133 return $this->startType;
1134 }

References $startType.

Referenced by create().

Here is the caller graph for this function:

◆ getVbsKeys()

getVbsKeys ( )
static

Returns an array of VBS keys used for service information.

Returns
array The array of VBS keys.

Definition at line 121 of file class.win32service.php.

121 : array
122 {
123 return array(
124 self::VBS_NAME,
125 self::VBS_DISPLAY_NAME,
126 self::VBS_DESCRIPTION,
127 self::VBS_PATH_NAME,
128 self::SERVICE_STATE
129 );
130 }

◆ getWin32ErrorCodeDesc()

getWin32ErrorCodeDesc ( $code)
private

Returns a description of the Win32 error code.

Parameters
string$codeThe error code.
Returns
string|null The description of the error code, or null if the code is not recognized.

Definition at line 1035 of file class.win32service.php.

1035 : ?string
1036 {
1037 switch ( $code ) {
1038 case self::WIN32_ERROR_ACCESS_DENIED:
1039 return 'The handle to the SCM database does not have the appropriate access rights.';
1040 // ... other cases ...
1041 default:
1042 return null;
1043 }
1044 }

Referenced by getError().

Here is the caller graph for this function:

◆ getWin32ServiceStatusDesc()

getWin32ServiceStatusDesc ( $status)
private

Returns a description of the Win32 service status.

Parameters
string$statusThe status code.
Returns
string|null The status description.

Definition at line 996 of file class.win32service.php.

996 : ?string
997 {
998 switch ( $status ) {
999 case self::WIN32_SERVICE_CONTINUE_PENDING:
1000 return 'The service continue is pending.';
1001
1002 case self::WIN32_SERVICE_PAUSE_PENDING:
1003 return 'The service pause is pending.';
1004
1005 case self::WIN32_SERVICE_PAUSED:
1006 return 'The service is paused.';
1007
1008 case self::WIN32_SERVICE_RUNNING:
1009 return 'The service is running.';
1010
1011 case self::WIN32_SERVICE_START_PENDING:
1012 return 'The service is starting.';
1013
1014 case self::WIN32_SERVICE_STOP_PENDING:
1015 return 'The service is stopping.';
1016
1017 case self::WIN32_SERVICE_STOPPED:
1018 return 'The service is not running.';
1019
1020 case self::WIN32_SERVICE_NA:
1021 return 'Cannot retrieve service status.';
1022
1023 default:
1024 return null;
1025 }
1026 }

Referenced by getError().

Here is the caller graph for this function:

◆ infos()

infos ( )

Retrieves information about the service. Performance optimization: Uses fast sc.exe check first, falls back to VBS if needed.

Returns
array|false The service information, or false on failure.

Definition at line 801 of file class.win32service.php.

802 {
803 Log::trace("Starting Win32Service::infos for service: " . $this->getName());
804
805 try {
806 // Set a timeout for the entire operation
807 $startTime = microtime(true);
808 $timeout = 10; // 10 seconds timeout for the entire operation
809
810 if ($this->getNssm() instanceof Nssm) {
811 Log::trace("Using NSSM to get service info");
812 $result = $this->getNssm()->infos();
813 Log::trace("NSSM info retrieval completed in " . round(microtime(true) - $startTime, 2) . " seconds");
814 return $result;
815 }
816
817 // Performance optimization: Try fast sc.exe check first
818 Log::trace("Attempting fast service check using sc.exe");
819 $fastResult = $this->fastServiceCheck();
820
821 if ($fastResult !== false) {
822 $duration = round(microtime(true) - $startTime, 3);
823 Log::trace("Fast service check succeeded in " . $duration . "s (saved 5-10s)");
824 Log::debug("Performance: Fast service check used for " . $this->getName() . ", saved 5-10 seconds");
825 return $fastResult;
826 }
827
828 // Fast check returned false - service doesn't exist
829 if ($fastResult === false) {
830 $duration = round(microtime(true) - $startTime, 3);
831 Log::trace("Fast service check determined service doesn't exist in " . $duration . "s");
832 return false;
833 }
834
835 // Fallback to VBS (should rarely be needed now)
836 Log::trace("Falling back to VBS for service info");
837
838 // Use set_time_limit to prevent PHP script timeout
839 $originalTimeout = ini_get('max_execution_time');
840 set_time_limit(15); // 15 seconds timeout
841
842 // Create a separate process to get service info with a timeout
844
845 // Reset the timeout
846 set_time_limit($originalTimeout);
847
848 // Check if we've exceeded our timeout
849 if (microtime(true) - $startTime > $timeout) {
850 Log::trace("Timeout exceeded in infos() method, returning false");
851 return false;
852 }
853
854 Log::trace("VBS info retrieval completed in " . round(microtime(true) - $startTime, 2) . " seconds");
855 return $result;
856 } catch (\Exception $e) {
857 Log::trace("Exception in infos() method: " . $e->getMessage() . ", returning false");
858 return false;
859 } catch (\Throwable $e) {
860 Log::trace("Throwable in infos() method: " . $e->getMessage() . ", returning false");
861 return false;
862 }
863 }
static debug($data, $file=null)
static getServiceInfo($serviceName, $properties=[])

References $result, Log\debug(), fastServiceCheck(), getName(), getNssm(), Win32Native\getServiceInfo(), and Log\trace().

Here is the call graph for this function:

◆ isInstalled()

isInstalled ( )

Checks if the service is installed.

Returns
bool True if the service is installed, false otherwise.

Definition at line 870 of file class.win32service.php.

870 : bool
871 {
872 Log::trace("Checking if service is installed: " . $this->getName());
873
874 try {
875 // Set a timeout for the entire operation
876 $startTime = microtime(true);
877 $timeout = 15; // 15 seconds timeout for the entire operation
878
879 // Call status() with a try-catch to ensure we don't get stuck
880 $status = $this->status();
881
882 // Check if we've exceeded our timeout
883 if (microtime(true) - $startTime > $timeout) {
884 Log::trace("Timeout exceeded in isInstalled() method, assuming service is not installed");
885 $this->writeLog('isInstalled ' . $this->getName() . ': NO (timeout exceeded)');
886 return false;
887 }
888
889 $isInstalled = $status != self::WIN32_SERVICE_NA;
890
891 Log::trace("Service " . $this->getName() . " installation status: " . ($isInstalled ? "YES" : "NO") . " (status code: " . $status . ")");
892 $this->writeLog('isInstalled ' . $this->getName() . ': ' . ($isInstalled ? 'YES' : 'NO') . ' (status: ' . $status . ')');
893
894 return $isInstalled;
895 } catch (\Exception $e) {
896 Log::trace("Exception in isInstalled() method: " . $e->getMessage() . ", assuming service is not installed");
897 $this->writeLog('isInstalled ' . $this->getName() . ': NO (exception: ' . $e->getMessage() . ')');
898 return false;
899 } catch (\Throwable $e) {
900 Log::trace("Throwable in isInstalled() method: " . $e->getMessage() . ", assuming service is not installed");
901 $this->writeLog('isInstalled ' . $this->getName() . ': NO (throwable: ' . $e->getMessage() . ')');
902 return false;
903 }
904 }

References getName(), status(), Log\trace(), and writeLog().

Referenced by create(), and delete().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ isPaused()

isPaused ( )

Checks if the service is paused.

Returns
bool True if the service is paused, false otherwise.

Definition at line 947 of file class.win32service.php.

947 : bool
948 {
949 Log::trace("Checking if service is paused: " . $this->getName());
950
951 $status = $this->status();
952 $isPaused = $status == self::WIN32_SERVICE_PAUSED;
953
954 Log::trace("Service " . $this->getName() . " paused status: " . ($isPaused ? "YES" : "NO") . " (status code: " . $status . ")");
955 $this->writeLog( 'isPaused ' . $this->getName() . ': ' . ($isPaused ? 'YES' : 'NO') . ' (status: ' . $status . ')' );
956
957 return $isPaused;
958 }

References getName(), status(), Log\trace(), and writeLog().

Here is the call graph for this function:

◆ isPending()

isPending ( $status)

Checks if the service is in a pending state.

Parameters
string$statusThe status to check.
Returns
bool True if the service is in a pending state, false otherwise.

Definition at line 967 of file class.win32service.php.

967 : bool
968 {
969 $isPending = $status == self::WIN32_SERVICE_START_PENDING || $status == self::WIN32_SERVICE_STOP_PENDING
970 || $status == self::WIN32_SERVICE_CONTINUE_PENDING || $status == self::WIN32_SERVICE_PAUSE_PENDING;
971
972 Log::trace("Checking if status is pending: " . $status . " - Result: " . ($isPending ? "YES" : "NO"));
973
974 if ($isPending) {
975 if ($status == self::WIN32_SERVICE_START_PENDING) {
976 Log::trace("Service is in START_PENDING state");
977 } else if ($status == self::WIN32_SERVICE_STOP_PENDING) {
978 Log::trace("Service is in STOP_PENDING state");
979 } else if ($status == self::WIN32_SERVICE_CONTINUE_PENDING) {
980 Log::trace("Service is in CONTINUE_PENDING state");
981 } else if ($status == self::WIN32_SERVICE_PAUSE_PENDING) {
982 Log::trace("Service is in PAUSE_PENDING state");
983 }
984 }
985
986 return $isPending;
987 }

References Log\trace().

Referenced by start(), status(), and stop().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ isRunning()

isRunning ( )

Checks if the service is running.

Returns
bool True if the service is running, false otherwise.

Definition at line 911 of file class.win32service.php.

911 : bool
912 {
913 Log::trace("Checking if service is running: " . $this->getName());
914
915 $status = $this->status();
916 $isRunning = $status == self::WIN32_SERVICE_RUNNING;
917
918 Log::trace("Service " . $this->getName() . " running status: " . ($isRunning ? "YES" : "NO") . " (status code: " . $status . ")");
919 $this->writeLog( 'isRunning ' . $this->getName() . ': ' . ($isRunning ? 'YES' : 'NO') . ' (status: ' . $status . ')' );
920
921 return $isRunning;
922 }

References getName(), status(), Log\trace(), and writeLog().

Referenced by ensureReset(), and start().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ isStopped()

isStopped ( )

Checks if the service is stopped.

Returns
bool True if the service is stopped, false otherwise.

Definition at line 929 of file class.win32service.php.

929 : bool
930 {
931 Log::trace("Checking if service is stopped: " . $this->getName());
932
933 $status = $this->status();
934 $isStopped = $status == self::WIN32_SERVICE_STOPPED;
935
936 Log::trace("Service " . $this->getName() . " stopped status: " . ($isStopped ? "YES" : "NO") . " (status code: " . $status . ")");
937 $this->writeLog( 'isStopped ' . $this->getName() . ': ' . ($isStopped ? 'YES' : 'NO') . ' (status: ' . $status . ')' );
938
939 return $isStopped;
940 }

References getName(), status(), Log\trace(), and writeLog().

Referenced by stop().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset()

reset ( )

Resets the service by deleting and recreating it.

Returns
bool True if the service was reset successfully, false otherwise.

Definition at line 553 of file class.win32service.php.

553 : bool
554 {
555 if ( $this->delete() ) {
556 usleep( self::SLEEP_TIME );
557
558 return $this->create();
559 }
560
561 return false;
562 }

References create().

Here is the call graph for this function:

◆ restart()

restart ( )

Restarts the service by stopping and then starting it.

Returns
bool True if the service was restarted successfully, false otherwise.

Definition at line 707 of file class.win32service.php.

707 : bool
708 {
709 if ( $this->stop() ) {
710 return $this->start();
711 }
712
713 return false;
714 }

References start(), and stop().

Here is the call graph for this function:

◆ setBinPath()

setBinPath ( $binPath)

Sets the binary path of the service.

Parameters
string$binPathThe binary path to set.

Definition at line 1101 of file class.win32service.php.

1101 : void
1102 {
1103 $this->binPath = str_replace( '"', '', Path::formatWindowsPath( $binPath ) );
1104 }
static formatWindowsPath($path)

References $binPath, and Path\formatWindowsPath().

Referenced by setNssm().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ setDisplayName()

setDisplayName ( $displayName)

Sets the display name of the service.

Parameters
string$displayNameThe display name to set.

Definition at line 1081 of file class.win32service.php.

1081 : void
1082 {
1083 $this->displayName = $displayName;
1084 }

References $displayName.

Referenced by setNssm().

Here is the caller graph for this function:

◆ setErrorControl()

setErrorControl ( $errorControl)

Sets the error control setting of the service.

Parameters
string$errorControlThe error control setting to set.

Definition at line 1161 of file class.win32service.php.

1161 : void
1162 {
1163 $this->errorControl = $errorControl;
1164 }

References $errorControl.

◆ setName()

setName ( $name)

Sets the name of the service.

Parameters
string$nameThe name to set.

Definition at line 1061 of file class.win32service.php.

1061 : void
1062 {
1063 $this->name = $name;
1064 }

References $name.

◆ setNssm()

setNssm ( $nssm)

Sets the NSSM instance associated with the service.

Parameters
Nssm$nssmThe NSSM instance to set.

Definition at line 1181 of file class.win32service.php.

1182 {
1183 if ( $nssm instanceof Nssm ) {
1184 $this->setDisplayName( $nssm->getDisplayName() );
1185 $this->setBinPath( $nssm->getBinPath() );
1186 $this->setParams( $nssm->getParams() );
1187 $this->setStartType( $nssm->getStart() );
1188 $this->nssm = $nssm;
1189 }
1190 }
setStartType($startType)
setDisplayName($displayName)

References $nssm, setBinPath(), setDisplayName(), setParams(), and setStartType().

Here is the call graph for this function:

◆ setParams()

setParams ( $params)

Sets the parameters for the service.

Parameters
string$paramsThe parameters to set.

Definition at line 1121 of file class.win32service.php.

1121 : void
1122 {
1123 $this->params = $params;
1124 }

References $params.

Referenced by setNssm().

Here is the caller graph for this function:

◆ setStartType()

setStartType ( $startType)

Sets the start type of the service.

Parameters
string$startTypeThe start type to set.

Definition at line 1141 of file class.win32service.php.

1141 : void
1142 {
1143 $this->startType = $startType;
1144 }

References $startType.

Referenced by setNssm().

Here is the caller graph for this function:

◆ start()

start ( )

Starts the service.

Returns
bool True if the service was started successfully, false otherwise.

Definition at line 569 of file class.win32service.php.

569 : bool
570 {
571 global $bearsamppBins;
572
573 Log::info('Attempting to start service: ' . $this->getName());
574
575 if ( $this->getName() == BinMysql::SERVICE_NAME ) {
576 $bearsamppBins->getMysql()->initData();
577 }
578 elseif ( $this->getName() == BinMariadb::SERVICE_NAME ) {
579 $bearsamppBins->getMariadb()->initData();
580 }
581 elseif ( $this->getName() == BinMailpit::SERVICE_NAME ) {
582 $bearsamppBins->getMailpit()->rebuildConf();
583 }
584 elseif ( $this->getName() == BinMemcached::SERVICE_NAME ) {
585 $bearsamppBins->getMemcached()->rebuildConf();
586 }
587 elseif ( $this->getName() == BinPostgresql::SERVICE_NAME ) {
588 $bearsamppBins->getPostgresql()->rebuildConf();
589 $bearsamppBins->getPostgresql()->initData();
590 }
591 elseif ( $this->getName() == BinXlight::SERVICE_NAME ) {
592 $bearsamppBins->getXlight()->rebuildConf();
593 }
594
595
596 $result = $this->callWin32Service( 'win32_start_service', $this->getName(), true );
597 // Ensure proper type conversion for PHP 8.5.7 compatibility
598 $resultInt = is_numeric($result) ? (int)$result : 0;
599 $start = $result !== null ? dechex( $resultInt ) : '0';
600 Log::debug( 'Start service ' . $this->getName() . ': ' . $start . ' (status: ' . $this->status() . ')' );
601
602 if ( $start != self::WIN32_NO_ERROR && $start != self::WIN32_ERROR_SERVICE_ALREADY_RUNNING ) {
603
604 // Write error to log
605 Log::error('Failed to start service: ' . $this->getName() . ' with error code: ' . $start);
606
607 if ( $this->getName() == BinApache::SERVICE_NAME ) {
608 $cmdOutput = $bearsamppBins->getApache()->getCmdLineOutput( BinApache::CMD_SYNTAX_CHECK );
609 if ( !$cmdOutput['syntaxOk'] ) {
610 file_put_contents(
611 $bearsamppBins->getApache()->getErrorLog(),
612 '[' . date( 'Y-m-d H:i:s', time() ) . '] [error] ' . $cmdOutput['content'] . PHP_EOL,
613 FILE_APPEND
614 );
615 }
616 }
617 elseif ( $this->getName() == BinMysql::SERVICE_NAME ) {
618 $cmdOutput = $bearsamppBins->getMysql()->getCmdLineOutput( BinMysql::CMD_SYNTAX_CHECK );
619 if ( !$cmdOutput['syntaxOk'] ) {
620 file_put_contents(
621 $bearsamppBins->getMysql()->getErrorLog(),
622 '[' . date( 'Y-m-d H:i:s', time() ) . '] [error] ' . $cmdOutput['content'] . PHP_EOL,
623 FILE_APPEND
624 );
625 }
626 }
627 elseif ( $this->getName() == BinMariadb::SERVICE_NAME ) {
628 $cmdOutput = $bearsamppBins->getMariadb()->getCmdLineOutput( BinMariadb::CMD_SYNTAX_CHECK );
629 if ( !$cmdOutput['syntaxOk'] ) {
630 file_put_contents(
631 $bearsamppBins->getMariadb()->getErrorLog(),
632 '[' . date( 'Y-m-d H:i:s', time() ) . '] [error] ' . $cmdOutput['content'] . PHP_EOL,
633 FILE_APPEND
634 );
635 }
636 }
637
638 return false;
639 }
640
641 // Wait for the service to actually start before checking if it's running
642 // We use a timeout to avoid hanging if the service fails to start properly
643 $maxtime = time() + self::PENDING_TIMEOUT;
644 while ($this->isPending($this->status(false)) && time() < $maxtime) {
645 usleep(self::SLEEP_TIME);
646 }
647
648 if ( !$this->isRunning() ) {
649 $this->latestError = self::WIN32_NO_ERROR;
650 Log::error('Service ' . $this->getName() . ' is not running after start attempt (status: ' . $this->status() . ').');
651 $this->latestError = null;
652 return false;
653 }
654
655 Log::info('Service ' . $this->getName() . ' started successfully.');
656 return true;
657 }
const CMD_SYNTAX_CHECK
const SERVICE_NAME
const CMD_SYNTAX_CHECK
static info($data, $file=null)
static error($data, $file=null)

References $bearsamppBins, $result, callWin32Service(), BinApache\CMD_SYNTAX_CHECK, BinMariadb\CMD_SYNTAX_CHECK, BinMysql\CMD_SYNTAX_CHECK, Log\debug(), Log\error(), getName(), Log\info(), isPending(), isRunning(), BinApache\SERVICE_NAME, BinMailpit\SERVICE_NAME, BinMariadb\SERVICE_NAME, BinMemcached\SERVICE_NAME, BinMysql\SERVICE_NAME, BinPostgresql\SERVICE_NAME, BinXlight\SERVICE_NAME, and status().

Referenced by restart().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ status()

status ( $timeout = true)

Queries the status of the service.

Parameters
bool$timeoutWhether to use a timeout.
Returns
string The status of the service.

Definition at line 283 of file class.win32service.php.

283 : string
284 {
285 $this->latestStatus = self::WIN32_SERVICE_NA;
286 $maxtime = time() + self::PENDING_TIMEOUT;
287
288 Log::trace("Querying status for service: " . $this->getName() . " (timeout: " . ($timeout ? "enabled" : "disabled") . ")");
289 if ($timeout) {
290 Log::trace("Max timeout time set to: " . date('Y-m-d H:i:s', $maxtime));
291 }
292
293 // Add a safety counter to prevent infinite loops
294 $loopCount = 0;
295 $maxLoops = 5; // Maximum number of attempts
296 $startTime = microtime(true);
297
298 try {
299 while ( ($this->latestStatus == self::WIN32_SERVICE_NA || $this->isPending( $this->latestStatus )) && $loopCount < $maxLoops ) {
300 $loopCount++;
301 Log::trace("Calling win32_query_service_status for service: " . $this->getName() . " (attempt " . $loopCount . " of " . $maxLoops . ")");
302
303 // Add a timeout check before making the call
304 if (microtime(true) - $startTime > 10) { // 10 seconds overall timeout
305 Log::trace("Overall timeout reached before making service status call");
306 break;
307 }
308
309 $this->latestStatus = $this->callWin32Service( 'win32_query_service_status', $this->getName() );
310
311 if ( is_array( $this->latestStatus ) && isset( $this->latestStatus['CurrentState'] ) ) {
312 // Ensure proper type conversion for PHP 8.5.7 compatibility
313 $stateInt = is_numeric($this->latestStatus['CurrentState']) ? (int)$this->latestStatus['CurrentState'] : 0;
314 $this->latestStatus = dechex( $stateInt );
315 Log::trace("Service status returned as array, CurrentState: " . $this->latestStatus);
316 }
317 elseif ( $this->latestStatus !== null ) {
318 // Ensure proper type conversion for PHP 8.5.7 compatibility
319 $statusInt = is_numeric($this->latestStatus) ? (int)$this->latestStatus : 0;
320 $statusHex = dechex( $statusInt );
321 Log::trace("Service status returned as value: " . $statusHex);
322
323 if ( $statusHex == self::WIN32_ERROR_SERVICE_DOES_NOT_EXIST ) {
324 $this->latestStatus = $statusHex;
325 Log::trace("Service does not exist, breaking loop");
326 break; // Exit the loop immediately if service doesn't exist
327 }
328 } else {
329 Log::trace("Service status query returned null");
330 // If we get a null result, assume service does not exist to avoid hanging
331 if ($loopCount >= 2) // Only do this after at least one retry
332 {
333 Log::trace("Multiple null results, assuming service does not exist");
334 $this->latestStatus = self::WIN32_ERROR_SERVICE_DOES_NOT_EXIST;
335 break;
336 }
337 }
338
339 if ( $timeout && $maxtime < time() ) {
340 Log::trace("Timeout reached while querying service status");
341 break;
342 }
343
344 // Only sleep if we're going to loop again
345 if ($loopCount < $maxLoops && ($this->latestStatus == self::WIN32_SERVICE_NA || $this->isPending($this->latestStatus))) {
346 Log::trace("Sleeping before next status check attempt");
347 usleep(self::SLEEP_TIME);
348 }
349 }
350 } catch (\Exception $e) {
351 Log::trace("Exception in status method: " . $e->getMessage());
352 // If an exception occurs, assume service does not exist
353 $this->latestStatus = self::WIN32_ERROR_SERVICE_DOES_NOT_EXIST;
354 } catch (\Throwable $e) {
355 Log::trace("Throwable in status method: " . $e->getMessage());
356 // If a throwable occurs, assume service does not exist
357 $this->latestStatus = self::WIN32_ERROR_SERVICE_DOES_NOT_EXIST;
358 }
359
360 if ($loopCount >= $maxLoops) {
361 Log::trace("Maximum query attempts reached for service: " . $this->getName());
362 }
363
364 $elapsedTime = microtime(true) - $startTime;
365 Log::trace("Status check completed in " . round($elapsedTime, 2) . " seconds after " . $loopCount . " attempts");
366
367 if ( $this->latestStatus == self::WIN32_ERROR_SERVICE_DOES_NOT_EXIST ) {
368 $this->latestError = $this->latestStatus;
369 $this->latestStatus = self::WIN32_SERVICE_NA;
370 Log::trace("Service does not exist, setting status to NA");
371 }
372
373 Log::trace("Final status for service " . $this->getName() . ": " . $this->latestStatus);
374 return $this->latestStatus;
375 }

References $latestStatus, callWin32Service(), getName(), isPending(), and Log\trace().

Referenced by create(), delete(), isInstalled(), isPaused(), isRunning(), isStopped(), start(), stop(), and waitForServiceDeletion().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ stop()

stop ( )

Stops the service.

Returns
bool True if the service was stopped successfully, false otherwise.

Definition at line 664 of file class.win32service.php.

664 : bool
665 {
666 Log::trace("Starting Win32Service::stop for service: " . $this->getName());
667
668 Log::trace("Calling win32_stop_service for service: " . $this->getName());
669 $result = $this->callWin32Service( 'win32_stop_service', $this->getName(), true );
670
671 // Ensure proper type conversion for PHP 8.5.7 compatibility
672 $resultInt = is_numeric($result) ? (int)$result : 0;
673 $stop = $result !== null ? dechex( $resultInt ) : '0';
674 Log::trace("Stop service result code: " . $stop);
675
676 Log::trace("Checking current status after stop attempt");
677 $currentStatus = $this->status();
678 Log::trace("Current status: " . $currentStatus);
679
680 $this->writeLog( 'Stop service ' . $this->getName() . ': ' . $stop . ' (status: ' . $currentStatus . ')' );
681
682 if ( $stop != self::WIN32_NO_ERROR ) {
683 return false;
684 }
685
686 // Wait for the service to actually stop before checking if it's stopped
687 $maxtime = time() + self::PENDING_TIMEOUT;
688 while ($this->isPending($this->status(false)) && time() < $maxtime) {
689 usleep(self::SLEEP_TIME);
690 }
691
692 if ( !$this->isStopped() ) {
693 $this->latestError = self::WIN32_NO_ERROR;
694 Log::error('Service ' . $this->getName() . ' is still running after stop attempt (status: ' . $this->status() . ').');
695 $this->latestError = null;
696 return false;
697 }
698
699 return true;
700 }

References $result, callWin32Service(), Log\error(), getName(), isPending(), isStopped(), status(), Log\trace(), and writeLog().

Referenced by delete(), ensureReset(), and restart().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ waitForServiceDeletion()

waitForServiceDeletion ( $maxWaitTime = 30)

Waits for the service to be completely removed from the SCM database. This is important after deletion because the SCM marks services for deletion but they remain visible briefly, preventing re-creation.

Parameters
int$maxWaitTimeMaximum time to wait in seconds (default 30)
Returns
bool True if service is confirmed deleted, false on timeout

Definition at line 1245 of file class.win32service.php.

1245 : bool
1246 {
1247 $startTime = time();
1248 $maxTime = $startTime + $maxWaitTime;
1249 $checkCount = 0;
1250
1251 Log::trace("Waiting for service deletion: " . $this->getName() . " (max wait: " . $maxWaitTime . "s)");
1252
1253 while (time() < $maxTime) {
1254 $checkCount++;
1255 $status = $this->status(false);
1256 Log::trace("Service deletion check #" . $checkCount . " - Status: " . $status . " at " . date('Y-m-d H:i:s'));
1257
1258 // Service doesn't exist or is definitely not there
1259 if ($status == self::WIN32_SERVICE_NA ||
1260 $status == self::WIN32_ERROR_SERVICE_DOES_NOT_EXIST) {
1261 $elapsedTime = time() - $startTime;
1262 Log::trace("Service deletion confirmed after " . $elapsedTime . " seconds");
1263 return true;
1264 }
1265
1266 // Wait a bit before checking again
1267 usleep(500000); // 0.5 seconds
1268 }
1269
1270 $totalWaitTime = time() - $startTime;
1271 Log::trace("Service deletion timeout after " . $totalWaitTime . " seconds - service still exists: " . $this->getName());
1272 return false;
1273 }

References getName(), status(), and Log\trace().

Referenced by ensureReset().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ writeLog()

writeLog ( $log)
private

Writes a log entry.

Parameters
string$logThe log message.

Definition at line 110 of file class.win32service.php.

110 : void
111 {
112 global $bearsamppRoot;
114 }
global $bearsamppRoot
static getServicesLogFilePath($aetrayPath=false)

References $bearsamppRoot, Log\debug(), and Path\getServicesLogFilePath().

Referenced by create(), delete(), isInstalled(), isPaused(), isRunning(), isStopped(), and stop().

Here is the call graph for this function:
Here is the caller graph for this function:

Field Documentation

◆ $binPath

$binPath
private

Definition at line 79 of file class.win32service.php.

Referenced by getBinPath(), and setBinPath().

◆ $displayName

$displayName
private

Definition at line 78 of file class.win32service.php.

Referenced by getDisplayName(), and setDisplayName().

◆ $errorControl

$errorControl
private

Definition at line 82 of file class.win32service.php.

Referenced by getErrorControl(), and setErrorControl().

◆ $latestError

$latestError
private

Definition at line 86 of file class.win32service.php.

Referenced by getLatestError().

◆ $latestStatus

$latestStatus
private

Definition at line 85 of file class.win32service.php.

Referenced by getLatestStatus(), and status().

◆ $loggedFunctions

$loggedFunctions = array()
staticprivate

Definition at line 92 of file class.win32service.php.

◆ $name

$name
private

Definition at line 77 of file class.win32service.php.

Referenced by __construct(), getName(), and setName().

◆ $nssm

$nssm
private

Definition at line 83 of file class.win32service.php.

Referenced by getNssm(), and setNssm().

◆ $params

$params
private

Definition at line 80 of file class.win32service.php.

Referenced by getParams(), and setParams().

◆ $serviceListCache

$serviceListCache = null
staticprivate

Definition at line 89 of file class.win32service.php.

◆ $startType

$startType
private

Definition at line 81 of file class.win32service.php.

Referenced by getStartType(), and setStartType().

◆ PENDING_TIMEOUT

const PENDING_TIMEOUT = 20

Definition at line 68 of file class.win32service.php.

◆ SERVER_ERROR_IGNORE

const SERVER_ERROR_IGNORE = '0'

Definition at line 61 of file class.win32service.php.

◆ SERVER_ERROR_NORMAL

const SERVER_ERROR_NORMAL = '1'

Definition at line 62 of file class.win32service.php.

Referenced by BinMariadb\reload(), BinMysql\reload(), and BinPostgresql\reload().

◆ SERVICE_AUTO_START

const SERVICE_AUTO_START = '2'

Definition at line 64 of file class.win32service.php.

◆ SERVICE_DEMAND_START

const SERVICE_DEMAND_START = '3'

Definition at line 65 of file class.win32service.php.

Referenced by BinMariadb\reload(), BinMysql\reload(), and BinPostgresql\reload().

◆ SERVICE_DISABLED

const SERVICE_DISABLED = '4'

Definition at line 66 of file class.win32service.php.

◆ SERVICE_STATE

const SERVICE_STATE = 'State'

Definition at line 75 of file class.win32service.php.

Referenced by ActionStartup\prepareService().

◆ SLEEP_TIME

const SLEEP_TIME = 100000

Definition at line 69 of file class.win32service.php.

◆ VBS_DESCRIPTION

const VBS_DESCRIPTION = 'Description'

Definition at line 73 of file class.win32service.php.

◆ VBS_DISPLAY_NAME

const VBS_DISPLAY_NAME = 'DisplayName'

Definition at line 72 of file class.win32service.php.

◆ VBS_NAME

const VBS_NAME = 'Name'

Definition at line 71 of file class.win32service.php.

◆ VBS_PATH_NAME

const VBS_PATH_NAME = 'PathName'

Definition at line 74 of file class.win32service.php.

Referenced by Nssm\infos(), and ActionStartup\prepareService().

◆ WIN32_ERROR_ACCESS_DENIED

const WIN32_ERROR_ACCESS_DENIED = '5'

Definition at line 30 of file class.win32service.php.

◆ WIN32_ERROR_CIRCULAR_DEPENDENCY

const WIN32_ERROR_CIRCULAR_DEPENDENCY = '423'

Definition at line 31 of file class.win32service.php.

◆ WIN32_ERROR_DATABASE_DOES_NOT_EXIST

const WIN32_ERROR_DATABASE_DOES_NOT_EXIST = '429'

Definition at line 32 of file class.win32service.php.

◆ WIN32_ERROR_DEPENDENT_SERVICES_RUNNING

const WIN32_ERROR_DEPENDENT_SERVICES_RUNNING = '41B'

Definition at line 33 of file class.win32service.php.

◆ WIN32_ERROR_DUPLICATE_SERVICE_NAME

const WIN32_ERROR_DUPLICATE_SERVICE_NAME = '436'

Definition at line 34 of file class.win32service.php.

◆ WIN32_ERROR_FAILED_SERVICE_CONTROLLER_CONNECT

const WIN32_ERROR_FAILED_SERVICE_CONTROLLER_CONNECT = '427'

Definition at line 35 of file class.win32service.php.

◆ WIN32_ERROR_INSUFFICIENT_BUFFER

const WIN32_ERROR_INSUFFICIENT_BUFFER = '7A'

Definition at line 36 of file class.win32service.php.

◆ WIN32_ERROR_INVALID_DATA

const WIN32_ERROR_INVALID_DATA = 'D'

Definition at line 37 of file class.win32service.php.

◆ WIN32_ERROR_INVALID_HANDLE

const WIN32_ERROR_INVALID_HANDLE = '6'

Definition at line 38 of file class.win32service.php.

◆ WIN32_ERROR_INVALID_LEVEL

const WIN32_ERROR_INVALID_LEVEL = '7C'

Definition at line 39 of file class.win32service.php.

◆ WIN32_ERROR_INVALID_NAME

const WIN32_ERROR_INVALID_NAME = '7B'

Definition at line 40 of file class.win32service.php.

◆ WIN32_ERROR_INVALID_PARAMETER

const WIN32_ERROR_INVALID_PARAMETER = '57'

Definition at line 41 of file class.win32service.php.

◆ WIN32_ERROR_INVALID_SERVICE_ACCOUNT

const WIN32_ERROR_INVALID_SERVICE_ACCOUNT = '421'

Definition at line 42 of file class.win32service.php.

◆ WIN32_ERROR_INVALID_SERVICE_CONTROL

const WIN32_ERROR_INVALID_SERVICE_CONTROL = '41C'

Definition at line 43 of file class.win32service.php.

◆ WIN32_ERROR_PATH_NOT_FOUND

const WIN32_ERROR_PATH_NOT_FOUND = '3'

Definition at line 44 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_ALREADY_RUNNING

const WIN32_ERROR_SERVICE_ALREADY_RUNNING = '420'

Definition at line 45 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_CANNOT_ACCEPT_CTRL

const WIN32_ERROR_SERVICE_CANNOT_ACCEPT_CTRL = '425'

Definition at line 46 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_DATABASE_LOCKED

const WIN32_ERROR_SERVICE_DATABASE_LOCKED = '41F'

Definition at line 47 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_DEPENDENCY_DELETED

const WIN32_ERROR_SERVICE_DEPENDENCY_DELETED = '433'

Definition at line 48 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_DEPENDENCY_FAIL

const WIN32_ERROR_SERVICE_DEPENDENCY_FAIL = '42C'

Definition at line 49 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_DISABLED

const WIN32_ERROR_SERVICE_DISABLED = '422'

Definition at line 50 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_DOES_NOT_EXIST

const WIN32_ERROR_SERVICE_DOES_NOT_EXIST = '424'

Definition at line 51 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_EXISTS

const WIN32_ERROR_SERVICE_EXISTS = '431'

Definition at line 52 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_LOGON_FAILED

const WIN32_ERROR_SERVICE_LOGON_FAILED = '42D'

Definition at line 53 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_MARKED_FOR_DELETE

const WIN32_ERROR_SERVICE_MARKED_FOR_DELETE = '430'

Definition at line 54 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_NO_THREAD

const WIN32_ERROR_SERVICE_NO_THREAD = '41E'

Definition at line 55 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_NOT_ACTIVE

const WIN32_ERROR_SERVICE_NOT_ACTIVE = '426'

Definition at line 56 of file class.win32service.php.

◆ WIN32_ERROR_SERVICE_REQUEST_TIMEOUT

const WIN32_ERROR_SERVICE_REQUEST_TIMEOUT = '41D'

Definition at line 57 of file class.win32service.php.

◆ WIN32_ERROR_SHUTDOWN_IN_PROGRESS

const WIN32_ERROR_SHUTDOWN_IN_PROGRESS = '45B'

Definition at line 58 of file class.win32service.php.

◆ WIN32_NO_ERROR

const WIN32_NO_ERROR = '0'

Definition at line 59 of file class.win32service.php.

◆ WIN32_SERVICE_CONTINUE_PENDING

const WIN32_SERVICE_CONTINUE_PENDING = '5'

Definition at line 20 of file class.win32service.php.

◆ WIN32_SERVICE_NA

const WIN32_SERVICE_NA = '0'

Definition at line 27 of file class.win32service.php.

◆ WIN32_SERVICE_PAUSE_PENDING

const WIN32_SERVICE_PAUSE_PENDING = '6'

Definition at line 21 of file class.win32service.php.

◆ WIN32_SERVICE_PAUSED

const WIN32_SERVICE_PAUSED = '7'

Definition at line 22 of file class.win32service.php.

◆ WIN32_SERVICE_RUNNING

const WIN32_SERVICE_RUNNING = '4'

Definition at line 23 of file class.win32service.php.

Referenced by ActionStartup\prepareService().

◆ WIN32_SERVICE_START_PENDING

const WIN32_SERVICE_START_PENDING = '2'

Definition at line 24 of file class.win32service.php.

◆ WIN32_SERVICE_STOP_PENDING

const WIN32_SERVICE_STOP_PENDING = '3'

Definition at line 25 of file class.win32service.php.

◆ WIN32_SERVICE_STOPPED

const WIN32_SERVICE_STOPPED = '1'

Definition at line 26 of file class.win32service.php.


The documentation for this class was generated from the following file: