From: Robert Obkircher <r.obkircher@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: Re: [PATCH pve-cluster 03/10] rust: ffi: add C ABI staticlib for pmxcfs
Date: Mon, 21 Sep 2026 11:57:15 +0200 [thread overview]
Message-ID: <e4fc1c22-d78f-453a-8b3a-1e765a2fff7f@proxmox.com> (raw)
In-Reply-To: <20260918144152.575163-4-h.laimer@proxmox.com>
On 18.09.26 16:42, Hannes Laimer wrote:
> pmxcfs is a C daemon, so the notification server needs a small C
> facing surface it can link statically. Three functions cover start,
> event emission and shutdown, and a log callback taking a syslog
> priority routes messages into the daemon's own logging. The emit call
> takes the memdb version of the mutation along, which becomes the
> sequence number clients see and resume from.
>
> Rust aborts the process when a panic reaches the C caller. That would
> take down /etc/pve because of a bug in an optional feature, so every
> entry point catches a panic at that boundary, and one only disables
> the notifier for the rest of the daemon's life. It also drops every
> connection, so clients notice, reconnect and log the outage rather
> than waiting on a socket that will never speak again.
>
> Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
> ---
> src/rust/Cargo.toml | 1 +
> src/rust/pmxcfs-ffi/Cargo.toml | 17 ++
> src/rust/pmxcfs-ffi/include/pmxcfs-notify.h | 29 ++
> src/rust/pmxcfs-ffi/src/lib.rs | 309 ++++++++++++++++++++
> 4 files changed, 356 insertions(+)
> create mode 100644 src/rust/pmxcfs-ffi/Cargo.toml
> create mode 100644 src/rust/pmxcfs-ffi/include/pmxcfs-notify.h
> create mode 100644 src/rust/pmxcfs-ffi/src/lib.rs
>
> diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
> index 6de0ab8..9324685 100644
> --- a/src/rust/Cargo.toml
> +++ b/src/rust/Cargo.toml
> @@ -1,5 +1,6 @@
> [workspace]
> members = [
> + "pmxcfs-ffi",
> "pmxcfs-notify",
> ]
> resolver = "3"
> diff --git a/src/rust/pmxcfs-ffi/Cargo.toml b/src/rust/pmxcfs-ffi/Cargo.toml
> new file mode 100644
> index 0000000..8264cde
> --- /dev/null
> +++ b/src/rust/pmxcfs-ffi/Cargo.toml
> @@ -0,0 +1,17 @@
> +[package]
> +name = "pmxcfs-ffi"
> +version = "0.1.0"
> +description = "C ABI bridge exposing pmxcfs-notify to the pmxcfs daemon"
> +authors.workspace = true
> +edition.workspace = true
> +license.workspace = true
> +homepage.workspace = true
> +rust-version.workspace = true
> +
> +[lib]
> +crate-type = ["staticlib"]
> +
> +[dependencies]
> +libc.workspace = true
> +log.workspace = true
> +pmxcfs-notify.workspace = true
> diff --git a/src/rust/pmxcfs-ffi/include/pmxcfs-notify.h b/src/rust/pmxcfs-ffi/include/pmxcfs-notify.h
> new file mode 100644
> index 0000000..811213d
> --- /dev/null
> +++ b/src/rust/pmxcfs-ffi/include/pmxcfs-notify.h
> @@ -0,0 +1,29 @@
> +#ifndef PMXCFS_NOTIFY_H
> +#define PMXCFS_NOTIFY_H
> +
> +#include <stdint.h>
> +#include <sys/types.h>
> +
> +enum pmxcfs_notify_type {
> + PMXCFS_NOTIFY_CREATE = 0,
> + PMXCFS_NOTIFY_WRITE = 1,
> + PMXCFS_NOTIFY_MTIME = 2,
> + PMXCFS_NOTIFY_RENAME = 3,
> + PMXCFS_NOTIFY_DELETE = 4,
> + PMXCFS_NOTIFY_MKDIR = 5,
> + PMXCFS_NOTIFY_RESYNC = 6,
> +};
> +
> +/* priority is a syslog(3) level */
> +typedef void (*pmxcfs_notify_log_fn)(int priority, const char *msg);
> +
> +/* seq is the version of the last mutation so far, a client resuming at it is current */
> +int pmxcfs_notify_init(
> + const char *socket_path, gid_t gid, pmxcfs_notify_log_fn log_cb, uint64_t seq
> +);
> +void pmxcfs_notify_emit(
> + enum pmxcfs_notify_type type, uint64_t seq, const char *path, const char *to
> +);
> +void pmxcfs_notify_shutdown(void);
> +
> +#endif /* PMXCFS_NOTIFY_H */
> diff --git a/src/rust/pmxcfs-ffi/src/lib.rs b/src/rust/pmxcfs-ffi/src/lib.rs
> new file mode 100644
> index 0000000..f8a9629
> --- /dev/null
> +++ b/src/rust/pmxcfs-ffi/src/lib.rs
> @@ -0,0 +1,309 @@
> +//! C ABI for the pmxcfs daemon, declared in include/pmxcfs-notify.h.
> +//!
> +//! Rust aborts the process when a panic reaches an `extern "C"` frame,
> +//! which must never happen to pmxcfs. Every entry point therefore runs
> +//! under catch_unwind and a panic switches the notifier off for the rest
> +//! of the daemon's life instead of taking it down.
> +
> +use std::ffi::{CStr, CString, OsStr, c_char, c_int};
> +use std::os::unix::ffi::OsStrExt;
> +use std::panic::{AssertUnwindSafe, catch_unwind};
> +use std::path::PathBuf;
> +use std::sync::atomic::{AtomicBool, Ordering};
> +use std::sync::{Mutex, MutexGuard, OnceLock};
> +
> +use log::{Level, LevelFilter, Log, Metadata, Record};
> +use pmxcfs_notify::{Config, EventKind, Server};
> +
> +pub type LogFn = unsafe extern "C" fn(c_int, *const c_char);
> +
> +const TYPE_CREATE: c_int = 0;
> +const TYPE_WRITE: c_int = 1;
> +const TYPE_MTIME: c_int = 2;
> +const TYPE_RENAME: c_int = 3;
> +const TYPE_DELETE: c_int = 4;
> +const TYPE_MKDIR: c_int = 5;
> +const TYPE_RESYNC: c_int = 6;
> +const RING_SIZE: usize = 8192;
> +
> +static SERVER: Mutex<Option<Server>> = Mutex::new(None);
> +static DISABLED: AtomicBool = AtomicBool::new(false);
> +static LOG_CB: Mutex<Option<LogFn>> = Mutex::new(None);
> +static LOGGER: CLogger = CLogger;
> +static LOGGER_INSTALLED: OnceLock<()> = OnceLock::new();
> +
> +struct CLogger;
> +
> +impl Log for CLogger {
> + fn enabled(&self, _: &Metadata) -> bool {
> + true
> + }
> +
> + fn log(&self, record: &Record) {
> + let Some(callback) = *lock(&LOG_CB) else {
> + return;
> + };
> + let priority = match record.level() {
> + Level::Error => libc::LOG_ERR,
> + Level::Warn => libc::LOG_WARNING,
> + Level::Info => libc::LOG_INFO,
> + Level::Debug | Level::Trace => libc::LOG_DEBUG,
> + };
> + let msg = record.args().to_string().replace('\0', " ");
> + let Ok(msg) = CString::new(msg) else {
> + return;
> + };
> + unsafe { callback(priority, msg.as_ptr()) };
> + }
> +
> + fn flush(&self) {}
> +}
> +
> +fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
> + mutex
> + .lock()
> + .unwrap_or_else(|poisoned| poisoned.into_inner())
> +}
> +
> +fn guard<R>(what: &str, fallback: R, f: impl FnOnce() -> R) -> R {
> + if DISABLED.load(Ordering::SeqCst) {
> + return fallback;
> + }
> + match catch_unwind(AssertUnwindSafe(f)) {
> + Ok(result) => result,
> + Err(payload) => {
> + DISABLED.store(true, Ordering::SeqCst);
> + let reason = payload
> + .downcast_ref::<&str>()
> + .map(|s| s.to_string())
> + .or_else(|| payload.downcast_ref::<String>().cloned())
to_string and cloned are theoretically allowed to panic on allocation
failure. I think you could just map the &String to &str instead.
(not a full review, I just quickly skimmed this patch)
> [..]
next prev parent reply other threads:[~2026-09-21 9:57 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-18 14:41 [RFC cluster/manager 00/10] pmxcfs: add a change notification socket Hannes Laimer
2026-09-18 14:41 ` [PATCH pve-cluster 01/10] buildsys: add rust workspace under src/rust Hannes Laimer
2026-09-18 14:41 ` [PATCH pve-cluster 02/10] rust: notify: add change notification socket server Hannes Laimer
2026-09-18 14:41 ` [PATCH pve-cluster 03/10] rust: ffi: add C ABI staticlib for pmxcfs Hannes Laimer
2026-09-21 9:57 ` Robert Obkircher [this message]
2026-09-18 14:41 ` [PATCH pve-cluster 04/10] pmxcfs: memdb: add change notification hook Hannes Laimer
2026-09-18 14:41 ` [PATCH pve-cluster 05/10] buildsys: link pmxcfs against the rust notify staticlib Hannes Laimer
2026-09-18 14:41 ` [PATCH pve-cluster 06/10] pmxcfs: notify: emit change events over the notification socket Hannes Laimer
2026-09-18 14:41 ` [PATCH pve-cluster 07/10] cfs: add perl client for the change " Hannes Laimer
2026-09-18 14:41 ` [PATCH pve-cluster 08/10] cfs: add hook registry for change notification consumers Hannes Laimer
2026-09-18 14:41 ` [PATCH pve-manager 09/10] hooks: add runner executing cluster change hooks in children Hannes Laimer
2026-09-18 14:41 ` [PATCH pve-manager 10/10] pvescheduler: run cluster change hooks from a listener child Hannes Laimer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e4fc1c22-d78f-453a-8b3a-1e765a2fff7f@proxmox.com \
--to=r.obkircher@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox