1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use crate::{crash::CortexError, CortexResult, CortexSync};
use std::ffi::CString;

fn get_name(shmem_key: i32) -> CortexResult<CString> {
    let name = CString::new(format!("cortex_semaphore_{}", shmem_key))?;
    Ok(name)
}

#[allow(dead_code)]
pub enum SemaphorePermission {
    OwnerOnly,
    OwnerAndGroup,
    ReadWriteForOthers,
    ReadOnlyForOthers,
    FullAccessForEveryone,
    Custom(libc::mode_t),
}

impl SemaphorePermission {
    fn into_mode(self) -> libc::mode_t {
        match self {
            SemaphorePermission::OwnerOnly => libc::S_IRWXU,
            SemaphorePermission::OwnerAndGroup => libc::S_IRWXU | libc::S_IRWXG,
            SemaphorePermission::ReadWriteForOthers => {
                libc::S_IRWXU | libc::S_IRWXG | libc::S_IROTH | libc::S_IWOTH
            }
            SemaphorePermission::ReadOnlyForOthers => libc::S_IRWXU | libc::S_IRWXG | libc::S_IROTH,
            SemaphorePermission::FullAccessForEveryone => {
                libc::S_IRWXU | libc::S_IRWXG | libc::S_IROTH | libc::S_IWOTH | libc::S_IXOTH
            }
            SemaphorePermission::Custom(mode) => mode,
        }
    }
}

/// Lock that uses a single semaphore for both read and write access
pub struct Semaphore {
    semaphore: *mut libc::sem_t,
    name: CString,
    is_owner: bool,
}

pub struct SemaphoreSettings {
    pub mode: SemaphorePermission,
}

impl Drop for Semaphore {
    fn drop(&mut self) {
        if !self.is_owner {
            return;
        }
        unsafe {
            if libc::sem_close(self.semaphore) == -1 {
                tracing::error!("Error during sem_close");
            };
            if libc::sem_unlink(self.name.as_ptr()) == -1 {
                tracing::error!("Error during sem_unlink");
            }
        }
    }
}

impl CortexSync for Semaphore {
    type Settings = SemaphoreSettings;

    fn new(shmem_key: i32, settings: Option<Self::Settings>) -> CortexResult<Self> {
        let permission = if let Some(settings) = settings {
            settings.mode
        } else {
            // Use most restrictive mode as default
            SemaphorePermission::OwnerOnly
        };
        let name = get_name(shmem_key)?;
        let name_ptr = name.as_ptr();
        let semaphore = unsafe {
            libc::sem_open(
                name_ptr,
                libc::O_EXCL | libc::O_CREAT,
                permission.into_mode(),
                1,
            )
        };
        if semaphore == libc::SEM_FAILED {
            return Err(CortexError::new_clean("Error during sem_open"));
        }
        Ok(Self {
            semaphore,
            name,
            is_owner: true,
        })
    }
    fn attach(shmem_key: i32) -> CortexResult<Self> {
        let name = get_name(shmem_key)?;
        let name_ptr = name.as_ptr();
        let semaphore = unsafe { libc::sem_open(name_ptr, 0, 0, 0) };
        if semaphore == libc::SEM_FAILED {
            return Err(CortexError::new_clean("Error during sem_open"));
        }
        Ok(Self {
            semaphore,
            name,
            is_owner: false,
        })
    }
    fn read_lock(&self) {
        unsafe {
            libc::sem_wait(self.semaphore);
        }
    }
    fn write_lock(&self) {
        unsafe {
            libc::sem_wait(self.semaphore);
        }
    }
    fn release(&self) {
        unsafe {
            libc::sem_post(self.semaphore);
        }
    }
}