diff --git a/app/System/Command.php b/app/System/Command.php deleted file mode 100644 index 88633ed..0000000 --- a/app/System/Command.php +++ /dev/null @@ -1,93 +0,0 @@ -command = $command; - $this->defaultArguments = $defaultArguments; - $this->path = static::find($command); - - if (!isset($this->path)) - { - throw new \Exception('Command not found: '.$command); - } - } - - public function getPath() - { - return $this->path; - } - - public function create(array $arguments) - { - $arguments = array_merge($this->defaultArguments, $arguments); - foreach ($arguments as &$argument) - { - if (!is_object($argument) || !($argument instanceof CommandArgument)) - { - $argument = new CommandArgument($argument); - } - } - - $command = []; - if (strpos($this->path, ' ') === false) - { - $command[] = '"'.$this->path.'"'; - } - else - { - $command[] = $this->path; - } - $command = array_merge($command, $arguments); - - $process = app()->make('App\\System\\Process', ['command' => $command]); - $process->setStartModeSync(); - return $process; - } - - public function __invoke(array $arguments) - { - return $this->create($arguments)->start()->getStdout(); - } - - protected static function find($command) - { - if (PHP_OS == 'WINNT') - { - $result = shell_exec('where '.escapeshellarg($command)); - // No idea why Windows is using unix line endings here - // Trim the trailing newline, last result is always empty - $result = explode("\n", trim($result)); - $result = end($result); - } - else - { - $result = shell_exec('command -v '.escapeshellarg($command)); - } - - if (!is_string($result)) - { - return; - } - - $result = trim($result); - - if (empty($result)) - { - return; - } - - return $result; - } -} diff --git a/app/System/Command/Xrandr.php b/app/System/Command/Xrandr.php index 5876c42..bbc5528 100644 --- a/app/System/Command/Xrandr.php +++ b/app/System/Command/Xrandr.php @@ -4,9 +4,7 @@ declare(strict_types=1); namespace App\System\Command; -use App\System\Command; - -class Xrandr extends Command +class Xrandr extends \Processes\Command { public function __construct() { diff --git a/app/System/CommandArgument.php b/app/System/CommandArgument.php deleted file mode 100644 index 2e1d543..0000000 --- a/app/System/CommandArgument.php +++ /dev/null @@ -1,25 +0,0 @@ -value = $val; - } - - public function get() - { - return escapeshellarg($this->value); - } - - public function __toString() - { - return escapeshellarg($this->value); - } -} diff --git a/app/System/Process.php b/app/System/Process.php deleted file mode 100644 index c1e1a8f..0000000 --- a/app/System/Process.php +++ /dev/null @@ -1,585 +0,0 @@ -command = $command; - } - - /** - * Set an environment variable to pass to the child process. - * - * @param string $key - * @param string|null $value - * @return Process - */ - public function setEnv($key, $value) - { - if (isset($value)) - { - $this->env[$key] = $value; - } - else - { - unset($this->env[$key]); - } - return $this; - } - - /** - * Set multiple environment variables from an associative array. - * - * @param array $values - * @return Process - */ - public function setEnvRange(array $values) - { - foreach ($values as $k => $v) - { - $this->setEnv($k, $v); - } - return $this; - } - - /** - * Whether to raise an exception if the process returns a non-zero exit - * code. - * - * @param bool $raiseException true to raise an exception - * @return void - */ - public function setRaiseExceptionOnFailure($raiseException) - { - $this->raiseExceptionOnFailure = $raiseException; - return $this; - } - - /** - * Add an output callback to receive process stdout and stderr output. - * - * Signature is function(Process $process, string $line, array $matches = null) - * - * @param callable $callback the callback to call - * @param string $filter regex to use to filter lines before calling callback - * @param mixed $streams - * @return void - */ - public function addOutputCallback(callable $callback, $filter = null, $streams = Process::STREAM_STDOUT) - { - $this->outputCallbacks[] = [$callback, $filter, $streams]; - return $this; - } - - /** - * Add a callback to be called when the process transitions into stopping. - * - * @param callable $callback - * @return void - */ - public function addExitCallback(callable $callback) - { - $this->exitCallback[] = $callback; - return $this; - } - - public function setStartModeSync() - { - $this->startMode = static::START_MODE_SYNC; - return $this; - } - - public function setStartModeAsync() - { - $this->startMode = static::START_MODE_ASYNC; - return $this; - } - - public function setStartModeReplace() - { - $this->startMode = static::START_MODE_SYNC | static::START_MODE_REPLACE; - return $this; - } - - public function getStdout() - { - return $this->stdout; - } - - public function getStdoutString() - { - return isset($this->stdout) ? implode(PHP_EOL, $this->stdout) : null; - } - - public function getStderr() - { - return $this->stderr; - } - - public function getStderrString() - { - return isset($this->stderr) ? implode(PHP_EOL, $this->stderr) : null; - } - - public function getExitStatus() - { - return $this->exitStatus; - } - - /** - * Check whether the process is still running. This will return false - * during the times it is transitioning to starting or into exiting. - * - * @return bool true if running - */ - public function isRunning() - { - return $this->status == static::STATUS_RUNNING; - } - - public function fork() - { - if (PHP_OS == 'WINNT') - { - $command = sprintf( - 'start /B %s', - $this->getCommand() - ); - } - else - { - $command = sprintf( - 'nohup %s > /dev/null 2>&1 &', - $this->getCommand() - ); - } - shell_exec($command); - } - - public function start() - { - $this->status = static::STATUS_STARTING; - if ($this->startMode & static::START_MODE_REPLACE) - { - $descriptors = [ - 0 => STDIN, - 1 => STDOUT, - 2 => STDERR - ]; - } - else - { - $descriptors = [ - 0 => ['pipe', 'r'], - 1 => ['pipe', 'w'], - 2 => ['pipe', 'w'] - ]; - } - $pipes = null; - - if (PHP_OS == 'WINNT') - { - $commandWrapper = '"'; - } - else - { - $commandWrapper = ''; - } - - $this->handle = $handle = proc_open( - $commandWrapper.$this->getCommand().$commandWrapper, - $descriptors, - $pipes, - $this->cwd, - array_merge(getenv(), $this->env) - ); - - if (!is_resource($handle)) - { - throw new \Exception('Failed to start process'); - } - - if (sizeof($pipes)) - { - $this->stream_stdin = $pipes[0]; - $this->stream_stdout = $pipes[1]; - $this->stream_stderr = $pipes[2]; - - stream_set_blocking($pipes[0], false); - stream_set_blocking($pipes[1], false); - stream_set_blocking($pipes[2], false); - } - - $this->status = static::STATUS_RUNNING; - - if ($this->startMode & static::START_MODE_ASYNC) - { - declare(ticks=1); - register_tick_function([&$this, 'tick']); - } - elseif ($this->startMode & static::START_MODE_SYNC) - { - while ($this->status != static::STATUS_EXITED) - { - if ($this->startMode & static::START_MODE_REPLACE) - { - sleep(1); - } - else - { - $read_streams = [$pipes[1], $pipes[2]]; - $write_streams = null; - $except_streams = null; - stream_select($read_streams, $write_streams, $except_streams, 1); - } - $this->tick(); - } - } - - return $this; - } - - public function stop() - { - proc_terminate($this->handle); - return $this; - } - - protected function tick() - { - if (!($this->startMode & static::START_MODE_REPLACE)) - { - $this->tickStreams(); - } - $this->tickProcessStatus(); - } - - protected function tickStreams() - { - // Pump streams if necessary - if (!isset($this->stdout) || !is_array($this->stdout)) - { - $this->stdout = []; - } - while ($line = fgets($this->stream_stdout)) - { - // This array is initialized a few lines above and not set anywhere else. - // @phan-suppress-next-line PhanTypeMismatchDimEmpty - $this->stdout[] = trim($line); - - foreach ($this->outputCallbacks as $callback) - { - if (($callback[2] & static::STREAM_STDOUT) == 0) - { - continue; - } - if (isset($callback[1])) - { - if (preg_match($callback[1], $line, $matches)) - { - call_user_func($callback[0], $this, $line, $matches); - } - } - else - { - call_user_func($callback[0], $this, $line); - } - } - } - - if (!isset($this->stderr) || !is_array($this->stderr)) - { - $this->stderr = []; - } - while ($line = fgets($this->stream_stderr)) - { - // This array is initialized a few lines above and not set anywhere else. - // @phan-suppress-next-line PhanTypeMismatchDimEmpty - $this->stderr[] = trim($line); - - foreach ($this->outputCallbacks as $callback) - { - if (($callback[2] & static::STREAM_STDERR) == 0) - { - continue; - } - if (isset($callback[1])) - { - if (preg_match($callback[1], $line, $matches)) - { - call_user_func($callback[0], $this, $line, $matches); - } - } - else - { - call_user_func($callback[0], $this, $line); - } - } - } - } - - protected function tickProcessStatus() - { - // Check current status - $status = proc_get_status($this->handle); - if (isset($status['exitcode']) && $status['exitcode'] !== -1) - { - $this->exitStatus = $status['exitcode']; - } - if ($status['running'] === false && $this->status == static::STATUS_RUNNING) - { - $this->stopping(); - } - elseif ($this->status == static::STATUS_STOPPED) - { - $this->status = static::STATUS_EXITED; - if ($this->startMode & static::START_MODE_ASYNC) - { - unregister_tick_function([&$this, 'tick']); - } - $this->exited(); - } - } - - protected function stopping() - { - $this->status = static::STATUS_STOPPING; - foreach ($this->exitCallback as $exitCallback) - { - call_user_func($exitCallback, $this); - } - $this->status = static::STATUS_STOPPED; - } - - protected function exited() - { - if (!($this->startMode & static::START_MODE_REPLACE)) - { - fclose($this->stream_stdin); - fclose($this->stream_stdout); - fclose($this->stream_stderr); - } - proc_close($this->handle); - - if ($this->raiseExceptionOnFailure && $this->exitStatus != 0) - { - throw new \Exception('Command execution failed ('.$this->exitStatus.'): '.implode(' ', $this->command).PHP_EOL. - 'Stdout:'.PHP_EOL. - $this->getStdoutString().PHP_EOL. - 'Stderr:'.PHP_EOL. - $this->getStderrString().PHP_EOL); - } - } - - public function write($data) - { - fwrite($this->stream_stdin, $data); - return $this; - } - - public function close() - { - fclose($this->stream_stdin); - return $this; - } - - protected function getCommand() - { - $processed = []; - $command = $this->command; - $commandName = array_shift($command); - $processed[] = strpos($commandName, ' ') === false ? $commandName : '"'.$commandName.'"'; - foreach ($command as $part) - { - if (is_object($part) && $part instanceof CommandArgument) - { - $processed[] = $part->get(); - } - else - { - $processed[] = $part; - } - } - return implode(' ', $processed); - } -} diff --git a/app/System/RawCommandArgument.php b/app/System/RawCommandArgument.php deleted file mode 100644 index 1b7aa12..0000000 --- a/app/System/RawCommandArgument.php +++ /dev/null @@ -1,18 +0,0 @@ -value; - } - - public function __toString() - { - return $this->value; - } -} diff --git a/app/System/SensitiveCommandArgument.php b/app/System/SensitiveCommandArgument.php deleted file mode 100644 index f528a4a..0000000 --- a/app/System/SensitiveCommandArgument.php +++ /dev/null @@ -1,13 +0,0 @@ -value))).'>'; - } -} diff --git a/composer.json b/composer.json index e91a636..1abbe7b 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "require": { "php": "^7.2.0", "laravel-zero/framework": "^7.0", + "nucleardog/processes": "dev-master", "symfony/yaml": "^5.0" }, "require-dev": { @@ -48,5 +49,8 @@ }, "minimum-stability": "dev", "prefer-stable": true, - "bin": ["monitor_layout"] + "bin": ["monitor_layout"], + "repositories": [ + { "type": "composer", "url": "https://composer.nucleardog.ca/" } + ] } diff --git a/composer.lock b/composer.lock index 3b575f3..7857001 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "c029b92d27ab874462d9886d60030953", + "content-hash": "7979201718112ab89d5243eed39c7ec2", "packages": [ { "name": "doctrine/inflector", @@ -1021,6 +1021,49 @@ ], "time": "2020-04-20T15:05:43+00:00" }, + { + "name": "nucleardog/processes", + "version": "dev-master", + "source": { + "type": "git", + "url": "ssh://git@git.nucleardog.ca/nucleardog/processes", + "reference": "8995dbb5f0de7eab45ca9fa1f7d6beed6622ad1a" + }, + "require": { + "php": "^7.2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "phan/phan": "^2.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Processes\\": "src/" + } + }, + "scripts": { + "post-autoload-dump": [ + "if [ -d .githooks ]; then cp .githooks/* .git/hooks/; fi" + ] + }, + "license": [ + "proprietary" + ], + "authors": [ + { + "name": "Adam Pippin", + "email": "hello@adampippin.ca" + } + ], + "description": "Library for invoking external commands and doing useful things with them", + "homepage": "https://adampippin.ca/", + "support": { + "issues": "https://git.nucleardog.ca/nucleardog/processes/issues", + "source": "https://git.nucleardog.ca/nucleardog/processes" + }, + "time": "2020-05-01T23:23:11+00:00" + }, { "name": "nunomaduro/collision", "version": "v4.2.0", @@ -5099,7 +5142,9 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": { + "nucleardog/processes": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": {