rust/hg-core/src/lock.rs
author Rapha?l Gom?s <rgomes@octobus.net>
Mon, 29 Jul 2024 20:47:43 +0200
changeset 52167 7be39c5110c9
parent 51864 db7dbe6f7bb2
permissions -rw-r--r--
hg-core: add a complete VFS This will be used from Python in a later change. More changes are needed in hg-core and rhg to properly clean up the APIs of the old VFS implementation but it can be done when the dust settles and we start adding more functionality to the pure Rust VFS.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     1
//! Filesystem-based locks for local repositories
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     2
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     3
use crate::errors::HgError;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     4
use crate::errors::HgResultExt;
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51864
diff changeset
     5
use crate::vfs::Vfs;
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49930
diff changeset
     6
use crate::vfs::VfsImpl;
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     7
use std::io;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     8
use std::io::ErrorKind;
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51864
diff changeset
     9
use std::path::Path;
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    10
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    11
#[derive(derive_more::From)]
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    12
pub enum LockError {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    13
    AlreadyHeld,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    14
    #[from]
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    15
    Other(HgError),
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    16
}
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    17
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    18
/// Try to call `f` with the lock acquired, without waiting.
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    19
///
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    20
/// If the lock is aready held, `f` is not called and `LockError::AlreadyHeld`
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    21
/// is returned. `LockError::Io` is returned for any unexpected I/O error
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    22
/// accessing the lock file, including for removing it after `f` was called.
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    23
/// The return value of `f` is dropped in that case. If all is successful, the
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    24
/// return value of `f` is forwarded.
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    25
pub fn try_with_lock_no_wait<R>(
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49930
diff changeset
    26
    hg_vfs: &VfsImpl,
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    27
    lock_filename: &str,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    28
    f: impl FnOnce() -> R,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    29
) -> Result<R, LockError> {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    30
    let our_lock_data = &*OUR_LOCK_DATA;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    31
    for _retry in 0..5 {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    32
        match make_lock(hg_vfs, lock_filename, our_lock_data) {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    33
            Ok(()) => {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    34
                let result = f();
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    35
                unlock(hg_vfs, lock_filename)?;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    36
                return Ok(result);
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    37
            }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    38
            Err(HgError::IoError { error, .. })
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    39
                if error.kind() == ErrorKind::AlreadyExists =>
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    40
            {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    41
                let lock_data = read_lock(hg_vfs, lock_filename)?;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    42
                if lock_data.is_none() {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    43
                    // Lock was apparently just released, retry acquiring it
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    44
                    continue;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    45
                }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    46
                if !lock_should_be_broken(&lock_data) {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    47
                    return Err(LockError::AlreadyHeld);
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    48
                }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    49
                // The lock file is left over from a process not running
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    50
                // anymore. Break it, but with another lock to
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    51
                // avoid a race.
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    52
                break_lock(hg_vfs, lock_filename)?;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    53
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    54
                // Retry acquiring
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    55
            }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    56
            Err(error) => Err(error)?,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    57
        }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    58
    }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    59
    Err(LockError::AlreadyHeld)
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    60
}
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    61
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49930
diff changeset
    62
fn break_lock(hg_vfs: &VfsImpl, lock_filename: &str) -> Result<(), LockError> {
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    63
    try_with_lock_no_wait(hg_vfs, &format!("{}.break", lock_filename), || {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    64
        // Check again in case some other process broke and
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    65
        // acquired the lock in the meantime
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    66
        let lock_data = read_lock(hg_vfs, lock_filename)?;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    67
        if !lock_should_be_broken(&lock_data) {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    68
            return Err(LockError::AlreadyHeld);
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    69
        }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51864
diff changeset
    70
        Ok(hg_vfs.unlink(Path::new(lock_filename))?)
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    71
    })?
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    72
}
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    73
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    74
#[cfg(unix)]
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    75
fn make_lock(
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49930
diff changeset
    76
    hg_vfs: &VfsImpl,
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    77
    lock_filename: &str,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    78
    data: &str,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    79
) -> Result<(), HgError> {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    80
    // Use a symbolic link because creating it is atomic.
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    81
    // The link’s "target" contains data not representing any path.
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    82
    let fake_symlink_target = data;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    83
    hg_vfs.create_symlink(lock_filename, fake_symlink_target)
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    84
}
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    85
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    86
fn read_lock(
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49930
diff changeset
    87
    hg_vfs: &VfsImpl,
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    88
    lock_filename: &str,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    89
) -> Result<Option<String>, HgError> {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    90
    let link_target =
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    91
        hg_vfs.read_link(lock_filename).io_not_found_as_none()?;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    92
    if let Some(target) = link_target {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    93
        let data = target
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    94
            .into_os_string()
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    95
            .into_string()
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    96
            .map_err(|_| HgError::corrupted("non-UTF-8 lock data"))?;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    97
        Ok(Some(data))
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    98
    } else {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    99
        Ok(None)
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   100
    }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   101
}
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   102
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49930
diff changeset
   103
