mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
Add safegaurd against running LibreQoS.py more than once at a time.
ISSUE #52 * Added file locking commands to the Python/Rust library. * When LibreQoS.py starts, it checks that /var/run/libreqos.lock does not exist. If it does, it checks that it contains a PID and that PID is not still running with a python process. * If the lock file exists and is valid, execution aborts. * If the lock file exists and is invalid, it is cleaned. * Cleans the lock on termination.
This commit is contained in:
@@ -23,7 +23,8 @@ from ispConfig import sqm, upstreamBandwidthCapacityDownloadMbps, upstreamBandwi
|
||||
runShellCommandsAsSudo, generatedPNDownloadMbps, generatedPNUploadMbps, queuesAvailableOverride, \
|
||||
OnAStick
|
||||
|
||||
from liblqos_python import is_lqosd_alive, clear_ip_mappings, delete_ip_mapping, validate_shaped_devices
|
||||
from liblqos_python import is_lqosd_alive, clear_ip_mappings, delete_ip_mapping, validate_shaped_devices, \
|
||||
is_libre_already_running, create_lock_file, free_lock_file
|
||||
|
||||
# Automatically account for TCP overhead of plans. For example a 100Mbps plan needs to be set to 109Mbps for the user to ever see that result on a speed test
|
||||
# Does not apply to nodes of any sort, just endpoint devices
|
||||
@@ -1260,12 +1261,21 @@ def refreshShapersUpdateOnly():
|
||||
print("refreshShapersUpdateOnly completed on " + datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Check that the host lqosd is running
|
||||
if is_lqosd_alive():
|
||||
print("lqosd is running")
|
||||
else:
|
||||
print("ERROR: lqosd is not running. Aborting")
|
||||
os._exit(-1)
|
||||
|
||||
# Check that we aren't running LibreQoS.py more than once at a time
|
||||
if is_libre_already_running():
|
||||
print("LibreQoS.py is already running in another process. Aborting.")
|
||||
os._exit(-1)
|
||||
|
||||
# We've got this far, so create a lock file
|
||||
create_lock_file()
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'-d', '--debug',
|
||||
@@ -1305,3 +1315,6 @@ if __name__ == '__main__':
|
||||
else:
|
||||
# Refresh and/or set up queues
|
||||
refreshShapers()
|
||||
|
||||
# Free the lock file
|
||||
free_lock_file()
|
||||
2
src/rust/Cargo.lock
generated
2
src/rust/Cargo.lock
generated
@@ -1362,7 +1362,9 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lqos_bus",
|
||||
"nix",
|
||||
"pyo3",
|
||||
"sysinfo",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
||||
@@ -12,3 +12,5 @@ pyo3 = "0.17"
|
||||
lqos_bus = { path = "../lqos_bus" }
|
||||
tokio = { version = "1.22", features = [ "rt", "macros", "net", "io-util", "time" ] }
|
||||
anyhow = "1"
|
||||
sysinfo = "0"
|
||||
nix = "0"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use std::{path::Path, fs::{File, remove_file}, io::Write};
|
||||
use lqos_bus::{BusRequest, BusResponse, TcHandle};
|
||||
use nix::libc::getpid;
|
||||
use pyo3::{
|
||||
exceptions::PyOSError, pyclass, pyfunction, pymodule, types::PyModule,
|
||||
wrap_pyfunction, PyResult, Python,
|
||||
@@ -6,6 +8,9 @@ use pyo3::{
|
||||
mod blocking;
|
||||
use anyhow::{Error, Result};
|
||||
use blocking::run_query;
|
||||
use sysinfo::{Pid, ProcessExt, System, SystemExt};
|
||||
|
||||
const LOCK_FILE: &str = "/run/lqos/libreqos.lock";
|
||||
|
||||
/// Defines the Python module exports.
|
||||
/// All exported functions have to be listed here.
|
||||
@@ -18,6 +23,9 @@ fn liblqos_python(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||
m.add_wrapped(wrap_pyfunction!(delete_ip_mapping))?;
|
||||
m.add_wrapped(wrap_pyfunction!(add_ip_mapping))?;
|
||||
m.add_wrapped(wrap_pyfunction!(validate_shaped_devices))?;
|
||||
m.add_wrapped(wrap_pyfunction!(is_libre_already_running))?;
|
||||
m.add_wrapped(wrap_pyfunction!(create_lock_file))?;
|
||||
m.add_wrapped(wrap_pyfunction!(free_lock_file))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -147,3 +155,46 @@ fn validate_shaped_devices() -> PyResult<String> {
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn is_libre_already_running() -> PyResult<bool> {
|
||||
let lock_path = Path::new(LOCK_FILE);
|
||||
if lock_path.exists() {
|
||||
let contents = std::fs::read_to_string(lock_path);
|
||||
if let Ok(contents) = contents {
|
||||
if let Ok(pid) = contents.parse::<i32>() {
|
||||
let sys = System::new_all();
|
||||
if let Some(process) = sys.processes().get(&Pid::from(pid)) {
|
||||
if process.name().contains("python") {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("{LOCK_FILE} did not contain a valid PID");
|
||||
return Ok(false);
|
||||
}
|
||||
} else {
|
||||
println!("Error reading contents of {LOCK_FILE}");
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn create_lock_file() -> PyResult<()> {
|
||||
let pid = unsafe { getpid() };
|
||||
let pid_format = format!("{pid}");
|
||||
{
|
||||
if let Ok(mut f) = File::create(LOCK_FILE) {
|
||||
f.write_all(pid_format.as_bytes())?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn free_lock_file() -> PyResult<()> {
|
||||
let _ = remove_file(LOCK_FILE); // Ignore result
|
||||
Ok(())
|
||||
}
|
||||
@@ -109,7 +109,7 @@ async fn main() -> Result<()> {
|
||||
let server = UnixSocketServer::new().expect("Unable to spawn server");
|
||||
server.listen(handle_bus_requests).await
|
||||
};
|
||||
tokio::spawn(listener);
|
||||
let handle = tokio::spawn(listener);
|
||||
|
||||
if lqos_config::LibreQoSConfig::config_exists() && lqos_config::ConfigShapedDevices::exists() {
|
||||
warn!("Since all the files exist, Launching LibreQoS.py to avoid empty queues.");
|
||||
@@ -118,6 +118,8 @@ async fn main() -> Result<()> {
|
||||
warn!("ispConfig.py or ShapedDevices.csv hasn't been setup yet. Not automatically running LibreQoS.py");
|
||||
}
|
||||
|
||||
info!("{:?}", handle.await?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user