removeOldPhpListener(); // ------------------------------------------------------------------ // 1. Resolve supervisor config directory // ------------------------------------------------------------------ if (is_dir('/etc/supervisord.d/')) { $configDir = '/etc/supervisord.d/'; $extension = '.ini'; } elseif (is_dir('/etc/supervisor/conf.d/')) { $configDir = '/etc/supervisor/conf.d/'; $extension = '.conf'; } else { $this->error('Cannot find supervisord config directory (/etc/supervisord.d/ or /etc/supervisor/conf.d/).'); $this->warn('Please install supervisord first: apt-get install supervisor -or- yum install supervisor'); return self::FAILURE; } $configFile = $configDir . $this->programName . $extension; // ------------------------------------------------------------------ // 2. Verify the binary exists // ------------------------------------------------------------------ if (! file_exists($this->binaryPath)) { $this->error("go-ami-listener binary not found at: {$this->binaryPath}"); $this->warn('Please build the binary first: cd go-ami-listener && go build -o go-ami-listener .'); return self::FAILURE; } // ------------------------------------------------------------------ // 3. Generate the supervisor config // ------------------------------------------------------------------ $config = $this->generateSupervisorConfig(); $this->info("Generated supervisor config:\n"); $this->line($config); $this->newLine(); // ------------------------------------------------------------------ // 4. Write the config file // ------------------------------------------------------------------ if (file_put_contents($configFile, $config) === false) { $this->error("Failed to write config file to: {$configFile}"); $this->warn('Try running the command with sudo.'); return self::FAILURE; } $this->info("Config written to: {$configFile}"); // ------------------------------------------------------------------ // 5. Create and fix ownership of the log file // ------------------------------------------------------------------ (new Process(['touch', $this->logFile]))->run(); (new Process(['chown', 'asterisk:asterisk', $this->logFile]))->run(); $this->info("Log file ready at: {$this->logFile}"); // ------------------------------------------------------------------ // 6. Reload supervisord and start the program // ------------------------------------------------------------------ $this->reloadSupervisor(); $this->info("✅ go-ami-listener is now managed by supervisord."); $this->line(" Check status: supervisorctl status {$this->programName}"); $this->line(" View logs: tail -f {$this->logFile}"); // ------------------------------------------------------------------ // 7. Run the egy:cache artisan command // ------------------------------------------------------------------ $this->newLine(); $this->info('--- Running egy:cache ---'); $this->call('egy:cache'); return self::SUCCESS; } /** * Stop and fully remove the legacy PHP-based laravel-worker AMI listener. * * Steps: * 1. Stop all running instances of the supervisor program. * 2. Delete the supervisor config file so the program is deregistered. * 3. Reload supervisord (reread + update) to apply the removal. * 4. Disable the systemd service that was used with the old listener. */ protected function removeOldPhpListener(): void { $this->info('--- Removing old PHP AMI listener (laravel-worker) ---'); // 1. Stop the running supervisor program instances ---------------- // The old config used numprocs=1 with process_name=%(program_name)s_%(process_num)02d, // so try the wildcard form first, then the bare name as a fallback. foreach (["{$this->legacyProgramName}:*", $this->legacyProgramName] as $target) { $stop = new Process(['supervisorctl', 'stop', $target]); $stop->run(); if ($stop->isSuccessful()) { $this->info(" [supervisorctl stop {$target}] OK"); break; } // "no such process" or "not running" are acceptable; surface real errors only. $out = trim($stop->getErrorOutput() ?: $stop->getOutput()); if (! str_contains(strtolower($out), 'no such process') && ! str_contains(strtolower($out), 'not running')) { $this->warn(" [supervisorctl stop {$target}] {$out}"); } } // 2. Delete the supervisor config file ---------------------------- $configCandidates = [ '/etc/supervisor/conf.d/' . $this->legacyProgramName . '.conf', '/etc/supervisord.d/' . $this->legacyProgramName . '.ini', ]; $deleted = false; foreach ($configCandidates as $file) { if (file_exists($file)) { if (@unlink($file)) { $this->info(" Deleted config file: {$file}"); $deleted = true; } else { $this->warn(" Failed to delete {$file} — try running with sudo."); } } } if (! $deleted) { $this->line(" No legacy supervisor config file found — already removed."); } // 3. Reload supervisord to deregister the old program ------------- foreach ([['supervisorctl', 'reread'], ['supervisorctl', 'update']] as $cmd) { $process = new Process($cmd); $process->run(); $label = implode(' ', $cmd); if ($process->isSuccessful()) { $this->info(" [{$label}] OK"); } else { $this->warn(" [{$label}] " . trim($process->getErrorOutput() ?: $process->getOutput())); } } // 4. Disable the systemd service used with the old listener ------- // The old setup called `systemctl enable supervisord` on RHEL/CentOS systems. // On Debian/Ubuntu the equivalent service is named `supervisor`. // We disable both gracefully so whichever is present gets cleaned up. $legacySystemdServices = ['supervisord', 'supervisor']; foreach ($legacySystemdServices as $service) { $check = new Process(['systemctl', 'is-enabled', $service]); $check->run(); $status = trim($check->getOutput()); if ($status === 'enabled') { // Disable the service but do NOT stop it — supervisor is reused // by the new go-ami-listener program that we are about to install. $disable = new Process(['systemctl', 'disable', $service]); $disable->run(); if ($disable->isSuccessful()) { $this->info(" [systemctl disable {$service}] OK"); } else { $this->warn(" [systemctl disable {$service}] " . trim($disable->getErrorOutput() ?: $disable->getOutput())); } // Only one of the two service names will exist; stop after the first match. break; } elseif (in_array($status, ['disabled', 'masked'], true)) { $this->line(" Systemd service '{$service}' is already {$status} — skipping."); break; } // Service not found on this system — try the next candidate silently. } $this->info('--- Old PHP listener cleanup complete ---'); $this->newLine(); } /** * Build the supervisor program config block. */ protected function generateSupervisorConfig(): string { return "[program:{$this->programName}] process_name=%(program_name)s command={$this->binaryPath} directory={$this->workDir} autostart=true autorestart=true startretries=999 stopwaitsecs=10 stopasgroup=true killasgroup=true user=asterisk numprocs=1 redirect_stderr=true stdout_logfile={$this->logFile} stdout_logfile_maxbytes=10MB stdout_logfile_backups=5 "; } /** * Run the standard supervisorctl reload cycle. */ protected function reloadSupervisor(): void { // Ensure the supervisor systemd service is enabled and running so it picks up the new config. foreach (['supervisor', 'supervisord'] as $service) { // enable (idempotent) $enable = new Process(['systemctl', 'enable', $service]); $enable->run(); // start (idempotent) $start = new Process(['systemctl', 'start', $service]); $start->run(); } $this->info('Supervisor systemd service enabled and started.'); $steps = [ ['supervisorctl', 'reread'], ['supervisorctl', 'update'], ['supervisorctl', 'restart', $this->programName], ]; foreach ($steps as $cmd) { $process = new Process($cmd); $process->run(); $label = implode(' ', $cmd); if ($process->isSuccessful()) { $this->info(" [{$label}] OK"); } else { // "restart" fails on first install (program not running yet); try start instead if ($cmd[1] === 'restart') { $start = new Process(['supervisorctl', 'start', $this->programName]); $start->run(); if ($start->isSuccessful()) { $this->info(" [supervisorctl start {$this->programName}] OK"); } else { $this->warn(" [{$label}] " . trim($process->getErrorOutput() ?: $process->getOutput())); } } else { $this->warn(" [{$label}] " . trim($process->getErrorOutput() ?: $process->getOutput())); } } } } }