Skip to content
Permalink
Fetching contributors…
Cannot retrieve contributors at this time
363 lines (352 sloc) 13.5 KB
<?php
/**
* This file is part of OpenMediaVault.
*
* @license http://www.gnu.org/licenses/gpl.html GPL Version 3
* @author Volker Theile <volker.theile@openmediavault.org>
* @copyright Copyright (c) 2009-2019 Volker Theile
*
* OpenMediaVault is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* OpenMediaVault is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenMediaVault. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Engined\Rpc;
class Apt extends \OMV\Rpc\ServiceAbstract {
/**
* Get the RPC service name.
*/
public function getName() {
return "Apt";
}
/**
* Initialize the RPC service.
*/
public function initialize() {
$this->registerMethod("getSettings");
$this->registerMethod("setSettings");
$this->registerMethod("enumerateUpgraded");
$this->registerMethod("getUpgradedList");
$this->registerMethod("upgrade");
$this->registerMethod("update");
$this->registerMethod("upload");
$this->registerMethod("getChangeLog");
}
/**
* Get settings.
* @param params The method parameters.
* @param context The context of the caller.
* @return The requested configuration object.
*/
function getSettings($params, $context) {
// Validate the RPC caller context.
$this->validateMethodContext($context, [
"role" => OMV_ROLE_ADMINISTRATOR
]);
// Get the configuration object.
$db = \OMV\Config\Database::getInstance();
$object = $db->get("conf.system.apt.distribution");
return $object->getAssoc();
}
/**
* Set settings.
* @param params The method parameters.
* @param context The context of the caller.
* @return The stored configuration object.
*/
function setSettings($params, $context) {
// Validate the RPC caller context.
$this->validateMethodContext($context, [
"role" => OMV_ROLE_ADMINISTRATOR
]);
// Validate the parameters of the RPC service method.
$this->validateMethodParams($params, "rpc.apt.setsettings");
// Prepare the configuration object.
$object = new \OMV\Config\ConfigObject("conf.system.apt.distribution");
$object->setAssoc($params);
// Update the configuration object.
$db = \OMV\Config\Database::getInstance();
$db->set($object);
// Apply the changes immediatelly (no user interaction is wanted).
\OMV\Rpc\Rpc::call("Config", "applyChanges", [
"modules" => [ "apt" ],
"force" => TRUE
], $context);
// Return the configuration object.
return $object->getAssoc();
}
/**
* Enumerate all packages that are available to be upgraded.
* @param params The method parameters.
* @param context The context of the caller.
* @return array An array of objects containing the fields \em name,
* \em version, \em oldversion, \em repository, \em architecture,
* \em package, \em priority, \em section, \em installedsize,
* \em maintainer, \em filename, \em size, \em md5sum, \em sha1,
* \em sha256, \em abstract and \em homepage.
* The following fields are optional: \em description, \em depends,
* \em replaces and \em conflicts.
*/
function enumerateUpgraded($params, $context) {
// Validate the RPC caller context.
$this->validateMethodContext($context, [
"role" => OMV_ROLE_ADMINISTRATOR
]);
return \OMV\System\System::getAptUpgradeList();
}
/**
* Get a list of all packages that are to be upgraded.
* @param params An array containing the following fields:
* \em start The index where to start.
* \em limit The number of objects to process.
* \em sortfield The name of the column used to sort.
* \em sortdir The sort direction, ASC or DESC.
* @param context The context of the caller.
* @return An array containing the requested objects. The field \em total
* contains the total number of objects, \em data contains the object
* array. An object contains the following fields: \em name, \em version,
* \em oldversion, \em repository, \em architecture, \em package,
* \em priority, \em section, \em installedsize, \em maintainer,
* \em filename, \em size, \em md5sum, \em sha1, \em sha256,
* \em abstract and \em homepage. The following fields are
* optional: \em description, \em extendeddescription, \em depends,
* \em replaces and \em conflicts.
*/
function getUpgradedList($params, $context) {
// Validate the RPC caller context.
$this->validateMethodContext($context, [
"role" => OMV_ROLE_ADMINISTRATOR
]);
// Validate the parameters of the RPC service method.
$this->validateMethodParams($params, "rpc.common.getlist");
// Enumerate all packages that are to be upgraded.
$objects = $this->callMethod("enumerateUpgraded", NULL, $context);
// Filter result.
return $this->applyFilter($objects, $params['start'],
$params['limit'], $params['sortfield'], $params['sortdir']);
}
/**
* Upgrade the given packages.
* @param params An array containing the following fields:
* \em packages An array of package names to upgrade.
* @param context The context of the caller.
* @return The name of the background process status file.
*/
function upgrade($params, $context) {
// Validate the RPC caller context.
$this->validateMethodContext($context, [
"role" => OMV_ROLE_ADMINISTRATOR
]);
// Validate the parameters of the RPC service method.
$this->validateMethodParams($params, "rpc.apt.upgrade");
// Create the background process.
return $this->execBgProc(function($bgStatusFilename, $bgOutputFilename)
use ($params) {
// Upgrade the given packages.
// http://raphaelhertzog.com/2010/09/21/debian-conffile-configuration-file-managed-by-dpkg/
$cmdArgs = [];
$cmdArgs[] = "--yes";
$cmdArgs[] = "--allow-downgrades";
$cmdArgs[] = "--allow-change-held-packages";
$cmdArgs[] = "--fix-broken"; // Fix broken dependencies in place.
$cmdArgs[] = "--fix-missing";
$cmdArgs[] = "--auto-remove";
$cmdArgs[] = "--allow-unauthenticated";
$cmdArgs[] = "--show-upgraded";
$cmdArgs[] = "--option DPkg::Options::=\"--force-confold\"";
$cmdArgs[] = "install";
$cmdArgs = array_merge($cmdArgs, $params['packages']);
$cmd = new \OMV\System\Process("apt-get", $cmdArgs);
$cmd->setRedirect2to1();
$cmd->setEnv("DEBIAN_FRONTEND", "noninteractive");
if (0 !== ($exitStatus = $this->exec($cmd->getCommandLine(),
$output, $bgOutputFilename))) {
throw new \OMV\ExecException($cmd->getCommandLine(), $output,
$exitStatus);
}
return $output;
});
}
/**
* Update APT cache.
* http://newbiedoc.sourceforge.net/system/apt-get-intro.html
* http://www.cyberciti.biz/tips/linux-debian-package-management-cheat-sheet.html
* @param params The method parameters.
* @param context The context of the caller.
* @return The name of the background process status file.
*/
function update($params, $context) {
// Validate the RPC caller context.
$this->validateMethodContext($context, [
"role" => OMV_ROLE_ADMINISTRATOR
]);
// Create the background process.
return $this->execBgProc(function($bgStatusFilename, $bgOutputFilename)
use ($params) {
// Update package database.
$cmd = new \OMV\System\Process("apt-get", "update");
$cmd->setRedirect2to1();
if (0 !== ($exitStatus = $this->exec($cmd->getCommandLine(),
$output, $bgOutputFilename))) {
throw new \OMV\ExecException($cmd->getCommandLine(), $output,
$exitStatus);
}
return $output;
});
}
/**
* Upload a package to the local package archive.
* @param params An array containing the following fields:
* \em filename The original name of the file.
* \em filepath The path to the uploaded file.
* @param context The context of the caller.
* @return void
*/
function upload($params, $context) {
// Validate the RPC caller context.
$this->validateMethodContext($context, [
"role" => OMV_ROLE_ADMINISTRATOR
]);
// Validate the parameters of the RPC service method.
$this->validateMethodParams($params, "rpc.apt.upload");
// Check the file type.
$finfo = new \finfo(FILEINFO_NONE);
$fileType = $finfo->file($params['filepath']);
if (0 == preg_match("/^Debian binary package.+$/", $fileType)) {
throw new \OMV\Exception(
"Failed to upload the file '%s'. It is no Debian binary package.",
$params['filename']);
}
// Move file to local package archive.
if (!rename($params['filepath'], build_path(DIRECTORY_SEPARATOR,
\OMV\Environment::get("OMV_DPKGARCHIVE_DIR"),
$params['filename']))) {
throw new \OMV\Exception(
"Failed to move the package '%s' to local package repository.",
$params['filename']);
}
// Create the 'Packages' file required by local APT archives.
// The 'packages' command should be run in the root of the tree.
$cmd = sprintf("export LANG=C; cd %s && apt-ftparchive " .
"packages . > Packages", \OMV\Environment::get(
"OMV_DPKGARCHIVE_DIR"));
if (0 !== $this->exec($cmd, $output))
throw new \OMV\ExecException($cmd, $output);
}
/**
* Get the changelog of an Debian package. The package is downloaded
* if necessary to be able to extract the changelog file.
* @param params An array containing the following fields:
* \em filename The name of the file, e.g. <ul>
* \li openssl_0.9.8o-4squeeze13_i386.deb
* \li pool/updates/main/e/eglibc/libc6_2.13-38+deb7u4_amd64.deb
* </ul>
* @param context The context of the caller.
* @return The name of the background process status file.
*/
function getChangeLog($params, $context) {
// Validate the RPC caller context.
$this->validateMethodContext($context, [
"role" => OMV_ROLE_ADMINISTRATOR
]);
// Validate the parameters of the RPC service method.
$this->validateMethodParams($params, "rpc.apt.getchangelog");
// Create the background process.
return $this->execBgProc(function($bgStatusFilename, $bgOutputFilename)
use ($params) {
// Get the package name. Note, there are files like
// bind9-host_1%3a9.9.5.dfsg-9+deb8u6_amd64.deb
// that need to be processed correctly. The problem is
// that the filename retrieved from the backend is
// bind9-host_9.9.5.dfsg-9+deb8u6_amd64.deb in this case.
$parts = explode("_", basename($params['filename'], ".deb"));
$packageName = $parts[0];
// Build the file path to the package archive using wildcards
// to be able to find packages mentioned above.
// A package name is build like this:
// <name>_<version>_<architecture>.deb
$packagePath = build_path(DIRECTORY_SEPARATOR,
"/var/cache/apt/archives", sprintf("%s_*%s_%s.deb",
$parts[0], $parts[1], $parts[2]));
// Force download of the package if it does not exist in the
// package archive (/var/cache/apt/archives). Do not redirect
// the command output to the backgropund output file. Only the
// changelog will be redirected to this file.
if (TRUE === empty(glob($packagePath))) {
$cmdArgs = [];
$cmdArgs[] = "--yes";
$cmdArgs[] = "--allow-downgrades";
$cmdArgs[] = "--allow-change-held-packages";
$cmdArgs[] = "--download-only";
$cmdArgs[] = "--reinstall";
$cmdArgs[] = "install";
$cmdArgs[] = escapeshellarg($packageName);
$cmd = new \OMV\System\Process("apt-get", $cmdArgs);
$cmd->setRedirect2to1();
$cmd->execute();
}
// The package should exist now.
$matches = glob($packagePath);
$packagePath = $matches[0];
if (FALSE === file_exists($packagePath)) {
throw new \OMV\Exception("Package file '%s' not found.",
$params['filename']);
}
// // Extract the changelog content and redirect it to the
// // background output file.
// $cmdArgs = [];
// $cmdArgs[] = "--all";
// $cmdArgs[] = "--frontend=text";
// $cmdArgs[] = "--which=changelogs";
// $cmdArgs[] = escapeshellarg($packagePath);
// $cmd = new \OMV\System\Process("apt-listchanges", $cmdArgs);
// $cmd->setRedirect2to1();
// $cmdLine = $cmd->getCommandLine();
// if (0 !== $this->exec($cmdLine, $output, $bgOutputFilename))
// throw new \OMV\ExecException($cmdLine, $output);
// return $output;
// Extract the changelog from the Debian package.
if (FALSE === ($tmpDir = mkdtemp())) {
throw new \OMV\Exception(
"Failed to created a temporary directory.");
}
$cmd = sprintf("export LANG=C; dpkg-deb --fsys-tarfile %s | ".
"tar --extract --wildcards --directory=%s ".
"./usr/share/doc/%s/changelog* 2>&1",
escapeshellarg($packagePath), escapeshellarg($tmpDir),
$packageName);
if (0 !== $this->exec($cmd, $output))
throw new \OMV\ExecException($cmd, $output);
// Process the extracted changelog. Note, the file is also
// compressed. The changelog file may be named like:
// - changelog.gz
// - changelog.Debian.gz
$matches = glob(build_path(DIRECTORY_SEPARATOR, $tmpDir,
"usr/share/doc", $packageName, "changelog*.gz"));
if (TRUE === empty($matches))
throw new \OMV\Exception("No changelog found.");
// Extract the changelog content and redirect it to the
// background output file.
$cmdArgs = [];
$cmdArgs[] = "--decompress";
$cmdArgs[] = "--stdout";
$cmd = new \OMV\System\Process("gzip", $cmdArgs);
$cmd->setInputFromFile($matches[0]);
$cmd->setRedirect2to1();
if (0 !== ($exitStatus = $this->exec($cmd->getCommandLine(),
$output, $bgOutputFilename))) {
throw new \OMV\ExecException($cmd->getCommandLine(), $output,
$exitStatus);
}
return $output;
});
}
}
You can’t perform that action at this time.