+
+pub fn join(data: &Vec<String>, sep: char) -> String {
+
+ let mut list = String::new();
+
+ for item in data {
+ if list.len() != 0 { list.push(sep); }
+ list.push_str(item);
+ }
+
+ list
+}
+
+/// normalize uri path
+///
+/// Do not allow ".", "..", or hidden files ".XXXX"
+/// Also remove empty path components
+pub fn normalize_uri_path(path: &str) -> Result<(String, Vec<&str>), Error> {
+
+ let items = path.split('/');
+
+ let mut path = String::new();
+ let mut components = vec![];
+
+ for name in items {
+ if name.is_empty() { continue; }
+ if name.starts_with(".") {
+ bail!("Path contains illegal components.");
+ }
+ path.push('/');
+ path.push_str(name);
+ components.push(name);
+ }
+
+ Ok((path, components))
+}
+
+pub fn fd_change_cloexec(fd: RawFd, on: bool) -> Result<(), Error> {
+ use nix::fcntl::{fcntl, F_GETFD, F_SETFD, FdFlag};
+ let mut flags = FdFlag::from_bits(fcntl(fd, F_GETFD)?)
+ .ok_or_else(|| format_err!("unhandled file flags"))?; // nix crate is stupid this way...
+ flags.set(FdFlag::FD_CLOEXEC, on);
+ fcntl(fd, F_SETFD(flags))?;
+ Ok(())
+}
+
+
+static mut SHUTDOWN_REQUESTED: bool = false;
+
+pub fn request_shutdown() {
+ unsafe { SHUTDOWN_REQUESTED = true; }
+ crate::server::server_shutdown();
+}
+
+#[inline(always)]
+pub fn shutdown_requested() -> bool {
+ unsafe { SHUTDOWN_REQUESTED }
+}
+
+pub fn fail_on_shutdown() -> Result<(), Error> {
+ if shutdown_requested() {
+ bail!("Server shutdown requested - aborting task");
+ }
+ Ok(())
+}
+
+/// Guard a raw file descriptor with a drop handler. This is mostly useful when access to an owned
+/// `RawFd` is required without the corresponding handler object (such as when only the file
+/// descriptor number is required in a closure which may be dropped instead of being executed).
+pub struct Fd(pub RawFd);
+
+impl Drop for Fd {
+ fn drop(&mut self) {
+ if self.0 != -1 {
+ unsafe {
+ libc::close(self.0);
+ }
+ }
+ }
+}
+
+impl AsRawFd for Fd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0
+ }
+}
+
+impl IntoRawFd for Fd {
+ fn into_raw_fd(mut self) -> RawFd {
+ let fd = self.0;
+ self.0 = -1;
+ fd
+ }
+}
+
+impl FromRawFd for Fd {
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ Self(fd)
+ }
+}
+
+// wrap nix::unistd::pipe2 + O_CLOEXEC into something returning guarded file descriptors
+pub fn pipe() -> Result<(Fd, Fd), Error> {
+ let (pin, pout) = nix::unistd::pipe2(nix::fcntl::OFlag::O_CLOEXEC)?;
+ Ok((Fd(pin), Fd(pout)))
+}
+
+/// An easy way to convert types to Any
+///
+/// Mostly useful to downcast trait objects (see RpcEnvironment).
+pub trait AsAny {
+ fn as_any(&self) -> &dyn Any;
+}
+
+impl<T: Any> AsAny for T {
+ fn as_any(&self) -> &dyn Any { self }
+}
+
+
+// /usr/include/linux/fs.h: #define BLKGETSIZE64 _IOR(0x12,114,size_t)
+// return device size in bytes (u64 *arg)
+nix::ioctl_read!(blkgetsize64, 0x12, 114, u64);
+
+/// Return file or block device size
+pub fn image_size(path: &Path) -> Result<u64, Error> {
+
+ use std::os::unix::fs::FileTypeExt;
+
+ let file = std::fs::File::open(path)?;
+ let metadata = file.metadata()?;
+ let file_type = metadata.file_type();
+
+ if file_type.is_block_device() {
+ let mut size : u64 = 0;
+ let res = unsafe { blkgetsize64(file.as_raw_fd(), &mut size) };
+
+ if let Err(err) = res {
+ bail!("blkgetsize64 failed for {:?} - {}", path, err);
+ }
+ Ok(size)
+ } else if file_type.is_file() {
+ Ok(metadata.len())
+ } else {
+ bail!("image size failed - got unexpected file type {:?}", file_type);
+ }
+}