mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-13 08:09:41 +00:00
Config, add more pessimistic locking option.
We usually only lock on write, but when data has a high posibility of changing in between reads, we should have an option to lock for writes exclusively. needed for https://github.com/opnsense/core/issues/3062
This commit is contained in:
parent
d27cc83642
commit
5d0d4e832e
@ -44,6 +44,12 @@ class Config extends Singleton
|
||||
*/
|
||||
private $config_file = "";
|
||||
|
||||
/**
|
||||
* config file handle
|
||||
* @var null|file
|
||||
*/
|
||||
private $config_file_handle = null;
|
||||
|
||||
/**
|
||||
* SimpleXML type reference to config
|
||||
* @var SimpleXML
|
||||
@ -157,7 +163,9 @@ class Config extends Singleton
|
||||
*/
|
||||
public function toArrayFromFile($filename, $forceList = null)
|
||||
{
|
||||
$xml = $this->loadFromFile($filename);
|
||||
$fp = fopen($filename, "r");
|
||||
$xml = $this->loadFromStream($fp);
|
||||
fclose($fp);
|
||||
return $this->toArray($forceList, $xml);
|
||||
}
|
||||
|
||||
@ -298,18 +306,15 @@ class Config extends Singleton
|
||||
}
|
||||
|
||||
/**
|
||||
* load xml config from file
|
||||
* @param string $filename config xml source
|
||||
* load xml config from file handle
|
||||
* @param file $fp config xml source
|
||||
* @return \SimpleXMLElement root node
|
||||
* @throws ConfigException when config could not be parsed
|
||||
*/
|
||||
private function loadFromFile($filename)
|
||||
private function loadFromStream($fp)
|
||||
{
|
||||
// exception handling
|
||||
if (!file_exists($filename)) {
|
||||
throw new ConfigException('file not found');
|
||||
}
|
||||
$xml = file_get_contents($filename);
|
||||
fseek($fp, 0);
|
||||
$xml = stream_get_contents($fp);
|
||||
if (trim($xml) == '') {
|
||||
throw new ConfigException('empty file');
|
||||
}
|
||||
@ -339,7 +344,17 @@ class Config extends Singleton
|
||||
{
|
||||
$this->simplexml = null;
|
||||
$this->statusIsValid = false;
|
||||
$this->simplexml = $this->loadFromFile($this->config_file);
|
||||
|
||||
// exception handling
|
||||
if (!file_exists($this->config_file)) {
|
||||
throw new ConfigException('file not found');
|
||||
}
|
||||
|
||||
if (!is_resource($this->config_file_handle)) {
|
||||
$this->config_file_handle = fopen($this->config_file, "r+");
|
||||
}
|
||||
|
||||
$this->simplexml = $this->loadFromStream($this->config_file_handle);
|
||||
$this->statusIsValid = true;
|
||||
}
|
||||
|
||||
@ -488,6 +503,7 @@ class Config extends Singleton
|
||||
if ($this->isValid()) {
|
||||
// if current config is valid,
|
||||
$simplexml = $this->simplexml;
|
||||
$config_file_handle = $this->config_file_handle;
|
||||
try {
|
||||
// try to restore config
|
||||
copy($filename, $this->config_file);
|
||||
@ -496,6 +512,7 @@ class Config extends Singleton
|
||||
} catch (ConfigException $e) {
|
||||
// copy / load failed, restore previous version
|
||||
$this->simplexml = $simplexml;
|
||||
$this->config_file_handle = $config_file_handle;
|
||||
$this->statusIsValid = true;
|
||||
$this->save(null, true);
|
||||
return false;
|
||||
@ -528,25 +545,54 @@ class Config extends Singleton
|
||||
// serialize to text
|
||||
$xml_text = $this->__toString();
|
||||
|
||||
// save configuration, try to obtain a lock before doing so.
|
||||
$target_filename = $this->config_file;
|
||||
if (file_exists($target_filename)) {
|
||||
$fp = fopen($target_filename, "r+");
|
||||
} else {
|
||||
// apparently we are missing the config, not expected but open a new one.
|
||||
$fp = fopen($target_filename, "w+");
|
||||
}
|
||||
|
||||
if (flock($fp, LOCK_EX)) {
|
||||
// lock aquired, truncate and write new data
|
||||
ftruncate($fp, 0);
|
||||
fwrite($fp, $xml_text);
|
||||
// flush, unlock and close file handler
|
||||
fflush($fp);
|
||||
flock($fp, LOCK_UN);
|
||||
fclose($fp);
|
||||
} else {
|
||||
throw new ConfigException("Unable to lock config");
|
||||
if ($this->config_file_handle !== null) {
|
||||
if (flock($this->config_file_handle, LOCK_EX)) {
|
||||
fseek($this->config_file_handle, 0);
|
||||
ftruncate($this->config_file_handle, 0);
|
||||
fwrite($this->config_file_handle, $xml_text);
|
||||
// flush, unlock, but keep the handle open
|
||||
fflush($this->config_file_handle);
|
||||
flock($this->config_file_handle, LOCK_UN);
|
||||
} else {
|
||||
throw new ConfigException("Unable to lock config");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cleanup, close file handle
|
||||
*/
|
||||
public function __destruct ()
|
||||
{
|
||||
if ($this->config_file_handle !== null) {
|
||||
fclose($this->config_file_handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* lock configuration
|
||||
* @param boolean $reload reload config from open file handle to enforce synchronicity
|
||||
*/
|
||||
public function lock($reload=true)
|
||||
{
|
||||
if ($this->config_file_handle !== null) {
|
||||
flock($this->config_file_handle, LOCK_EX);
|
||||
if ($reload) {
|
||||
$this->load();
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* unlock configuration
|
||||
*/
|
||||
public function unlock()
|
||||
{
|
||||
if (is_resource($this->config_file_handle)) {
|
||||
flock($this->config_file_handle, LOCK_UN);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user