From b2ff5b0763bcf89d00a3811d51f7e925f470f86f Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Thu, 18 Sep 2025 21:30:58 +0200 Subject: [PATCH] terrible nixos module --- Cargo.toml | 6 +++++ default.nix | 4 ++- flake.lock | 27 ++++++++++++++++++++ flake.nix | 47 +++++++++++++++++++++++++++++++++++ src/bin/clippyboard-daemon.rs | 4 +++ src/bin/clippyboard-select.rs | 4 +++ src/daemon.rs | 32 ++++++++++++++++++++++-- src/{main.rs => lib.rs} | 26 ++++++------------- 8 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 src/bin/clippyboard-daemon.rs create mode 100644 src/bin/clippyboard-select.rs rename src/{main.rs => lib.rs} (64%) diff --git a/Cargo.toml b/Cargo.toml index 2b8e8c8..7acd52d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,12 @@ name = "clippyboard" version = "0.1.0" edition = "2024" +[[bin]] +name = "clippyboard-daemon" + +[[bin]] +name = "clippyboard-select" + [dependencies] ciborium = "0.2.2" dirs = "6.0.0" diff --git a/default.nix b/default.nix index 87757dc..ecfed20 100644 --- a/default.nix +++ b/default.nix @@ -21,7 +21,9 @@ ]; postFixup = '' - wrapProgram $out/bin/clippyboard \ + wrapProgram $out/bin/clippyboard-select \ + --suffix LD_LIBRARY_PATH : ${pkgs.lib.makeLibraryPath buildInputs} + wrapProgram $out/bin/clippyboard-daemon \ --suffix LD_LIBRARY_PATH : ${pkgs.lib.makeLibraryPath buildInputs} ''; diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..ce0785e --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1758029226, + "narHash": "sha256-TjqVmbpoCqWywY9xIZLTf6ANFvDCXdctCjoYuYPYdMI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "08b8f92ac6354983f5382124fef6006cade4a1c1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..0d4dec1 --- /dev/null +++ b/flake.nix @@ -0,0 +1,47 @@ +# warning: this flake is probably terrible, whatever +{ + description = "clippyboard: a clipboard manager"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; + + outputs = { nixpkgs, ... }: + + let + lib = nixpkgs.lib; + clippyboard-package = ./default.nix; + systems = lib.intersectLists lib.systems.flakeExposed lib.platforms.linux; + forAllSystems = lib.genAttrs systems; + in + { + packages = forAllSystems (system: { default = nixpkgs.${system}.callPackage clippyboard-package { }; }); + nixosModules.default = { lib, config, pkgs, ... }: + let + cfg = config.services.clippyboard; + clippyboard = pkgs.callPackage clippyboard-package { }; + in + { + options.services.clippyboard = { + enable = lib.mkEnableOption "Enable the clippyboard daemon and clippyboard program"; + }; + + config = lib.mkIf cfg.enable { + nixpkgs.overlays = [ + (final: prev: { + clipboard = clippyboard; + }) + ]; + systemd.user.services.clippyboard = { + description = "a clipboard manager"; + wantedBy = [ "graphical-session.target" ]; + after = [ "graphical-session.target" ]; + serviceConfig = { + ExecStart = lib.getExe' clippyboard "clippyboard-daemon"; + }; + }; + environment.systemPackages = [ clippyboard ]; + }; + }; + }; +} diff --git a/src/bin/clippyboard-daemon.rs b/src/bin/clippyboard-daemon.rs new file mode 100644 index 0000000..7c0da2d --- /dev/null +++ b/src/bin/clippyboard-daemon.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), eyre::Error> { + let socket_path: std::path::PathBuf = clippyboard::socket_path()?; + clippyboard::daemon::main(&socket_path) +} diff --git a/src/bin/clippyboard-select.rs b/src/bin/clippyboard-select.rs new file mode 100644 index 0000000..5c7b811 --- /dev/null +++ b/src/bin/clippyboard-select.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), eyre::Error> { + let socket_path = clippyboard::socket_path()?; + clippyboard::display::main(&socket_path) +} diff --git a/src/daemon.rs b/src/daemon.rs index 751df98..937e591 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -8,11 +8,16 @@ use rustix::event::PollFd; use rustix::event::PollFlags; use rustix::fs::OFlags; use std::collections::HashMap; +use std::convert::Infallible; +use std::io; +use std::io::ErrorKind; use std::io::PipeReader; use std::io::{BufReader, BufWriter, PipeWriter, Read, Write}; use std::os::fd::AsFd; use std::os::unix::net::{UnixListener, UnixStream}; use std::path::PathBuf; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex, OnceLock, atomic::AtomicU64}; use std::time::Duration; use std::time::SystemTime; @@ -430,11 +435,24 @@ fn read_fd_into_history( } pub fn main(socket_path: &PathBuf) -> eyre::Result<()> { + let Err(err) = main_inner(socket_path); + + if let Some(ioerr) = err.downcast_ref::() + && ioerr.kind() == ErrorKind::AddrInUse + { + // no cleanup + return Err(err); + } + + cleanup(socket_path); + Err(err) +} + +pub fn main_inner(socket_path: &PathBuf) -> eyre::Result { tracing_subscriber::fmt() .with_env_filter(EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new("info"))) .init(); - let _ = std::fs::remove_file(&socket_path); // lol let socket = UnixListener::bind(&socket_path) .wrap_err_with(|| format!("binding path {}", socket_path.display()))?; @@ -481,9 +499,11 @@ pub fn main(socket_path: &PathBuf) -> eyre::Result<()> { rustix::fs::fcntl_setfl(notify_write_recv.as_fd(), OFlags::NONBLOCK).expect("todo"); rustix::fs::fcntl_setfl(conn.as_fd(), OFlags::NONBLOCK).expect("TODO"); + let socket_path_clone = socket_path.to_owned(); std::thread::spawn(move || { if let Err(err) = dispatch_wayland(queue, wl_state, notify_write_recv) { error!("error on Wayland thread: {err:?}"); + cleanup(&socket_path_clone); std::process::exit(1); } }); @@ -507,5 +527,13 @@ pub fn main(socket_path: &PathBuf) -> eyre::Result<()> { } } - Ok(()) + unreachable!("socket.incoming will never return None") +} + +fn cleanup(socket_path: &PathBuf) { + static HAS_DONE_CLEANUP: AtomicBool = AtomicBool::new(false); + + if !HAS_DONE_CLEANUP.swap(true, Ordering::Relaxed) { + let _ = std::fs::remove_file(&socket_path); + } } diff --git a/src/main.rs b/src/lib.rs similarity index 64% rename from src/main.rs rename to src/lib.rs index b5ef861..3c9fc6f 100644 --- a/src/main.rs +++ b/src/lib.rs @@ -1,9 +1,9 @@ -mod daemon; -mod display; +pub mod daemon; +pub mod display; -use eyre::{OptionExt, bail}; +use eyre::OptionExt; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; const MAX_ENTRY_SIZE: u64 = 50_000_000; const MAX_HISTORY_BYTE_SIZE: usize = 100_000_000; @@ -33,20 +33,8 @@ const MESSAGE_READ: u8 = 1; /// Argument: One u64-bit LE value, the ID const MESSAGE_COPY: u8 = 2; -fn main() -> eyre::Result<()> { - let Some(mode) = std::env::args().nth(1) else { - bail!("missing mode"); - }; - - let socket_path = dirs::runtime_dir() +pub fn socket_path() -> eyre::Result { + Ok(dirs::runtime_dir() .ok_or_eyre("missing XDG_RUNTIME_DIR")? - .join("clippyboard.sock"); - - match mode.as_str() { - "daemon" => daemon::main(&socket_path)?, - "display" => display::main(&socket_path)?, - _ => panic!("invalid mode, supported: daemon, display"), - } - - Ok(()) + .join("clippyboard.sock")) }