fn unlock(hg_vfs: &VfsImpl, lock_filename: &str) -> Result<(), HgError> {
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51864
diff changeset
   104
    hg_vfs.unlink(Path::new(lock_filename))
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   105
}
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   106
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   107
/// Return whether the process that is/was holding the lock is known not to be
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   108
/// running anymore.
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   109
fn lock_should_be_broken(data: &Option<String>) -> bool {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   110
    (|| -> Option<bool> {
49632
29cf3167e459 hg-core: remove unneeded trait now that we support Rust 1.52+
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48962
diff changeset
   111
        let (prefix, pid) = data.as_ref()?.split_once(':')?;
49930
e98fd81bb151 rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49632
diff changeset
   112
        if prefix != *LOCK_PREFIX {
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   113
            return Some(false);
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   114
        }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   115
        let process_is_running;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   116
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   117
        #[cfg(unix)]
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   118
        {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   119
            let pid: libc::pid_t = pid.parse().ok()?;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   120
            unsafe {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   121
                let signal = 0; // Test if we could send a signal, without sending
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   122
                let result = libc::kill(pid, signal);
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   123
                if result == 0 {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   124
                    process_is_running = true
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   125
                } else {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   126
                    let errno =
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   127
                        io::Error::last_os_error().raw_os_error().unwrap();
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   128
                    process_is_running = errno != libc::ESRCH
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   129
                }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   130
            }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   131
        }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   132
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   133
        Some(!process_is_running)
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   134
    })()
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   135
    .unwrap_or(false)
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   136
}
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   137
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   138
lazy_static::lazy_static! {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   139
    /// A string which is used to differentiate pid namespaces
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   140
    ///
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   141
    /// It's useful to detect "dead" processes and remove stale locks with
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   142
    /// confidence. Typically it's just hostname. On modern linux, we include an
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   143
    /// extra Linux-specific pid namespace identifier.
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   144
    static ref LOCK_PREFIX: String = {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   145
        // Note: this must match the behavior of `_getlockprefix` in `mercurial/lock.py`
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   146
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   147
        /// Same as https://github.com/python/cpython/blob/v3.10.0/Modules/socketmodule.c#L5414
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   148
        const BUFFER_SIZE: usize = 1024;
49930
e98fd81bb151 rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49632
diff changeset
   149
        // This cast is *needed* for platforms with signed chars
e98fd81bb151 rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49632
diff changeset
   150
        #[allow(clippy::unnecessary_cast)]
48962
59be65b7cdfd rust-hg-core: use correct type for libc hostname buffer
Luke Granger-Brown <hg@lukegb.com>
parents: 48417
diff changeset
   151
        let mut buffer = [0 as libc::c_char; BUFFER_SIZE];
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   152
        let hostname_bytes = unsafe {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   153
            let result = libc::gethostname(buffer.as_mut_ptr(), BUFFER_SIZE);
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   154
            if result != 0 {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   155
                panic!("gethostname: {}", io::Error::last_os_error())
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   156
            }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   157
            std::ffi::CStr::from_ptr(buffer.as_mut_ptr()).to_bytes()
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   158
        };
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   159
        let hostname =
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   160
            std::str::from_utf8(hostname_bytes).expect("non-UTF-8 hostname");
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   161
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   162
        #[cfg(target_os = "linux")]
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   163
        {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   164
            use std::os::linux::fs::MetadataExt;
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   165
            match std::fs::metadata("/proc/self/ns/pid") {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   166
                Ok(meta) => {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   167
                    return format!("{}/{:x}", hostname, meta.st_ino())
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   168
                }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   169
                Err(error) => {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   170
                    // TODO: match on `error.kind()` when `NotADirectory`
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   171
                    // is available on all supported Rust versions:
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   172
                    // https://github.com/rust-lang/rust/issues/86442
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   173
                    use libc::{
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   174
                        ENOENT, // ErrorKind::NotFound
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   175
                        ENOTDIR, // ErrorKind::NotADirectory
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   176
                        EACCES, // ErrorKind::PermissionDenied
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   177
                    };
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   178
                    match error.raw_os_error() {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   179
                        Some(ENOENT) | Some(ENOTDIR) | Some(EACCES) => {}
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   180
                        _ => panic!("stat /proc/self/ns/pid: {}", error),
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   181
                    }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   182
                }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   183
            }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   184
        }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   185
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   186
        hostname.to_owned()
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   187
    };
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   188
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   189
    static ref OUR_LOCK_DATA: String = format!("{}:{}", &*LOCK_PREFIX, std::process::id());
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   190
}