remove timer and lock functions, fix building with proxmox 0.3.2
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
		| @ -2,6 +2,7 @@ use anyhow::{bail, Error}; | ||||
| use ::serde::{Deserialize, Serialize}; | ||||
|  | ||||
| use proxmox::api::{api, Router, RpcEnvironment, Permission}; | ||||
| use proxmox::tools::fs::open_file_locked; | ||||
|  | ||||
| use crate::api2::types::*; | ||||
| use crate::config::acl; | ||||
| @ -174,7 +175,7 @@ pub fn update_acl( | ||||
|     _rpcenv: &mut dyn RpcEnvironment, | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(acl::ACL_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(acl::ACL_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let (mut tree, expected_digest) = acl::config()?; | ||||
|  | ||||
|  | ||||
| @ -3,6 +3,7 @@ use serde_json::Value; | ||||
|  | ||||
| use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; | ||||
| use proxmox::api::schema::{Schema, StringSchema}; | ||||
| use proxmox::tools::fs::open_file_locked; | ||||
|  | ||||
| use crate::api2::types::*; | ||||
| use crate::config::user; | ||||
| @ -87,7 +88,7 @@ pub fn list_users( | ||||
| /// Create new user. | ||||
| pub fn create_user(password: Option<String>, param: Value) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let user: user::User = serde_json::from_value(param)?; | ||||
|  | ||||
| @ -193,7 +194,7 @@ pub fn update_user( | ||||
|     digest: Option<String>, | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let (mut config, expected_digest) = user::config()?; | ||||
|  | ||||
| @ -265,7 +266,7 @@ pub fn update_user( | ||||
| /// Remove a user from the configuration file. | ||||
| pub fn delete_user(userid: String, digest: Option<String>) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let (mut config, expected_digest) = user::config()?; | ||||
|  | ||||
|  | ||||
| @ -5,6 +5,7 @@ use serde_json::Value; | ||||
| use ::serde::{Deserialize, Serialize}; | ||||
|  | ||||
| use proxmox::api::{api, Router, RpcEnvironment, Permission}; | ||||
| use proxmox::tools::fs::open_file_locked; | ||||
|  | ||||
| use crate::api2::types::*; | ||||
| use crate::backup::*; | ||||
| @ -99,7 +100,7 @@ pub fn list_datastores( | ||||
| /// Create new datastore config. | ||||
| pub fn create_datastore(param: Value) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let datastore: datastore::DataStoreConfig = serde_json::from_value(param.clone())?; | ||||
|  | ||||
| @ -253,7 +254,7 @@ pub fn update_datastore( | ||||
|     digest: Option<String>, | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     // pass/compare digest | ||||
|     let (mut config, expected_digest) = datastore::config()?; | ||||
| @ -327,7 +328,7 @@ pub fn update_datastore( | ||||
| /// Remove a datastore configuration. | ||||
| pub fn delete_datastore(name: String, digest: Option<String>) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let (mut config, expected_digest) = datastore::config()?; | ||||
|  | ||||
|  | ||||
| @ -4,6 +4,7 @@ use ::serde::{Deserialize, Serialize}; | ||||
| use base64; | ||||
|  | ||||
| use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; | ||||
| use proxmox::tools::fs::open_file_locked; | ||||
|  | ||||
| use crate::api2::types::*; | ||||
| use crate::config::remote; | ||||
| @ -78,7 +79,7 @@ pub fn list_remotes( | ||||
| /// Create new remote. | ||||
| pub fn create_remote(password: String, param: Value) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let mut data = param.clone(); | ||||
|     data["password"] = Value::from(base64::encode(password.as_bytes())); | ||||
| @ -194,7 +195,7 @@ pub fn update_remote( | ||||
|     digest: Option<String>, | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let (mut config, expected_digest) = remote::config()?; | ||||
|  | ||||
| @ -255,7 +256,7 @@ pub fn update_remote( | ||||
| /// Remove a remote from the configuration file. | ||||
| pub fn delete_remote(name: String, digest: Option<String>) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let (mut config, expected_digest) = remote::config()?; | ||||
|  | ||||
|  | ||||
| @ -3,6 +3,7 @@ use serde_json::Value; | ||||
| use ::serde::{Deserialize, Serialize}; | ||||
|  | ||||
| use proxmox::api::{api, Router, RpcEnvironment}; | ||||
| use proxmox::tools::fs::open_file_locked; | ||||
|  | ||||
| use crate::api2::types::*; | ||||
| use crate::config::sync::{self, SyncJobConfig}; | ||||
| @ -68,7 +69,7 @@ pub fn list_sync_jobs( | ||||
| /// Create a new sync job. | ||||
| pub fn create_sync_job(param: Value) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let sync_job: sync::SyncJobConfig = serde_json::from_value(param.clone())?; | ||||
|  | ||||
| @ -184,7 +185,7 @@ pub fn update_sync_job( | ||||
|     digest: Option<String>, | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     // pass/compare digest | ||||
|     let (mut config, expected_digest) = sync::config()?; | ||||
| @ -247,7 +248,7 @@ pub fn update_sync_job( | ||||
| /// Remove a sync job configuration | ||||
| pub fn delete_sync_job(id: String, digest: Option<String>) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let (mut config, expected_digest) = sync::config()?; | ||||
|  | ||||
|  | ||||
| @ -4,6 +4,7 @@ use ::serde::{Deserialize, Serialize}; | ||||
|  | ||||
| use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; | ||||
| use proxmox::api::schema::parse_property_string; | ||||
| use proxmox::tools::fs::open_file_locked; | ||||
|  | ||||
| use crate::config::network::{self, NetworkConfig}; | ||||
| use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}; | ||||
| @ -230,7 +231,7 @@ pub fn create_interface( | ||||
|     let interface_type = crate::tools::required_string_param(¶m, "type")?; | ||||
|     let interface_type: NetworkInterfaceType = serde_json::from_value(interface_type.into())?; | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let (mut config, _digest) = network::config()?; | ||||
|  | ||||
| @ -463,7 +464,7 @@ pub fn update_interface( | ||||
|     param: Value, | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let (mut config, expected_digest) = network::config()?; | ||||
|  | ||||
| @ -586,7 +587,7 @@ pub fn update_interface( | ||||
| /// Remove network interface configuration. | ||||
| pub fn delete_interface(iface: String, digest: Option<String>) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|     let _lock = open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
|     let (mut config, expected_digest) = network::config()?; | ||||
|  | ||||
|  | ||||
| @ -155,7 +155,7 @@ impl BackupGroup { | ||||
|  | ||||
|         // acquire in non-blocking mode, no point in waiting here since other | ||||
|         // backups could still take a very long time | ||||
|         tools::lock_file(&mut handle, true, Some(Duration::from_nanos(0))) | ||||
|         proxmox::tools::fs::lock_file(&mut handle, true, Some(Duration::from_nanos(0))) | ||||
|             .map_err(|err| { | ||||
|                 format_err!( | ||||
|                     "unable to acquire lock on backup group {:?} - {}", | ||||
|  | ||||
| @ -15,7 +15,7 @@ use tokio::sync::oneshot; | ||||
|  | ||||
| use proxmox::sys::linux::procfs; | ||||
| use proxmox::try_block; | ||||
| use proxmox::tools::fs::{create_path, replace_file, CreateOptions}; | ||||
| use proxmox::tools::fs::{create_path, open_file_locked, replace_file, CreateOptions}; | ||||
|  | ||||
| use super::UPID; | ||||
|  | ||||
| @ -247,7 +247,7 @@ fn update_active_workers(new_upid: Option<&UPID>) -> Result<Vec<TaskListInfo>, E | ||||
|  | ||||
|     let backup_user = crate::backup::backup_user()?; | ||||
|  | ||||
|     let lock = crate::tools::open_file_locked(PROXMOX_BACKUP_TASK_LOCK_FN, std::time::Duration::new(10, 0))?; | ||||
|     let lock = open_file_locked(PROXMOX_BACKUP_TASK_LOCK_FN, std::time::Duration::new(10, 0))?; | ||||
|     nix::unistd::chown(PROXMOX_BACKUP_TASK_LOCK_FN, Some(backup_user.uid), Some(backup_user.gid))?; | ||||
|  | ||||
|     let reader = match File::open(PROXMOX_BACKUP_ACTIVE_TASK_FN) { | ||||
|  | ||||
							
								
								
									
										73
									
								
								src/tools.rs
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								src/tools.rs
									
									
									
									
									
								
							| @ -4,9 +4,9 @@ | ||||
| use std::any::Any; | ||||
| use std::collections::HashMap; | ||||
| use std::hash::BuildHasher; | ||||
| use std::fs::{File, OpenOptions}; | ||||
| use std::fs::File; | ||||
| use std::io::{self, BufRead, ErrorKind, Read}; | ||||
| use std::os::unix::io::{AsRawFd, RawFd}; | ||||
| use std::os::unix::io::RawFd; | ||||
| use std::path::Path; | ||||
| use std::time::Duration; | ||||
| use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH}; | ||||
| @ -17,7 +17,6 @@ use openssl::hash::{hash, DigestBytes, MessageDigest}; | ||||
| use percent_encoding::AsciiSet; | ||||
|  | ||||
| use proxmox::tools::vec; | ||||
| use proxmox::sys::error::SysResult; | ||||
|  | ||||
| pub use proxmox::tools::fd::Fd; | ||||
|  | ||||
| @ -32,7 +31,6 @@ pub mod format; | ||||
| pub mod lru_cache; | ||||
| pub mod runtime; | ||||
| pub mod ticket; | ||||
| pub mod timer; | ||||
| pub mod statistics; | ||||
| pub mod systemd; | ||||
| pub mod nom; | ||||
| @ -90,73 +88,6 @@ pub fn map_struct_mut<T>(buffer: &mut [u8]) -> Result<&mut T, Error> { | ||||
|     Ok(unsafe { &mut *(buffer.as_ptr() as *mut T) }) | ||||
| } | ||||
|  | ||||
| /// Create a file lock using fntl. This function allows you to specify | ||||
| /// a timeout if you want to avoid infinite blocking. | ||||
| /// | ||||
| /// With timeout set to 0, non-blocking mode is used and the function | ||||
| /// will fail immediately if the lock can't be acquired. | ||||
| pub fn lock_file<F: AsRawFd>( | ||||
|     file: &mut F, | ||||
|     exclusive: bool, | ||||
|     timeout: Option<Duration>, | ||||
| ) -> Result<(), io::Error> { | ||||
|     let lockarg = if exclusive { | ||||
|         nix::fcntl::FlockArg::LockExclusive | ||||
|     } else { | ||||
|         nix::fcntl::FlockArg::LockShared | ||||
|     }; | ||||
|  | ||||
|     let timeout = match timeout { | ||||
|         None => { | ||||
|             nix::fcntl::flock(file.as_raw_fd(), lockarg).into_io_result()?; | ||||
|             return Ok(()); | ||||
|         } | ||||
|         Some(t) => t, | ||||
|     }; | ||||
|  | ||||
|     if timeout.as_nanos() == 0 { | ||||
|         let lockarg = if exclusive { | ||||
|             nix::fcntl::FlockArg::LockExclusiveNonblock | ||||
|         } else { | ||||
|             nix::fcntl::FlockArg::LockSharedNonblock | ||||
|         }; | ||||
|         nix::fcntl::flock(file.as_raw_fd(), lockarg).into_io_result()?; | ||||
|         return Ok(()); | ||||
|     } | ||||
|  | ||||
|     // unblock the timeout signal temporarily | ||||
|     let _sigblock_guard = timer::unblock_timeout_signal(); | ||||
|  | ||||
|     // setup a timeout timer | ||||
|     let mut timer = timer::Timer::create( | ||||
|         timer::Clock::Realtime, | ||||
|         timer::TimerEvent::ThisThreadSignal(timer::SIGTIMEOUT), | ||||
|     )?; | ||||
|  | ||||
|     timer.arm( | ||||
|         timer::TimerSpec::new() | ||||
|             .value(Some(timeout)) | ||||
|             .interval(Some(Duration::from_millis(10))), | ||||
|     )?; | ||||
|  | ||||
|     nix::fcntl::flock(file.as_raw_fd(), lockarg).into_io_result()?; | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| /// Open or create a lock file (append mode). Then try to | ||||
| /// acquire a lock using `lock_file()`. | ||||
| pub fn open_file_locked<P: AsRef<Path>>(path: P, timeout: Duration) -> Result<File, Error> { | ||||
|     let path = path.as_ref(); | ||||
|     let mut file = match OpenOptions::new().create(true).append(true).open(path) { | ||||
|         Ok(file) => file, | ||||
|         Err(err) => bail!("Unable to open lock {:?} - {}", path, err), | ||||
|     }; | ||||
|     match lock_file(&mut file, true, Some(timeout)) { | ||||
|         Ok(_) => Ok(file), | ||||
|         Err(err) => bail!("Unable to acquire lock {:?} - {}", path, err), | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Split a file into equal sized chunks. The last chunk may be | ||||
| /// smaller. Note: We cannot implement an `Iterator`, because iterators | ||||
| /// cannot return a borrowed buffer ref (we want zero-copy) | ||||
|  | ||||
| @ -1,370 +0,0 @@ | ||||
| //! POSIX per-process timer interface. | ||||
| //! | ||||
| //! This module provides a wrapper around POSIX timers (see `timer_create(2)`) and utilities to | ||||
| //! setup thread-targeted signaling and signal masks. | ||||
|  | ||||
| use std::mem::MaybeUninit; | ||||
| use std::time::Duration; | ||||
| use std::{io, mem}; | ||||
|  | ||||
| use libc::{c_int, clockid_t, pid_t}; | ||||
|  | ||||
| /// Timers can use various clocks. See `timer_create(2)`. | ||||
| pub enum Clock { | ||||
|     /// Use `CLOCK_REALTIME` for the timer. | ||||
|     Realtime, | ||||
|     /// Use `CLOCK_MONOTONIC` for the timer. | ||||
|     Monotonic, | ||||
| } | ||||
|  | ||||
| /// Strong thread-id type to prevent accidental conversion of pid_t. | ||||
| pub struct Tid(pid_t); | ||||
|  | ||||
| /// Convenience helper to get the current thread ID suitable to pass to a | ||||
| /// `TimerEvent::ThreadSignal` entry. | ||||
| pub fn gettid() -> Tid { | ||||
|     Tid(unsafe { libc::syscall(libc::SYS_gettid) } as pid_t) | ||||
| } | ||||
|  | ||||
| /// Strong signal type which is more advanced than nix::sys::signal::Signal as | ||||
| /// it doesn't prevent you from using signals that the nix crate is unaware | ||||
| /// of...! | ||||
| pub struct Signal(c_int); | ||||
|  | ||||
| impl Into<c_int> for Signal { | ||||
|     fn into(self) -> c_int { | ||||
|         self.0 | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<c_int> for Signal { | ||||
|     fn from(v: c_int) -> Signal { | ||||
|         Signal(v) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// When instantiating a Timer, it needs to have an event type associated with | ||||
| /// it to be fired whenever the timer expires. Most of the time this will be a | ||||
| /// `Signal`. Sometimes we need to be able to send signals to specific threads. | ||||
| pub enum TimerEvent { | ||||
|     /// This will act like passing `NULL` to `timer_create()`, which maps to | ||||
|     /// using the same as `Signal(SIGALRM)`. | ||||
|     None, | ||||
|  | ||||
|     /// When the timer expires, send a specific signal to the current process. | ||||
|     Signal(Signal), | ||||
|  | ||||
|     /// When the timer expires, send a specific signal to a specific thread. | ||||
|     ThreadSignal(Tid, Signal), | ||||
|  | ||||
|     /// Convenience value to send a signal to the current thread. This is | ||||
|     /// equivalent to using `ThreadSignal(gettid(), signal)`. | ||||
|     ThisThreadSignal(Signal), | ||||
| } | ||||
|  | ||||
| // timer_t is a pointer type, so we create a strongly typed internal handle | ||||
| // type for it | ||||
| #[repr(C)] | ||||
| struct InternalTimerT(u32); | ||||
| type TimerT = *mut InternalTimerT; | ||||
|  | ||||
| // These wrappers are defined in -lrt. | ||||
| #[link(name = "rt")] | ||||
| extern "C" { | ||||
|     fn timer_create(clockid: clockid_t, evp: *mut libc::sigevent, timer: *mut TimerT) -> c_int; | ||||
|     fn timer_delete(timer: TimerT) -> c_int; | ||||
|     fn timer_settime( | ||||
|         timerid: TimerT, | ||||
|         flags: c_int, | ||||
|         new_value: *const libc::itimerspec, | ||||
|         old_value: *mut libc::itimerspec, | ||||
|     ) -> c_int; | ||||
| } | ||||
|  | ||||
| /// Represents a POSIX per-process timer as created via `timer_create(2)`. | ||||
| pub struct Timer { | ||||
|     timer: TimerT, | ||||
| } | ||||
|  | ||||
| /// Timer specification used to arm a `Timer`. | ||||
| #[derive(Default)] | ||||
| pub struct TimerSpec { | ||||
|     /// The timeout to the next timer event. | ||||
|     pub value: Option<Duration>, | ||||
|  | ||||
|     /// When a timer expires, it may be automatically rearmed with another | ||||
|     /// timeout. This will keep happening until this is explicitly disabled | ||||
|     /// or the timer deleted. | ||||
|     pub interval: Option<Duration>, | ||||
| } | ||||
|  | ||||
| // Helpers to convert between libc::timespec and Option<Duration> | ||||
| fn opt_duration_to_timespec(v: Option<Duration>) -> libc::timespec { | ||||
|     match v { | ||||
|         None => libc::timespec { | ||||
|             tv_sec: 0, | ||||
|             tv_nsec: 0, | ||||
|         }, | ||||
|         Some(value) => libc::timespec { | ||||
|             tv_sec: value.as_secs() as i64, | ||||
|             tv_nsec: value.subsec_nanos() as i64, | ||||
|         }, | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn timespec_to_opt_duration(v: libc::timespec) -> Option<Duration> { | ||||
|     if v.tv_sec == 0 && v.tv_nsec == 0 { | ||||
|         None | ||||
|     } else { | ||||
|         Some(Duration::new(v.tv_sec as u64, v.tv_nsec as u32)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl TimerSpec { | ||||
|     // Helpers to convert between TimerSpec and libc::itimerspec | ||||
|     fn to_itimerspec(&self) -> libc::itimerspec { | ||||
|         libc::itimerspec { | ||||
|             it_value: opt_duration_to_timespec(self.value), | ||||
|             it_interval: opt_duration_to_timespec(self.interval), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn from_itimerspec(ts: libc::itimerspec) -> Self { | ||||
|         TimerSpec { | ||||
|             value: timespec_to_opt_duration(ts.it_value), | ||||
|             interval: timespec_to_opt_duration(ts.it_interval), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Create an empty timer specification representing a disabled timer. | ||||
|     pub fn new() -> Self { | ||||
|         TimerSpec { | ||||
|             value: None, | ||||
|             interval: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Change the specification to have a specific value. | ||||
|     pub fn value(self, value: Option<Duration>) -> Self { | ||||
|         TimerSpec { | ||||
|             value, | ||||
|             interval: self.interval, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Change the specification to have a specific interval. | ||||
|     pub fn interval(self, interval: Option<Duration>) -> Self { | ||||
|         TimerSpec { | ||||
|             value: self.value, | ||||
|             interval, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Timer { | ||||
|     /// Create a Timer object governing a POSIX timer. | ||||
|     pub fn create(clock: Clock, event: TimerEvent) -> io::Result<Timer> { | ||||
|         // Map from our clock type to the libc id | ||||
|         let clkid = match clock { | ||||
|             Clock::Realtime => libc::CLOCK_REALTIME, | ||||
|             Clock::Monotonic => libc::CLOCK_MONOTONIC, | ||||
|         } as clockid_t; | ||||
|  | ||||
|         // Map the TimerEvent to libc::sigevent | ||||
|         let mut ev: libc::sigevent = unsafe { mem::zeroed() }; | ||||
|         match event { | ||||
|             TimerEvent::None => ev.sigev_notify = libc::SIGEV_NONE, | ||||
|             TimerEvent::Signal(signo) => { | ||||
|                 ev.sigev_signo = signo.0; | ||||
|                 ev.sigev_notify = libc::SIGEV_SIGNAL; | ||||
|             } | ||||
|             TimerEvent::ThreadSignal(tid, signo) => { | ||||
|                 ev.sigev_signo = signo.0; | ||||
|                 ev.sigev_notify = libc::SIGEV_THREAD_ID; | ||||
|                 ev.sigev_notify_thread_id = tid.0; | ||||
|             } | ||||
|             TimerEvent::ThisThreadSignal(signo) => { | ||||
|                 ev.sigev_signo = signo.0; | ||||
|                 ev.sigev_notify = libc::SIGEV_THREAD_ID; | ||||
|                 ev.sigev_notify_thread_id = gettid().0; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Create the timer | ||||
|         let mut timer: TimerT = unsafe { mem::zeroed() }; | ||||
|         let rc = unsafe { timer_create(clkid, &mut ev, &mut timer) }; | ||||
|         if rc != 0 { | ||||
|             Err(io::Error::last_os_error()) | ||||
|         } else { | ||||
|             Ok(Timer { timer }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Arm a timer. This returns the previous timer specification. | ||||
|     pub fn arm(&mut self, spec: TimerSpec) -> io::Result<TimerSpec> { | ||||
|         let newspec = spec.to_itimerspec(); | ||||
|         let mut oldspec = MaybeUninit::<libc::itimerspec>::uninit(); | ||||
|  | ||||
|         let rc = unsafe { timer_settime(self.timer, 0, &newspec, &mut *oldspec.as_mut_ptr()) }; | ||||
|         if rc != 0 { | ||||
|             return Err(io::Error::last_os_error()); | ||||
|         } | ||||
|  | ||||
|         Ok(TimerSpec::from_itimerspec(unsafe { oldspec.assume_init() })) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Drop for Timer { | ||||
|     fn drop(&mut self) { | ||||
|         unsafe { | ||||
|             timer_delete(self.timer); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// This is the signal number we use in our timeout implementations. We expect | ||||
| /// the signal handler for this signal to never be replaced by some other | ||||
| /// library. If this does happen, we need to find another signal. There should | ||||
| /// be plenty. | ||||
| /// Currently this is SIGRTMIN+4, the 5th real-time signal. glibc reserves the | ||||
| /// first two for pthread internals. | ||||
| pub const SIGTIMEOUT: Signal = Signal(32 + 4); | ||||
|  | ||||
| // Our timeout handler does exactly nothing. We only need it to interrupt | ||||
| // system calls. | ||||
| extern "C" fn sig_timeout_handler(_: c_int) {} | ||||
|  | ||||
| // See setup_timeout_handler(). | ||||
| fn do_setup_timeout_handler() -> io::Result<()> { | ||||
|     // Unfortunately nix::sys::signal::Signal cannot represent real time | ||||
|     // signals, so we need to use libc instead... | ||||
|     // | ||||
|     // This WOULD be a nicer impl though: | ||||
|     //nix::sys::signal::sigaction( | ||||
|     //    SIGTIMEOUT, | ||||
|     //    nix::sys::signal::SigAction::new( | ||||
|     //        nix::sys::signal::SigHandler::Handler(sig_timeout_handler), | ||||
|     //        nix::sys::signal::SaFlags::empty(), | ||||
|     //        nix::sys::signal::SigSet::all())) | ||||
|     //    .map(|_|()) | ||||
|  | ||||
|     unsafe { | ||||
|         let mut sa_mask = MaybeUninit::<libc::sigset_t>::uninit(); | ||||
|         if libc::sigemptyset(&mut *sa_mask.as_mut_ptr()) != 0 | ||||
|             || libc::sigaddset(&mut *sa_mask.as_mut_ptr(), SIGTIMEOUT.0) != 0 | ||||
|         { | ||||
|             return Err(io::Error::last_os_error()); | ||||
|         } | ||||
|  | ||||
|         let sa = libc::sigaction { | ||||
|             sa_sigaction: | ||||
|                 // libc::sigaction uses `usize` for the function pointer... | ||||
|                 sig_timeout_handler as *const extern "C" fn(i32) as usize, | ||||
|             sa_mask: sa_mask.assume_init(), | ||||
|             sa_flags: 0, | ||||
|             sa_restorer: None, | ||||
|         }; | ||||
|         if libc::sigaction(SIGTIMEOUT.0, &sa, std::ptr::null_mut()) != 0 { | ||||
|             return Err(io::Error::last_os_error()); | ||||
|         } | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| // The first time we unblock SIGTIMEOUT should cause approprate initialization: | ||||
| static SETUP_TIMEOUT_HANDLER: std::sync::Once = std::sync::Once::new(); | ||||
|  | ||||
| /// Setup our timeout-signal workflow. This establishes the signal handler for | ||||
| /// our `SIGTIMEOUT` and should be called once during initialization. | ||||
| #[inline] | ||||
| pub fn setup_timeout_handler() { | ||||
|     SETUP_TIMEOUT_HANDLER.call_once(|| { | ||||
|         // We unwrap here. | ||||
|         // If setting up this handler fails you have other problems already, | ||||
|         // plus, if setting up fails you can't *use* it either, so everything | ||||
|         // goes to die. | ||||
|         do_setup_timeout_handler().unwrap(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| /// This guards the state of the timeout signal: We want it blocked usually. | ||||
| pub struct TimeoutBlockGuard(bool); | ||||
| impl Drop for TimeoutBlockGuard { | ||||
|     fn drop(&mut self) { | ||||
|         if self.0 { | ||||
|             block_timeout_signal(); | ||||
|         } else { | ||||
|             unblock_timeout_signal().forget(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl TimeoutBlockGuard { | ||||
|     /// Convenience helper to "forget" to restore the signal block mask. | ||||
|     #[inline(always)] | ||||
|     pub fn forget(self) { | ||||
|         std::mem::forget(self); | ||||
|     } | ||||
|  | ||||
|     /// Convenience helper to trigger the guard behavior immediately. | ||||
|     #[inline(always)] | ||||
|     pub fn trigger(self) { | ||||
|         std::mem::drop(self); // be explicit here... | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Unblock the timeout signal for the current thread. By default we block the | ||||
| /// signal this behavior should be restored when done using timeouts, therefor this | ||||
| /// returns a guard: | ||||
| #[inline(always)] | ||||
| pub fn unblock_timeout_signal() -> TimeoutBlockGuard { | ||||
|     // This calls std::sync::Once: | ||||
|     setup_timeout_handler(); | ||||
|     //let mut set = nix::sys::signal::SigSet::empty(); | ||||
|     //set.add(SIGTIMEOUT.0); | ||||
|     //set.thread_unblock()?; | ||||
|     //Ok(TimeoutBlockGuard{}) | ||||
|     // Again, nix crate and its signal limitations... | ||||
|  | ||||
|     // NOTE: | ||||
|     //   sigsetops(3) and pthread_sigmask(3) can only fail if invalid memory is | ||||
|     //   passed to the kernel, or signal numbers are "invalid", since we know | ||||
|     //   neither is the case we will panic on error... | ||||
|     let was_blocked = unsafe { | ||||
|         let mut mask = MaybeUninit::<libc::sigset_t>::uninit(); | ||||
|         let mut oldset = MaybeUninit::<libc::sigset_t>::uninit(); | ||||
|         if libc::sigemptyset(&mut *mask.as_mut_ptr()) != 0 | ||||
|             || libc::sigaddset(&mut *mask.as_mut_ptr(), SIGTIMEOUT.0) != 0 | ||||
|             || libc::pthread_sigmask( | ||||
|                 libc::SIG_UNBLOCK, | ||||
|                 &mask.assume_init(), | ||||
|                 &mut *oldset.as_mut_ptr(), | ||||
|             ) != 0 | ||||
|         { | ||||
|             panic!("Impossibly failed to unblock SIGTIMEOUT"); | ||||
|             //return Err(io::Error::last_os_error()); | ||||
|         } | ||||
|  | ||||
|         libc::sigismember(&oldset.assume_init(), SIGTIMEOUT.0) == 1 | ||||
|     }; | ||||
|     TimeoutBlockGuard(was_blocked) | ||||
| } | ||||
|  | ||||
| /// Block the timeout signal for the current thread. This is the default. | ||||
| #[inline(always)] | ||||
| pub fn block_timeout_signal() { | ||||
|     //let mut set = nix::sys::signal::SigSet::empty(); | ||||
|     //set.add(SIGTIMEOUT); | ||||
|     //set.thread_block() | ||||
|     unsafe { | ||||
|         let mut mask = MaybeUninit::<libc::sigset_t>::uninit(); | ||||
|         if libc::sigemptyset(&mut *mask.as_mut_ptr()) != 0 | ||||
|             || libc::sigaddset(&mut *mask.as_mut_ptr(), SIGTIMEOUT.0) != 0 | ||||
|             || libc::pthread_sigmask(libc::SIG_BLOCK, &mask.assume_init(), std::ptr::null_mut()) | ||||
|                 != 0 | ||||
|         { | ||||
|             panic!("Impossibly failed to block SIGTIMEOUT"); | ||||
|             //return Err(io::Error::last_os_error()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user