libreschool-pxeserver/root/srv/ipxe/EsmithDatabase.php

391 lines
11 KiB
PHP

<?php
/*
* Copyright (C) 2011 Nethesis S.r.l.
*
* This script is part of NethServer.
*
* NethServer 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
* (at your option) any later version.
*
* NethServer 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 NethServer. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Read and write parameters into SME DB
*
* Ths class implements an interface to SME database executing the command /sbin/e-smith/db with sudo.
* The class needs /etc/sudoers configurazione. In the sudoers file you must have something like this:
* <code>
* Cmnd_Alias SME = /sbin/e-smith/db, /sbin/e-smith/signal-event
* www ALL=NOPASSWD: SME
* </code>
*
*
* @author Giacomo Sanchietti <giacomo.sanchietti@nethesis.it>
* @author Davide Principi <davide.principi@nethesis.it>
* @since 1.0
* @internal
*/
class EsmithDatabase
{
/**
* @var SME DB database command
* */
private $command = "/usr/bin/sudo /sbin/e-smith/db";
/**
* @var $db Database name, it's translated into the db file path. For example: /home/e-smith/db/testdb
* */
private $db;
/**
* This static class variable shares a socket connection to smwingsd
*
* @var resource
*/
private static $socket;
/**
* Construct an object to access a SME Configuration database file
* with $user's privileges.
*
* @param string $database Database name
*/
public function __construct($database)
{
if ( ! $database)
sprintf("%s: You must provide a valid database name.", get_class($this));
$this->db = $database;
}
public function getAll($type = NULL)
{
$output = "";
$ret = $this->dbRead('getjson', array(), $output);
if ($ret !== 0) {
sprintf("%s: internal database command failed!", __CLASS__);
}
$data = json_decode($output, TRUE);
if ( ! is_array($data)) {
sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
}
$result = array();
foreach ($data as $item) {
// Apply type check filter:
if (isset($type) && $type !== $item['type']) {
continue;
}
$props = isset($item['props']) ? $item['props'] : array();
$result[$item['name']] = array_merge($props, array('type' => $item['type']));
}
return $result;
}
public function getAllByProp($propName = NULL, $propValue = NULL)
{
$output = "";
$ret = $this->dbRead('getjson', array(), $output);
if ($ret !== 0) {
sprintf("%s: internal database command failed!", __CLASS__);
}
$data = json_decode($output, TRUE);
if ( ! is_array($data)) {
sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
}
$result = array();
foreach ($data as $item) {
$item['props']['type'] = $item['type'];
$item['props']['name'] = $item['name'];
if (isset($propName) && isset($propValue)) {
if (isset($item['props'][$propName]) && $propValue == $item['props'][$propName]) {
$result[$item['name']] = $item['props'];
}
} else {
$result[$item['name']] = $item['props'];
}
}
return $result;
}
public function getKey($key)
{
$output = '';
$ret = $this->dbRead('getjson', array($key), $output);
if ($ret !== 0) {
sprintf("%s: internal database command failed", __CLASS__);
}
$data = json_decode($output, TRUE);
if ($data === 1) {
// Key has not been found
return array();
} elseif ( ! is_array($data)) {
sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
}
$data['props']['type'] = $data['type'];
$data['props']['name'] = $data['name'];
return $data['props'];
}
public function getKeyValue($key)
{
$output = '';
$ret = $this->dbRead('getjson', array($key), $output);
if ($ret !== 0) {
sprintf("%s: internal database command failed", __CLASS__);
}
$data = json_decode($output, TRUE);
if ($data === 1) {
// Key has not been found
return array();
} elseif ( ! is_array($data)) {
sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
}
return $data['type'];
}
public function setKey($key, $type, $props)
{
$output = NULL;
$ret = $this->dbExec('set', $this->prepareArguments($key, $type, $props), $output);
return ($ret == 0);
}
public function deleteKey($key)
{
$output = NULL;
$output = NULL;
$ret = $this->dbExec('delete', $this->prepareArguments($key), $output);
return ($ret == 0);
}
/**
* Return the type of a key
* Act like: /sbin/e-smith/db dbfile gettype key
*
* @param string $key the key to retrieve
* @access public
* @return string the type of the key
*/
public function getType($key)
{
$output = NULL;
$ret = $this->dbRead('gettype', array($key), $output);
return trim($output);
}
/**
* Set the type of a key
* Act like: /sbin/e-smith/db dbfile settype key type
*
* @param string $key the key to change
* @param string $type the new type
* @access public
* @return bool true on success, FALSE otherwise
*/
public function setType($key, $type)
{
$output = NULL;
$ret = $this->dbExec('settype', $this->prepareArguments($key, $type), $output);
return ($ret == 0);
}
public function getProp($key, $prop)
{
$output = NULL;
$ret = $this->dbRead('getprop', array($key, $prop), $output);
return trim($output);
}
public function setProp($key, $props)
{
$output = NULL;
$ret = $this->dbExec('setprop', $this->prepareArguments($key, $props), $output);
return ($ret == 0);
}
public function delProp($key, $props)
{
$output = NULL;
$ret = $this->dbExec('delprop', array_merge(array($key), array_values($props)), $output);
return ($ret == 0);
}
/**
* Execute the db command
* @param string $command The command to invoke
* @param array $args The command arguments
* @param string &$output The output from the command process
* @return int The command exit code
*/
private function dbExec($command, $args, &$output)
{
// prepend the database name and command
array_unshift($args, $this->db, $command);
$exitCode = 0;
$oArr = array();
exec($this->command . ' ' . implode(' ', array_map('escapeshellarg', $args)), $oArr, $exitCode);
$output = implode("\n", $oArr);
return $exitCode;
}
/**
* Read db data from memory cache daemon
* @param string $command The command to invoke
* @param array $args The command arguments
* @param string &$output The output from the read socket
* @return int The command exit code
*/
private function dbRead($command, $args, &$output)
{
if ( ! isset(self::$socket) ) {
$socketPath = '/var/run/smwingsd.sock';
$errno = 0;
$errstr = '';
self::$socket = fsockopen('unix://' . $socketPath, -1, $errno, $errstr);
if( ! is_resource(self::$socket)) {
sprintf("Invalid socket (%d): %s. Fall back to exec().", $errno, $errstr);
}
}
if ( ! is_resource(self::$socket)) {
return $this->dbExec($command, call_user_func_array(array($this, 'prepareArguments'), $args), $output);
}
// prepend the database name and command
array_unshift($args, $this->db, $command);
$ret = $this->sendMessage(self::$socket, 0x10, $args);
if ( ! $ret ) {
return 1;
}
$ret = $this->recvMessage(self::$socket);
if ( ! $ret ) {
return 1;
}
if ($command === 'getjson') {
$output = $ret;
} else {
$output = json_decode($ret, TRUE);
}
return 0;
}
private function sendMessage($socket, $type, $args = array())
{
$payload = json_encode($args);
$data = pack('CN', (int) $type, strlen($payload)) . $payload;
$written = fwrite($socket, $data);
if ($written !== strlen($data)) {
echo 'Socket write error';
}
return TRUE;
}
private function recvMessage($socket)
{
$buf = $this->safeRead($socket, 5);
if ($buf === FALSE) {
echo 'Socket read error';
return false;
}
$header = unpack('Ctype/Nsize', $buf);
if ( ! is_array($header)) {
echo 'Invalid message header';
return false;
}
$message = $this->safeRead($socket, $header['size']);
if ($message === FALSE) {
echo 'Socket read error';
return false;
}
if ($header['type'] & 0x02) {
return NULL;
}
return $message;
}
private function safeRead($socket, $size)
{
$buffer = "";
$count = 0;
while($count < $size) {
if(feof($socket)) {
return FALSE;
}
$chunk = fread($socket, $size - $count);
$count += strlen($chunk);
if($chunk === FALSE) {
return FALSE;
}
$buffer .= $chunk;
}
return $buffer;
}
/**
* Take arbitrary arguments and flattenize to an array
*
* @param mixed $_
* @return array
*/
private function prepareArguments()
{
$args = array();
foreach (func_get_args() as $arg) {
if (is_array($arg)) {
foreach ($arg as $propName => $propValue) {
$args[] = $propName;
$args[] = $propValue;
}
} else {
$args[] = (String) $arg;
}
}
return $args;
}
}