Bearsampp 2026.9.23
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 171 of file class.win32service.php.

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

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

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

1306 : bool
1307 {
1308 Log::trace("Starting ensureReset for service: " . $this->getName());
1309
1310 // First, make sure service is stopped
1311 if ($this->isRunning()) {
1312 Log::trace("Service is still running, stopping it first");
1313 if (!$this->stop()) {
1314 Log::trace("Failed to stop service during ensureReset");
1315
1316 return false;
1317 }
1318 usleep(1000000); // 1 second wait after stop
1319 }
1320
1321 // Delete the service
1322 Log::trace("Deleting service");
1323 if (!$this->delete()) {
1324 Log::trace("Service deletion failed, but continuing with wait");
1325 }
1326
1327 // Wait for the service to be completely removed
1328 if (!$this->waitForServiceDeletion(30)) {
1329 Log::trace("Service deletion did not complete within timeout, but continuing");
1330 // Even if we timeout, give it a moment and try to create anyway
1331 usleep(2000000); // 2 seconds
1332 }
1333
1334 Log::trace("ensureReset completed for service: " . $this->getName());
1335
1336 return true;
1337 }
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 724 of file class.win32service.php.

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

1113 : string
1114 {
1115 return $this->binPath;
1116 }

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 1093 of file class.win32service.php.

1093 : string
1094 {
1095 return $this->displayName;
1096 }

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 1239 of file class.win32service.php.

1240 {
1241 global $bearsamppLang;
1242 if ($this->latestError != self::WIN32_NO_ERROR) {
1243 // Ensure proper type conversion for PHP 8.5.7 compatibility
1244 $errorInt = is_numeric($this->latestError) ? hexdec($this->latestError) : 0;
1245
1246 return $bearsamppLang->getValue(Lang::ERROR) . ' ' .
1247 $this->latestError . ' (' . $errorInt . ' : ' . $this->getWin32ErrorCodeDesc($this->latestError) . ')';
1248 } elseif ($this->latestStatus != self::WIN32_SERVICE_NA) {
1249 // Ensure proper type conversion for PHP 8.5.7 compatibility
1250 $statusInt = is_numeric($this->latestStatus) ? hexdec($this->latestStatus) : 0;
1251
1252 return $bearsamppLang->getValue(Lang::STATUS) . ' ' .
1253 $this->latestStatus . ' (' . $statusInt . ' : ' . $this->getWin32ServiceStatusDesc($this->latestStatus) . ')';
1254 }
1255
1256 return null;
1257 }
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 1173 of file class.win32service.php.

1173 : string
1174 {
1175 return $this->errorControl;
1176 }

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 1229 of file class.win32service.php.

1230 {
1231 return $this->latestError;
1232 }

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 1219 of file class.win32service.php.

1220 {
1221 return $this->latestStatus;
1222 }

References $latestStatus.

◆ getName()

getName ( )

Gets the name of the service.

Returns
string The name of the service.

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

1073 : string
1074 {
1075 return $this->name;
1076 }

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 1193 of file class.win32service.php.

1194 {
1195 return $this->nssm;
1196 }

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 1133 of file class.win32service.php.

1133 : string
1134 {
1135 return $this->params;
1136 }

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 140 of file class.win32service.php.

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

1153 : string
1154 {
1155 return $this->startType;
1156 }

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 1057 of file class.win32service.php.

1057 : ?string
1058 {
1059 switch ($code) {
1060 case self::WIN32_ERROR_ACCESS_DENIED:
1061 return 'The handle to the SCM database does not have the appropriate access rights.';
1062 // ... other cases ...
1063 default:
1064 return null;
1065 }
1066 }

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 1018 of file class.win32service.php.

1018 : ?string
1019 {
1020 switch ($status) {
1021 case self::WIN32_SERVICE_CONTINUE_PENDING:
1022 return 'The service continue is pending.';
1023
1024 case self::WIN32_SERVICE_PAUSE_PENDING:
1025 return 'The service pause is pending.';
1026
1027 case self::WIN32_SERVICE_PAUSED:
1028 return 'The service is paused.';
1029
1030 case self::WIN32_SERVICE_RUNNING:
1031 return 'The service is running.';
1032
1033 case self::WIN32_SERVICE_START_PENDING:
1034 return 'The service is starting.';
1035
1036 case self::WIN32_SERVICE_STOP_PENDING:
1037 return 'The service is stopping.';
1038
1039 case self::WIN32_SERVICE_STOPPED:
1040 return 'The service is not running.';
1041
1042 case self::WIN32_SERVICE_NA:
1043 return 'Cannot retrieve service status.';
1044
1045 default:
1046 return null;
1047 }
1048 }

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 807 of file class.win32service.php.

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

883 : bool
884 {
885 Log::trace("Checking if service is installed: " . $this->getName());
886
887 try {
888 // Set a timeout for the entire operation
889 $startTime = microtime(true);
890 $timeout = 15; // 15 seconds timeout for the entire operation
891
892 // Call status() with a try-catch to ensure we don't get stuck
893 $status = $this->status();
894
895 // Check if we've exceeded our timeout
896 if (microtime(true) - $startTime > $timeout) {
897 Log::trace("Timeout exceeded in isInstalled() method, assuming service is not installed");
898 $this->writeLog('isInstalled ' . $this->getName() . ': NO (timeout exceeded)');
899
900 return false;
901 }
902
903 $isInstalled = $status != self::WIN32_SERVICE_NA;
904
905 Log::trace("Service " . $this->getName() . " installation status: " . ($isInstalled ? "YES" : "NO") . " (status code: " . $status . ")");
906 $this->writeLog('isInstalled ' . $this->getName() . ': ' . ($isInstalled ? 'YES' : 'NO') . ' (status: ' . $status . ')');
907
908 return $isInstalled;
909 } catch (\Exception $e) {
910 Log::trace("Exception in isInstalled() method: " . $e->getMessage() . ", assuming service is not installed");
911 $this->writeLog('isInstalled ' . $this->getName() . ': NO (exception: ' . $e->getMessage() . ')');
912
913 return false;
914 } catch (\Throwable $e) {
915 Log::trace("Throwable in isInstalled() method: " . $e->getMessage() . ", assuming service is not installed");
916 $this->writeLog('isInstalled ' . $this->getName() . ': NO (throwable: ' . $e->getMessage() . ')');
917
918 return false;
919 }
920 }

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 963 of file class.win32service.php.

