Usage: $manager = new \Platformsh\Cli\Util\ProcessManager(); Fork: everything after this will run in a child. The user's shell will be blocked until the parent is killed. $manager->fork(). $logFile = 'path/to/logFile'; $log = new \Symfony\Component\Console\Output\StreamOutput(fopen($logFile, 'a')); Create multiple external processes. foreach ($commands as $key => $command) { $process = new \Symfony\Component\Process\Process($command); $pidFile = 'path/to/pidFile' . $key; Start the process with the manager. $manager->startProcess($process, $pidFile, $log); Report this to the shell. echo "Started process: " . $process->getCommandLine() . "\n"; } Kill the parent process to release the shell prompt. $manager->killParent(); Monitor the external process(es). This keeps them running until the $pidFile is deleted or they are otherwise stopped. $manager->monitor();
 /**
  * {@inheritdoc}
  */
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $this->validateInput($input);
     $project = $this->getSelectedProject();
     $environment = $this->getSelectedEnvironment();
     if ($environment->id === 'master') {
         $this->stdErr->writeln('Tunnelling to the <error>master</error> environment is not safe.');
         if (count($this->getEnvironments($project)) === 1) {
             $this->stdErr->writeln('Use <info>platform branch</info> to create a new development environment.');
         }
         return 1;
     }
     $appName = $this->selectApp($input);
     $sshUrl = $environment->getSshUrl($appName);
     $util = new RelationshipsUtil($this->stdErr);
     $relationships = $util->getRelationships($sshUrl);
     if (!$relationships) {
         $this->stdErr->writeln('No relationships found.');
         return 1;
     }
     $logFile = $this->getConfigDir() . '/tunnels.log';
     if (!($log = $this->openLog($logFile))) {
         $this->stdErr->writeln(sprintf('Failed to open log file for writing: %s', $logFile));
         return 1;
     }
     $extraSshArgs = [];
     if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE) {
         $extraSshArgs[] = '-vv';
     } elseif ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
         $extraSshArgs[] = '-v';
     }
     $log->setVerbosity($output->getVerbosity());
     $processManager = new ProcessManager();
     $processManager->fork();
     $error = false;
     $processIds = [];
     foreach ($relationships as $relationship => $services) {
         foreach ($services as $serviceKey => $service) {
             $remoteHost = $service['host'];
             $remotePort = $service['port'];
             $localPort = $this->getPort();
             $tunnel = ['projectId' => $project->id, 'environmentId' => $environment->id, 'appName' => $appName, 'relationship' => $relationship, 'serviceKey' => $serviceKey, 'remotePort' => $remotePort, 'remoteHost' => $remoteHost, 'localPort' => $localPort, 'service' => $service, 'pid' => null];
             $relationshipString = $this->formatTunnelRelationship($tunnel);
             if ($openTunnelInfo = $this->isTunnelOpen($tunnel)) {
                 $this->stdErr->writeln("A tunnel is already open for the relationship: <info>{$relationshipString}</info>");
                 continue;
             }
             $process = $this->createTunnelProcess($sshUrl, $remoteHost, $remotePort, $localPort, $extraSshArgs);
             $pidFile = $this->getPidFile($tunnel);
             try {
                 $pid = $processManager->startProcess($process, $pidFile, $log);
             } catch (\Exception $e) {
                 $this->stdErr->writeln(sprintf('Failed to open tunnel for relationship <error>%s</error>: %s', $relationshipString, $e->getMessage()));
                 $error = true;
                 continue;
             }
             // Wait a very small time to capture any immediate errors.
             usleep(100000);
             if (!$process->isRunning() && !$process->isSuccessful()) {
                 $this->stdErr->writeln(trim($process->getErrorOutput()));
                 $this->stdErr->writeln(sprintf('Failed to open tunnel for relationship: <error>%s</error>', $relationshipString));
                 unlink($pidFile);
                 $error = true;
                 continue;
             }
             // Save information about the tunnel for use in other commands.
             $tunnel['pid'] = $pid;
             $this->tunnelInfo[] = $tunnel;
             $this->saveTunnelInfo();
             $this->stdErr->writeln(sprintf('SSH tunnel opened on port %s to relationship: <info>%s</info>', $localPort, $relationshipString));
             $processIds[] = $pid;
         }
     }
     if (count($processIds)) {
         $this->stdErr->writeln("Logs are written to: {$logFile}");
     }
     if (!$error) {
         $this->stdErr->writeln('');
         $this->stdErr->writeln("List tunnels with: <info>platform tunnels</info>");
         $this->stdErr->writeln("View tunnel details with: <info>platform tunnel:info</info>");
         $this->stdErr->writeln("Close tunnels with: <info>platform tunnel:close</info>");
     }
     $processManager->killParent($error);
     $processManager->monitor($log);
     return 0;
 }
 public function isEnabled()
 {
     return ProcessManager::supported() && parent::isEnabled();
 }