Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
1
third-party/vendor/kqueue/.cargo-checksum.json
vendored
Normal file
1
third-party/vendor/kqueue/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{"CHANGELOG.md":"707e7cd3d6ddbbf72f20d25570a40d3b572b60ea226eb5009aab878a14b7940d","Cargo.lock":"c9a9665861e7e4c921110a0397da14c3ca04acea41eeafc50bc91e545cf369a0","Cargo.toml":"6971e564a391ed35ebe414b58024b0c1676f8fe6ff302c7d554193bbdb38523c","LICENSE":"9dc7535841863372cb757676c5acd69c010572a85df23d3467d6bb3cc2829e84","README.md":"4a9ef79cde4356b5f3db80acca8c6961b8c04d4976e799858ee6a69bf61a02be","TODOs.org":"9ca51ce2490bfaf6fb25886fd6567514ff42575f463ef66b12b41e2e03170b45","Vagrantfile":"a2711fa161c44bf5c48b05f1010d9c176c90a2dfdb6768a7c9c7299c8be37791","examples/file.rs":"5d1211f98374dc2de3f489d5f856ea74af31897b992070f83b8361089158bcaa","examples/pid.rs":"032292056542aed93eb33c2dbaaefb5f5be4f8d3d863393d5a5dfdfef65299ae","src/lib.rs":"fa468350b29a8fbacd9d0403343a9fcbebcfbe79a3f771f87dce6d727baee53e","src/os/mod.rs":"e868718e39324516000d723951cdb21be3bcbcd8b7e4118cfc699ba6419181f6","src/os/vnode.rs":"845082c6ca33c82348241ec4cfb59189daabff8fa2d5ac062c259e71df59aa8d","src/time.rs":"4dc0a179cc0c6f252cc7b603907b4cc3ec1a1d028c39573eeca753a58fb11ce2"},"package":"7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"}
|
||||
71
third-party/vendor/kqueue/CHANGELOG.md
vendored
Normal file
71
third-party/vendor/kqueue/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Changelog
|
||||
|
||||
## 1.0.8
|
||||
|
||||
Adds:
|
||||
|
||||
* ios support
|
||||
|
||||
Fixes:
|
||||
|
||||
* panic on overflow on more than max 32bit files
|
||||
|
||||
## 1.0.7
|
||||
|
||||
Fixes:
|
||||
|
||||
* Fixes broken 32bit builds by matching `timespec` defs to libc
|
||||
|
||||
## 1.0.6
|
||||
|
||||
Fixes:
|
||||
|
||||
* marks `Vnode` enum `non_exhaustive` to fix backwards compatibility in 1.x
|
||||
|
||||
## 1.0.5
|
||||
|
||||
Adds:
|
||||
|
||||
* docs.rs support
|
||||
* added new enum variants specific to FreeBSD (broke backwards compatibility)
|
||||
|
||||
Fixes:
|
||||
|
||||
* Fixes broken 32bit builds
|
||||
|
||||
## 1.0.4
|
||||
|
||||
Fixes:
|
||||
|
||||
* Fixes broken NetBSD build
|
||||
|
||||
## 1.0.3
|
||||
|
||||
Adds:
|
||||
|
||||
* #6: Adds a new `Watcher.poll_forever()` method which blocks on new events. This works
|
||||
around buggy behavior in the original `Watcher.poll()` method.
|
||||
* !3: Adds an implementation for `std::os::unix::io::AsRawFd` for `Watcher` for
|
||||
nested kqueues.
|
||||
|
||||
## 1.0.2
|
||||
|
||||
* Fixed #4: Fix bug where wrong data types were used on i386 FreeBSD
|
||||
|
||||
## 1.0.1
|
||||
|
||||
* Merged !1 as a fix for #3. We properly fill in the `ext` field for `kqueue`
|
||||
extensions on FreeBSD.
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Bumped `bitflags` in `rust-kqueue-sys`: Now all bitflag constants must be qualified:
|
||||
|
||||
`EV_DELETE` -> `EventFlag::EV_DELETE`
|
||||
`NOTE_WRITE` > `FilterFlag::NOT_WRITE`
|
||||
|
||||
### Other changes
|
||||
|
||||
* 2018 edition and clippy changes
|
||||
112
third-party/vendor/kqueue/Cargo.lock
generated
vendored
Normal file
112
third-party/vendor/kqueue/Cargo.lock
generated
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.8"
|
||||
dependencies = [
|
||||
"kqueue-sys",
|
||||
"libc",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue-sys"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.124"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
52
third-party/vendor/kqueue/Cargo.toml
vendored
Normal file
52
third-party/vendor/kqueue/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "kqueue"
|
||||
version = "1.0.8"
|
||||
authors = ["William Orr <will@worrbase.com>"]
|
||||
description = "kqueue interface for BSDs"
|
||||
documentation = "https://docs.worrbase.com/rust/kqueue/"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"kqueue",
|
||||
"kevent",
|
||||
"bsd",
|
||||
]
|
||||
categories = [
|
||||
"os::unix-apis",
|
||||
"filesystem",
|
||||
]
|
||||
license = "MIT"
|
||||
repository = "https://gitlab.com/rust-kqueue/rust-kqueue"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = [
|
||||
"x86_64-unknown-netbsd",
|
||||
"x86_64-unknown-freebsd",
|
||||
"i686-unknown-freebsd",
|
||||
"x86_64-unknown-dragonfly",
|
||||
"x86_64-unknown-openbsd",
|
||||
"aarch64-apple-darwin",
|
||||
"x86_64-apple-darwin",
|
||||
"aarch64-apple-ios",
|
||||
"x86_64-apple-ios",
|
||||
]
|
||||
|
||||
[dependencies.kqueue-sys]
|
||||
version = "^1.0.4"
|
||||
|
||||
[dependencies.libc]
|
||||
version = "^0.2.17"
|
||||
|
||||
[dev-dependencies.tempfile]
|
||||
version = "3.1.0"
|
||||
19
third-party/vendor/kqueue/LICENSE
vendored
Normal file
19
third-party/vendor/kqueue/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2016 William Orr <will@worrbase.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
22
third-party/vendor/kqueue/README.md
vendored
Normal file
22
third-party/vendor/kqueue/README.md
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# kqueue
|
||||
|
||||
[](https://gitlab.com/rust-kqueue/rust-kqueue/-/commits/main)
|
||||
[](https://travis-ci.com/gitlab/rust-kqueue/rust-kqueue)
|
||||
|
||||
`kqueue(2)` library for rust
|
||||
|
||||
`kqueue(2)` is a powerful API in BSDs that allows you to get events based on
|
||||
fs events, buffer readiness, timers, process events and signals.
|
||||
|
||||
This is useful for code that's either BSD-specific, or as a component in an
|
||||
abstraction over similar APIs in cross-platform code.
|
||||
|
||||
## Docs
|
||||
|
||||
Docs are mirrored here: https://docs.worrbase.com/rust/kqueue/ .
|
||||
|
||||
## Examples
|
||||
|
||||
There are some basic usage examples in `examples/`.
|
||||
18
third-party/vendor/kqueue/TODOs.org
vendored
Normal file
18
third-party/vendor/kqueue/TODOs.org
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
* TODOs
|
||||
|
||||
** Breaking
|
||||
|
||||
*** TODO Why does ~add_pid~ take a ~filter~
|
||||
*** TODO Don't expose all of the constants from ~kqueue-sys~
|
||||
*** TODO Express which filters are appropriate for which functions in the type system
|
||||
|
||||
** Non-breaking
|
||||
|
||||
*** TODO Why does remove_* call ~kevent~ and not add_*?
|
||||
*** TODO Try and abstract away ~NOTE_TRACK~ and ~NOTE_CHILD~ if possible and non-breaking
|
||||
*** TODO ~get_event~ should probably check ~started~ status
|
||||
*** TODO Make sure that filters and flags are sensible
|
||||
*** TODO Why does ~as_size~ exist?
|
||||
*** TODO implement ~timer~ and ~signal~
|
||||
*** TODO ummm add ~stop~?
|
||||
*** TODO iterator with a timeout?
|
||||
74
third-party/vendor/kqueue/Vagrantfile
vendored
Normal file
74
third-party/vendor/kqueue/Vagrantfile
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# All Vagrant configuration is done below. The "2" in Vagrant.configure
|
||||
# configures the configuration version (we support older styles for
|
||||
# backwards compatibility). Please don't change it unless you know what
|
||||
# you're doing.
|
||||
Vagrant.configure(2) do |config|
|
||||
# The most common configuration options are documented and commented below.
|
||||
# For a complete reference, please see the online documentation at
|
||||
# https://docs.vagrantup.com.
|
||||
|
||||
# Every Vagrant development environment requires a box. You can search for
|
||||
# boxes at https://atlas.hashicorp.com/search.
|
||||
config.vm.box = "bento/freebsd-10.3"
|
||||
|
||||
# Disable automatic box update checking. If you disable this, then
|
||||
# boxes will only be checked for updates when the user runs
|
||||
# `vagrant box outdated`. This is not recommended.
|
||||
# config.vm.box_check_update = false
|
||||
config.vm.network "private_network", ip: "172.16.1.2"
|
||||
|
||||
# Create a forwarded port mapping which allows access to a specific port
|
||||
# within the machine from a port on the host machine. In the example below,
|
||||
# accessing "localhost:8080" will access port 80 on the guest machine.
|
||||
# config.vm.network "forwarded_port", guest: 80, host: 8080
|
||||
|
||||
# Create a private network, which allows host-only access to the machine
|
||||
# using a specific IP.
|
||||
# config.vm.network "private_network", ip: "192.168.33.10"
|
||||
|
||||
# Create a public network, which generally matched to bridged network.
|
||||
# Bridged networks make the machine appear as another physical device on
|
||||
# your network.
|
||||
# config.vm.network "public_network"
|
||||
|
||||
# Share an additional folder to the guest VM. The first argument is
|
||||
# the path on the host to the actual folder. The second argument is
|
||||
# the path on the guest to mount the folder. And the optional third
|
||||
# argument is a set of non-required options.
|
||||
config.vm.synced_folder ".", "/vagrant_data", :nfs => true, :nfs_udp => false, :nfs_options => ['vers=3']
|
||||
|
||||
# Provider-specific configuration so you can fine-tune various
|
||||
# backing providers for Vagrant. These expose provider-specific options.
|
||||
# Example for VirtualBox:
|
||||
#
|
||||
# config.vm.provider "virtualbox" do |vb|
|
||||
# # Display the VirtualBox GUI when booting the machine
|
||||
# vb.gui = true
|
||||
#
|
||||
# # Customize the amount of memory on the VM:
|
||||
# vb.memory = "1024"
|
||||
# end
|
||||
#
|
||||
# View the documentation for the provider you are using for more
|
||||
# information on available options.
|
||||
|
||||
# Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
|
||||
# such as FTP and Heroku are also available. See the documentation at
|
||||
# https://docs.vagrantup.com/v2/push/atlas.html for more information.
|
||||
# config.push.define "atlas" do |push|
|
||||
# push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
|
||||
# end
|
||||
|
||||
# Enable provisioning with a shell script. Additional provisioners such as
|
||||
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
|
||||
# documentation for more information about their specific syntax and use.
|
||||
config.vm.provision "shell", inline: <<-SHELL
|
||||
sudo pkg update
|
||||
sudo pkg install -y rust cargo
|
||||
sudo printf '\nrpc_lockd_enable=YES' >> /etc/rc.conf
|
||||
sudo service lockd start
|
||||
SHELL
|
||||
end
|
||||
33
third-party/vendor/kqueue/examples/file.rs
vendored
Normal file
33
third-party/vendor/kqueue/examples/file.rs
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use std::env;
|
||||
use std::io::Result;
|
||||
|
||||
fn watch_file(filename: &str) -> Result<()> {
|
||||
let mut watcher = kqueue::Watcher::new()?;
|
||||
|
||||
watcher.add_filename(
|
||||
filename,
|
||||
kqueue::EventFilter::EVFILT_VNODE,
|
||||
kqueue::FilterFlag::NOTE_DELETE
|
||||
| kqueue::FilterFlag::NOTE_WRITE
|
||||
| kqueue::FilterFlag::NOTE_RENAME,
|
||||
)?;
|
||||
|
||||
watcher.watch()?;
|
||||
|
||||
println!("Watching for events, press Ctrl+C to stop...");
|
||||
for ev in watcher.iter() {
|
||||
println!("{:?}", ev);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Some(filename) = env::args().nth(1) {
|
||||
if let Err(err) = watch_file(&filename) {
|
||||
println!("{:?}", err);
|
||||
}
|
||||
} else {
|
||||
println!("Usage: cargo run --example file <filename>");
|
||||
}
|
||||
}
|
||||
33
third-party/vendor/kqueue/examples/pid.rs
vendored
Normal file
33
third-party/vendor/kqueue/examples/pid.rs
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use std::env;
|
||||
use std::io::Result;
|
||||
|
||||
fn watch_pid(pid: libc::pid_t) -> Result<()> {
|
||||
let mut watcher = kqueue::Watcher::new()?;
|
||||
|
||||
watcher.add_pid(
|
||||
pid,
|
||||
kqueue::EventFilter::EVFILT_PROC,
|
||||
kqueue::FilterFlag::NOTE_EXIT,
|
||||
)?;
|
||||
|
||||
watcher.watch()?;
|
||||
|
||||
println!("Watching for events, press Ctrl+C to stop...");
|
||||
for ev in watcher.iter() {
|
||||
println!("{:?}", ev);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Some(pid) = env::args().nth(1) {
|
||||
if let Ok(npid) = pid.parse::<libc::pid_t>() {
|
||||
if let Err(err) = watch_pid(npid) {
|
||||
println!("{:?}", err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Usage: cargo run --example pid <pid>");
|
||||
}
|
||||
}
|
||||
913
third-party/vendor/kqueue/src/lib.rs
vendored
Normal file
913
third-party/vendor/kqueue/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,913 @@
|
|||
use kqueue_sys::{kevent, kqueue};
|
||||
use libc::{pid_t, uintptr_t};
|
||||
use std::convert::{AsRef, Into, TryFrom, TryInto};
|
||||
use std::default::Default;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Error, Result};
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
use std::time::Duration;
|
||||
|
||||
pub use kqueue_sys::constants::*;
|
||||
|
||||
mod os;
|
||||
use crate::os::vnode;
|
||||
|
||||
mod time;
|
||||
use crate::time::duration_to_timespec;
|
||||
|
||||
/// The watched object that fired the event
|
||||
#[derive(Debug, Eq, Clone)]
|
||||
pub enum Ident {
|
||||
Filename(RawFd, String),
|
||||
Fd(RawFd),
|
||||
Pid(pid_t),
|
||||
Signal(i32),
|
||||
Timer(i32),
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Watched {
|
||||
filter: EventFilter,
|
||||
flags: FilterFlag,
|
||||
ident: Ident,
|
||||
}
|
||||
|
||||
/// Watches one or more resources
|
||||
///
|
||||
/// These can be created with `Watcher::new()`. You can create as many
|
||||
/// `Watcher`s as you want, and they can watch as many objects as you wish.
|
||||
/// The objects do not need to be the same type.
|
||||
///
|
||||
/// Each `Watcher` is backed by a `kqueue(2)` queue. These resources are freed
|
||||
/// on the `Watcher`s destruction. If the destructor cannot run for whatever
|
||||
/// reason, the underlying kernel object will be leaked.
|
||||
#[derive(Debug)]
|
||||
pub struct Watcher {
|
||||
watched: Vec<Watched>,
|
||||
queue: RawFd,
|
||||
started: bool,
|
||||
opts: KqueueOpts,
|
||||
}
|
||||
|
||||
/// Vnode events
|
||||
///
|
||||
/// These are OS-specific, and may not all be supported on your platform. Check
|
||||
/// `kqueue(2)` for more information.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum Vnode {
|
||||
/// The file was deleted
|
||||
Delete,
|
||||
|
||||
/// The file received a write
|
||||
Write,
|
||||
|
||||
/// The file was extended with `truncate(2)`
|
||||
Extend,
|
||||
|
||||
/// The file was shrunk with `truncate(2)`
|
||||
Truncate,
|
||||
|
||||
/// The attributes of the file were changed
|
||||
Attrib,
|
||||
|
||||
/// The link count of the file was changed
|
||||
Link,
|
||||
|
||||
/// The file was renamed
|
||||
Rename,
|
||||
|
||||
/// Access to the file was revoked with `revoke(2)` or the fs was unmounted
|
||||
Revoke,
|
||||
|
||||
/// File was opened by a process (FreeBSD-specific)
|
||||
Open,
|
||||
|
||||
/// File was closed and the descriptor had write access (FreeBSD-specific)
|
||||
CloseWrite,
|
||||
|
||||
/// File was closed and the descriptor had read access (FreeBSD-specific)
|
||||
Close,
|
||||
}
|
||||
|
||||
/// Process events
|
||||
///
|
||||
/// These are OS-specific, and may not all be supported on your platform. Check
|
||||
/// `kqueue(2)` for more information.
|
||||
#[derive(Debug)]
|
||||
pub enum Proc {
|
||||
/// The watched process exited with the returned exit code
|
||||
Exit(usize),
|
||||
|
||||
/// The process called `fork(2)`
|
||||
Fork,
|
||||
|
||||
/// The process called `exec(2)`
|
||||
Exec,
|
||||
|
||||
/// The process called `fork(2)`, and returned the child pid.
|
||||
Track(libc::pid_t),
|
||||
|
||||
/// The process called `fork(2)`, but we were not able to track the child
|
||||
Trackerr,
|
||||
|
||||
/// The process called `fork(2)`, and returned the child pid.
|
||||
// TODO: this is FreeBSD-specific. We can probably convert this to `Track`.
|
||||
Child(libc::pid_t),
|
||||
}
|
||||
|
||||
/// Event-specific data returned with the event.
|
||||
///
|
||||
/// Like much of this library, this is OS-specific. Check `kqueue(2)` for more
|
||||
/// details on your target OS.
|
||||
#[derive(Debug)]
|
||||
pub enum EventData {
|
||||
/// Data relating to `Vnode` events
|
||||
Vnode(Vnode),
|
||||
|
||||
/// Data relating to process events
|
||||
Proc(Proc),
|
||||
|
||||
/// The returned number of bytes are ready for reading from the watched
|
||||
/// descriptor
|
||||
ReadReady(usize),
|
||||
|
||||
/// The file is ready for writing. On some files (like sockets, pipes, etc),
|
||||
/// the number of bytes in the write buffer will be returned.
|
||||
WriteReady(usize),
|
||||
|
||||
/// One of the watched signals fired. The number of times this signal was received
|
||||
/// is returned.
|
||||
Signal(usize),
|
||||
|
||||
/// One of the watched timers fired. The number of times this timer fired
|
||||
/// is returned.
|
||||
Timer(usize),
|
||||
|
||||
/// Some error was received
|
||||
Error(Error),
|
||||
}
|
||||
|
||||
/// An event from a `Watcher` object.
|
||||
///
|
||||
/// An event contains both the a signifier of the watched object that triggered
|
||||
/// the event, as well as any event-specific. See the `EventData` enum for info
|
||||
/// on what event-specific data is returned for each event.
|
||||
#[derive(Debug)]
|
||||
pub struct Event {
|
||||
/// The watched resource that triggered the event
|
||||
pub ident: Ident,
|
||||
|
||||
/// Any event-specific data returned with the event.
|
||||
pub data: EventData,
|
||||
}
|
||||
|
||||
pub struct EventIter<'a> {
|
||||
watcher: &'a Watcher,
|
||||
}
|
||||
|
||||
/// Options for a `Watcher`
|
||||
#[derive(Debug)]
|
||||
pub struct KqueueOpts {
|
||||
/// Clear state on watched objects
|
||||
clear: bool,
|
||||
}
|
||||
|
||||
impl Default for KqueueOpts {
|
||||
/// Returns the default options for a `Watcher`
|
||||
///
|
||||
/// `clear` is set to `true`
|
||||
fn default() -> KqueueOpts {
|
||||
KqueueOpts { clear: true }
|
||||
}
|
||||
}
|
||||
|
||||
// We don't have enough information to turn a `usize` into
|
||||
// an `Ident`, so we only implement `Into<usize>` here.
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<usize> for Ident {
|
||||
fn into(self) -> usize {
|
||||
match self {
|
||||
Ident::Filename(fd, _) => fd as usize,
|
||||
Ident::Fd(fd) => fd as usize,
|
||||
Ident::Pid(pid) => pid as usize,
|
||||
Ident::Signal(sig) => sig as usize,
|
||||
Ident::Timer(timer) => timer as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Ident> for Ident {
|
||||
fn eq(&self, other: &Ident) -> bool {
|
||||
match *self {
|
||||
Ident::Filename(_, ref name) => {
|
||||
if let Ident::Filename(_, ref othername) = *other {
|
||||
name == othername
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => self.as_usize() == other.as_usize(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ident {
|
||||
fn as_usize(&self) -> usize {
|
||||
match *self {
|
||||
Ident::Filename(fd, _) => fd as usize,
|
||||
Ident::Fd(fd) => fd as usize,
|
||||
Ident::Pid(pid) => pid as usize,
|
||||
Ident::Signal(sig) => sig as usize,
|
||||
Ident::Timer(timer) => timer as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Watcher {
|
||||
/// Creates a new `Watcher`
|
||||
///
|
||||
/// Creates a brand new `Watcher` with `KqueueOpts::default()`. Will return
|
||||
/// an `io::Error` if creation fails.
|
||||
pub fn new() -> Result<Watcher> {
|
||||
let queue = unsafe { kqueue() };
|
||||
|
||||
if queue == -1 {
|
||||
Err(Error::last_os_error())
|
||||
} else {
|
||||
Ok(Watcher {
|
||||
watched: Vec::new(),
|
||||
queue,
|
||||
started: false,
|
||||
opts: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables the `clear` flag on a `Watcher`. New events will no longer
|
||||
/// be added with the `EV_CLEAR` flag on `watch`.
|
||||
pub fn disable_clears(&mut self) -> &mut Self {
|
||||
self.opts.clear = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a `pid` to the `Watcher` to be watched
|
||||
pub fn add_pid(
|
||||
&mut self,
|
||||
pid: libc::pid_t,
|
||||
filter: EventFilter,
|
||||
flags: FilterFlag,
|
||||
) -> Result<()> {
|
||||
let watch = Watched {
|
||||
filter,
|
||||
flags,
|
||||
ident: Ident::Pid(pid),
|
||||
};
|
||||
|
||||
if !self.watched.contains(&watch) {
|
||||
self.watched.push(watch);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds a file by filename to be watched
|
||||
///
|
||||
/// **NB**: `kqueue(2)` is an `fd`-based API. If you add a filename with
|
||||
/// `add_filename`, internally we open it and pass the file descriptor to
|
||||
/// `kqueue(2)`. If the file is moved or deleted, and a new file is created
|
||||
/// with the same name, you will not receive new events for it without
|
||||
/// calling `add_filename` again.
|
||||
///
|
||||
/// TODO: Adding new files requires calling `Watcher.watch` again
|
||||
pub fn add_filename<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
filename: P,
|
||||
filter: EventFilter,
|
||||
flags: FilterFlag,
|
||||
) -> Result<()> {
|
||||
let file = File::open(filename.as_ref())?;
|
||||
let watch = Watched {
|
||||
filter,
|
||||
flags,
|
||||
ident: Ident::Filename(
|
||||
file.into_raw_fd(),
|
||||
filename.as_ref().to_string_lossy().into_owned(),
|
||||
),
|
||||
};
|
||||
|
||||
if !self.watched.contains(&watch) {
|
||||
self.watched.push(watch);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds a descriptor to a `Watcher`. This or `add_file` is the preferred
|
||||
/// way to watch a file
|
||||
///
|
||||
/// TODO: Adding new files requires calling `Watcher.watch` again
|
||||
pub fn add_fd(&mut self, fd: RawFd, filter: EventFilter, flags: FilterFlag) -> Result<()> {
|
||||
let watch = Watched {
|
||||
filter,
|
||||
flags,
|
||||
ident: Ident::Fd(fd),
|
||||
};
|
||||
|
||||
if !self.watched.contains(&watch) {
|
||||
self.watched.push(watch);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds a `File` to a `Watcher`. This, or `add_fd` is the preferred way
|
||||
/// to watch a file
|
||||
///
|
||||
/// TODO: Adding new files requires calling `Watcher.watch` again
|
||||
pub fn add_file(&mut self, file: &File, filter: EventFilter, flags: FilterFlag) -> Result<()> {
|
||||
self.add_fd(file.as_raw_fd(), filter, flags)
|
||||
}
|
||||
|
||||
fn delete_kevents(&self, ident: Ident, filter: EventFilter) -> Result<()> {
|
||||
let kev = vec![kevent::new(
|
||||
ident.as_usize(),
|
||||
filter,
|
||||
EventFlag::EV_DELETE,
|
||||
FilterFlag::empty(),
|
||||
)];
|
||||
|
||||
let ret = unsafe {
|
||||
kevent(
|
||||
self.queue,
|
||||
kev.as_ptr(),
|
||||
// On NetBSD, this is passed as a usize, not i32
|
||||
#[allow(clippy::useless_conversion)]
|
||||
i32::try_from(kev.len()).unwrap().try_into().unwrap(),
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
ptr::null(),
|
||||
)
|
||||
};
|
||||
|
||||
match ret {
|
||||
-1 => Err(Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a pid from a `Watcher`
|
||||
pub fn remove_pid(&mut self, pid: libc::pid_t, filter: EventFilter) -> Result<()> {
|
||||
let new_watched = self
|
||||
.watched
|
||||
.drain(..)
|
||||
.filter(|x| {
|
||||
if let Ident::Pid(iterpid) = x.ident {
|
||||
iterpid != pid
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.watched = new_watched;
|
||||
self.delete_kevents(Ident::Pid(pid), filter)
|
||||
}
|
||||
|
||||
/// Removes a filename from a `Watcher`.
|
||||
///
|
||||
/// *NB*: This matches the `filename` that this item was initially added under.
|
||||
/// If a file has been moved, it will not be removable by the new name.
|
||||
pub fn remove_filename<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
filename: P,
|
||||
filter: EventFilter,
|
||||
) -> Result<()> {
|
||||
let mut fd: RawFd = 0;
|
||||
let new_watched = self
|
||||
.watched
|
||||
.drain(..)
|
||||
.filter(|x| {
|
||||
if let Ident::Filename(iterfd, ref iterfile) = x.ident {
|
||||
if iterfile == filename.as_ref().to_str().unwrap() {
|
||||
fd = iterfd;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.watched = new_watched;
|
||||
self.delete_kevents(Ident::Fd(fd), filter)
|
||||
}
|
||||
|
||||
/// Removes an fd from a `Watcher`
|
||||
pub fn remove_fd(&mut self, fd: RawFd, filter: EventFilter) -> Result<()> {
|
||||
let new_watched = self
|
||||
.watched
|
||||
.drain(..)
|
||||
.filter(|x| {
|
||||
if let Ident::Fd(iterfd) = x.ident {
|
||||
iterfd != fd
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.watched = new_watched;
|
||||
self.delete_kevents(Ident::Fd(fd), filter)
|
||||
}
|
||||
|
||||
/// Removes a `File` from a `Watcher`
|
||||
pub fn remove_file(&mut self, file: &File, filter: EventFilter) -> Result<()> {
|
||||
self.remove_fd(file.as_raw_fd(), filter)
|
||||
}
|
||||
|
||||
/// Starts watching for events from `kqueue(2)`. This function needs to
|
||||
/// be called before `Watcher.iter()` or `Watcher.poll()` to actually
|
||||
/// start listening for events.
|
||||
pub fn watch(&mut self) -> Result<()> {
|
||||
let mut kevs: Vec<kevent> = Vec::new();
|
||||
|
||||
for watched in &self.watched {
|
||||
let raw_ident = match watched.ident {
|
||||
Ident::Fd(fd) => fd as uintptr_t,
|
||||
Ident::Filename(fd, _) => fd as uintptr_t,
|
||||
Ident::Pid(pid) => pid as uintptr_t,
|
||||
Ident::Signal(sig) => sig as uintptr_t,
|
||||
Ident::Timer(ident) => ident as uintptr_t,
|
||||
};
|
||||
|
||||
kevs.push(kevent::new(
|
||||
raw_ident,
|
||||
watched.filter,
|
||||
if self.opts.clear {
|
||||
EventFlag::EV_ADD | EventFlag::EV_CLEAR
|
||||
} else {
|
||||
EventFlag::EV_ADD
|
||||
},
|
||||
watched.flags,
|
||||
));
|
||||
}
|
||||
|
||||
let ret = unsafe {
|
||||
kevent(
|
||||
self.queue,
|
||||
kevs.as_ptr(),
|
||||
// On NetBSD, this is passed as a usize, not i32
|
||||
#[allow(clippy::useless_conversion)]
|
||||
i32::try_from(kevs.len()).unwrap().try_into().unwrap(),
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
ptr::null(),
|
||||
)
|
||||
};
|
||||
|
||||
self.started = true;
|
||||
match ret {
|
||||
-1 => Err(Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Polls for a new event, with an optional timeout. If no `timeout`
|
||||
/// is passed, then it will return immediately.
|
||||
pub fn poll(&self, timeout: Option<Duration>) -> Option<Event> {
|
||||
// poll will not block indefinitely
|
||||
// None -> return immediately
|
||||
match timeout {
|
||||
Some(timeout) => get_event(self, Some(timeout)),
|
||||
None => get_event(self, Some(Duration::new(0, 0))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Polls for a new event, with an optional timeout. If no `timeout`
|
||||
/// is passed, then it will block until an event is received.
|
||||
pub fn poll_forever(&self, timeout: Option<Duration>) -> Option<Event> {
|
||||
if timeout.is_some() {
|
||||
self.poll(timeout)
|
||||
} else {
|
||||
get_event(self, None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an iterator that iterates over the queue. This iterator will block
|
||||
/// until a new event is received.
|
||||
pub fn iter(&self) -> EventIter {
|
||||
EventIter { watcher: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Watcher {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.queue
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Watcher {
|
||||
fn drop(&mut self) {
|
||||
unsafe { libc::close(self.queue) };
|
||||
for watched in &self.watched {
|
||||
match watched.ident {
|
||||
Ident::Fd(fd) => unsafe { libc::close(fd) },
|
||||
Ident::Filename(fd, _) => unsafe { libc::close(fd) },
|
||||
_ => continue,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_file_ident(watcher: &Watcher, fd: RawFd) -> Option<Ident> {
|
||||
for watched in &watcher.watched {
|
||||
match watched.ident.clone() {
|
||||
Ident::Fd(ident_fd) => {
|
||||
if fd == ident_fd {
|
||||
return Some(Ident::Fd(fd));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Ident::Filename(ident_fd, ident_str) => {
|
||||
if fd == ident_fd {
|
||||
return Some(Ident::Filename(ident_fd, ident_str));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn get_event(watcher: &Watcher, timeout: Option<Duration>) -> Option<Event> {
|
||||
let mut kev = kevent::new(
|
||||
0,
|
||||
EventFilter::EVFILT_SYSCOUNT,
|
||||
EventFlag::empty(),
|
||||
FilterFlag::empty(),
|
||||
);
|
||||
let ret = if let Some(ts) = timeout {
|
||||
unsafe {
|
||||
kevent(
|
||||
watcher.queue,
|
||||
ptr::null(),
|
||||
0,
|
||||
&mut kev,
|
||||
1,
|
||||
&duration_to_timespec(ts),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
unsafe { kevent(watcher.queue, ptr::null(), 0, &mut kev, 1, ptr::null()) }
|
||||
};
|
||||
|
||||
match ret {
|
||||
-1 => Some(Event::from_error(kev, watcher)),
|
||||
0 => None, // timeout expired
|
||||
_ => Some(Event::new(kev, watcher)),
|
||||
}
|
||||
}
|
||||
|
||||
// OS specific
|
||||
// TODO: Events can have more than one filter flag
|
||||
impl Event {
|
||||
#[doc(hidden)]
|
||||
pub fn new(ev: kevent, watcher: &Watcher) -> Event {
|
||||
let data = match ev.filter {
|
||||
EventFilter::EVFILT_READ => EventData::ReadReady(ev.data as usize),
|
||||
EventFilter::EVFILT_WRITE => EventData::WriteReady(ev.data as usize),
|
||||
EventFilter::EVFILT_SIGNAL => EventData::Signal(ev.data as usize),
|
||||
EventFilter::EVFILT_TIMER => EventData::Timer(ev.data as usize),
|
||||
EventFilter::EVFILT_PROC => {
|
||||
let inner = if ev.fflags.contains(FilterFlag::NOTE_EXIT) {
|
||||
Proc::Exit(ev.data as usize)
|
||||
} else if ev.fflags.contains(FilterFlag::NOTE_FORK) {
|
||||
Proc::Fork
|
||||
} else if ev.fflags.contains(FilterFlag::NOTE_EXEC) {
|
||||
Proc::Exec
|
||||
} else if ev.fflags.contains(FilterFlag::NOTE_TRACK) {
|
||||
Proc::Track(ev.data as libc::pid_t)
|
||||
} else if ev.fflags.contains(FilterFlag::NOTE_CHILD) {
|
||||
Proc::Child(ev.data as libc::pid_t)
|
||||
} else {
|
||||
panic!("not supported: {:?}", ev.fflags)
|
||||
};
|
||||
|
||||
EventData::Proc(inner)
|
||||
}
|
||||
EventFilter::EVFILT_VNODE => {
|
||||
let inner = if ev.fflags.contains(FilterFlag::NOTE_DELETE) {
|
||||
Vnode::Delete
|
||||
} else if ev.fflags.contains(FilterFlag::NOTE_WRITE) {
|
||||
Vnode::Write
|
||||
} else if ev.fflags.contains(FilterFlag::NOTE_EXTEND) {
|
||||
Vnode::Extend
|
||||
} else if ev.fflags.contains(FilterFlag::NOTE_ATTRIB) {
|
||||
Vnode::Attrib
|
||||
} else if ev.fflags.contains(FilterFlag::NOTE_LINK) {
|
||||
Vnode::Link
|
||||
} else if ev.fflags.contains(FilterFlag::NOTE_RENAME) {
|
||||
Vnode::Rename
|
||||
} else if ev.fflags.contains(FilterFlag::NOTE_REVOKE) {
|
||||
Vnode::Revoke
|
||||
} else {
|
||||
// This handles any filter flags that are OS-specific
|
||||
vnode::handle_vnode_extras(ev.fflags)
|
||||
};
|
||||
|
||||
EventData::Vnode(inner)
|
||||
}
|
||||
_ => panic!("not supported"),
|
||||
};
|
||||
|
||||
let ident = match ev.filter {
|
||||
EventFilter::EVFILT_READ => find_file_ident(watcher, ev.ident as RawFd).unwrap(),
|
||||
EventFilter::EVFILT_WRITE => find_file_ident(watcher, ev.ident as RawFd).unwrap(),
|
||||
EventFilter::EVFILT_VNODE => find_file_ident(watcher, ev.ident as RawFd).unwrap(),
|
||||
EventFilter::EVFILT_SIGNAL => Ident::Signal(ev.ident as i32),
|
||||
EventFilter::EVFILT_TIMER => Ident::Timer(ev.ident as i32),
|
||||
EventFilter::EVFILT_PROC => Ident::Pid(ev.ident as pid_t),
|
||||
_ => panic!("not supported"),
|
||||
};
|
||||
|
||||
Event { ident, data }
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn from_error(ev: kevent, watcher: &Watcher) -> Event {
|
||||
let ident = match ev.filter {
|
||||
EventFilter::EVFILT_READ => find_file_ident(watcher, ev.ident as RawFd).unwrap(),
|
||||
EventFilter::EVFILT_WRITE => find_file_ident(watcher, ev.ident as RawFd).unwrap(),
|
||||
EventFilter::EVFILT_VNODE => find_file_ident(watcher, ev.ident as RawFd).unwrap(),
|
||||
EventFilter::EVFILT_SIGNAL => Ident::Signal(ev.ident as i32),
|
||||
EventFilter::EVFILT_TIMER => Ident::Timer(ev.ident as i32),
|
||||
EventFilter::EVFILT_PROC => Ident::Pid(ev.ident as pid_t),
|
||||
_ => panic!("not supported"),
|
||||
};
|
||||
|
||||
Event {
|
||||
data: EventData::Error(io::Error::last_os_error()),
|
||||
ident,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn is_err(&self) -> bool {
|
||||
matches!(self.data, EventData::Error(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for EventIter<'a> {
|
||||
type Item = Event;
|
||||
|
||||
// rather than call kevent(2) each time, we can likely optimize and
|
||||
// call it once for like 100 items
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if !self.watcher.started {
|
||||
return None;
|
||||
}
|
||||
|
||||
get_event(self.watcher, None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{EventData, EventFilter, FilterFlag, Ident, Vnode, Watcher};
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::path::Path;
|
||||
use std::thread;
|
||||
use std::time;
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
use std::process;
|
||||
|
||||
#[test]
|
||||
fn test_new_watcher() {
|
||||
let mut watcher = Watcher::new().expect("new failed");
|
||||
let file = tempfile::tempfile().expect("Couldn't create tempfile");
|
||||
|
||||
watcher
|
||||
.add_file(&file, EventFilter::EVFILT_VNODE, FilterFlag::NOTE_WRITE)
|
||||
.expect("add failed");
|
||||
watcher.watch().expect("watch failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_filename() {
|
||||
let mut watcher = Watcher::new().expect("new failed");
|
||||
let file = tempfile::NamedTempFile::new().expect("Couldn't create tempfile");
|
||||
|
||||
watcher
|
||||
.add_filename(
|
||||
file.path(),
|
||||
EventFilter::EVFILT_VNODE,
|
||||
FilterFlag::NOTE_WRITE,
|
||||
)
|
||||
.expect("add failed");
|
||||
watcher.watch().expect("watch failed");
|
||||
|
||||
let mut new_file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(file.path())
|
||||
.expect("open failed");
|
||||
|
||||
new_file.write_all(b"foo").expect("write failed");
|
||||
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
|
||||
let ev = watcher.iter().next().expect("Could not get a watch");
|
||||
assert!(matches!(ev.data, EventData::Vnode(Vnode::Write)));
|
||||
|
||||
match ev.ident {
|
||||
Ident::Filename(_, name) => assert!(Path::new(&name) == file.path()),
|
||||
_ => panic!(),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file() {
|
||||
let mut watcher = Watcher::new().expect("new failed");
|
||||
let mut file = tempfile::tempfile().expect("Could not create tempfile");
|
||||
|
||||
watcher
|
||||
.add_file(&file, EventFilter::EVFILT_VNODE, FilterFlag::NOTE_WRITE)
|
||||
.expect("add failed");
|
||||
watcher.watch().expect("watch failed");
|
||||
file.write_all(b"foo").expect("write failed");
|
||||
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
|
||||
let ev = watcher.iter().next().expect("Didn't get an event");
|
||||
|
||||
assert!(matches!(ev.data, EventData::Vnode(Vnode::Write)));
|
||||
assert!(matches!(ev.ident, Ident::Fd(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_filename() {
|
||||
let mut watcher = Watcher::new().expect("new failed");
|
||||
|
||||
let file = tempfile::NamedTempFile::new().expect("Could not create tempfile");
|
||||
let filename = file.path();
|
||||
|
||||
watcher
|
||||
.add_filename(filename, EventFilter::EVFILT_VNODE, FilterFlag::NOTE_WRITE)
|
||||
.expect("add failed");
|
||||
watcher.watch().expect("watch failed");
|
||||
watcher
|
||||
.remove_filename(filename, EventFilter::EVFILT_VNODE)
|
||||
.expect("delete failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dupe() {
|
||||
let mut watcher = Watcher::new().expect("new failed");
|
||||
let file = tempfile::NamedTempFile::new().expect("Couldn't create tempfile");
|
||||
let filename = file.path();
|
||||
|
||||
watcher
|
||||
.add_filename(filename, EventFilter::EVFILT_VNODE, FilterFlag::NOTE_WRITE)
|
||||
.expect("add failed");
|
||||
watcher
|
||||
.add_filename(filename, EventFilter::EVFILT_VNODE, FilterFlag::NOTE_WRITE)
|
||||
.expect("second add failed");
|
||||
|
||||
assert_eq!(
|
||||
watcher.watched.len(),
|
||||
1,
|
||||
"Did not get an expected number of events"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_files() {
|
||||
let mut watcher = Watcher::new().expect("new failed");
|
||||
|
||||
let mut first_file = tempfile::tempfile().expect("Unable to create first temporary file");
|
||||
let mut second_file = tempfile::tempfile().expect("Unable to create second temporary file");
|
||||
|
||||
watcher
|
||||
.add_file(
|
||||
&first_file,
|
||||
EventFilter::EVFILT_VNODE,
|
||||
FilterFlag::NOTE_WRITE,
|
||||
)
|
||||
.expect("add failed");
|
||||
|
||||
watcher
|
||||
.add_file(
|
||||
&second_file,
|
||||
EventFilter::EVFILT_VNODE,
|
||||
FilterFlag::NOTE_WRITE,
|
||||
)
|
||||
.expect("add failed");
|
||||
|
||||
watcher.watch().expect("watch failed");
|
||||
first_file.write_all(b"foo").expect("first write failed");
|
||||
second_file.write_all(b"foo").expect("second write failed");
|
||||
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
|
||||
watcher.iter().next().expect("didn't get any events");
|
||||
watcher.iter().next().expect("didn't get any events");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_kqueue() {
|
||||
let mut watcher = Watcher::new().expect("Failed to create main watcher");
|
||||
let mut nested_watcher = Watcher::new().expect("Failed to create nested watcher");
|
||||
|
||||
let kqueue_file = unsafe { fs::File::from_raw_fd(nested_watcher.as_raw_fd()) };
|
||||
watcher
|
||||
.add_file(&kqueue_file, EventFilter::EVFILT_READ, FilterFlag::empty())
|
||||
.expect("add_file failed for main watcher");
|
||||
|
||||
let mut file = tempfile::tempfile().expect("Couldn't create tempfile");
|
||||
nested_watcher
|
||||
.add_file(&file, EventFilter::EVFILT_VNODE, FilterFlag::NOTE_WRITE)
|
||||
.expect("add_file failed for nested watcher");
|
||||
|
||||
watcher.watch().expect("watch failed on main watcher");
|
||||
nested_watcher
|
||||
.watch()
|
||||
.expect("watch failed on nested watcher");
|
||||
|
||||
file.write_all(b"foo").expect("write failed");
|
||||
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
|
||||
watcher.iter().next().expect("didn't get any events");
|
||||
nested_watcher.iter().next().expect("didn't get any events");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn test_close_read() {
|
||||
let mut watcher = Watcher::new().expect("new failed");
|
||||
|
||||
{
|
||||
let file = tempfile::NamedTempFile::new().expect("temporary file failed to create");
|
||||
watcher
|
||||
.add_filename(
|
||||
file.path(),
|
||||
EventFilter::EVFILT_VNODE,
|
||||
FilterFlag::NOTE_CLOSE,
|
||||
)
|
||||
.expect("add failed");
|
||||
watcher.watch().expect("watch failed");
|
||||
|
||||
// we launch this in a separate process since it appears that FreeBSD does not fire
|
||||
// off a NOTE_CLOSE(_WRITE)? event for the same process closing a file descriptor.
|
||||
process::Command::new("cat")
|
||||
.arg(file.path())
|
||||
.spawn()
|
||||
.expect("should spawn a file");
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
}
|
||||
let ev = watcher.iter().next().expect("did not receive event");
|
||||
assert!(matches!(ev.data, EventData::Vnode(Vnode::Close)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn test_close_write() {
|
||||
let mut watcher = match Watcher::new() {
|
||||
Ok(wat) => wat,
|
||||
Err(_) => panic!("new failed"),
|
||||
};
|
||||
|
||||
{
|
||||
let file = tempfile::NamedTempFile::new().expect("couldn't create tempfile");
|
||||
watcher
|
||||
.add_filename(
|
||||
file.path(),
|
||||
EventFilter::EVFILT_VNODE,
|
||||
FilterFlag::NOTE_CLOSE_WRITE,
|
||||
)
|
||||
.expect("add failed");
|
||||
watcher.watch().expect("watch failed");
|
||||
|
||||
// See above for rationale as to why we use a separate process here
|
||||
process::Command::new("cat")
|
||||
.arg(file.path())
|
||||
.spawn()
|
||||
.expect("should spawn a file");
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
}
|
||||
let ev = watcher.iter().next().expect("didn't get an event");
|
||||
assert!(matches!(ev.data, EventData::Vnode(Vnode::CloseWrite)));
|
||||
}
|
||||
}
|
||||
1
third-party/vendor/kqueue/src/os/mod.rs
vendored
Normal file
1
third-party/vendor/kqueue/src/os/mod.rs
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub(crate) mod vnode;
|
||||
21
third-party/vendor/kqueue/src/os/vnode.rs
vendored
Normal file
21
third-party/vendor/kqueue/src/os/vnode.rs
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
use kqueue_sys::constants::FilterFlag;
|
||||
|
||||
use super::super::Vnode;
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
pub(crate) fn handle_vnode_extras(ff: FilterFlag) -> Vnode {
|
||||
if ff.contains(FilterFlag::NOTE_CLOSE_WRITE) {
|
||||
Vnode::CloseWrite
|
||||
} else if ff.contains(FilterFlag::NOTE_CLOSE) {
|
||||
Vnode::Close
|
||||
} else if ff.contains(FilterFlag::NOTE_OPEN) {
|
||||
Vnode::Open
|
||||
} else {
|
||||
panic!("not supported")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
pub(crate) fn handle_vnode_extras(_ff: FilterFlag) -> Vnode {
|
||||
panic!("not supported")
|
||||
}
|
||||
49
third-party/vendor/kqueue/src/time.rs
vendored
Normal file
49
third-party/vendor/kqueue/src/time.rs
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use libc::{c_long, time_t, timespec};
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
|
||||
type NSec = i64;
|
||||
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
|
||||
type NSec = c_long;
|
||||
|
||||
pub(crate) fn duration_to_timespec(d: Duration) -> timespec {
|
||||
let tv_sec = d.as_secs() as time_t;
|
||||
let tv_nsec = d.subsec_nanos() as NSec;
|
||||
|
||||
if tv_sec.is_negative() {
|
||||
panic!("Duration seconds is negative");
|
||||
}
|
||||
|
||||
if tv_nsec.is_negative() {
|
||||
panic!("Duration nsecs is negative");
|
||||
}
|
||||
|
||||
timespec { tv_sec, tv_nsec }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::duration_to_timespec;
|
||||
use std::time::Duration;
|
||||
use std::{i64, u32};
|
||||
|
||||
#[test]
|
||||
fn test_basic_duration_to_ts() {
|
||||
let d = Duration::new(4, 20);
|
||||
|
||||
let ts = duration_to_timespec(d);
|
||||
|
||||
assert_eq!(ts.tv_sec, 4);
|
||||
assert_eq!(ts.tv_nsec, 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_overflow() {
|
||||
let d = Duration::new(i64::MAX as u64 + 1, u32::MAX);
|
||||
let ts = duration_to_timespec(d);
|
||||
|
||||
assert_eq!(ts.tv_sec, 1);
|
||||
assert_eq!(ts.tv_nsec, 1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue