[POSIX] Best practices to detect application uniqueness without DBus

Hi, what is the best and fail-safe way to detect whether the running program is an unique instance or not on Unix? I prefer not using DBus because (1) I'm not used to DBus myself (2) cannot find any easily portable simple example.

So far I've found the following strategies:

  1. Creating a file: This crate provides an easy to use API to automate the process of detecting uniqueness. The idea is that if I can create a file in RAMDisk (for example, $XDG_RUNTIME_DIR or /dev/shm) when the app is first run, all other instances can check if that file exists to determine if the program is unique. Problem with this approach is that I don't know if the file is going to be deleted if the application is terminated unexpectedly.

  2. Via TCP sockets: This example demonstrates uniqueness detection by binding a TCP port. It has the same problem of not being able to "un-bind" the port if the app terminates forcefully.

  3. Linux Abstract Sockets: This article demonstrates a really easy way to detect uniqueness but it also has some caveats as stated in the article, one of which can pose security risk (even though it's not a big problem for the project I'm working on).

What is the industry standard way to approach application uniqueness in POSIX (or even just Linux)? Is DBus the only way?


Update, I just went with Linux Abstract Sockets. Easy to understand and the possible security risks are not fatal for the project I'm working on.

use nix::sys::socket::AddressFamily;
use nix::sys::socket::SockAddr;
use nix::sys::socket::SockFlag;
use nix::sys::socket::SockType;
use nix::sys::socket::UnixAddr;
use nix::sys::socket::bind;
use nix::sys::socket::socket;

pub fn is_unique() -> Result<bool, Box<dyn std::error::Error>> {
   
    let socket = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None)?;
    let addr = SockAddr::Unix(UnixAddr::new_abstract(crate::consts::APP_ID.as_bytes())?);

    match bind(socket, &addr) {
        Err(e) => match e {
            nix::Error::Sys(nix::errno::Errno::EADDRINUSE) => Ok(false),
            _ => Err(Box::new(e))
        },
        _ => Ok(true)
    }
}

how about locking a file? if doesn't exist create it.
only first app instance can lock, the second can't.
if the app crashes/exists/whatever... the file lock is released.

3 Likes

The other approach I've used is to create a folder holding the process pid and then check whether that pid exists and is my process. It's cumbersome and race prone, but generally works. Also make sure any file path used contains the hostname, in case NFS is in use.

If you figure out a good approach, that would make an awesome small crate.

1 Like

That's not going to be reliable unless you can guarantee no remote filesystems are in play: part 1, part 2.

$XDG_RUNTIME_DIR provides a useful contract, with a local, flock-enabled filesystem. You do have to opt-out of file cleanup.

Personally I would let the service manager take care of this.

What kind of application is it? If it is a desktop type for Linux, I'd recommend d-bus. It doesn't have race problems or stale lockfile issues. I wouldn't count on portability outside Linux though.