icanhazstring/systemctl-php

View on GitHub
src/SystemCtl.php

Summary

Maintainability
C
1 day
Test Coverage
<?php

namespace icanhazstring\SystemCtl;

use icanhazstring\SystemCtl\Command\CommandDispatcherInterface;
use icanhazstring\SystemCtl\Command\SymfonyCommandDispatcher;
use icanhazstring\SystemCtl\Exception\UnitNotFoundException;
use icanhazstring\SystemCtl\Exception\UnitTypeNotSupportedException;
use icanhazstring\SystemCtl\Unit\Device;
use icanhazstring\SystemCtl\Unit\Service;
use icanhazstring\SystemCtl\Unit\Timer;
use icanhazstring\SystemCtl\Unit\Socket;
use icanhazstring\SystemCtl\Unit\Scope;
use icanhazstring\SystemCtl\Unit\Slice;
use icanhazstring\SystemCtl\Unit\Swap;
use icanhazstring\SystemCtl\Unit\Target;
use icanhazstring\SystemCtl\Unit\Automount;
use icanhazstring\SystemCtl\Unit\Mount;
use icanhazstring\SystemCtl\Unit\UnitInterface;

/**
 * Class SystemCtl
 *
 * @package SystemCtl
 */
class SystemCtl
{
    /** @var string systemctl binary path */
    private static $binary = '/bin/systemctl';

    /** @var int timeout for commands */
    private static $timeout = 3;

    public const AVAILABLE_UNITS = [
        Service::UNIT,
        'socket',
        'device',
        'mount',
        'automount',
        'swap',
        'target',
        'path',
        Timer::UNIT,
        'slice',
        'scope'
    ];

    public const SUPPORTED_UNITS = [
        Service::UNIT,
        Timer::UNIT,
    ];

    /** @var CommandDispatcherInterface */
    private $commandDispatcher;

    /**
     * Change systemctl binary
     *
     * @param string $binary
     */
    public static function setBinary(string $binary): void
    {
        self::$binary = $binary;
    }

    /**
     * Change command execution timeout
     *
     * @param int $timeout
     */
    public static function setTimeout(int $timeout): void
    {
        self::$timeout = $timeout;
    }

    /**
     * @param string $unitSuffix
     * @param string $unitName
     *
     * @return UnitInterface
     * @throws UnitTypeNotSupportedException
     * @deprecated This static method is deprecated, please refer to a specifc get method for a unit
     */
    public static function unitFromSuffix(string $unitSuffix, string $unitName): UnitInterface
    {
        $unitClass = 'icanhazstring\SystemCtl\\Unit\\' . ucfirst($unitSuffix);

        if (!class_exists($unitClass)) {
            throw new UnitTypeNotSupportedException('Unit type ' . $unitSuffix . ' not supported');
        }

        $commandDispatcher = (new SymfonyCommandDispatcher())
            ->setTimeout(self::$timeout)
            ->setBinary(self::$binary);

        // @phpstan-ignore-next-line
        return new $unitClass($unitName, $commandDispatcher);
    }

    /**
     * List all supported units
     *
     * @param null|string $unitPrefix
     * @param string[] $unitTypes
     *
     * @return array|string[]
     * @throws Exception\CommandFailedException
     */
    public function listUnits(?string $unitPrefix = null, array $unitTypes = self::SUPPORTED_UNITS): array
    {
        $commands = ['list-units'];

        if ($unitPrefix) {
            $commands[] = $unitPrefix . '*';
        }

        $output = $this->getCommandDispatcher()->dispatch(...$commands)->getOutput();

        return array_reduce($unitTypes, function ($carry, $unitSuffix) use ($output) {
            $result = Utils\OutputFetcher::fetchUnitNames($unitSuffix, $output);

            return array_merge($carry, $result);
        }, []);
    }

    /**
     * @param string $name
     *
     * @return Service
     * @throws Exception\CommandFailedException
     */
    public function getService(string $name): Service
    {
        $units = $this->listUnits($name, [Service::UNIT]);

        $unitName = $this->searchForUnitInUnits($name, $units);

        if ($unitName === null) {
            throw UnitNotFoundException::create(Service::UNIT, $name);
        }

        return new Service($unitName, $this->getCommandDispatcher());
    }

    /**
     * @param string $unitName
     * @param array<string>|string[] $units
     *
     * @return null|string
     */
    protected function searchForUnitInUnits(string $unitName, array $units): ?string
    {
        foreach ($units as $unit) {
            if ($unit === $unitName) {
                return $unit;
            }
        }

        return null;
    }

    /**
     * @param null|string $unitPrefix
     *
     * @return Service[]
     */
    public function getServices(?string $unitPrefix = null): array
    {
        $units = $this->listUnits($unitPrefix, [Service::UNIT]);

        return array_map(function ($unitName) {
            return new Service($unitName, $this->getCommandDispatcher());
        }, $units);
    }

    /**
     * @param string $name
     *
     * @return Timer
     */
    public function getTimer(string $name): Timer
    {
        $units = $this->listUnits($name, [Timer::UNIT]);

        $unitName = $this->searchForUnitInUnits($name, $units);

        if (is_null($unitName)) {
            throw UnitNotFoundException::create(Timer::UNIT, $name);
        }

        return new Timer($unitName, $this->getCommandDispatcher());
    }

    /**
     * @param null|string $unitPrefix
     *
     * @return Timer[]
     */
    public function getTimers(?string $unitPrefix = null): array
    {
        $units = $this->listUnits($unitPrefix, [Timer::UNIT]);

        return array_map(function ($unitName) {
            return new Timer($unitName, $this->getCommandDispatcher());
        }, $units);
    }

    /**
     * @param string $name
     *
     * @return Socket
     */
    public function getSocket(string $name): Socket
    {
        $units = $this->listUnits($name, [Socket::UNIT]);

        $unitName = $this->searchForUnitInUnits($name, $units);

        if (is_null($unitName)) {
            throw UnitNotFoundException::create(Socket::UNIT, $name);
        }

        return new Socket($unitName, $this->getCommandDispatcher());
    }

    /**
     * @param null|string $unitPrefix
     *
     * @return Socket[]
     */
    public function getSockets(?string $unitPrefix = null): array
    {
        $units = $this->listUnits($unitPrefix, [Socket::UNIT]);

        return array_map(function ($unitName) {
            return new Socket($unitName, $this->getCommandDispatcher());
        }, $units);
    }

    /**
     * @param string $name
     *
     * @return Scope
     */
    public function getScope(string $name): Scope
    {
        $units = $this->listUnits($name, [Scope::UNIT]);

        $unitName = $this->searchForUnitInUnits($name, $units);

        if (is_null($unitName)) {
            throw UnitNotFoundException::create(Scope::UNIT, $name);
        }

        return new Scope($unitName, $this->getCommandDispatcher());
    }

    /**
     * @param null|string $unitPrefix
     *
     * @return Scope[]
     */
    public function getScopes(?string $unitPrefix = null): array
    {
        $units = $this->listUnits($unitPrefix, [Scope::UNIT]);

        return array_map(function ($unitName) {
            return new Scope($unitName, $this->getCommandDispatcher());
        }, $units);
    }

    /**
     * @param string $name
     *
     * @return Slice
     */
    public function getSlice(string $name): Slice
    {
        $units = $this->listUnits($name, [Slice::UNIT]);

        $unitName = $this->searchForUnitInUnits($name, $units);

        if (is_null($unitName)) {
            throw UnitNotFoundException::create(Slice::UNIT, $name);
        }

        return new Slice($unitName, $this->getCommandDispatcher());
    }

    /**
     * @param null|string $unitPrefix
     *
     * @return Slice[]
     */
    public function getSlices(?string $unitPrefix = null): array
    {
        $units = $this->listUnits($unitPrefix, [Slice::UNIT]);

        return array_map(function ($unitName) {
            return new Slice($unitName, $this->getCommandDispatcher());
        }, $units);
    }

    /**
     * @param string $name
     *
     * @return Target
     */
    public function getTarget(string $name): Target
    {
        $units = $this->listUnits($name, [Target::UNIT]);

        $unitName = $this->searchForUnitInUnits($name, $units);

        if (is_null($unitName)) {
            throw UnitNotFoundException::create(Target::UNIT, $name);
        }

        return new Target($unitName, $this->getCommandDispatcher());
    }

    /**
     * @param null|string $unitPrefix
     *
     * @return Target[]
     */
    public function getTargets(?string $unitPrefix = null): array
    {
        $units = $this->listUnits($unitPrefix, [Target::UNIT]);

        return array_map(function ($unitName) {
            return new Target($unitName, $this->getCommandDispatcher());
        }, $units);
    }