963 : bool
964 {
965 Log::trace("Checking if service is paused: " . $this->getName());
966
967 $status = $this->status();
968 $isPaused = $status == self::WIN32_SERVICE_PAUSED;
969
970 Log::trace("Service " . $this->getName() . " paused status: " . ($isPaused ? "YES" : "NO") . " (status code: " . $status . ")");
971 $this->writeLog('isPaused ' . $this->getName() . ': ' . ($isPaused ? 'YES' : 'NO') . ' (status: ' . $status . ')');
972
973 return $isPaused;
974 }

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 983 of file class.win32service.php.

983 : bool
984 {
985 $isPending = $status == self::WIN32_SERVICE_START_PENDING || $status == self::WIN32_SERVICE_STOP_PENDING
986 || $status == self::WIN32_SERVICE_CONTINUE_PENDING || $status == self::WIN32_SERVICE_PAUSE_PENDING;
987
988 Log::trace("Checking if status is pending: " . $status . " - Result: " . ($isPending ? "YES" : "NO"));
989
990 if ($isPending) {
991 if ($status == self::WIN32_SERVICE_START_PENDING) {
992 Log::trace("Service is in START_PENDING state");
993 } else {
994 if ($status == self::WIN32_SERVICE_STOP_PENDING) {
995 Log::trace("Service is in STOP_PENDING state");
996 } else {
997 if ($status == self::WIN32_SERVICE_CONTINUE_PENDING) {
998 Log::trace("Service is in CONTINUE_PENDING state");
999 } else {
1000 if ($status == self::WIN32_SERVICE_PAUSE_PENDING) {
1001 Log::trace("Service is in PAUSE_PENDING state");
1002 }
1003 }
1004 }
1005 }
1006 }
1007
1008 return $isPending;
1009 }

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 927 of file class.win32service.php.

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

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 945 of file class.win32service.php.

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

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 560 of file class.win32service.php.

560 : bool
561 {
562 if ($this->delete()) {
563 usleep(self::SLEEP_TIME);
564
565 return $this->create();
566 }
567
568 return false;
569 }

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 709 of file class.win32service.php.

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

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 1123 of file class.win32service.php.

1123 : void
1124 {
1125 $this->binPath = str_replace('"', '', Path::formatWindowsPath($binPath));
1126 }
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 1103 of file class.win32service.php.

1103 : void
1104 {
1105 $this->displayName = $displayName;
1106 }

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 1183 of file class.win32service.php.

1183 : void
1184 {
1185 $this->errorControl = $errorControl;
1186 }

References $errorControl.

◆ setName()

setName ( $name)

Sets the name of the service.

Parameters
string$nameThe name to set.

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

1083 : void
1084 {
1085 $this->name = $name;
1086 }

References $name.

◆ setNssm()

setNssm ( $nssm)

Sets the NSSM instance associated with the service.

Parameters
Nssm$nssmThe NSSM instance to set.

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

1204 {
1205 if ($nssm instanceof Nssm) {
1206 $this->setDisplayName($nssm->getDisplayName());
1207 $this->setBinPath($nssm->getBinPath());
1208 $this->setParams($nssm->getParams());
1209 $this->setStartType($nssm->getStart());
1210 $this->nssm = $nssm;
1211 }
1212 }
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 1143 of file class.win32service.php.

1143 : void
1144 {
1145 $this->params = $params;
1146 }

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 1163 of file class.win32service.php.

1163 : void
1164 {
1165 $this->startType = $startType;
1166 }

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 576 of file class.win32service.php.

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

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

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 665 of file class.win32service.php.

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

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 1268 of file class.win32service.php.

1268 : bool
1269 {
1270 $startTime = time();
1271 $maxTime = $startTime + $maxWaitTime;
1272 $checkCount = 0;
1273
1274 Log::trace("Waiting for service deletion: " . $this->getName() . " (max wait: " . $maxWaitTime . "s)");
1275
1276 while (time() < $maxTime) {
1277 $checkCount++;
1278 $status = $this->status(false);
1279 Log::trace("Service deletion check #" . $checkCount . " - Status: " . $status . " at " . date('Y-m-d H:i:s'));
1280
1281 // Service doesn't exist or is definitely not there
1282 if ($status == self::WIN32_SERVICE_NA ||
1283 $status == self::WIN32_ERROR_SERVICE_DOES_NOT_EXIST) {
1284 $elapsedTime = time() - $startTime;
1285 Log::trace("Service deletion confirmed after " . $elapsedTime . " seconds");
1286
1287 return true;
1288 }
1289
1290 // Wait a bit before checking again
1291 usleep(500000); // 0.5 seconds
1292 }
1293
1294 $totalWaitTime = time() - $startTime;
1295 Log::trace("Service deletion timeout after " . $totalWaitTime . " seconds - service still exists: " . $this->getName());
1296
1297 return false;
1298 }

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: