Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,22 @@
# kqueue
[![Gitlab
Pipelines](https://gitlab.com/rust-kqueue/rust-kqueue/badges/main/pipeline.svg)](https://gitlab.com/rust-kqueue/rust-kqueue/-/commits/main)
[![Travis Build
Status](https://travis-ci.com/rust-kqueue/rust-kqueue.svg?branch=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
View 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
View 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

View 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>");
}
}

View 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
View 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)));
}
}

View file

@ -0,0 +1 @@
pub(crate) mod vnode;

View 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
View 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);
}
}