    /**
     * @param string $name
     *
     * @return Swap
     */
    public function getSwap(string $name): Swap
    {
        $units = $this->listUnits($name, [Swap::UNIT]);

        $unitName = $this->searchForUnitInUnits($name, $units);

        if (is_null($unitName)) {
            throw UnitNotFoundException::create(Swap::UNIT, $name);
        }

        return new Swap($unitName, $this->getCommandDispatcher());
    }

    /**
     * @param null|string $unitPrefix
     *
     * @return Swap[]
     */
    public function getSwaps(?string $unitPrefix = null): array
    {
        $units = $this->listUnits($unitPrefix, [Swap::UNIT]);

        return array_map(function ($unitName) {
            return new Swap($unitName, $this->getCommandDispatcher());
        }, $units);
    }

    /**
     * Restart the daemon to reload specs and new units
     *
     * @return bool
     * @throws Exception\CommandFailedException
     */
    public function daemonReload(): bool
    {
        return $this->getCommandDispatcher()->dispatch('daemon-reload')->isSuccessful();
    }

    /**
     * Reset failed state of all unit so they won't be listed using listUnits
     *
     * @return bool
     * @throws Exception\CommandFailedException
     */
    public function resetFailed(): bool
    {
        return $this->getCommandDispatcher()->dispatch('reset-failed')->isSuccessful();
    }

    /**
     * @return CommandDispatcherInterface
     */
    public function getCommandDispatcher(): CommandDispatcherInterface
    {
        if ($this->commandDispatcher === null) {
            $this->commandDispatcher = (new SymfonyCommandDispatcher())
                ->setTimeout(self::$timeout)
                ->setBinary(self::$binary);
        }

        return $this->commandDispatcher;
    }

    /**
     * @param CommandDispatcherInterface $dispatcher
     *
     * @return SystemCtl
     */
    public function setCommandDispatcher(CommandDispatcherInterface $dispatcher)
    {
        $this->commandDispatcher = $dispatcher
            ->setTimeout(self::$timeout)
            ->setBinary(self::$binary);

        return $this;
    }

    /**
     * @param string $name
     *
     * @return Device
     */
    public function getDevice(string $name): Device
    {
        $units = $this->listUnits($name, [Device::UNIT]);

        $unitName = $this->searchForUnitInUnits($name, $units);

        if (is_null($unitName)) {
            throw UnitNotFoundException::create(Device::UNIT, $name);
        }

        return new Device($unitName, $this->getCommandDispatcher());
    }

    /**
     * @param string|null $unitPrefix
     *
     * @return Device[]
     */
    public function getDevices(?string $unitPrefix = null): array
    {
         $units = $this->listUnits($unitPrefix, [Device::UNIT]);

         return array_map(function ($unitName) {
             return new Device($unitName, $this->getCommandDispatcher());
         }, $units);
    }

     /**
     * @param string $name
     *
     * @return Automount
     */
    public function getAutomount(string $name): Automount
    {
        $units = $this->listUnits($name, [Automount::UNIT]);

        $unitName = $this->searchForUnitInUnits($name, $units);

        if (is_null($unitName)) {
            throw UnitNotFoundException::create(Automount::UNIT, $name);
        }

        return new Automount($unitName, $this->getCommandDispatcher());
    }

    /**
     * @param null|string $unitPrefix
     *
     * @return Automount[]
     */
    public function getAutomounts(?string $unitPrefix = null): array
    {
        $units = $this->listUnits($unitPrefix, [Automount::UNIT]);

        return array_map(function ($unitName) {
            return new Automount($unitName, $this->getCommandDispatcher());
        }, $units);
    }


    /**
     * @param string $name
     *
     * @return Mount
     */
    public function getMount(string $name): Mount
    {
        $units = $this->listUnits($name, [Mount::UNIT]);

        $unitName = $this->searchForUnitInUnits($name, $units);

        if (is_null($unitName)) {
            throw UnitNotFoundException::create(Mount::UNIT, $name);
        }

        return new Mount($unitName, $this->getCommandDispatcher());
    }

    /**
     * @param null|string $unitPrefix
     *
     * @return Mount[]
     */
    public function getMounts(?string $unitPrefix = null): array
    {
        $units = $this->listUnits($unitPrefix, [Mount::UNIT]);

        return array_map(function ($unitName) {
            return new Mount($unitName, $this->getCommandDispatcher());
        }, $units);
    }
}