Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make search path for BinaryFinder customizable. #43968

Merged
merged 1 commit into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions config/config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -2533,4 +2533,20 @@
* Defaults to ``true``
*/
'enable_non-accessible_features' => true,

/**
* Directories where nextcloud looks for binaries.
* This is used to find external binaries like libreoffice, sendmail, ffmpeg and more.
*
* Defaults to ``['/usr/local/sbin','/usr/local/bin','/usr/sbin','/usr/bin','/sbin','/bin','/opt/bin']``
*/
'binary_search_paths' => [
'/usr/local/sbin',
'/usr/local/bin',
'/usr/sbin',
'/usr/bin',
'/sbin',
'/bin',
'/opt/bin',
],
];
28 changes: 18 additions & 10 deletions lib/private/BinaryFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,28 @@
use OCP\IBinaryFinder;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;
use Symfony\Component\Process\ExecutableFinder;

/**
* Service that find the binary path for a program
*/
class BinaryFinder implements IBinaryFinder {
public const DEFAULT_BINARY_SEARCH_PATHS = [
'/usr/local/sbin',
'/usr/local/bin',
'/usr/sbin',
'/usr/bin',
'/sbin',
'/bin',
'/opt/bin',
];
private ICache $cache;

public function __construct(ICacheFactory $cacheFactory) {
public function __construct(
ICacheFactory $cacheFactory,
private IConfig $config,
) {
$this->cache = $cacheFactory->createLocal('findBinaryPath');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we assume people are clever enough to purge the local cache when changing the config?
Or should we add e.g. md5(json_encode($this->config->getSystemValue('binary_search_paths'))) to the cache prefix?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache is only for 5 minutes + we currently cache not found/fallback results and find that acceptable. So I don't think it's necessary.

}

Expand All @@ -37,15 +50,10 @@ public function findBinaryPath(string $program) {
if (\OCP\Util::isFunctionEnabled('exec')) {
$exeSniffer = new ExecutableFinder();
// Returns null if nothing is found
$result = $exeSniffer->find($program, null, [
'/usr/local/sbin',
'/usr/local/bin',
'/usr/sbin',
'/usr/bin',
'/sbin',
'/bin',
'/opt/bin',
]);
$result = $exeSniffer->find(
$program,
null,
$this->config->getSystemValue('binary_search_paths', self::DEFAULT_BINARY_SEARCH_PATHS));
if ($result === null) {
$result = false;
}
exi marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
84 changes: 84 additions & 0 deletions tests/lib/BinaryFinderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types = 1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace Test;

use OC\BinaryFinder;
use OC\Memcache\ArrayCache;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;

class BinaryFinderTest extends TestCase {
private ICache $cache;
private ICacheFactory $cacheFactory;
private $oldEnv;

protected function setUp(): void {
$this->oldEnv = getenv('PATH');
// BinaryFinder always includes the "PATH" environment variable into the search path,
// which we want to avoid in this test because they are not usually found in webserver
Copy link
Contributor

@kesselb kesselb Mar 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Why should the path environment variable not be available in webserver deployments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apache does not set or pass it through by default. Phpfpm does not set it as well. Not sure about others but I would suspect that most servers don't provide it by default.

// deployments.
putenv('PATH=""');
$this->cacheFactory = $this->createMock(ICacheFactory::class);
$this->cache = new ArrayCache();
$this->cacheFactory->method('createLocal')->with('findBinaryPath')->willReturn($this->cache);
}

protected function tearDown(): void {
putenv('PATH=' . $this->oldEnv);
}

public function testDefaultFindsCat() {
$config = $this->createMock(IConfig::class);
$config
->method('getSystemValue')
->with('binary_search_paths', $this->anything())
->will($this->returnCallback(function ($key, $default) {
return $default;
}));
$finder = new BinaryFinder($this->cacheFactory, $config);
$this->assertEquals($finder->findBinaryPath('cat'), '/usr/bin/cat');
$this->assertEquals($this->cache->get('cat'), '/usr/bin/cat');
}

public function testDefaultDoesNotFindCata() {
$config = $this->createMock(IConfig::class);
$config
->method('getSystemValue')
->with('binary_search_paths', $this->anything())
->will($this->returnCallback(function ($key, $default) {
return $default;
}));
$finder = new BinaryFinder($this->cacheFactory, $config);
$this->assertFalse($finder->findBinaryPath('cata'));
$this->assertFalse($this->cache->get('cata'));
}

public function testCustomPathFindsCat() {
$config = $this->createMock(IConfig::class);
$config
->method('getSystemValue')
->with('binary_search_paths', $this->anything())
->willReturn(['/usr/bin']);
$finder = new BinaryFinder($this->cacheFactory, $config);
$this->assertEquals($finder->findBinaryPath('cat'), '/usr/bin/cat');
$this->assertEquals($this->cache->get('cat'), '/usr/bin/cat');
}

public function testWrongCustomPathDoesNotFindCat() {
$config = $this->createMock(IConfig::class);
$config
->method('getSystemValue')
->with('binary_search_paths')
->willReturn(['/wrong']);
$finder = new BinaryFinder($this->cacheFactory, $config);
$this->assertFalse($finder->findBinaryPath('cat'));
$this->assertFalse($this->cache->get('cat'));
}
}
Loading