Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.00% covered (success)
95.00%
38 / 40
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
InstallProcessManager
95.00% covered (success)
95.00%
38 / 40
60.00% covered (warning)
60.00%
3 / 5
13
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 start
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
2
 findRunningPid
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
6.01
 buildCommand
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 createProcess
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Service\Install;
10
11use Closure;
12use OC;
13use OCA\Libresign\Service\Process\ProcessManager;
14use OCA\Libresign\Vendor\Symfony\Component\Process\Process;
15
16class InstallProcessManager {
17    private const string PROCESS_SOURCE = 'install';
18
19    /**
20     * @param null|Closure(array<int, string>): Process $processFactory
21     */
22    public function __construct(
23        private ProcessManager $processManager,
24        private ?Closure $processFactory = null,
25    ) {
26    }
27
28    public function start(string $resource, InstallTarget $target): ?int {
29        $process = $this->createProcess($this->buildCommand($resource, $target));
30        $process->setOptions(['create_new_console' => true]);
31        $process->setTimeout(null);
32        $process->start();
33
34        $pid = $process->getPid();
35        if (!$pid) {
36            return null;
37        }
38
39        $this->processManager->register(self::PROCESS_SOURCE, $pid, [
40            'resource' => $resource,
41            'architecture' => $target->architecture(),
42            'distro' => $target->distro(),
43        ]);
44
45        return $pid;
46    }
47
48    public function findRunningPid(string $resource, InstallTarget $target, int $pid = 0): int {
49        $matchesTarget = static fn (array $entry): bool
50            => ($entry['context']['resource'] ?? '') === $resource
51            && ($entry['context']['architecture'] ?? $target->architecture()) === $target->architecture()
52            && ($entry['context']['distro'] ?? $target->distro()) === $target->distro();
53
54        if ($pid > 0) {
55            $registeredPid = $this->processManager->findRunningPid(
56                self::PROCESS_SOURCE,
57                static fn (array $entry): bool => $entry['pid'] === $pid && $matchesTarget($entry),
58            );
59            if ($registeredPid > 0) {
60                return $registeredPid;
61            }
62
63            $this->processManager->unregister(self::PROCESS_SOURCE, $pid);
64            return 0;
65        }
66
67        return $this->processManager->findRunningPid(self::PROCESS_SOURCE, $matchesTarget);
68    }
69
70    /**
71     * @return list<string>
72     */
73    public function buildCommand(string $resource, InstallTarget $target): array {
74        $command = [
75            OC::$SERVERROOT . '/occ',
76            'libresign:install',
77            '--' . $resource,
78            '--architecture=' . $target->architecture(),
79        ];
80
81        if ($resource === 'java') {
82            $command[] = '--distro=' . $target->distro();
83        }
84
85        return $command;
86    }
87
88    /**
89     * @param list<string> $command
90     */
91    private function createProcess(array $command): Process {
92        if ($this->processFactory !== null) {
93            return ($this->processFactory)($command);
94        }
95        return new Process($command);
96    }
97}