From 096cb21703f5663fc4ec3e111cc68fadb55795b7 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 13 Feb 2024 10:11:57 +0100 Subject: [PATCH 01/58] First try at implementing SystemV --- src/sys/mman.rs | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/src/sys/mman.rs b/src/sys/mman.rs index a64f14f588..87a6d89205 100644 --- a/src/sys/mman.rs +++ b/src/sys/mman.rs @@ -633,3 +633,117 @@ pub fn shm_unlink(name: &P) -> Result<()> { Errno::result(ret).map(drop) } + + +libc_bitflags! { + pub struct ShmgetFlag: c_int + { + IPC_PRIVATE; + IPC_CREAT; + IPC_EXCL; + SHM_HUGETLB; + SHM_NORESERVE; + } +} +/// Creates and returns a new, or returns an existing, System V shared memory +/// segment identifier. +/// +/// For more information, see [`shmget(2)`]. +/// +/// [`shmget(2)`]: https://man7.org/linux/man-pages/man2/shmget.2.html +pub fn shmget( + key: key_t, + size: size_t, + shmflg: Vec, +) -> Result { + let mut flags: c_int = ShmgetFlag::empty().bits(); + for flag in shmflg { + flags |= flag.bits; + } + Errno::result(unsafe { libc::shmget(key, size, flags) }) +} + +libc_bitflags! { + pub struct ShmatFlag: c_int + { + SHM_EXEC; + SHM_RND; + SHM_RDONLY; + #[cfg(any(target_os = "linux"))] + SHM_REMAP; + } +} +/// Attaches the System V shared memory segment identified by `shmid` to the +/// address space of the calling process. +/// +/// For more information, see [`shmat(2)`]. +/// +/// # Safety +/// +/// `shmid` should be a valid shared memory identifier and +/// `shmaddr` must meet the requirements described in the [`shmat(2)`] man page. +/// +/// [`shmat(2)`]: https://man7.org/linux/man-pages/man2/shmat.2.html +pub fn shmat( + shmid: c_int, + shmaddr: *const c_void, + shmflg: Vec, +) -> Result<*mut c_void> { + let mut flags: c_int = ShmatFlag::empty().bits(); + for flag in shmflg { + flags |= flag.bits; + } + Errno::result(unsafe { libc::shmat(shmid, shmaddr, flags) }) +} + +/// Performs the reverse of [`shmat`], detaching the shared memory segment at +/// the given address from the address space of the calling process. +/// +/// For more information, see [`shmdt(2)`]. +/// +/// # Safety +/// +/// `shmaddr` must meet the requirements described in the [`shmdt(2)`] man page. +/// +/// [`shmdt(2)`]: https://man7.org/linux/man-pages/man2/shmdt.2.html +pub fn shmdt(shmaddr: *const c_void) -> Result<()> { + Errno::result(unsafe {libc::shmdt(shmaddr)}).map(drop) +} + +libc_bitflags! { + pub struct ShmctlFlag: c_int { + #[cfg(any(target_os = "linux"))] + IPC_INFO; + IPC_SET; + IPC_STAT; + IPC_RMID; + // not available in libc but should be? + // #[cfg(any(target_os = "linux"))] + // SHM_INFO; + // #[cfg(any(target_os = "linux"))] + // SHM_STAT; + // #[cfg(any(target_os = "linux"))] + // SHM_STAT_ANY; + #[cfg(any(target_os = "linux"))] + SHM_LOCK; + #[cfg(any(target_os = "linux"))] + SHM_UNLOCK; + } +} +/// Performs control operation specified by `cmd` on the System V shared +/// memory segment given by `shmid`. +/// +/// For more information, see [`shmctl(2)`]. +/// +/// # Safety +/// +/// All arguments should be valid and meet the requirements described in the [`shmctl(2)`] man page. +/// +/// [`shmctl(2)`]: https://man7.org/linux/man-pages/man2/shmctl.2.html +pub fn shmctl( + shmid: c_int, + cmd: c_int, + buf: *mut shmid_ds, +) -> Result { + Errno::result(unsafe {libc::shmctl(shmid, cmd, buf)} ) +} From 072352c0dfc1b69eb5671a48cfa2a9674c21f4e0 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Wed, 14 Feb 2024 17:54:46 +0100 Subject: [PATCH 02/58] Add permission struct --- src/sys/mman.rs | 92 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 11 deletions(-) diff --git a/src/sys/mman.rs b/src/sys/mman.rs index 87a6d89205..4d1e6937f7 100644 --- a/src/sys/mman.rs +++ b/src/sys/mman.rs @@ -634,14 +634,85 @@ pub fn shm_unlink(name: &P) -> Result<()> { Errno::result(ret).map(drop) } +#[derive(Debug, Default, Clone, Copy)] +/// Type used to transform a raw number to an octal permission, while performing a clamp to u9 +/// +/// # Example +/// +/// ``` +/// # use nix::errno::Errno; +/// # use nix::sys::mman::Permissions; +/// +/// # fn main() -> Result<(), Errno> { +/// assert_eq!(Permissions::new(511)?.get_permission(), &(0o0777 as u16)); +/// assert_eq!(Permissions::new(512).expect_err("512 is bigger than what u9 can store"), Errno::E2BIG); +/// # Ok(()) +/// # } +/// ``` +pub struct Permissions { + permission: u16, +} + +impl Permissions { + /// Create a new Permissions object + /// + /// Clamp to a u9 size, return Errno::E2BIG if it fails + /// + pub fn new(octal: u16) -> Result { + if octal >= 2_u16.pow(9) { + return Err(Errno::E2BIG); + } + Ok(Permissions { permission: octal }) + } + + pub fn get_permission(&self) -> &u16 { + &self.permission + } + + /// Using the current stored permission, do a bitor operation on the + /// bitflags enums given + /// + pub fn to_octal>( + &self, + vec_flags: Vec, + ) -> c_int { + let mut flags: c_int = T::empty().bits(); + for flag in vec_flags { + flags |= flag.bits(); + } + flags |= self.permission as i32; + flags + } +} libc_bitflags! { + /// Different flags for the command `shmget` pub struct ShmgetFlag: c_int { + /// A new shared memory segment is created if key has this value IPC_PRIVATE; + /// Create a new segment. If this flag is not used, then shmget() will + /// find the segment associated with key and check to see if the user + /// has permission to access the segment. IPC_CREAT; + /// This flag is used with IPC_CREAT to ensure that this call creates + /// the segment. If the segment already exists, the call fails. IPC_EXCL; + /// Allocate the segment using "huge" pages. See the Linux kernel + /// source file Documentation/admin-guide/mm/hugetlbpage.rst for + /// further information. + #[cfg(any(target_os = "linux"))] SHM_HUGETLB; + // Does not exist in libc but should + // SHM_HUGE_2MB; + // SHM_HUGE_1GB; + /// This flag serves the same purpose as the mmap(2) MAP_NORESERVE flag. + /// Do not reserve swap space for this segment. When swap space is + /// reserved, one has the guarantee that it is possible to modify the + /// segment. When swap space is not reserved one might get SIGSEGV upon + /// a write if no physical memory is available. See also the discussion + /// of the file /proc/sys/vm/overcommit_memory in proc(5). + #[cfg(any(target_os = "linux"))] SHM_NORESERVE; } } @@ -655,11 +726,9 @@ pub fn shmget( key: key_t, size: size_t, shmflg: Vec, + permission: Permissions, ) -> Result { - let mut flags: c_int = ShmgetFlag::empty().bits(); - for flag in shmflg { - flags |= flag.bits; - } + let flags = permission.to_octal(shmflg); Errno::result(unsafe { libc::shmget(key, size, flags) }) } @@ -688,11 +757,9 @@ pub fn shmat( shmid: c_int, shmaddr: *const c_void, shmflg: Vec, + permission: Permissions, ) -> Result<*mut c_void> { - let mut flags: c_int = ShmatFlag::empty().bits(); - for flag in shmflg { - flags |= flag.bits; - } + let flags = permission.to_octal(shmflg); Errno::result(unsafe { libc::shmat(shmid, shmaddr, flags) }) } @@ -707,7 +774,7 @@ pub fn shmat( /// /// [`shmdt(2)`]: https://man7.org/linux/man-pages/man2/shmdt.2.html pub fn shmdt(shmaddr: *const c_void) -> Result<()> { - Errno::result(unsafe {libc::shmdt(shmaddr)}).map(drop) + Errno::result(unsafe { libc::shmdt(shmaddr) }).map(drop) } libc_bitflags! { @@ -742,8 +809,11 @@ libc_bitflags! { /// [`shmctl(2)`]: https://man7.org/linux/man-pages/man2/shmctl.2.html pub fn shmctl( shmid: c_int, - cmd: c_int, + cmd: ShmctlFlag, buf: *mut shmid_ds, + permission: Permissions, ) -> Result { - Errno::result(unsafe {libc::shmctl(shmid, cmd, buf)} ) + let command = permission.to_octal(vec![cmd]); + Errno::result(unsafe { libc::shmctl(shmid, command, buf) }) } + From adeaaa2184e472eabc0f0d8e27f473c3e4913d5c Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Wed, 14 Feb 2024 17:55:26 +0100 Subject: [PATCH 03/58] Add crude semaphore support --- src/sys/mman.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/src/sys/mman.rs b/src/sys/mman.rs index 4d1e6937f7..db5bb00033 100644 --- a/src/sys/mman.rs +++ b/src/sys/mman.rs @@ -7,7 +7,10 @@ use crate::Result; #[cfg(not(target_os = "android"))] #[cfg(feature = "fs")] use crate::{fcntl::OFlag, sys::stat::Mode}; -use libc::{self, c_int, c_void, off_t, size_t}; +use libc::{ + self, c_int, c_short, c_void, key_t, off_t, semid_ds, seminfo, shmid_ds, + size_t, +}; use std::ptr::NonNull; use std::{ num::NonZeroUsize, @@ -732,6 +735,30 @@ pub fn shmget( Errno::result(unsafe { libc::shmget(key, size, flags) }) } +libc_bitflags! { + pub struct SemgetFlag: c_int + { + IPC_PRIVATE; + IPC_CREAT; + IPC_EXCL; + } +} +/// Creates and return a new, or returns an existing, System V shared memory +/// semaphore identifier. +/// +/// For more information, see [`semget(2)`]. +/// +/// [`semget(2)`]: https://man7.org/linux/man-pages/man2/semget.2.html +pub fn semget( + key: key_t, + size: i32, + semflg: Vec, + permission: Permissions, +) -> Result { + let flags = permission.to_octal(semflg); + Errno::result(unsafe { libc::semget(key, size, flags) }) +} + libc_bitflags! { pub struct ShmatFlag: c_int { @@ -784,7 +811,7 @@ libc_bitflags! { IPC_SET; IPC_STAT; IPC_RMID; - // not available in libc but should be? + // not available in libc/linux, but should be? // #[cfg(any(target_os = "linux"))] // SHM_INFO; // #[cfg(any(target_os = "linux"))] @@ -817,3 +844,61 @@ pub fn shmctl( Errno::result(unsafe { libc::shmctl(shmid, command, buf) }) } + +libc_bitflags! { + pub struct SemctlCmd: c_int { + IPC_STAT; + IPC_SET; + IPC_RMID; + #[cfg(any(target_os = "linux"))] + IPC_INFO; + // #[cfg(any(target_os = "linux"))] + // SEM_INFO; + // #[cfg(any(target_os = "linux"))] + // SEM_STAT; + // #[cfg(any(target_os = "linux"))] + // SEM_STAT_ANY; + // GETALL; + // GETNCNT; + // GETPID; + // GETVAL; + // GETZCNT; + // SETALL; + // SETVAL; + } +} + +#[derive(Debug)] +pub enum Semun { + /// Value for SETVAL + val(c_int), + /// Buffer for IPC_STAT, IPC_SET + buf(*mut semid_ds), + /// Array for GETALL, SETALL + array(*mut c_short), + /// Buffer for IPC_INFO + #[cfg(any(target_os = "linux"))] + __buf(*mut seminfo), +} + +/// Performs control operation specified by `cmd` on the System V shared +/// semaphore segment given by `semid`. +/// +/// For more information, see [`semctl(2)`]. +/// +/// # +/// +/// [`semctl(2)`]: https://man7.org/linux/man-pages/man2/semctl.2.html +pub fn semctl( + semid: c_int, + semnum: c_int, + cmd: SemctlCmd, + permission: Permissions, + semun: Option, +) -> Result { + let command = permission.to_octal(vec![cmd]); + if semun.is_none() { + return Errno::result(unsafe { libc::semctl(semid, semnum, command) }); + } + Errno::result(unsafe { libc::semctl(semid, semnum, command, semun) }) +} From 135488ee753525fac72cf10b06f3a8c6eca7083a Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Wed, 14 Feb 2024 19:43:05 +0100 Subject: [PATCH 04/58] Add documentation that was missing --- src/sys/mman.rs | 139 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 116 insertions(+), 23 deletions(-) diff --git a/src/sys/mman.rs b/src/sys/mman.rs index db5bb00033..a3d07d5d0d 100644 --- a/src/sys/mman.rs +++ b/src/sys/mman.rs @@ -668,6 +668,8 @@ impl Permissions { Ok(Permissions { permission: octal }) } + /// Getter for permission + /// pub fn get_permission(&self) -> &u16 { &self.permission } @@ -689,14 +691,15 @@ impl Permissions { } libc_bitflags! { - /// Different flags for the command `shmget` + /// Valid flags for the third parameter of the function [`shmget`] pub struct ShmgetFlag: c_int { - /// A new shared memory segment is created if key has this value + /// A new shared memory segment is created if key has this value. IPC_PRIVATE; - /// Create a new segment. If this flag is not used, then shmget() will - /// find the segment associated with key and check to see if the user - /// has permission to access the segment. + /// Create a new segment. + /// If this flag is not used, then shmget() will find the segment + /// associated with key and check to see if the user has permission + /// to access the segment. IPC_CREAT; /// This flag is used with IPC_CREAT to ensure that this call creates /// the segment. If the segment already exists, the call fails. @@ -706,8 +709,9 @@ libc_bitflags! { /// further information. #[cfg(any(target_os = "linux"))] SHM_HUGETLB; - // Does not exist in libc but should + // TODO: Does not exist in libc/linux, but should? Maybe open an issue in their repo // SHM_HUGE_2MB; + // TODO: Same for this one // SHM_HUGE_1GB; /// This flag serves the same purpose as the mmap(2) MAP_NORESERVE flag. /// Do not reserve swap space for this segment. When swap space is @@ -736,10 +740,18 @@ pub fn shmget( } libc_bitflags! { + /// Valid flags for the third parameter of the function [`semget`] pub struct SemgetFlag: c_int { + /// A new shared memory segment is created if key has this value IPC_PRIVATE; + /// Create a new segment. + /// If this flag is not used, then shmget() will find the segment + /// associated with key and check to see if the user has permission + /// to access the segment. IPC_CREAT; + /// This flag is used with IPC_CREAT to ensure that this call creates + /// the segment. If the segment already exists, the call fails. IPC_EXCL; } } @@ -760,13 +772,29 @@ pub fn semget( } libc_bitflags! { + /// Valid flags for the third parameter of the function [`shmat`] pub struct ShmatFlag: c_int { + /// Allow the contents of the segment to be executed. The caller must + /// have execute permission on the segment. + #[cfg(any(target_os = "linux"))] SHM_EXEC; - SHM_RND; - SHM_RDONLY; #[cfg(any(target_os = "linux"))] + /// This flag specifies that the mapping of the segment should replace + /// any existing mapping in the range starting at shmaddr and + /// continuing for the size of the segment. + /// (Normally, an EINVAL error would result if a mapping already exists + /// in this address range.) + /// In this case, shmaddr must not be NULL. SHM_REMAP; + /// Attach the segment for read-only access. The process must have read + /// permission for the segment. If this flag is not specified, the + /// segment is attached for read and write access, and the process must + /// have read and write permission for the segment. + /// There is no notion of a write-only shared memory segment. + SHM_RDONLY; + /// TODO: I have no clue at what this does + SHM_RND; } } /// Attaches the System V shared memory segment identified by `shmid` to the @@ -805,11 +833,45 @@ pub fn shmdt(shmaddr: *const c_void) -> Result<()> { } libc_bitflags! { + /// Valid flags for the second parameter of the function [`shmctl`] pub struct ShmctlFlag: c_int { #[cfg(any(target_os = "linux"))] + /// Returns the index of the highest used entry in the kernel's internal + /// array recording information about all shared memory segment IPC_INFO; + /// Write the values of some members of the shmid_ds structure pointed + /// to by buf to the kernel data structure associated with this shared + /// memory segment, updating also its shm_ctime member. + /// + /// The following fields are updated: shm_perm.uid, + /// shm_perm.gid, and (the least significant 9 bits of) + /// shm_perm.mode. + /// + /// The effective UID of the calling process must match the owner + /// (shm_perm.uid) or creator (shm_perm.cuid) of the shared memory + /// segment, or the caller must be privileged. IPC_SET; + /// Copy information from the kernel data structure associated with + /// shmid into the shmid_ds structure pointed to by buf. + /// The caller must have read permission on the shared memory segment. IPC_STAT; + /// Mark the segment to be destroyed. The segment will actually be + /// destroyed only after the last process detaches it + /// (i.e., when the shm_nattch member of the associated structure + /// shmid_ds is zero). + /// The caller must be the owner or creator of the segment, + /// or be privileged. The buf argument is ignored. + /// + /// If a segment has been marked for destruction, then the + /// (nonstandard) SHM_DEST flag of the shm_perm.mode field in the + /// associated data structure retrieved by IPC_STAT will be set. + /// + /// The caller must ensure that a segment is eventually destroyed; + /// otherwise its pages that were faulted in will remain in memory + /// or swap. + /// + /// See also the description of /proc/sys/kernel/shm_rmid_forced + /// in proc(5). IPC_RMID; // not available in libc/linux, but should be? // #[cfg(any(target_os = "linux"))] @@ -819,8 +881,15 @@ libc_bitflags! { // #[cfg(any(target_os = "linux"))] // SHM_STAT_ANY; #[cfg(any(target_os = "linux"))] + /// Prevent swapping of the shared memory segment. The caller must + /// fault in any pages that are required to be present after locking is + /// enabled. + /// If a segment has been locked, then the (nonstandard) SHM_LOCKED + /// flag of the shm_perm.mode field in the associated data structure + /// retrieved by IPC_STAT will be set. SHM_LOCK; #[cfg(any(target_os = "linux"))] + /// Unlock the segment, allowing it to be swapped out. SHM_UNLOCK; } } @@ -844,14 +913,52 @@ pub fn shmctl( Errno::result(unsafe { libc::shmctl(shmid, command, buf) }) } - +#[derive(Debug)] +/// Called as the fourth parameter of the function [`semctl`] +/// +pub enum Semun { + /// Value for SETVAL + val(c_int), + /// Buffer for IPC_STAT, IPC_SET + buf(*mut semid_ds), + /// Array for GETALL, SETALL + array(*mut c_short), + /// Buffer for IPC_INFO + #[cfg(any(target_os = "linux"))] + __buf(*mut seminfo), +} libc_bitflags! { + /// Valid flags for the third parameter of the function [`shmctl`] pub struct SemctlCmd: c_int { + /// Copy information from the kernel data structure associated with + /// shmid into the shmid_ds structure pointed to by buf. + /// The caller must have read permission on the shared memory segment. IPC_STAT; + /// Write the values of some members of the semid_ds structure pointed + /// to by arg.buf to the kernel data structure associated with this + /// semaphore set, updating also its sem_ctime member. + /// + /// The following members of the structure are updated: + /// sem_perm.uid, sem_perm.gid, and (the least significant 9 bits of) + /// sem_perm.mode. + /// + /// The effective UID of the calling process must match the owner + /// (sem_perm.uid) or creator (sem_perm.cuid) of the semaphore set, + /// or the caller must be privileged. The argument semnum is ignored. IPC_SET; + /// Immediately remove the semaphore set, awakening all processes + /// blocked in semop(2) calls on the set + /// (with an error return and errno set to EIDRM). + /// The effective user ID of the calling process must match the creator + /// or owner of the semaphore set, or the caller must be privileged. + /// The argument semnum is ignored. IPC_RMID; #[cfg(any(target_os = "linux"))] + /// Return information about system-wide semaphore limits and + /// parameters in the structure pointed to by arg.__buf. This structure + /// is of type [`seminfo`]. IPC_INFO; + // TODO: None of the one following are defined in libc // #[cfg(any(target_os = "linux"))] // SEM_INFO; // #[cfg(any(target_os = "linux"))] @@ -867,20 +974,6 @@ libc_bitflags! { // SETVAL; } } - -#[derive(Debug)] -pub enum Semun { - /// Value for SETVAL - val(c_int), - /// Buffer for IPC_STAT, IPC_SET - buf(*mut semid_ds), - /// Array for GETALL, SETALL - array(*mut c_short), - /// Buffer for IPC_INFO - #[cfg(any(target_os = "linux"))] - __buf(*mut seminfo), -} - /// Performs control operation specified by `cmd` on the System V shared /// semaphore segment given by `semid`. /// From ac357e2d71ac6862875ed5f49ef82a179c1f7af1 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Wed, 14 Feb 2024 20:24:26 +0100 Subject: [PATCH 05/58] Add PR changelog file --- changelog/2313.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/2313.added.md diff --git a/changelog/2313.added.md b/changelog/2313.added.md new file mode 100644 index 0000000000..483aa48d7b --- /dev/null +++ b/changelog/2313.added.md @@ -0,0 +1 @@ +Add SystemV IPC support From 3f6a4c3fb03f54fa2cdb088697337b66837c5207 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 19 Feb 2024 21:25:02 +0100 Subject: [PATCH 06/58] Move SystemV to it's own file --- src/sys/mman.rs | 364 +------------------------------------------ src/sys/mod.rs | 3 + src/sys/system_v.rs | 365 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+), 363 deletions(-) create mode 100644 src/sys/system_v.rs diff --git a/src/sys/mman.rs b/src/sys/mman.rs index a3d07d5d0d..a64f14f588 100644 --- a/src/sys/mman.rs +++ b/src/sys/mman.rs @@ -7,10 +7,7 @@ use crate::Result; #[cfg(not(target_os = "android"))] #[cfg(feature = "fs")] use crate::{fcntl::OFlag, sys::stat::Mode}; -use libc::{ - self, c_int, c_short, c_void, key_t, off_t, semid_ds, seminfo, shmid_ds, - size_t, -}; +use libc::{self, c_int, c_void, off_t, size_t}; use std::ptr::NonNull; use std::{ num::NonZeroUsize, @@ -636,362 +633,3 @@ pub fn shm_unlink(name: &P) -> Result<()> { Errno::result(ret).map(drop) } - -#[derive(Debug, Default, Clone, Copy)] -/// Type used to transform a raw number to an octal permission, while performing a clamp to u9 -/// -/// # Example -/// -/// ``` -/// # use nix::errno::Errno; -/// # use nix::sys::mman::Permissions; -/// -/// # fn main() -> Result<(), Errno> { -/// assert_eq!(Permissions::new(511)?.get_permission(), &(0o0777 as u16)); -/// assert_eq!(Permissions::new(512).expect_err("512 is bigger than what u9 can store"), Errno::E2BIG); -/// # Ok(()) -/// # } -/// ``` -pub struct Permissions { - permission: u16, -} - -impl Permissions { - /// Create a new Permissions object - /// - /// Clamp to a u9 size, return Errno::E2BIG if it fails - /// - pub fn new(octal: u16) -> Result { - if octal >= 2_u16.pow(9) { - return Err(Errno::E2BIG); - } - Ok(Permissions { permission: octal }) - } - - /// Getter for permission - /// - pub fn get_permission(&self) -> &u16 { - &self.permission - } - - /// Using the current stored permission, do a bitor operation on the - /// bitflags enums given - /// - pub fn to_octal>( - &self, - vec_flags: Vec, - ) -> c_int { - let mut flags: c_int = T::empty().bits(); - for flag in vec_flags { - flags |= flag.bits(); - } - flags |= self.permission as i32; - flags - } -} - -libc_bitflags! { - /// Valid flags for the third parameter of the function [`shmget`] - pub struct ShmgetFlag: c_int - { - /// A new shared memory segment is created if key has this value. - IPC_PRIVATE; - /// Create a new segment. - /// If this flag is not used, then shmget() will find the segment - /// associated with key and check to see if the user has permission - /// to access the segment. - IPC_CREAT; - /// This flag is used with IPC_CREAT to ensure that this call creates - /// the segment. If the segment already exists, the call fails. - IPC_EXCL; - /// Allocate the segment using "huge" pages. See the Linux kernel - /// source file Documentation/admin-guide/mm/hugetlbpage.rst for - /// further information. - #[cfg(any(target_os = "linux"))] - SHM_HUGETLB; - // TODO: Does not exist in libc/linux, but should? Maybe open an issue in their repo - // SHM_HUGE_2MB; - // TODO: Same for this one - // SHM_HUGE_1GB; - /// This flag serves the same purpose as the mmap(2) MAP_NORESERVE flag. - /// Do not reserve swap space for this segment. When swap space is - /// reserved, one has the guarantee that it is possible to modify the - /// segment. When swap space is not reserved one might get SIGSEGV upon - /// a write if no physical memory is available. See also the discussion - /// of the file /proc/sys/vm/overcommit_memory in proc(5). - #[cfg(any(target_os = "linux"))] - SHM_NORESERVE; - } -} -/// Creates and returns a new, or returns an existing, System V shared memory -/// segment identifier. -/// -/// For more information, see [`shmget(2)`]. -/// -/// [`shmget(2)`]: https://man7.org/linux/man-pages/man2/shmget.2.html -pub fn shmget( - key: key_t, - size: size_t, - shmflg: Vec, - permission: Permissions, -) -> Result { - let flags = permission.to_octal(shmflg); - Errno::result(unsafe { libc::shmget(key, size, flags) }) -} - -libc_bitflags! { - /// Valid flags for the third parameter of the function [`semget`] - pub struct SemgetFlag: c_int - { - /// A new shared memory segment is created if key has this value - IPC_PRIVATE; - /// Create a new segment. - /// If this flag is not used, then shmget() will find the segment - /// associated with key and check to see if the user has permission - /// to access the segment. - IPC_CREAT; - /// This flag is used with IPC_CREAT to ensure that this call creates - /// the segment. If the segment already exists, the call fails. - IPC_EXCL; - } -} -/// Creates and return a new, or returns an existing, System V shared memory -/// semaphore identifier. -/// -/// For more information, see [`semget(2)`]. -/// -/// [`semget(2)`]: https://man7.org/linux/man-pages/man2/semget.2.html -pub fn semget( - key: key_t, - size: i32, - semflg: Vec, - permission: Permissions, -) -> Result { - let flags = permission.to_octal(semflg); - Errno::result(unsafe { libc::semget(key, size, flags) }) -} - -libc_bitflags! { - /// Valid flags for the third parameter of the function [`shmat`] - pub struct ShmatFlag: c_int - { - /// Allow the contents of the segment to be executed. The caller must - /// have execute permission on the segment. - #[cfg(any(target_os = "linux"))] - SHM_EXEC; - #[cfg(any(target_os = "linux"))] - /// This flag specifies that the mapping of the segment should replace - /// any existing mapping in the range starting at shmaddr and - /// continuing for the size of the segment. - /// (Normally, an EINVAL error would result if a mapping already exists - /// in this address range.) - /// In this case, shmaddr must not be NULL. - SHM_REMAP; - /// Attach the segment for read-only access. The process must have read - /// permission for the segment. If this flag is not specified, the - /// segment is attached for read and write access, and the process must - /// have read and write permission for the segment. - /// There is no notion of a write-only shared memory segment. - SHM_RDONLY; - /// TODO: I have no clue at what this does - SHM_RND; - } -} -/// Attaches the System V shared memory segment identified by `shmid` to the -/// address space of the calling process. -/// -/// For more information, see [`shmat(2)`]. -/// -/// # Safety -/// -/// `shmid` should be a valid shared memory identifier and -/// `shmaddr` must meet the requirements described in the [`shmat(2)`] man page. -/// -/// [`shmat(2)`]: https://man7.org/linux/man-pages/man2/shmat.2.html -pub fn shmat( - shmid: c_int, - shmaddr: *const c_void, - shmflg: Vec, - permission: Permissions, -) -> Result<*mut c_void> { - let flags = permission.to_octal(shmflg); - Errno::result(unsafe { libc::shmat(shmid, shmaddr, flags) }) -} - -/// Performs the reverse of [`shmat`], detaching the shared memory segment at -/// the given address from the address space of the calling process. -/// -/// For more information, see [`shmdt(2)`]. -/// -/// # Safety -/// -/// `shmaddr` must meet the requirements described in the [`shmdt(2)`] man page. -/// -/// [`shmdt(2)`]: https://man7.org/linux/man-pages/man2/shmdt.2.html -pub fn shmdt(shmaddr: *const c_void) -> Result<()> { - Errno::result(unsafe { libc::shmdt(shmaddr) }).map(drop) -} - -libc_bitflags! { - /// Valid flags for the second parameter of the function [`shmctl`] - pub struct ShmctlFlag: c_int { - #[cfg(any(target_os = "linux"))] - /// Returns the index of the highest used entry in the kernel's internal - /// array recording information about all shared memory segment - IPC_INFO; - /// Write the values of some members of the shmid_ds structure pointed - /// to by buf to the kernel data structure associated with this shared - /// memory segment, updating also its shm_ctime member. - /// - /// The following fields are updated: shm_perm.uid, - /// shm_perm.gid, and (the least significant 9 bits of) - /// shm_perm.mode. - /// - /// The effective UID of the calling process must match the owner - /// (shm_perm.uid) or creator (shm_perm.cuid) of the shared memory - /// segment, or the caller must be privileged. - IPC_SET; - /// Copy information from the kernel data structure associated with - /// shmid into the shmid_ds structure pointed to by buf. - /// The caller must have read permission on the shared memory segment. - IPC_STAT; - /// Mark the segment to be destroyed. The segment will actually be - /// destroyed only after the last process detaches it - /// (i.e., when the shm_nattch member of the associated structure - /// shmid_ds is zero). - /// The caller must be the owner or creator of the segment, - /// or be privileged. The buf argument is ignored. - /// - /// If a segment has been marked for destruction, then the - /// (nonstandard) SHM_DEST flag of the shm_perm.mode field in the - /// associated data structure retrieved by IPC_STAT will be set. - /// - /// The caller must ensure that a segment is eventually destroyed; - /// otherwise its pages that were faulted in will remain in memory - /// or swap. - /// - /// See also the description of /proc/sys/kernel/shm_rmid_forced - /// in proc(5). - IPC_RMID; - // not available in libc/linux, but should be? - // #[cfg(any(target_os = "linux"))] - // SHM_INFO; - // #[cfg(any(target_os = "linux"))] - // SHM_STAT; - // #[cfg(any(target_os = "linux"))] - // SHM_STAT_ANY; - #[cfg(any(target_os = "linux"))] - /// Prevent swapping of the shared memory segment. The caller must - /// fault in any pages that are required to be present after locking is - /// enabled. - /// If a segment has been locked, then the (nonstandard) SHM_LOCKED - /// flag of the shm_perm.mode field in the associated data structure - /// retrieved by IPC_STAT will be set. - SHM_LOCK; - #[cfg(any(target_os = "linux"))] - /// Unlock the segment, allowing it to be swapped out. - SHM_UNLOCK; - } -} -/// Performs control operation specified by `cmd` on the System V shared -/// memory segment given by `shmid`. -/// -/// For more information, see [`shmctl(2)`]. -/// -/// # Safety -/// -/// All arguments should be valid and meet the requirements described in the [`shmctl(2)`] man page. -/// -/// [`shmctl(2)`]: https://man7.org/linux/man-pages/man2/shmctl.2.html -pub fn shmctl( - shmid: c_int, - cmd: ShmctlFlag, - buf: *mut shmid_ds, - permission: Permissions, -) -> Result { - let command = permission.to_octal(vec![cmd]); - Errno::result(unsafe { libc::shmctl(shmid, command, buf) }) -} - -#[derive(Debug)] -/// Called as the fourth parameter of the function [`semctl`] -/// -pub enum Semun { - /// Value for SETVAL - val(c_int), - /// Buffer for IPC_STAT, IPC_SET - buf(*mut semid_ds), - /// Array for GETALL, SETALL - array(*mut c_short), - /// Buffer for IPC_INFO - #[cfg(any(target_os = "linux"))] - __buf(*mut seminfo), -} -libc_bitflags! { - /// Valid flags for the third parameter of the function [`shmctl`] - pub struct SemctlCmd: c_int { - /// Copy information from the kernel data structure associated with - /// shmid into the shmid_ds structure pointed to by buf. - /// The caller must have read permission on the shared memory segment. - IPC_STAT; - /// Write the values of some members of the semid_ds structure pointed - /// to by arg.buf to the kernel data structure associated with this - /// semaphore set, updating also its sem_ctime member. - /// - /// The following members of the structure are updated: - /// sem_perm.uid, sem_perm.gid, and (the least significant 9 bits of) - /// sem_perm.mode. - /// - /// The effective UID of the calling process must match the owner - /// (sem_perm.uid) or creator (sem_perm.cuid) of the semaphore set, - /// or the caller must be privileged. The argument semnum is ignored. - IPC_SET; - /// Immediately remove the semaphore set, awakening all processes - /// blocked in semop(2) calls on the set - /// (with an error return and errno set to EIDRM). - /// The effective user ID of the calling process must match the creator - /// or owner of the semaphore set, or the caller must be privileged. - /// The argument semnum is ignored. - IPC_RMID; - #[cfg(any(target_os = "linux"))] - /// Return information about system-wide semaphore limits and - /// parameters in the structure pointed to by arg.__buf. This structure - /// is of type [`seminfo`]. - IPC_INFO; - // TODO: None of the one following are defined in libc - // #[cfg(any(target_os = "linux"))] - // SEM_INFO; - // #[cfg(any(target_os = "linux"))] - // SEM_STAT; - // #[cfg(any(target_os = "linux"))] - // SEM_STAT_ANY; - // GETALL; - // GETNCNT; - // GETPID; - // GETVAL; - // GETZCNT; - // SETALL; - // SETVAL; - } -} -/// Performs control operation specified by `cmd` on the System V shared -/// semaphore segment given by `semid`. -/// -/// For more information, see [`semctl(2)`]. -/// -/// # -/// -/// [`semctl(2)`]: https://man7.org/linux/man-pages/man2/semctl.2.html -pub fn semctl( - semid: c_int, - semnum: c_int, - cmd: SemctlCmd, - permission: Permissions, - semun: Option, -) -> Result { - let command = permission.to_octal(vec![cmd]); - if semun.is_none() { - return Errno::result(unsafe { libc::semctl(semid, semnum, command) }); - } - Errno::result(unsafe { libc::semctl(semid, semnum, command, semun) }) -} diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 93339d1935..d365f31961 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -143,6 +143,9 @@ feature! { #[allow(missing_docs)] pub mod sysinfo; +#[allow(missing_docs)] +pub mod system_v; + feature! { #![feature = "term"] #[allow(missing_docs)] diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs new file mode 100644 index 0000000000..fe980775f9 --- /dev/null +++ b/src/sys/system_v.rs @@ -0,0 +1,365 @@ +use crate::errno::Errno; +use crate::Result; + +use libc::{ + self, c_int, c_short, c_void, key_t, shmid_ds, size_t, semid_ds, seminfo +}; + +#[derive(Debug, Default, Clone, Copy)] +/// Type used to transform a raw number to an octal permission, while performing a clamp to u9 +/// +/// # Example +/// +/// ``` +/// # use nix::errno::Errno; +/// # use nix::sys::mman::Permissions; +/// +/// # fn main() -> Result<(), Errno> { +/// assert_eq!(Permissions::new(511)?.get_permission(), &(0o0777 as u16)); +/// assert_eq!(Permissions::new(512).expect_err("512 is bigger than what u9 can store"), Errno::E2BIG); +/// # Ok(()) +/// # } +/// ``` +pub struct Permissions { + permission: u16, +} + +impl Permissions { + /// Create a new Permissions object + /// + /// Clamp to a u9 size, return Errno::E2BIG if it fails + /// + pub fn new(octal: u16) -> Result { + if octal >= 2_u16.pow(9) { + return Err(Errno::E2BIG); + } + Ok(Permissions { permission: octal }) + } + + /// Getter for permission + /// + pub fn get_permission(&self) -> &u16 { + &self.permission + } + + /// Using the current stored permission, do a bitor operation on the + /// bitflags enums given + /// + pub fn to_octal>( + &self, + vec_flags: Vec, + ) -> c_int { + let mut flags: c_int = T::empty().bits(); + for flag in vec_flags { + flags |= flag.bits(); + } + flags |= self.permission as i32; + flags + } +} + +libc_bitflags! { + /// Valid flags for the third parameter of the function [`shmget`] + pub struct ShmgetFlag: c_int + { + /// A new shared memory segment is created if key has this value. + IPC_PRIVATE; + /// Create a new segment. + /// If this flag is not used, then shmget() will find the segment + /// associated with key and check to see if the user has permission + /// to access the segment. + IPC_CREAT; + /// This flag is used with IPC_CREAT to ensure that this call creates + /// the segment. If the segment already exists, the call fails. + IPC_EXCL; + /// Allocate the segment using "huge" pages. See the Linux kernel + /// source file Documentation/admin-guide/mm/hugetlbpage.rst for + /// further information. + #[cfg(any(target_os = "linux"))] + SHM_HUGETLB; + // TODO: Does not exist in libc/linux, but should? Maybe open an issue in their repo + // SHM_HUGE_2MB; + // TODO: Same for this one + // SHM_HUGE_1GB; + /// This flag serves the same purpose as the mmap(2) MAP_NORESERVE flag. + /// Do not reserve swap space for this segment. When swap space is + /// reserved, one has the guarantee that it is possible to modify the + /// segment. When swap space is not reserved one might get SIGSEGV upon + /// a write if no physical memory is available. See also the discussion + /// of the file /proc/sys/vm/overcommit_memory in proc(5). + #[cfg(any(target_os = "linux"))] + SHM_NORESERVE; + } +} +/// Creates and returns a new, or returns an existing, System V shared memory +/// segment identifier. +/// +/// For more information, see [`shmget(2)`]. +/// +/// [`shmget(2)`]: https://man7.org/linux/man-pages/man2/shmget.2.html +pub fn shmget( + key: key_t, + size: size_t, + shmflg: Vec, + permission: Permissions, +) -> Result { + let flags = permission.to_octal(shmflg); + Errno::result(unsafe { libc::shmget(key, size, flags) }) +} + +libc_bitflags! { + /// Valid flags for the third parameter of the function [`semget`] + pub struct SemgetFlag: c_int + { + /// A new shared memory segment is created if key has this value + IPC_PRIVATE; + /// Create a new segment. + /// If this flag is not used, then shmget() will find the segment + /// associated with key and check to see if the user has permission + /// to access the segment. + IPC_CREAT; + /// This flag is used with IPC_CREAT to ensure that this call creates + /// the segment. If the segment already exists, the call fails. + IPC_EXCL; + } +} +/// Creates and return a new, or returns an existing, System V shared memory +/// semaphore identifier. +/// +/// For more information, see [`semget(2)`]. +/// +/// [`semget(2)`]: https://man7.org/linux/man-pages/man2/semget.2.html +pub fn semget( + key: key_t, + size: i32, + semflg: Vec, + permission: Permissions, +) -> Result { + let flags = permission.to_octal(semflg); + Errno::result(unsafe { libc::semget(key, size, flags) }) +} + +libc_bitflags! { + /// Valid flags for the third parameter of the function [`shmat`] + pub struct ShmatFlag: c_int + { + /// Allow the contents of the segment to be executed. The caller must + /// have execute permission on the segment. + #[cfg(any(target_os = "linux"))] + SHM_EXEC; + #[cfg(any(target_os = "linux"))] + /// This flag specifies that the mapping of the segment should replace + /// any existing mapping in the range starting at shmaddr and + /// continuing for the size of the segment. + /// (Normally, an EINVAL error would result if a mapping already exists + /// in this address range.) + /// In this case, shmaddr must not be NULL. + SHM_REMAP; + /// Attach the segment for read-only access. The process must have read + /// permission for the segment. If this flag is not specified, the + /// segment is attached for read and write access, and the process must + /// have read and write permission for the segment. + /// There is no notion of a write-only shared memory segment. + SHM_RDONLY; + /// TODO: I have no clue at what this does + SHM_RND; + } +} +/// Attaches the System V shared memory segment identified by `shmid` to the +/// address space of the calling process. +/// +/// For more information, see [`shmat(2)`]. +/// +/// # Safety +/// +/// `shmid` should be a valid shared memory identifier and +/// `shmaddr` must meet the requirements described in the [`shmat(2)`] man page. +/// +/// [`shmat(2)`]: https://man7.org/linux/man-pages/man2/shmat.2.html +pub fn shmat( + shmid: c_int, + shmaddr: *const c_void, + shmflg: Vec, + permission: Permissions, +) -> Result<*mut c_void> { + let flags = permission.to_octal(shmflg); + Errno::result(unsafe { libc::shmat(shmid, shmaddr, flags) }) +} + +/// Performs the reverse of [`shmat`], detaching the shared memory segment at +/// the given address from the address space of the calling process. +/// +/// For more information, see [`shmdt(2)`]. +/// +/// # Safety +/// +/// `shmaddr` must meet the requirements described in the [`shmdt(2)`] man page. +/// +/// [`shmdt(2)`]: https://man7.org/linux/man-pages/man2/shmdt.2.html +pub fn shmdt(shmaddr: *const c_void) -> Result<()> { + Errno::result(unsafe { libc::shmdt(shmaddr) }).map(drop) +} + +libc_bitflags! { + /// Valid flags for the second parameter of the function [`shmctl`] + pub struct ShmctlFlag: c_int { + #[cfg(any(target_os = "linux"))] + /// Returns the index of the highest used entry in the kernel's internal + /// array recording information about all shared memory segment + IPC_INFO; + /// Write the values of some members of the shmid_ds structure pointed + /// to by buf to the kernel data structure associated with this shared + /// memory segment, updating also its shm_ctime member. + /// + /// The following fields are updated: shm_perm.uid, + /// shm_perm.gid, and (the least significant 9 bits of) + /// shm_perm.mode. + /// + /// The effective UID of the calling process must match the owner + /// (shm_perm.uid) or creator (shm_perm.cuid) of the shared memory + /// segment, or the caller must be privileged. + IPC_SET; + /// Copy information from the kernel data structure associated with + /// shmid into the shmid_ds structure pointed to by buf. + /// The caller must have read permission on the shared memory segment. + IPC_STAT; + /// Mark the segment to be destroyed. The segment will actually be + /// destroyed only after the last process detaches it + /// (i.e., when the shm_nattch member of the associated structure + /// shmid_ds is zero). + /// The caller must be the owner or creator of the segment, + /// or be privileged. The buf argument is ignored. + /// + /// If a segment has been marked for destruction, then the + /// (nonstandard) SHM_DEST flag of the shm_perm.mode field in the + /// associated data structure retrieved by IPC_STAT will be set. + /// + /// The caller must ensure that a segment is eventually destroyed; + /// otherwise its pages that were faulted in will remain in memory + /// or swap. + /// + /// See also the description of /proc/sys/kernel/shm_rmid_forced + /// in proc(5). + IPC_RMID; + // not available in libc/linux, but should be? + // #[cfg(any(target_os = "linux"))] + // SHM_INFO; + // #[cfg(any(target_os = "linux"))] + // SHM_STAT; + // #[cfg(any(target_os = "linux"))] + // SHM_STAT_ANY; + #[cfg(any(target_os = "linux"))] + /// Prevent swapping of the shared memory segment. The caller must + /// fault in any pages that are required to be present after locking is + /// enabled. + /// If a segment has been locked, then the (nonstandard) SHM_LOCKED + /// flag of the shm_perm.mode field in the associated data structure + /// retrieved by IPC_STAT will be set. + SHM_LOCK; + #[cfg(any(target_os = "linux"))] + /// Unlock the segment, allowing it to be swapped out. + SHM_UNLOCK; + } +} +/// Performs control operation specified by `cmd` on the System V shared +/// memory segment given by `shmid`. +/// +/// For more information, see [`shmctl(2)`]. +/// +/// # Safety +/// +/// All arguments should be valid and meet the requirements described in the [`shmctl(2)`] man page. +/// +/// [`shmctl(2)`]: https://man7.org/linux/man-pages/man2/shmctl.2.html +pub fn shmctl( + shmid: c_int, + cmd: ShmctlFlag, + buf: *mut shmid_ds, + permission: Permissions, +) -> Result { + let command = permission.to_octal(vec![cmd]); + Errno::result(unsafe { libc::shmctl(shmid, command, buf) }) +} + +#[derive(Debug)] +/// Called as the fourth parameter of the function [`semctl`] +/// +pub enum Semun { + /// Value for SETVAL + val(c_int), + /// Buffer for IPC_STAT, IPC_SET + buf(*mut semid_ds), + /// Array for GETALL, SETALL + array(*mut c_short), + /// Buffer for IPC_INFO + #[cfg(any(target_os = "linux"))] + __buf(*mut seminfo), +} +libc_bitflags! { + /// Valid flags for the third parameter of the function [`shmctl`] + pub struct SemctlCmd: c_int { + /// Copy information from the kernel data structure associated with + /// shmid into the shmid_ds structure pointed to by buf. + /// The caller must have read permission on the shared memory segment. + IPC_STAT; + /// Write the values of some members of the semid_ds structure pointed + /// to by arg.buf to the kernel data structure associated with this + /// semaphore set, updating also its sem_ctime member. + /// + /// The following members of the structure are updated: + /// sem_perm.uid, sem_perm.gid, and (the least significant 9 bits of) + /// sem_perm.mode. + /// + /// The effective UID of the calling process must match the owner + /// (sem_perm.uid) or creator (sem_perm.cuid) of the semaphore set, + /// or the caller must be privileged. The argument semnum is ignored. + IPC_SET; + /// Immediately remove the semaphore set, awakening all processes + /// blocked in semop(2) calls on the set + /// (with an error return and errno set to EIDRM). + /// The effective user ID of the calling process must match the creator + /// or owner of the semaphore set, or the caller must be privileged. + /// The argument semnum is ignored. + IPC_RMID; + #[cfg(any(target_os = "linux"))] + /// Return information about system-wide semaphore limits and + /// parameters in the structure pointed to by arg.__buf. This structure + /// is of type [`seminfo`]. + IPC_INFO; + // TODO: None of the one following are defined in libc + // #[cfg(any(target_os = "linux"))] + // SEM_INFO; + // #[cfg(any(target_os = "linux"))] + // SEM_STAT; + // #[cfg(any(target_os = "linux"))] + // SEM_STAT_ANY; + // GETALL; + // GETNCNT; + // GETPID; + // GETVAL; + // GETZCNT; + // SETALL; + // SETVAL; + } +} +/// Performs control operation specified by `cmd` on the System V shared +/// semaphore segment given by `semid`. +/// +/// For more information, see [`semctl(2)`]. +/// +/// # +/// +/// [`semctl(2)`]: https://man7.org/linux/man-pages/man2/semctl.2.html +pub fn semctl( + semid: c_int, + semnum: c_int, + cmd: SemctlCmd, + permission: Permissions, + semun: Option, +) -> Result { + let command = permission.to_octal(vec![cmd]); + if semun.is_none() { + return Errno::result(unsafe { libc::semctl(semid, semnum, command) }); + } + Errno::result(unsafe { libc::semctl(semid, semnum, command, semun) }) +} From 5047036ceb52054d8731d37f9b40b5da8c59e635 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 10:51:02 +0100 Subject: [PATCH 07/58] Fix not compiling on Musl because of missing struct --- src/sys/system_v.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs index fe980775f9..db3887866b 100644 --- a/src/sys/system_v.rs +++ b/src/sys/system_v.rs @@ -288,11 +288,12 @@ pub enum Semun { /// Value for SETVAL val(c_int), /// Buffer for IPC_STAT, IPC_SET + #[cfg(all(target_os = "linux", target_env = "gnu"))] buf(*mut semid_ds), /// Array for GETALL, SETALL array(*mut c_short), /// Buffer for IPC_INFO - #[cfg(any(target_os = "linux"))] + #[cfg(all(target_os = "linux", target_env = "gnu"))] __buf(*mut seminfo), } libc_bitflags! { From b428de0fd9f20517bf1d704ee7b5f2534e87a2c8 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 11:01:04 +0100 Subject: [PATCH 08/58] Fix clippy unsafe ptr arg dereference lint --- src/sys/system_v.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs index db3887866b..a44df2f2d8 100644 --- a/src/sys/system_v.rs +++ b/src/sys/system_v.rs @@ -178,12 +178,16 @@ libc_bitflags! { /// [`shmat(2)`]: https://man7.org/linux/man-pages/man2/shmat.2.html pub fn shmat( shmid: c_int, - shmaddr: *const c_void, + shmaddr: Option, shmflg: Vec, permission: Permissions, ) -> Result<*mut c_void> { + let shmaddr_ptr: *const c_void = match shmaddr { + Some(_) => &mut shmaddr.unwrap(), + None => null(), + }; let flags = permission.to_octal(shmflg); - Errno::result(unsafe { libc::shmat(shmid, shmaddr, flags) }) + Errno::result(unsafe { libc::shmat(shmid, shmaddr_ptr, flags) }) } /// Performs the reverse of [`shmat`], detaching the shared memory segment at @@ -196,8 +200,9 @@ pub fn shmat( /// `shmaddr` must meet the requirements described in the [`shmdt(2)`] man page. /// /// [`shmdt(2)`]: https://man7.org/linux/man-pages/man2/shmdt.2.html -pub fn shmdt(shmaddr: *const c_void) -> Result<()> { - Errno::result(unsafe { libc::shmdt(shmaddr) }).map(drop) +pub fn shmdt(shmaddr: c_void) -> Result<()> { + let shmaddr_ptr: *const c_void = &shmaddr; + Errno::result(unsafe { libc::shmdt(shmaddr_ptr) }).map(drop) } libc_bitflags! { @@ -274,11 +279,15 @@ libc_bitflags! { pub fn shmctl( shmid: c_int, cmd: ShmctlFlag, - buf: *mut shmid_ds, + buf: Option, permission: Permissions, ) -> Result { + let buf_ptr: *mut shmid_ds = match buf { + Some(_) => &mut buf.unwrap(), + None => null_mut(), + }; let command = permission.to_octal(vec![cmd]); - Errno::result(unsafe { libc::shmctl(shmid, command, buf) }) + Errno::result(unsafe { libc::shmctl(shmid, command, buf_ptr) }) } #[derive(Debug)] From 07a6f33a3755c2278e42446befec9ba912fd86e3 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 11:01:18 +0100 Subject: [PATCH 09/58] Formatting parse --- src/sys/system_v.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs index a44df2f2d8..3e36e4168b 100644 --- a/src/sys/system_v.rs +++ b/src/sys/system_v.rs @@ -292,7 +292,7 @@ pub fn shmctl( #[derive(Debug)] /// Called as the fourth parameter of the function [`semctl`] -/// +/// pub enum Semun { /// Value for SETVAL val(c_int), @@ -315,11 +315,11 @@ libc_bitflags! { /// Write the values of some members of the semid_ds structure pointed /// to by arg.buf to the kernel data structure associated with this /// semaphore set, updating also its sem_ctime member. - /// + /// /// The following members of the structure are updated: /// sem_perm.uid, sem_perm.gid, and (the least significant 9 bits of) /// sem_perm.mode. - /// + /// /// The effective UID of the calling process must match the owner /// (sem_perm.uid) or creator (sem_perm.cuid) of the semaphore set, /// or the caller must be privileged. The argument semnum is ignored. From ac33ca0057e8d40d04440791103dee3341f03cc7 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 11:02:45 +0100 Subject: [PATCH 10/58] Add missing import that I forgot to commit --- src/sys/system_v.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs index 3e36e4168b..662ff3b8ad 100644 --- a/src/sys/system_v.rs +++ b/src/sys/system_v.rs @@ -1,9 +1,11 @@ +use std::ptr::{null, null_mut}; + use crate::errno::Errno; use crate::Result; -use libc::{ - self, c_int, c_short, c_void, key_t, shmid_ds, size_t, semid_ds, seminfo -}; +use libc::{self, c_int, c_short, c_void, key_t, shmid_ds, size_t}; +#[cfg(all(target_os = "linux", target_env = "gnu"))] +use libc::{semid_ds, seminfo}; #[derive(Debug, Default, Clone, Copy)] /// Type used to transform a raw number to an octal permission, while performing a clamp to u9 From 11aad71ab00df14770e4b6a2d58145c70a403288 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 11:55:01 +0100 Subject: [PATCH 11/58] Update doctest path that changed --- src/sys/system_v.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs index 662ff3b8ad..3e28e2dcec 100644 --- a/src/sys/system_v.rs +++ b/src/sys/system_v.rs @@ -14,7 +14,7 @@ use libc::{semid_ds, seminfo}; /// /// ``` /// # use nix::errno::Errno; -/// # use nix::sys::mman::Permissions; +/// # use nix::sys::system_v::Permissions; /// /// # fn main() -> Result<(), Errno> { /// assert_eq!(Permissions::new(511)?.get_permission(), &(0o0777 as u16)); From d8db9fadca6dcabb12342d2abc652cf8812cfa98 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 12:00:30 +0100 Subject: [PATCH 12/58] Add basic IPC test. Next step would be to increase coverage by trying the different flags --- test/sys/mod.rs | 1 + test/sys/test_system_v.rs | 110 ++++++++++++++++++++++++++++++++++++++ test/test.rs | 2 + 3 files changed, 113 insertions(+) create mode 100644 test/sys/test_system_v.rs diff --git a/test/sys/mod.rs b/test/sys/mod.rs index fb3f6be0e5..5e042681d1 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -32,6 +32,7 @@ mod test_sockopt; mod test_stat; #[cfg(linux_android)] mod test_sysinfo; +mod test_system_v; #[cfg(not(any( target_os = "redox", target_os = "fuchsia", diff --git a/test/sys/test_system_v.rs b/test/sys/test_system_v.rs new file mode 100644 index 0000000000..cae5c6bc56 --- /dev/null +++ b/test/sys/test_system_v.rs @@ -0,0 +1,110 @@ +use nix::{errno::Errno, sys::system_v::*, Result}; + +use crate::SYSTEMV_MTX; + +const IPC_TEST: i32 = 1337; + +/// Test struct used to try storing data on the IPC +/// +struct IpcStruct { + pub test_info: i64 +} + +#[derive(Debug)] +/// RAII fixture that delete the SystemV IPC and Semaphore on drop +/// +struct FixtureSystemV { + pub id_ipc: i32, + pub id_sem: i32, +} + +impl FixtureSystemV { + fn setup() -> Result { + shmget( + IPC_TEST, + std::mem::size_of::(), + vec![ShmgetFlag::IPC_CREAT, ShmgetFlag::IPC_EXCL], + Permissions::new(0o0777).expect("Octal is smaller than u9"), + )?; + semget( + IPC_TEST, + 1, + vec![SemgetFlag::IPC_CREAT, SemgetFlag::IPC_EXCL], + Permissions::new(0o0777).expect("Octal is smaller than u9"), + )?; + Ok(Self {id_ipc: shmget( + IPC_TEST, + 0, + vec![], + Permissions::new(0o0).expect("Octal is smaller than u9"), + ).expect("IPC exist"), id_sem: semget( + IPC_TEST, + 0, + vec![], + Permissions::new(0o0).expect("Octal is smaller than u9"), + ) + .expect("Sem exist")}) + } +} + +impl Drop for FixtureSystemV { + fn drop(&mut self) { + let _ = shmctl( + self.id_ipc, + ShmctlFlag::IPC_RMID, + None, + Permissions::new(0o0).expect("Octal is smaller than u9"), + ) + .inspect_err(|_| panic!("Failed to delete the test IPC")); + let _ = semctl( + self.id_sem, + 0, + SemctlCmd::IPC_RMID, + Permissions::new(0o0).expect("Octal is smaller than u9"), + None, + ) + .inspect_err(|_| panic!("Failed to delete the test semaphore")); + } +} + +#[test] +fn create_ipc() -> Result<()> { + let _m = SYSTEMV_MTX.lock(); + + FixtureSystemV::setup()?; + Ok(()) +} + +#[test] +fn create_ipc_already_exist() -> Result<()> { + let _m = SYSTEMV_MTX.lock(); + + // Keep the IPC in scope, so we don't destroy it + let _ipc = FixtureSystemV::setup()?; + let expected = Errno::EEXIST; + let actual = FixtureSystemV::setup().expect_err("Return EExist"); + + assert_eq!(expected, actual); + Ok(()) +} + +#[test] +fn create_ipc_and_get_value() -> Result<()> { + let _m = SYSTEMV_MTX.lock(); + + let ipc = FixtureSystemV::setup()?; + let mem: *mut IpcStruct = shmat( + ipc.id_ipc, + None, + vec![], + Permissions::new(0o0).expect("Octal is smaller than u9") + )?.cast(); + + let expected = 0xDEADBEEF; + unsafe { + mem.as_mut().unwrap().test_info = expected; + assert_eq!(expected, mem.read().test_info); + } + + Ok(()) +} diff --git a/test/test.rs b/test/test.rs index c7231426c2..8d032580a1 100644 --- a/test/test.rs +++ b/test/test.rs @@ -75,6 +75,8 @@ pub static KMOD_MTX: Mutex<()> = Mutex::new(()); pub static PTSNAME_MTX: Mutex<()> = Mutex::new(()); /// Any test that alters signal handling must grab this mutex. pub static SIGNAL_MTX: Mutex<()> = Mutex::new(()); +/// Any test that use SystemV must grab this mutex. +pub static SYSTEMV_MTX: Mutex<()> = Mutex::new(()); /// RAII object that restores a test's original directory on drop struct DirRestore<'a> { From bd938e6fb13d40d8929c592942240c02f4f85384 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 12:03:27 +0100 Subject: [PATCH 13/58] Fix using latest Rust feature that was unstable before --- test/sys/test_system_v.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/sys/test_system_v.rs b/test/sys/test_system_v.rs index cae5c6bc56..b9aa59d8dd 100644 --- a/test/sys/test_system_v.rs +++ b/test/sys/test_system_v.rs @@ -55,7 +55,7 @@ impl Drop for FixtureSystemV { None, Permissions::new(0o0).expect("Octal is smaller than u9"), ) - .inspect_err(|_| panic!("Failed to delete the test IPC")); + .map_err(|_| panic!("Failed to delete the test IPC")); let _ = semctl( self.id_sem, 0, @@ -63,7 +63,7 @@ impl Drop for FixtureSystemV { Permissions::new(0o0).expect("Octal is smaller than u9"), None, ) - .inspect_err(|_| panic!("Failed to delete the test semaphore")); + .map_err(|_| panic!("Failed to delete the test semaphore")); } } From b9a59058ed374b02d59bb3711e05115748294b65 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 12:08:43 +0100 Subject: [PATCH 14/58] Forgot a formatting parse --- test/sys/test_system_v.rs | 41 ++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/test/sys/test_system_v.rs b/test/sys/test_system_v.rs index b9aa59d8dd..4bff428c1a 100644 --- a/test/sys/test_system_v.rs +++ b/test/sys/test_system_v.rs @@ -5,14 +5,14 @@ use crate::SYSTEMV_MTX; const IPC_TEST: i32 = 1337; /// Test struct used to try storing data on the IPC -/// +/// struct IpcStruct { - pub test_info: i64 + pub test_info: i64, } #[derive(Debug)] /// RAII fixture that delete the SystemV IPC and Semaphore on drop -/// +/// struct FixtureSystemV { pub id_ipc: i32, pub id_sem: i32, @@ -32,18 +32,22 @@ impl FixtureSystemV { vec![SemgetFlag::IPC_CREAT, SemgetFlag::IPC_EXCL], Permissions::new(0o0777).expect("Octal is smaller than u9"), )?; - Ok(Self {id_ipc: shmget( - IPC_TEST, - 0, - vec![], - Permissions::new(0o0).expect("Octal is smaller than u9"), - ).expect("IPC exist"), id_sem: semget( - IPC_TEST, - 0, - vec![], - Permissions::new(0o0).expect("Octal is smaller than u9"), - ) - .expect("Sem exist")}) + Ok(Self { + id_ipc: shmget( + IPC_TEST, + 0, + vec![], + Permissions::new(0o0).expect("Octal is smaller than u9"), + ) + .expect("IPC exist"), + id_sem: semget( + IPC_TEST, + 0, + vec![], + Permissions::new(0o0).expect("Octal is smaller than u9"), + ) + .expect("Sem exist"), + }) } } @@ -97,14 +101,15 @@ fn create_ipc_and_get_value() -> Result<()> { ipc.id_ipc, None, vec![], - Permissions::new(0o0).expect("Octal is smaller than u9") - )?.cast(); + Permissions::new(0o0).expect("Octal is smaller than u9"), + )? + .cast(); let expected = 0xDEADBEEF; unsafe { mem.as_mut().unwrap().test_info = expected; assert_eq!(expected, mem.read().test_info); } - + Ok(()) } From 50916a432498ebc04e2c8e6a9d902ccf4b72b442 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 12:30:51 +0100 Subject: [PATCH 15/58] Enable SystemV only for linux, but not Android SystemV is disabled on Android, see [here](https://android.googlesource.com/platform/ndk/+/4e159d95ebf23b5f72bb707b0cb1518ef96b3d03/docs/system/libc/SYSV-IPC.TXT) --- src/sys/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sys/mod.rs b/src/sys/mod.rs index d365f31961..f1361f2d1d 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -144,6 +144,10 @@ feature! { pub mod sysinfo; #[allow(missing_docs)] +#[cfg(any( + target_os = "linux", + not(linux_android) +))] pub mod system_v; feature! { From f3e4771b9ae258268361d015a74127c8d0ee5ea1 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 12:32:39 +0100 Subject: [PATCH 16/58] Update conditional compilation for GNU extension --- src/sys/system_v.rs | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs index 3e28e2dcec..6d3c3ca0a6 100644 --- a/src/sys/system_v.rs +++ b/src/sys/system_v.rs @@ -3,9 +3,9 @@ use std::ptr::{null, null_mut}; use crate::errno::Errno; use crate::Result; -use libc::{self, c_int, c_short, c_void, key_t, shmid_ds, size_t}; -#[cfg(all(target_os = "linux", target_env = "gnu"))] -use libc::{semid_ds, seminfo}; +use libc::{self, c_int, c_short, c_void, key_t, size_t}; +#[cfg(target_env = "gnu")] +use libc::{shmid_ds, semid_ds, seminfo}; #[derive(Debug, Default, Clone, Copy)] /// Type used to transform a raw number to an octal permission, while performing a clamp to u9 @@ -77,7 +77,6 @@ libc_bitflags! { /// Allocate the segment using "huge" pages. See the Linux kernel /// source file Documentation/admin-guide/mm/hugetlbpage.rst for /// further information. - #[cfg(any(target_os = "linux"))] SHM_HUGETLB; // TODO: Does not exist in libc/linux, but should? Maybe open an issue in their repo // SHM_HUGE_2MB; @@ -89,7 +88,6 @@ libc_bitflags! { /// segment. When swap space is not reserved one might get SIGSEGV upon /// a write if no physical memory is available. See also the discussion /// of the file /proc/sys/vm/overcommit_memory in proc(5). - #[cfg(any(target_os = "linux"))] SHM_NORESERVE; } } @@ -147,9 +145,7 @@ libc_bitflags! { { /// Allow the contents of the segment to be executed. The caller must /// have execute permission on the segment. - #[cfg(any(target_os = "linux"))] SHM_EXEC; - #[cfg(any(target_os = "linux"))] /// This flag specifies that the mapping of the segment should replace /// any existing mapping in the range starting at shmaddr and /// continuing for the size of the segment. @@ -210,7 +206,6 @@ pub fn shmdt(shmaddr: c_void) -> Result<()> { libc_bitflags! { /// Valid flags for the second parameter of the function [`shmctl`] pub struct ShmctlFlag: c_int { - #[cfg(any(target_os = "linux"))] /// Returns the index of the highest used entry in the kernel's internal /// array recording information about all shared memory segment IPC_INFO; @@ -249,13 +244,9 @@ libc_bitflags! { /// in proc(5). IPC_RMID; // not available in libc/linux, but should be? - // #[cfg(any(target_os = "linux"))] // SHM_INFO; - // #[cfg(any(target_os = "linux"))] // SHM_STAT; - // #[cfg(any(target_os = "linux"))] // SHM_STAT_ANY; - #[cfg(any(target_os = "linux"))] /// Prevent swapping of the shared memory segment. The caller must /// fault in any pages that are required to be present after locking is /// enabled. @@ -263,7 +254,6 @@ libc_bitflags! { /// flag of the shm_perm.mode field in the associated data structure /// retrieved by IPC_STAT will be set. SHM_LOCK; - #[cfg(any(target_os = "linux"))] /// Unlock the segment, allowing it to be swapped out. SHM_UNLOCK; } @@ -299,12 +289,12 @@ pub enum Semun { /// Value for SETVAL val(c_int), /// Buffer for IPC_STAT, IPC_SET - #[cfg(all(target_os = "linux", target_env = "gnu"))] + #[cfg(target_env = "gnu")] buf(*mut semid_ds), /// Array for GETALL, SETALL array(*mut c_short), /// Buffer for IPC_INFO - #[cfg(all(target_os = "linux", target_env = "gnu"))] + #[cfg(target_env = "gnu")] __buf(*mut seminfo), } libc_bitflags! { @@ -333,17 +323,13 @@ libc_bitflags! { /// or owner of the semaphore set, or the caller must be privileged. /// The argument semnum is ignored. IPC_RMID; - #[cfg(any(target_os = "linux"))] /// Return information about system-wide semaphore limits and /// parameters in the structure pointed to by arg.__buf. This structure /// is of type [`seminfo`]. IPC_INFO; - // TODO: None of the one following are defined in libc - // #[cfg(any(target_os = "linux"))] + // TODO: None of the one following are defined in libc/linux // SEM_INFO; - // #[cfg(any(target_os = "linux"))] // SEM_STAT; - // #[cfg(any(target_os = "linux"))] // SEM_STAT_ANY; // GETALL; // GETNCNT; From 9c71d976b2c0c00b76c169595138742ff01fc90f Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 12:35:18 +0100 Subject: [PATCH 17/58] Fix macro not compiling for NetBSD --- src/sys/system_v.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs index 6d3c3ca0a6..2872885ced 100644 --- a/src/sys/system_v.rs +++ b/src/sys/system_v.rs @@ -60,7 +60,7 @@ impl Permissions { } } -libc_bitflags! { +libc_bitflags!( /// Valid flags for the third parameter of the function [`shmget`] pub struct ShmgetFlag: c_int { @@ -90,7 +90,7 @@ libc_bitflags! { /// of the file /proc/sys/vm/overcommit_memory in proc(5). SHM_NORESERVE; } -} +); /// Creates and returns a new, or returns an existing, System V shared memory /// segment identifier. /// @@ -107,7 +107,7 @@ pub fn shmget( Errno::result(unsafe { libc::shmget(key, size, flags) }) } -libc_bitflags! { +libc_bitflags!( /// Valid flags for the third parameter of the function [`semget`] pub struct SemgetFlag: c_int { @@ -122,7 +122,7 @@ libc_bitflags! { /// the segment. If the segment already exists, the call fails. IPC_EXCL; } -} +); /// Creates and return a new, or returns an existing, System V shared memory /// semaphore identifier. /// @@ -203,7 +203,7 @@ pub fn shmdt(shmaddr: c_void) -> Result<()> { Errno::result(unsafe { libc::shmdt(shmaddr_ptr) }).map(drop) } -libc_bitflags! { +libc_bitflags!( /// Valid flags for the second parameter of the function [`shmctl`] pub struct ShmctlFlag: c_int { /// Returns the index of the highest used entry in the kernel's internal @@ -257,7 +257,7 @@ libc_bitflags! { /// Unlock the segment, allowing it to be swapped out. SHM_UNLOCK; } -} +); /// Performs control operation specified by `cmd` on the System V shared /// memory segment given by `shmid`. /// @@ -297,7 +297,7 @@ pub enum Semun { #[cfg(target_env = "gnu")] __buf(*mut seminfo), } -libc_bitflags! { +libc_bitflags! ( /// Valid flags for the third parameter of the function [`shmctl`] pub struct SemctlCmd: c_int { /// Copy information from the kernel data structure associated with @@ -339,7 +339,7 @@ libc_bitflags! { // SETALL; // SETVAL; } -} +); /// Performs control operation specified by `cmd` on the System V shared /// semaphore segment given by `semid`. /// From 5d0d37c87c37b98b8732e10c93e5f724c47763fa Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 12:40:27 +0100 Subject: [PATCH 18/58] Fix moving shmid_ds to GNU extension when it's not --- src/sys/system_v.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs index 2872885ced..4b13822f46 100644 --- a/src/sys/system_v.rs +++ b/src/sys/system_v.rs @@ -3,9 +3,9 @@ use std::ptr::{null, null_mut}; use crate::errno::Errno; use crate::Result; -use libc::{self, c_int, c_short, c_void, key_t, size_t}; +use libc::{self, c_int, c_short, c_void, key_t, size_t, shmid_ds}; #[cfg(target_env = "gnu")] -use libc::{shmid_ds, semid_ds, seminfo}; +use libc::{semid_ds, seminfo}; #[derive(Debug, Default, Clone, Copy)] /// Type used to transform a raw number to an octal permission, while performing a clamp to u9 From 315eb3130353aeef47edfefb9a8f01a841bc762a Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:07:13 +0100 Subject: [PATCH 19/58] Fix mistake in conditional compilation that didn't disable SystemV for android --- src/sys/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sys/mod.rs b/src/sys/mod.rs index f1361f2d1d..9ca62d0fec 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -144,10 +144,7 @@ feature! { pub mod sysinfo; #[allow(missing_docs)] -#[cfg(any( - target_os = "linux", - not(linux_android) -))] +#[cfg(any(bsd, target_os = "linux", not(target_os = "android")))] pub mod system_v; feature! { From 9374dcf043f079c4962a1a7aa886ef4a5252a3cc Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:07:55 +0100 Subject: [PATCH 20/58] Add back cfg that disable some flags for BSD --- src/sys/system_v.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs index 4b13822f46..2653cc2a4a 100644 --- a/src/sys/system_v.rs +++ b/src/sys/system_v.rs @@ -77,6 +77,7 @@ libc_bitflags!( /// Allocate the segment using "huge" pages. See the Linux kernel /// source file Documentation/admin-guide/mm/hugetlbpage.rst for /// further information. + #[cfg(linux)] SHM_HUGETLB; // TODO: Does not exist in libc/linux, but should? Maybe open an issue in their repo // SHM_HUGE_2MB; @@ -88,6 +89,7 @@ libc_bitflags!( /// segment. When swap space is not reserved one might get SIGSEGV upon /// a write if no physical memory is available. See also the discussion /// of the file /proc/sys/vm/overcommit_memory in proc(5). + #[cfg(linux)] SHM_NORESERVE; } ); @@ -145,6 +147,7 @@ libc_bitflags! { { /// Allow the contents of the segment to be executed. The caller must /// have execute permission on the segment. + #[cfg(linux)] SHM_EXEC; /// This flag specifies that the mapping of the segment should replace /// any existing mapping in the range starting at shmaddr and @@ -152,6 +155,7 @@ libc_bitflags! { /// (Normally, an EINVAL error would result if a mapping already exists /// in this address range.) /// In this case, shmaddr must not be NULL. + #[cfg(linux)] SHM_REMAP; /// Attach the segment for read-only access. The process must have read /// permission for the segment. If this flag is not specified, the @@ -208,6 +212,7 @@ libc_bitflags!( pub struct ShmctlFlag: c_int { /// Returns the index of the highest used entry in the kernel's internal /// array recording information about all shared memory segment + #[cfg(linux)] IPC_INFO; /// Write the values of some members of the shmid_ds structure pointed /// to by buf to the kernel data structure associated with this shared @@ -253,8 +258,10 @@ libc_bitflags!( /// If a segment has been locked, then the (nonstandard) SHM_LOCKED /// flag of the shm_perm.mode field in the associated data structure /// retrieved by IPC_STAT will be set. + #[cfg(linux)] SHM_LOCK; /// Unlock the segment, allowing it to be swapped out. + #[cfg(linux)] SHM_UNLOCK; } ); @@ -282,6 +289,20 @@ pub fn shmctl( Errno::result(unsafe { libc::shmctl(shmid, command, buf_ptr) }) } +libc_bitflags! { + /// Valid flags for the fourth parameter of the function [`semop`] + pub struct SemopFlag: c_int { + /// Fail the operation instead of blocking if it can't be done. + /// If it fails, return [`Errno::EAGAIN`] + IPC_NOWAIT; + // TODO: Not available in libc + // SEM_UNDO; + } +} +// pub fn semop(semid: c_int, semopflg: Vec, sops: *mut sembuf, nsops: size_t) -> Result<()> { +// Errno::result(unsafe { libc::semop(semid, sops, nsops) }) +// } + #[derive(Debug)] /// Called as the fourth parameter of the function [`semctl`] /// @@ -326,6 +347,7 @@ libc_bitflags! ( /// Return information about system-wide semaphore limits and /// parameters in the structure pointed to by arg.__buf. This structure /// is of type [`seminfo`]. + #[cfg(linux)] IPC_INFO; // TODO: None of the one following are defined in libc/linux // SEM_INFO; From bb07d5b50f8c858ffd147043e7dd0b0699857c2f Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 14:18:17 +0100 Subject: [PATCH 21/58] Final (I hope) formatting pass --- src/sys/system_v.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs index 2653cc2a4a..49507d2632 100644 --- a/src/sys/system_v.rs +++ b/src/sys/system_v.rs @@ -3,7 +3,7 @@ use std::ptr::{null, null_mut}; use crate::errno::Errno; use crate::Result; -use libc::{self, c_int, c_short, c_void, key_t, size_t, shmid_ds}; +use libc::{self, c_int, c_short, c_void, key_t, shmid_ds, size_t}; #[cfg(target_env = "gnu")] use libc::{semid_ds, seminfo}; From 19c713dc7437f49d5c3ac3e21b91cdef5903b3b2 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 20 Feb 2024 14:43:27 +0100 Subject: [PATCH 22/58] Disable test for non systemV platform --- test/sys/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/sys/mod.rs b/test/sys/mod.rs index 5e042681d1..2b3bdf2b35 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -32,6 +32,7 @@ mod test_sockopt; mod test_stat; #[cfg(linux_android)] mod test_sysinfo; +#[cfg(any(bsd, target_os = "linux", not(target_os = "android")))] mod test_system_v; #[cfg(not(any( target_os = "redox", From 17561e64fa09e6ac211179b8b1d078369e2f269c Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Thu, 21 Mar 2024 14:04:16 +0100 Subject: [PATCH 23/58] Fix wrong changelog number and content --- changelog/2313.added.md | 1 - changelog/2314.added.md | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changelog/2313.added.md create mode 100644 changelog/2314.added.md diff --git a/changelog/2313.added.md b/changelog/2313.added.md deleted file mode 100644 index 483aa48d7b..0000000000 --- a/changelog/2313.added.md +++ /dev/null @@ -1 +0,0 @@ -Add SystemV IPC support diff --git a/changelog/2314.added.md b/changelog/2314.added.md new file mode 100644 index 0000000000..6eafab1de3 --- /dev/null +++ b/changelog/2314.added.md @@ -0,0 +1 @@ +Add SystemV semaphore set and shared memory segments support. From 2b76711b5a625f6e45fc5f051e2c25aeeb6b5a0c Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Fri, 22 Mar 2024 16:51:00 +0100 Subject: [PATCH 24/58] Remove first try at SystemV, replacing with refractor --- src/sys/system_v.rs | 385 -------------------------------------- test/sys/mod.rs | 2 - test/sys/test_system_v.rs | 115 ------------ 3 files changed, 502 deletions(-) delete mode 100644 src/sys/system_v.rs delete mode 100644 test/sys/test_system_v.rs diff --git a/src/sys/system_v.rs b/src/sys/system_v.rs deleted file mode 100644 index 49507d2632..0000000000 --- a/src/sys/system_v.rs +++ /dev/null @@ -1,385 +0,0 @@ -use std::ptr::{null, null_mut}; - -use crate::errno::Errno; -use crate::Result; - -use libc::{self, c_int, c_short, c_void, key_t, shmid_ds, size_t}; -#[cfg(target_env = "gnu")] -use libc::{semid_ds, seminfo}; - -#[derive(Debug, Default, Clone, Copy)] -/// Type used to transform a raw number to an octal permission, while performing a clamp to u9 -/// -/// # Example -/// -/// ``` -/// # use nix::errno::Errno; -/// # use nix::sys::system_v::Permissions; -/// -/// # fn main() -> Result<(), Errno> { -/// assert_eq!(Permissions::new(511)?.get_permission(), &(0o0777 as u16)); -/// assert_eq!(Permissions::new(512).expect_err("512 is bigger than what u9 can store"), Errno::E2BIG); -/// # Ok(()) -/// # } -/// ``` -pub struct Permissions { - permission: u16, -} - -impl Permissions { - /// Create a new Permissions object - /// - /// Clamp to a u9 size, return Errno::E2BIG if it fails - /// - pub fn new(octal: u16) -> Result { - if octal >= 2_u16.pow(9) { - return Err(Errno::E2BIG); - } - Ok(Permissions { permission: octal }) - } - - /// Getter for permission - /// - pub fn get_permission(&self) -> &u16 { - &self.permission - } - - /// Using the current stored permission, do a bitor operation on the - /// bitflags enums given - /// - pub fn to_octal>( - &self, - vec_flags: Vec, - ) -> c_int { - let mut flags: c_int = T::empty().bits(); - for flag in vec_flags { - flags |= flag.bits(); - } - flags |= self.permission as i32; - flags - } -} - -libc_bitflags!( - /// Valid flags for the third parameter of the function [`shmget`] - pub struct ShmgetFlag: c_int - { - /// A new shared memory segment is created if key has this value. - IPC_PRIVATE; - /// Create a new segment. - /// If this flag is not used, then shmget() will find the segment - /// associated with key and check to see if the user has permission - /// to access the segment. - IPC_CREAT; - /// This flag is used with IPC_CREAT to ensure that this call creates - /// the segment. If the segment already exists, the call fails. - IPC_EXCL; - /// Allocate the segment using "huge" pages. See the Linux kernel - /// source file Documentation/admin-guide/mm/hugetlbpage.rst for - /// further information. - #[cfg(linux)] - SHM_HUGETLB; - // TODO: Does not exist in libc/linux, but should? Maybe open an issue in their repo - // SHM_HUGE_2MB; - // TODO: Same for this one - // SHM_HUGE_1GB; - /// This flag serves the same purpose as the mmap(2) MAP_NORESERVE flag. - /// Do not reserve swap space for this segment. When swap space is - /// reserved, one has the guarantee that it is possible to modify the - /// segment. When swap space is not reserved one might get SIGSEGV upon - /// a write if no physical memory is available. See also the discussion - /// of the file /proc/sys/vm/overcommit_memory in proc(5). - #[cfg(linux)] - SHM_NORESERVE; - } -); -/// Creates and returns a new, or returns an existing, System V shared memory -/// segment identifier. -/// -/// For more information, see [`shmget(2)`]. -/// -/// [`shmget(2)`]: https://man7.org/linux/man-pages/man2/shmget.2.html -pub fn shmget( - key: key_t, - size: size_t, - shmflg: Vec, - permission: Permissions, -) -> Result { - let flags = permission.to_octal(shmflg); - Errno::result(unsafe { libc::shmget(key, size, flags) }) -} - -libc_bitflags!( - /// Valid flags for the third parameter of the function [`semget`] - pub struct SemgetFlag: c_int - { - /// A new shared memory segment is created if key has this value - IPC_PRIVATE; - /// Create a new segment. - /// If this flag is not used, then shmget() will find the segment - /// associated with key and check to see if the user has permission - /// to access the segment. - IPC_CREAT; - /// This flag is used with IPC_CREAT to ensure that this call creates - /// the segment. If the segment already exists, the call fails. - IPC_EXCL; - } -); -/// Creates and return a new, or returns an existing, System V shared memory -/// semaphore identifier. -/// -/// For more information, see [`semget(2)`]. -/// -/// [`semget(2)`]: https://man7.org/linux/man-pages/man2/semget.2.html -pub fn semget( - key: key_t, - size: i32, - semflg: Vec, - permission: Permissions, -) -> Result { - let flags = permission.to_octal(semflg); - Errno::result(unsafe { libc::semget(key, size, flags) }) -} - -libc_bitflags! { - /// Valid flags for the third parameter of the function [`shmat`] - pub struct ShmatFlag: c_int - { - /// Allow the contents of the segment to be executed. The caller must - /// have execute permission on the segment. - #[cfg(linux)] - SHM_EXEC; - /// This flag specifies that the mapping of the segment should replace - /// any existing mapping in the range starting at shmaddr and - /// continuing for the size of the segment. - /// (Normally, an EINVAL error would result if a mapping already exists - /// in this address range.) - /// In this case, shmaddr must not be NULL. - #[cfg(linux)] - SHM_REMAP; - /// Attach the segment for read-only access. The process must have read - /// permission for the segment. If this flag is not specified, the - /// segment is attached for read and write access, and the process must - /// have read and write permission for the segment. - /// There is no notion of a write-only shared memory segment. - SHM_RDONLY; - /// TODO: I have no clue at what this does - SHM_RND; - } -} -/// Attaches the System V shared memory segment identified by `shmid` to the -/// address space of the calling process. -/// -/// For more information, see [`shmat(2)`]. -/// -/// # Safety -/// -/// `shmid` should be a valid shared memory identifier and -/// `shmaddr` must meet the requirements described in the [`shmat(2)`] man page. -/// -/// [`shmat(2)`]: https://man7.org/linux/man-pages/man2/shmat.2.html -pub fn shmat( - shmid: c_int, - shmaddr: Option, - shmflg: Vec, - permission: Permissions, -) -> Result<*mut c_void> { - let shmaddr_ptr: *const c_void = match shmaddr { - Some(_) => &mut shmaddr.unwrap(), - None => null(), - }; - let flags = permission.to_octal(shmflg); - Errno::result(unsafe { libc::shmat(shmid, shmaddr_ptr, flags) }) -} - -/// Performs the reverse of [`shmat`], detaching the shared memory segment at -/// the given address from the address space of the calling process. -/// -/// For more information, see [`shmdt(2)`]. -/// -/// # Safety -/// -/// `shmaddr` must meet the requirements described in the [`shmdt(2)`] man page. -/// -/// [`shmdt(2)`]: https://man7.org/linux/man-pages/man2/shmdt.2.html -pub fn shmdt(shmaddr: c_void) -> Result<()> { - let shmaddr_ptr: *const c_void = &shmaddr; - Errno::result(unsafe { libc::shmdt(shmaddr_ptr) }).map(drop) -} - -libc_bitflags!( - /// Valid flags for the second parameter of the function [`shmctl`] - pub struct ShmctlFlag: c_int { - /// Returns the index of the highest used entry in the kernel's internal - /// array recording information about all shared memory segment - #[cfg(linux)] - IPC_INFO; - /// Write the values of some members of the shmid_ds structure pointed - /// to by buf to the kernel data structure associated with this shared - /// memory segment, updating also its shm_ctime member. - /// - /// The following fields are updated: shm_perm.uid, - /// shm_perm.gid, and (the least significant 9 bits of) - /// shm_perm.mode. - /// - /// The effective UID of the calling process must match the owner - /// (shm_perm.uid) or creator (shm_perm.cuid) of the shared memory - /// segment, or the caller must be privileged. - IPC_SET; - /// Copy information from the kernel data structure associated with - /// shmid into the shmid_ds structure pointed to by buf. - /// The caller must have read permission on the shared memory segment. - IPC_STAT; - /// Mark the segment to be destroyed. The segment will actually be - /// destroyed only after the last process detaches it - /// (i.e., when the shm_nattch member of the associated structure - /// shmid_ds is zero). - /// The caller must be the owner or creator of the segment, - /// or be privileged. The buf argument is ignored. - /// - /// If a segment has been marked for destruction, then the - /// (nonstandard) SHM_DEST flag of the shm_perm.mode field in the - /// associated data structure retrieved by IPC_STAT will be set. - /// - /// The caller must ensure that a segment is eventually destroyed; - /// otherwise its pages that were faulted in will remain in memory - /// or swap. - /// - /// See also the description of /proc/sys/kernel/shm_rmid_forced - /// in proc(5). - IPC_RMID; - // not available in libc/linux, but should be? - // SHM_INFO; - // SHM_STAT; - // SHM_STAT_ANY; - /// Prevent swapping of the shared memory segment. The caller must - /// fault in any pages that are required to be present after locking is - /// enabled. - /// If a segment has been locked, then the (nonstandard) SHM_LOCKED - /// flag of the shm_perm.mode field in the associated data structure - /// retrieved by IPC_STAT will be set. - #[cfg(linux)] - SHM_LOCK; - /// Unlock the segment, allowing it to be swapped out. - #[cfg(linux)] - SHM_UNLOCK; - } -); -/// Performs control operation specified by `cmd` on the System V shared -/// memory segment given by `shmid`. -/// -/// For more information, see [`shmctl(2)`]. -/// -/// # Safety -/// -/// All arguments should be valid and meet the requirements described in the [`shmctl(2)`] man page. -/// -/// [`shmctl(2)`]: https://man7.org/linux/man-pages/man2/shmctl.2.html -pub fn shmctl( - shmid: c_int, - cmd: ShmctlFlag, - buf: Option, - permission: Permissions, -) -> Result { - let buf_ptr: *mut shmid_ds = match buf { - Some(_) => &mut buf.unwrap(), - None => null_mut(), - }; - let command = permission.to_octal(vec![cmd]); - Errno::result(unsafe { libc::shmctl(shmid, command, buf_ptr) }) -} - -libc_bitflags! { - /// Valid flags for the fourth parameter of the function [`semop`] - pub struct SemopFlag: c_int { - /// Fail the operation instead of blocking if it can't be done. - /// If it fails, return [`Errno::EAGAIN`] - IPC_NOWAIT; - // TODO: Not available in libc - // SEM_UNDO; - } -} -// pub fn semop(semid: c_int, semopflg: Vec, sops: *mut sembuf, nsops: size_t) -> Result<()> { -// Errno::result(unsafe { libc::semop(semid, sops, nsops) }) -// } - -#[derive(Debug)] -/// Called as the fourth parameter of the function [`semctl`] -/// -pub enum Semun { - /// Value for SETVAL - val(c_int), - /// Buffer for IPC_STAT, IPC_SET - #[cfg(target_env = "gnu")] - buf(*mut semid_ds), - /// Array for GETALL, SETALL - array(*mut c_short), - /// Buffer for IPC_INFO - #[cfg(target_env = "gnu")] - __buf(*mut seminfo), -} -libc_bitflags! ( - /// Valid flags for the third parameter of the function [`shmctl`] - pub struct SemctlCmd: c_int { - /// Copy information from the kernel data structure associated with - /// shmid into the shmid_ds structure pointed to by buf. - /// The caller must have read permission on the shared memory segment. - IPC_STAT; - /// Write the values of some members of the semid_ds structure pointed - /// to by arg.buf to the kernel data structure associated with this - /// semaphore set, updating also its sem_ctime member. - /// - /// The following members of the structure are updated: - /// sem_perm.uid, sem_perm.gid, and (the least significant 9 bits of) - /// sem_perm.mode. - /// - /// The effective UID of the calling process must match the owner - /// (sem_perm.uid) or creator (sem_perm.cuid) of the semaphore set, - /// or the caller must be privileged. The argument semnum is ignored. - IPC_SET; - /// Immediately remove the semaphore set, awakening all processes - /// blocked in semop(2) calls on the set - /// (with an error return and errno set to EIDRM). - /// The effective user ID of the calling process must match the creator - /// or owner of the semaphore set, or the caller must be privileged. - /// The argument semnum is ignored. - IPC_RMID; - /// Return information about system-wide semaphore limits and - /// parameters in the structure pointed to by arg.__buf. This structure - /// is of type [`seminfo`]. - #[cfg(linux)] - IPC_INFO; - // TODO: None of the one following are defined in libc/linux - // SEM_INFO; - // SEM_STAT; - // SEM_STAT_ANY; - // GETALL; - // GETNCNT; - // GETPID; - // GETVAL; - // GETZCNT; - // SETALL; - // SETVAL; - } -); -/// Performs control operation specified by `cmd` on the System V shared -/// semaphore segment given by `semid`. -/// -/// For more information, see [`semctl(2)`]. -/// -/// # -/// -/// [`semctl(2)`]: https://man7.org/linux/man-pages/man2/semctl.2.html -pub fn semctl( - semid: c_int, - semnum: c_int, - cmd: SemctlCmd, - permission: Permissions, - semun: Option, -) -> Result { - let command = permission.to_octal(vec![cmd]); - if semun.is_none() { - return Errno::result(unsafe { libc::semctl(semid, semnum, command) }); - } - Errno::result(unsafe { libc::semctl(semid, semnum, command, semun) }) -} diff --git a/test/sys/mod.rs b/test/sys/mod.rs index 2b3bdf2b35..fb3f6be0e5 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -32,8 +32,6 @@ mod test_sockopt; mod test_stat; #[cfg(linux_android)] mod test_sysinfo; -#[cfg(any(bsd, target_os = "linux", not(target_os = "android")))] -mod test_system_v; #[cfg(not(any( target_os = "redox", target_os = "fuchsia", diff --git a/test/sys/test_system_v.rs b/test/sys/test_system_v.rs deleted file mode 100644 index 4bff428c1a..0000000000 --- a/test/sys/test_system_v.rs +++ /dev/null @@ -1,115 +0,0 @@ -use nix::{errno::Errno, sys::system_v::*, Result}; - -use crate::SYSTEMV_MTX; - -const IPC_TEST: i32 = 1337; - -/// Test struct used to try storing data on the IPC -/// -struct IpcStruct { - pub test_info: i64, -} - -#[derive(Debug)] -/// RAII fixture that delete the SystemV IPC and Semaphore on drop -/// -struct FixtureSystemV { - pub id_ipc: i32, - pub id_sem: i32, -} - -impl FixtureSystemV { - fn setup() -> Result { - shmget( - IPC_TEST, - std::mem::size_of::(), - vec![ShmgetFlag::IPC_CREAT, ShmgetFlag::IPC_EXCL], - Permissions::new(0o0777).expect("Octal is smaller than u9"), - )?; - semget( - IPC_TEST, - 1, - vec![SemgetFlag::IPC_CREAT, SemgetFlag::IPC_EXCL], - Permissions::new(0o0777).expect("Octal is smaller than u9"), - )?; - Ok(Self { - id_ipc: shmget( - IPC_TEST, - 0, - vec![], - Permissions::new(0o0).expect("Octal is smaller than u9"), - ) - .expect("IPC exist"), - id_sem: semget( - IPC_TEST, - 0, - vec![], - Permissions::new(0o0).expect("Octal is smaller than u9"), - ) - .expect("Sem exist"), - }) - } -} - -impl Drop for FixtureSystemV { - fn drop(&mut self) { - let _ = shmctl( - self.id_ipc, - ShmctlFlag::IPC_RMID, - None, - Permissions::new(0o0).expect("Octal is smaller than u9"), - ) - .map_err(|_| panic!("Failed to delete the test IPC")); - let _ = semctl( - self.id_sem, - 0, - SemctlCmd::IPC_RMID, - Permissions::new(0o0).expect("Octal is smaller than u9"), - None, - ) - .map_err(|_| panic!("Failed to delete the test semaphore")); - } -} - -#[test] -fn create_ipc() -> Result<()> { - let _m = SYSTEMV_MTX.lock(); - - FixtureSystemV::setup()?; - Ok(()) -} - -#[test] -fn create_ipc_already_exist() -> Result<()> { - let _m = SYSTEMV_MTX.lock(); - - // Keep the IPC in scope, so we don't destroy it - let _ipc = FixtureSystemV::setup()?; - let expected = Errno::EEXIST; - let actual = FixtureSystemV::setup().expect_err("Return EExist"); - - assert_eq!(expected, actual); - Ok(()) -} - -#[test] -fn create_ipc_and_get_value() -> Result<()> { - let _m = SYSTEMV_MTX.lock(); - - let ipc = FixtureSystemV::setup()?; - let mem: *mut IpcStruct = shmat( - ipc.id_ipc, - None, - vec![], - Permissions::new(0o0).expect("Octal is smaller than u9"), - )? - .cast(); - - let expected = 0xDEADBEEF; - unsafe { - mem.as_mut().unwrap().test_info = expected; - assert_eq!(expected, mem.read().test_info); - } - - Ok(()) -} From 94e4e114d2596665570df9ef1dedfb2a852a5ba7 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Fri, 22 Mar 2024 16:52:06 +0100 Subject: [PATCH 25/58] First try at refractoring SystemV to be more Rusty Separated shared memory and semaphore --- src/sys/system_v/mod.rs | 4 + src/sys/system_v/sem.rs | 0 src/sys/system_v/shm.rs | 442 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 446 insertions(+) create mode 100644 src/sys/system_v/mod.rs create mode 100644 src/sys/system_v/sem.rs create mode 100644 src/sys/system_v/shm.rs diff --git a/src/sys/system_v/mod.rs b/src/sys/system_v/mod.rs new file mode 100644 index 0000000000..544c54ae52 --- /dev/null +++ b/src/sys/system_v/mod.rs @@ -0,0 +1,4 @@ +#[cfg(any(bsd, target_os = "linux"))] +pub mod sem; +#[cfg(any(bsd, target_os = "linux"))] +pub mod shm; diff --git a/src/sys/system_v/sem.rs b/src/sys/system_v/sem.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs new file mode 100644 index 0000000000..15c53e68d2 --- /dev/null +++ b/src/sys/system_v/shm.rs @@ -0,0 +1,442 @@ +//! Safe wrapper around a SystemV shared memory segment +//! + +use std::{ + mem::ManuallyDrop, + ops::{Deref, DerefMut}, + ptr::{null, null_mut}, +}; + +use crate::Result; +use crate::{errno::Errno, sys::stat::Mode}; + +use libc::{self, c_int, c_void, key_t, shmid_ds}; + +#[derive(Debug, Clone)] +/// Safe wrapper around a SystemV shared memory segment +/// +/// The shared memory segment size is equal to the size of T. +/// +/// This is a smart pointer, and so implement the [`Deref`] and [`DerefMut`] traits. +/// This means that you can work with the shared memory zone like you would with a [`Box`]. +/// +/// This type does not automatically create or destroy a shared memory segment, +/// but only attach and detach from them using RAII. +/// +/// To create one, use [`SharedMemory::shmget`], with the key [`ShmgetFlag::IPC_CREAT`].\ +/// To delete one, use [`SharedMemory::shmctl`], with the key [`ShmctlFlag::IPC_RMID`]. +/// +/// # Example +/// +/// ```no_run +/// # use nix::errno::Errno; +/// # use nix::sys::system_v::shm::*; +/// # use nix::sys::stat::Mode; +/// # +/// struct MyData(i64); +/// const MY_KEY: i32 = 1337; +/// +/// let id = SharedMemory::::shmget( +/// MY_KEY, +/// ShmgetFlag::IPC_CREAT | ShmgetFlag::IPC_EXCL, +/// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, +/// )?; +/// let mut shared_memory = SharedMemory::::new( +/// id, +/// None, +/// ShmatFlag::empty(), +/// Mode::empty(), +/// )?; +/// +/// // This is writing to the stored [`MyData`] struct +/// shared_memory.0 = 0xDEADBEEF; +/// # Ok::<(), Errno>(()) +/// ``` +/// +pub struct SharedMemory { + id: i32, + shm: ManuallyDrop>, +} + +impl Deref for SharedMemory { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.shm + } +} +impl DerefMut for SharedMemory { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.shm + } +} + +impl Drop for SharedMemory { + fn drop(&mut self) { + Self::shmdt(self).expect("SharedMemory detach from SysV IPC"); + } +} + +impl SharedMemory { + /// Create a new SharedMemory object + /// + /// Attach to an existing SystemV shared memory segment. + /// + /// To create a new segment, use [`SharedMemory::shmget`], with the key [`ShmgetFlag::IPC_CREAT`]. + /// + /// # Example + /// + /// ```no_run + /// # use nix::errno::Errno; + /// # use nix::sys::system_v::shm::*; + /// # use nix::sys::stat::Mode; + /// # + /// struct MyData(i64); + /// const MY_KEY: i32 = 1337; + /// + /// let mut shared_memory = SharedMemory::::new( + /// id, + /// None, + /// ShmatFlag::empty(), + /// Mode::empty(), + /// )?; + /// # Ok::<(), Errno>(()) + /// ``` + /// + pub fn new( + shmid: c_int, + shmaddr: Option, + shmat_flag: ShmatFlag, + mode: Mode, + ) -> Result { + unsafe { + Ok(Self { + id: shmid, + shm: ManuallyDrop::new(Box::from_raw(Self::shmat( + shmid, shmaddr, shmat_flag, mode, + )?)), + }) + } + } + + /// Creates and returns a new, or returns an existing, System V shared memory + /// segment identifier. + /// + /// For more information, see [`shmget(2)`]. + /// + /// # Example + /// + /// ## Creating a shared memory zone + /// + /// ```no_run + /// # use nix::errno::Errno; + /// # use nix::sys::system_v::shm::*; + /// # use nix::sys::stat::Mode; + /// # + /// struct MyData(i64); + /// const MY_KEY: i32 = 1337; + /// + /// let id = SharedMemory::::shmget( + /// MY_KEY, + /// ShmgetFlag::IPC_CREAT | ShmgetFlag::IPC_EXCL, + /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, + /// )?; + /// # Ok::<(), Errno>(()) + /// ``` + /// + /// [`shmget(2)`]: https://man7.org/linux/man-pages/man2/shmget.2.html + /// + pub fn shmget( + key: key_t, + shmget_flag: ShmgetFlag, + mode: Mode, + ) -> Result { + let size = std::mem::size_of::(); + let flags = mode.bits() as i32 | shmget_flag.bits(); + Errno::result(unsafe { libc::shmget(key, size, flags) }) + } + + /// Performs control operation specified by `cmd` on the System V shared + /// memory segment given by `shmid`. + /// + /// For more information, see [`shmctl(2)`]. + /// + /// # Example + /// + /// ## Deleting a shared memory zone + /// + /// ```no_run + /// # use nix::errno::Errno; + /// # use nix::sys::system_v::shm::*; + /// # use nix::sys::stat::Mode; + /// # + /// struct MyData(i64); + /// const ID: i32 = 1337; + /// + /// let mut shared_memory = SharedMemory::::new( + /// ID, + /// None, + /// ShmatFlag::empty(), + /// Mode::empty(), + /// )?; + /// + /// shared_memory.shmctl(ShmctlFlag::IPC_RMID, None, Mode::empty()) = 0xDEADBEEF; + /// # Ok::<(), Errno>(()) + /// ``` + /// + /// [`shmctl(2)`]: https://man7.org/linux/man-pages/man2/shmctl.2.html + pub fn shmctl( + &self, + shmctl_flag: ShmctlFlag, + buf: Option, + mode: Mode, + ) -> Result { + let buf_ptr: *mut shmid_ds = match buf { + Some(mut ptr) => &mut ptr, + None => null_mut(), + }; + let flags = mode.bits() as i32 | shmctl_flag.bits(); + Errno::result(unsafe { libc::shmctl(self.id, flags, buf_ptr) }) + } + + // -- Private -- + + /// Attaches the System V shared memory segment identified by `shmid` to the + /// address space of the calling process. + /// + /// This is called automatically on [`SharedMemory::new`]. + /// + /// For more information, see [`shmat(2)`]. + /// + /// [`shmat(2)`]: https://man7.org/linux/man-pages/man2/shmat.2.html + fn shmat( + shmid: c_int, + shmaddr: Option, + shmat_flag: ShmatFlag, + mode: Mode, + ) -> Result<*mut T> { + let shmaddr_ptr: *const c_void = match shmaddr { + Some(mut ptr) => &mut ptr, + None => null(), + }; + let flags = mode.bits() as i32 | shmat_flag.bits(); + Errno::result(unsafe { libc::shmat(shmid, shmaddr_ptr, flags) }) + .map(|ok| ok.cast::()) + } + + /// Performs the reverse of [`SharedMemory::shmat`], detaching the shared memory segment at + /// the given address from the address space of the calling process. + /// + /// This is called automatically on [`Drop`]. + /// + /// For more information, see [`shmdt(2)`]. + /// + /// [`shmdt(2)`]: https://man7.org/linux/man-pages/man2/shmdt.2.html + fn shmdt(&self) -> Result<()> { + let shmaddr_ref: *const T = &**self; + Errno::result(unsafe { libc::shmdt(shmaddr_ref.cast::()) }) + .map(drop) + } +} + +libc_bitflags!( + /// Valid flags for the third parameter of the function [`shmget`] + pub struct ShmgetFlag: c_int + { + /// A new shared memory segment is created if key has this value. + IPC_PRIVATE; + /// Create a new segment. + /// If this flag is not used, then shmget() will find the segment + /// associated with key and check to see if the user has permission + /// to access the segment. + IPC_CREAT; + /// This flag is used with IPC_CREAT to ensure that this call creates + /// the segment. If the segment already exists, the call fails. + IPC_EXCL; + /// Allocate the segment using "huge" pages. See the Linux kernel + /// source file Documentation/admin-guide/mm/hugetlbpage.rst for + /// further information. + #[cfg(linux)] + SHM_HUGETLB; + // TODO: Does not exist in libc/linux, but should? Maybe open an issue in their repo + // SHM_HUGE_2MB; + // TODO: Same for this one + // SHM_HUGE_1GB; + /// This flag serves the same purpose as the mmap(2) MAP_NORESERVE flag. + /// Do not reserve swap space for this segment. When swap space is + /// reserved, one has the guarantee that it is possible to modify the + /// segment. When swap space is not reserved one might get SIGSEGV upon + /// a write if no physical memory is available. See also the discussion + /// of the file /proc/sys/vm/overcommit_memory in proc(5). + #[cfg(linux)] + SHM_NORESERVE; + } +); +libc_bitflags! { + /// Valid flags for the third parameter of the function [`shmat`] + pub struct ShmatFlag: c_int + { + /// Allow the contents of the segment to be executed. The caller must + /// have execute permission on the segment. + #[cfg(linux)] + SHM_EXEC; + /// This flag specifies that the mapping of the segment should replace + /// any existing mapping in the range starting at shmaddr and + /// continuing for the size of the segment. + /// (Normally, an EINVAL error would result if a mapping already exists + /// in this address range.) + /// In this case, shmaddr must not be NULL. + #[cfg(linux)] + SHM_REMAP; + /// Attach the segment for read-only access. The process must have read + /// permission for the segment. If this flag is not specified, the + /// segment is attached for read and write access, and the process must + /// have read and write permission for the segment. + /// There is no notion of a write-only shared memory segment. + SHM_RDONLY; + /// TODO: I have no clue at what this does + SHM_RND; + } +} + +libc_bitflags!( + /// Valid flags for the second parameter of the function [`shmctl`] + pub struct ShmctlFlag: c_int { + /// Returns the index of the highest used entry in the kernel's internal + /// array recording information about all shared memory segment + #[cfg(linux)] + IPC_INFO; + /// Write the values of some members of the shmid_ds structure pointed + /// to by buf to the kernel data structure associated with this shared + /// memory segment, updating also its shm_ctime member. + /// + /// The following fields are updated: shm_perm.uid, + /// shm_perm.gid, and (the least significant 9 bits of) + /// shm_perm.mode. + /// + /// The effective UID of the calling process must match the owner + /// (shm_perm.uid) or creator (shm_perm.cuid) of the shared memory + /// segment, or the caller must be privileged. + IPC_SET; + /// Copy information from the kernel data structure associated with + /// shmid into the shmid_ds structure pointed to by buf. + /// The caller must have read permission on the shared memory segment. + IPC_STAT; + /// Mark the segment to be destroyed. The segment will actually be + /// destroyed only after the last process detaches it + /// (i.e., when the shm_nattch member of the associated structure + /// shmid_ds is zero). + /// The caller must be the owner or creator of the segment, + /// or be privileged. The buf argument is ignored. + /// + /// If a segment has been marked for destruction, then the + /// (nonstandard) SHM_DEST flag of the shm_perm.mode field in the + /// associated data structure retrieved by IPC_STAT will be set. + /// + /// The caller must ensure that a segment is eventually destroyed; + /// otherwise its pages that were faulted in will remain in memory + /// or swap. + /// + /// See also the description of /proc/sys/kernel/shm_rmid_forced + /// in proc(5). + IPC_RMID; + // not available in libc/linux, but should be? + // SHM_INFO; + // SHM_STAT; + // SHM_STAT_ANY; + /// Prevent swapping of the shared memory segment. The caller must + /// fault in any pages that are required to be present after locking is + /// enabled. + /// If a segment has been locked, then the (nonstandard) SHM_LOCKED + /// flag of the shm_perm.mode field in the associated data structure + /// retrieved by IPC_STAT will be set. + #[cfg(linux)] + SHM_LOCK; + /// Unlock the segment, allowing it to be swapped out. + #[cfg(linux)] + SHM_UNLOCK; + } +); + +#[cfg(test)] +mod tests { + use super::*; + use parking_lot::Mutex; + + static SHM_MTX: Mutex<()> = Mutex::new(()); + + const SHM_TEST: i32 = 1337; + + #[derive(Debug)] + /// Test struct used to store some data on the shared memory zone + /// + struct TestData { + data: i64, + } + + #[derive(Debug)] + struct FixtureShm { + ipc: SharedMemory, + } + + impl FixtureShm { + fn setup() -> Result { + let id = SharedMemory::::shmget( + SHM_TEST, + ShmgetFlag::IPC_CREAT | ShmgetFlag::IPC_EXCL, + Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, + )?; + Ok(Self { + ipc: SharedMemory::::new( + id, + None, + ShmatFlag::empty(), + Mode::empty(), + )?, + }) + } + } + + impl Drop for FixtureShm { + fn drop(&mut self) { + let _ = self + .ipc + .shmctl(ShmctlFlag::IPC_RMID, None, Mode::empty()) + .map_err(|_| { + panic!("Failed to delete the test shared memory zone") + }); + } + } + + #[test] + fn create_ipc() -> Result<()> { + let _m = SHM_MTX.lock(); + + FixtureShm::setup()?; + Ok(()) + } + + #[test] + fn create_ipc_already_exist() -> Result<()> { + let _m = SHM_MTX.lock(); + + // Keep the IPC in scope, so we don't destroy it + let _ipc = FixtureShm::setup()?; + let expected = Errno::EEXIST; + let actual = FixtureShm::setup().expect_err("Return EExist"); + + assert_eq!(expected, actual); + Ok(()) + } + + #[test] + fn create_ipc_and_get_value() -> Result<()> { + let _m = SHM_MTX.lock(); + + let mut sem = FixtureShm::setup()?; + let expected = 0xDEADBEEF; + sem.ipc.data = expected; + assert_eq!(expected, sem.ipc.data); + Ok(()) + } +} From aea858898192b8dc3417a61d7057efa985622f84 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Fri, 22 Mar 2024 16:56:46 +0100 Subject: [PATCH 26/58] Add a feature gate --- Cargo.toml | 1 + src/sys/mod.rs | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5abd1344da..ac3cd34501 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ resource = [] sched = ["process"] signal = ["process"] socket = ["memoffset"] +system_v = ["fs"] term = [] time = [] ucontext = ["signal"] diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 9ca62d0fec..bbd503f0a0 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -143,9 +143,11 @@ feature! { #[allow(missing_docs)] pub mod sysinfo; -#[allow(missing_docs)] -#[cfg(any(bsd, target_os = "linux", not(target_os = "android")))] -pub mod system_v; +#[cfg(any(bsd, target_os = "linux"))] +feature! { + #![feature = "system_v"] + pub mod system_v; +} feature! { #![feature = "term"] From 02b0de2f3c94a8a7fa3f45bc06daeb6f29439c77 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:41:19 +0100 Subject: [PATCH 27/58] Add missing doc --- src/sys/system_v/mod.rs | 2 ++ src/sys/system_v/sem.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/sys/system_v/mod.rs b/src/sys/system_v/mod.rs index 544c54ae52..c194c632b5 100644 --- a/src/sys/system_v/mod.rs +++ b/src/sys/system_v/mod.rs @@ -1,3 +1,5 @@ +//! Interact with SystemV inter-process communication API + #[cfg(any(bsd, target_os = "linux"))] pub mod sem; #[cfg(any(bsd, target_os = "linux"))] diff --git a/src/sys/system_v/sem.rs b/src/sys/system_v/sem.rs index e69de29bb2..c13d719bc5 100644 --- a/src/sys/system_v/sem.rs +++ b/src/sys/system_v/sem.rs @@ -0,0 +1,2 @@ +//! Safe wrapper around a SystemV semaphore +//! From ed997634eeedd15eff5dd169c3fb97242cce853a Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:44:44 +0100 Subject: [PATCH 28/58] Fix doctest copy and paste error --- src/sys/system_v/shm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 15c53e68d2..70c8558606 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -95,7 +95,7 @@ impl SharedMemory { /// const MY_KEY: i32 = 1337; /// /// let mut shared_memory = SharedMemory::::new( - /// id, + /// MY_KEY, /// None, /// ShmatFlag::empty(), /// Mode::empty(), @@ -180,7 +180,7 @@ impl SharedMemory { /// Mode::empty(), /// )?; /// - /// shared_memory.shmctl(ShmctlFlag::IPC_RMID, None, Mode::empty()) = 0xDEADBEEF; + /// let _ = shared_memory.shmctl(ShmctlFlag::IPC_RMID, None, Mode::empty())?; /// # Ok::<(), Errno>(()) /// ``` /// From b614d8115755f89fee025bd59b89f6956f95b4aa Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:53:03 +0100 Subject: [PATCH 29/58] Remove Clone derive, wasn't working correctly --- src/sys/system_v/shm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 70c8558606..2c0213276e 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -12,7 +12,7 @@ use crate::{errno::Errno, sys::stat::Mode}; use libc::{self, c_int, c_void, key_t, shmid_ds}; -#[derive(Debug, Clone)] +#[derive(Debug)] /// Safe wrapper around a SystemV shared memory segment /// /// The shared memory segment size is equal to the size of T. From 262919252b07dcd989433d0a707ee7b79d03e1ae Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:53:48 +0100 Subject: [PATCH 30/58] Update test to create an entirely new SharedMemory object This way we can be sure the Shm is actually updated correctly by libc, and not just internally by Rust --- src/sys/system_v/shm.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 2c0213276e..935a0fe5b0 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -436,7 +436,14 @@ mod tests { let mut sem = FixtureShm::setup()?; let expected = 0xDEADBEEF; sem.ipc.data = expected; - assert_eq!(expected, sem.ipc.data); + + let actual = SharedMemory::::new( + sem.ipc.id, + None, + ShmatFlag::empty(), + Mode::empty(), + )?.data; + assert_eq!(expected, actual); Ok(()) } } From b7b8818303d9a79c494b261352d4c151581f9d26 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:54:00 +0100 Subject: [PATCH 31/58] Misc formatting --- src/sys/system_v/shm.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 935a0fe5b0..fdf803c41d 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -127,7 +127,7 @@ impl SharedMemory { /// # Example /// /// ## Creating a shared memory zone - /// + /// /// ```no_run /// # use nix::errno::Errno; /// # use nix::sys::system_v::shm::*; @@ -145,7 +145,6 @@ impl SharedMemory { /// ``` /// /// [`shmget(2)`]: https://man7.org/linux/man-pages/man2/shmget.2.html - /// pub fn shmget( key: key_t, shmget_flag: ShmgetFlag, @@ -164,7 +163,7 @@ impl SharedMemory { /// # Example /// /// ## Deleting a shared memory zone - /// + /// /// ```no_run /// # use nix::errno::Errno; /// # use nix::sys::system_v::shm::*; @@ -205,7 +204,7 @@ impl SharedMemory { /// address space of the calling process. /// /// This is called automatically on [`SharedMemory::new`]. - /// + /// /// For more information, see [`shmat(2)`]. /// /// [`shmat(2)`]: https://man7.org/linux/man-pages/man2/shmat.2.html @@ -228,7 +227,7 @@ impl SharedMemory { /// the given address from the address space of the calling process. /// /// This is called automatically on [`Drop`]. - /// + /// /// For more information, see [`shmdt(2)`]. /// /// [`shmdt(2)`]: https://man7.org/linux/man-pages/man2/shmdt.2.html From 930f3528d869df07dd2ece27c3f5bb95c95a5cdd Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Sun, 24 Mar 2024 16:23:24 +0100 Subject: [PATCH 32/58] Remove unused static mutex for system_v Test are now internal to access private var --- test/test.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test.rs b/test/test.rs index 8d032580a1..c7231426c2 100644 --- a/test/test.rs +++ b/test/test.rs @@ -75,8 +75,6 @@ pub static KMOD_MTX: Mutex<()> = Mutex::new(()); pub static PTSNAME_MTX: Mutex<()> = Mutex::new(()); /// Any test that alters signal handling must grab this mutex. pub static SIGNAL_MTX: Mutex<()> = Mutex::new(()); -/// Any test that use SystemV must grab this mutex. -pub static SYSTEMV_MTX: Mutex<()> = Mutex::new(()); /// RAII object that restores a test's original directory on drop struct DirRestore<'a> { From 4bd41cf4dd5ac61d57f257ad87741ed444d7e6d7 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Sun, 24 Mar 2024 16:29:57 +0100 Subject: [PATCH 33/58] Add `Shm` wrapper for creating a shared memory segment --- src/sys/system_v/shm.rs | 162 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 2 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index fdf803c41d..8232208e46 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -13,9 +13,167 @@ use crate::{errno::Errno, sys::stat::Mode}; use libc::{self, c_int, c_void, key_t, shmid_ds}; #[derive(Debug)] -/// Safe wrapper around a SystemV shared memory segment +/// Safe wrapper to create and connect to a SystemV shared memory segment. /// -/// The shared memory segment size is equal to the size of T. +/// # Example +/// +/// ```no_run +/// # use nix::errno::Errno; +/// # use nix::sys::system_v::shm::*; +/// # use nix::sys::stat::Mode; +/// # +/// struct MyData(i64); +/// +/// const MY_KEY: i32 = 1337; +/// let mem_segment = Shm::::create_and_connect( +/// MY_KEY, +/// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, +/// )?; +/// let mut shared_memory = mem_segment.attach(ShmatFlag::empty())?; +/// # Ok::<(), Errno>(()) +/// ``` +/// +pub struct Shm { + id: c_int, + _phantom: PhantomData, +} + +impl Shm { + /// Attach to the current SystemV shared memory segment. +/// + /// To create a new segment, use [`Shm::create_and_connect`].\ + /// If you need more customisation, use the unsafe version, + /// [`Shm::shmget`], with the key [`ShmgetFlag::IPC_CREAT`]. + /// + /// Attaching a segment to a specific adress isn't supported. This is + /// because there is no way to create a void pointer on rust. + /// + /// # Example + /// + /// ```no_run + /// # use nix::errno::Errno; + /// # use nix::sys::system_v::shm::*; + /// # use nix::sys::stat::Mode; + /// # + /// struct MyData(i64); + /// + /// const MY_KEY: i32 = 1337; + /// let mem_segment = Shm::::create_and_connect( + /// MY_KEY, + /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, + /// )?; + /// let mut shared_memory = mem_segment.attach(ShmatFlag::empty())?; + /// # Ok::<(), Errno>(()) + /// ``` + /// + pub fn attach(&self, shmat_flag: ShmatFlag) -> Result> { + unsafe { + Ok(SharedMemory:: { + id: self.id, + shm: ManuallyDrop::new(Box::from_raw(self.shmat(shmat_flag)?)), + }) + } + } + + /// Creates and returns a new System V shared memory segment identifier. + /// + /// # Example + /// ```no_run + /// # use nix::errno::Errno; + /// # use nix::sys::system_v::shm::*; + /// # use nix::sys::stat::Mode; + /// # + /// struct MyData(i64); + /// const MY_KEY: i32 = 1337; + /// + /// let mem_segment = Shm::::create_and_connect( + /// MY_KEY, + /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, + /// )?; + /// # Ok::<(), Errno>(()) + /// ``` + /// + pub fn create_and_connect(key: key_t, mode: Mode) -> Result { + let size = std::mem::size_of::(); + // This is the main difference between this function and [`Shm::shmget`] + // Because we are always creating a new segment, we can be sure that the size match + let shmget_flag = ShmgetFlag::IPC_CREAT | ShmgetFlag::IPC_EXCL; + let flags = mode.bits() as i32 | shmget_flag.bits(); + let id = Errno::result(unsafe { libc::shmget(key, size, flags) })?; + Ok(Self { + id, + _phantom: PhantomData, + }) + } + + /// Creates and returns a new, or returns an existing, System V shared memory + /// segment identifier. + /// + /// For more information, see [`shmget(2)`]. + /// + /// # Safety + /// + /// If you are using this function to connect to an existing memory segment, + /// care must be taken that the generic type `T` matches what is actually + /// stored on the memory segment.\ + /// For example, if a memory segment of size 4 bytes exist, and you connect + /// with a type of size 8 bytes, then undefined behaviour will be invoked. + /// + /// # Example + /// + /// ## Connecting to an existing shared memory segment + /// + /// ```no_run + /// # use nix::errno::Errno; + /// # use nix::sys::system_v::shm::*; + /// # use nix::sys::stat::Mode; + /// # + /// struct MyData(i64); + /// const MY_KEY: i32 = 1337; + /// + /// unsafe { + /// let mem_segment = Shm::::shmget( + /// MY_KEY, + /// ShmgetFlag::empty(), + /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, + /// )?; + /// } + /// # Ok::<(), Errno>(()) + /// ``` + /// + /// [`shmget(2)`]: https://man7.org/linux/man-pages/man2/shmget.2.html + pub unsafe fn shmget( + key: key_t, + shmget_flag: ShmgetFlag, + mode: Mode, + ) -> Result { + let size = std::mem::size_of::(); + let flags = mode.bits() as i32 | shmget_flag.bits(); + let id = Errno::result(unsafe { libc::shmget(key, size, flags) })?; + Ok(Self { + id, + _phantom: PhantomData, + }) + } + + // -- Private -- + + /// Attaches the System V shared memory segment identified by `shmid` to the + /// address space of the calling process. + /// + /// This is called automatically on [`Shm::attach`]. + /// + /// For more information, see [`shmat(2)`]. + /// + /// [`shmat(2)`]: https://man7.org/linux/man-pages/man2/shmat.2.html + fn shmat(&self, shmat_flag: ShmatFlag) -> Result<*mut T> { + Errno::result(unsafe { + libc::shmat(self.id, ptr::null(), shmat_flag.bits()) + }) + .map(|ok| ok.cast::()) + } +} + /// /// This is a smart pointer, and so implement the [`Deref`] and [`DerefMut`] traits. /// This means that you can work with the shared memory zone like you would with a [`Box`]. From 2c714c28ac92565e894393a65f53fc368585b17d Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Sun, 24 Mar 2024 16:30:58 +0100 Subject: [PATCH 34/58] Remove function that create a memory segment from `SharedMemory` --- src/sys/system_v/shm.rs | 123 +++------------------------------------- 1 file changed, 9 insertions(+), 114 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 8232208e46..74298cbb8d 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -174,15 +174,16 @@ impl Shm { } } +#[derive(Debug)] +/// Safe wrapper around a SystemV shared memory segment data /// /// This is a smart pointer, and so implement the [`Deref`] and [`DerefMut`] traits. -/// This means that you can work with the shared memory zone like you would with a [`Box`]. +/// This means that you can work with the shared memory segment like you would with a [`Box`]. /// -/// This type does not automatically create or destroy a shared memory segment, -/// but only attach and detach from them using RAII. +/// This type does not automatically destroy the shared memory segment, but +/// only detach from it using RAII. /// -/// To create one, use [`SharedMemory::shmget`], with the key [`ShmgetFlag::IPC_CREAT`].\ -/// To delete one, use [`SharedMemory::shmctl`], with the key [`ShmctlFlag::IPC_RMID`]. +/// To delete a shared memory segment, use [`SharedMemory::shmctl`], with the key [`ShmctlFlag::IPC_RMID`]. /// /// # Example /// @@ -194,17 +195,11 @@ impl Shm { /// struct MyData(i64); /// const MY_KEY: i32 = 1337; /// -/// let id = SharedMemory::::shmget( +/// let mem_segment = Shm::::create_and_connect( /// MY_KEY, -/// ShmgetFlag::IPC_CREAT | ShmgetFlag::IPC_EXCL, /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, /// )?; -/// let mut shared_memory = SharedMemory::::new( -/// id, -/// None, -/// ShmatFlag::empty(), -/// Mode::empty(), -/// )?; +/// let mut shared_memory = mem_segment.attach(ShmatFlag::empty())?; /// /// // This is writing to the stored [`MyData`] struct /// shared_memory.0 = 0xDEADBEEF; @@ -212,7 +207,7 @@ impl Shm { /// ``` /// pub struct SharedMemory { - id: i32, + id: c_int, shm: ManuallyDrop>, } @@ -236,83 +231,6 @@ impl Drop for SharedMemory { } impl SharedMemory { - /// Create a new SharedMemory object - /// - /// Attach to an existing SystemV shared memory segment. - /// - /// To create a new segment, use [`SharedMemory::shmget`], with the key [`ShmgetFlag::IPC_CREAT`]. - /// - /// # Example - /// - /// ```no_run - /// # use nix::errno::Errno; - /// # use nix::sys::system_v::shm::*; - /// # use nix::sys::stat::Mode; - /// # - /// struct MyData(i64); - /// const MY_KEY: i32 = 1337; - /// - /// let mut shared_memory = SharedMemory::::new( - /// MY_KEY, - /// None, - /// ShmatFlag::empty(), - /// Mode::empty(), - /// )?; - /// # Ok::<(), Errno>(()) - /// ``` - /// - pub fn new( - shmid: c_int, - shmaddr: Option, - shmat_flag: ShmatFlag, - mode: Mode, - ) -> Result { - unsafe { - Ok(Self { - id: shmid, - shm: ManuallyDrop::new(Box::from_raw(Self::shmat( - shmid, shmaddr, shmat_flag, mode, - )?)), - }) - } - } - - /// Creates and returns a new, or returns an existing, System V shared memory - /// segment identifier. - /// - /// For more information, see [`shmget(2)`]. - /// - /// # Example - /// - /// ## Creating a shared memory zone - /// - /// ```no_run - /// # use nix::errno::Errno; - /// # use nix::sys::system_v::shm::*; - /// # use nix::sys::stat::Mode; - /// # - /// struct MyData(i64); - /// const MY_KEY: i32 = 1337; - /// - /// let id = SharedMemory::::shmget( - /// MY_KEY, - /// ShmgetFlag::IPC_CREAT | ShmgetFlag::IPC_EXCL, - /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, - /// )?; - /// # Ok::<(), Errno>(()) - /// ``` - /// - /// [`shmget(2)`]: https://man7.org/linux/man-pages/man2/shmget.2.html - pub fn shmget( - key: key_t, - shmget_flag: ShmgetFlag, - mode: Mode, - ) -> Result { - let size = std::mem::size_of::(); - let flags = mode.bits() as i32 | shmget_flag.bits(); - Errno::result(unsafe { libc::shmget(key, size, flags) }) - } - /// Performs control operation specified by `cmd` on the System V shared /// memory segment given by `shmid`. /// @@ -358,29 +276,6 @@ impl SharedMemory { // -- Private -- - /// Attaches the System V shared memory segment identified by `shmid` to the - /// address space of the calling process. - /// - /// This is called automatically on [`SharedMemory::new`]. - /// - /// For more information, see [`shmat(2)`]. - /// - /// [`shmat(2)`]: https://man7.org/linux/man-pages/man2/shmat.2.html - fn shmat( - shmid: c_int, - shmaddr: Option, - shmat_flag: ShmatFlag, - mode: Mode, - ) -> Result<*mut T> { - let shmaddr_ptr: *const c_void = match shmaddr { - Some(mut ptr) => &mut ptr, - None => null(), - }; - let flags = mode.bits() as i32 | shmat_flag.bits(); - Errno::result(unsafe { libc::shmat(shmid, shmaddr_ptr, flags) }) - .map(|ok| ok.cast::()) - } - /// Performs the reverse of [`SharedMemory::shmat`], detaching the shared memory segment at /// the given address from the address space of the calling process. /// From 6877712e43a2395204680624b0644b4f846f6d24 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Sun, 24 Mar 2024 16:31:35 +0100 Subject: [PATCH 35/58] Make `buf` parameter pass by ref --- src/sys/system_v/shm.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 74298cbb8d..0cd97d3fc5 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -238,7 +238,7 @@ impl SharedMemory { /// /// # Example /// - /// ## Deleting a shared memory zone + /// ## Deleting a shared memory segment /// /// ```no_run /// # use nix::errno::Errno; @@ -246,32 +246,29 @@ impl SharedMemory { /// # use nix::sys::stat::Mode; /// # /// struct MyData(i64); - /// const ID: i32 = 1337; + /// const MY_KEY: i32 = 1337; /// - /// let mut shared_memory = SharedMemory::::new( - /// ID, - /// None, - /// ShmatFlag::empty(), - /// Mode::empty(), + /// let mem_segment = Shm::::create_and_connect( + /// MY_KEY, + /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, /// )?; + /// let shared_memory = mem_segment.attach(ShmatFlag::empty())?; /// - /// let _ = shared_memory.shmctl(ShmctlFlag::IPC_RMID, None, Mode::empty())?; + /// let _ = shared_memory.shmctl(ShmctlFlag::IPC_RMID, None)?; /// # Ok::<(), Errno>(()) /// ``` /// /// [`shmctl(2)`]: https://man7.org/linux/man-pages/man2/shmctl.2.html pub fn shmctl( &self, - shmctl_flag: ShmctlFlag, - buf: Option, - mode: Mode, + shm_cmd: ShmctlFlag, + buf: Option<&mut shmid_ds>, ) -> Result { let buf_ptr: *mut shmid_ds = match buf { - Some(mut ptr) => &mut ptr, - None => null_mut(), + Some(ptr) => ptr::from_mut(ptr), + None => ptr::null_mut(), }; - let flags = mode.bits() as i32 | shmctl_flag.bits(); - Errno::result(unsafe { libc::shmctl(self.id, flags, buf_ptr) }) + Errno::result(unsafe { libc::shmctl(self.id, shm_cmd.bits(), buf_ptr) }) } // -- Private -- From b7f6a09e54860598db3877e16be30f817347c136 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Sun, 24 Mar 2024 16:32:38 +0100 Subject: [PATCH 36/58] Remove the `shmat` enum value that work with incompatible void ptr --- src/sys/system_v/shm.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 0cd97d3fc5..315e43f090 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -329,22 +329,12 @@ libc_bitflags! { /// have execute permission on the segment. #[cfg(linux)] SHM_EXEC; - /// This flag specifies that the mapping of the segment should replace - /// any existing mapping in the range starting at shmaddr and - /// continuing for the size of the segment. - /// (Normally, an EINVAL error would result if a mapping already exists - /// in this address range.) - /// In this case, shmaddr must not be NULL. - #[cfg(linux)] - SHM_REMAP; /// Attach the segment for read-only access. The process must have read /// permission for the segment. If this flag is not specified, the /// segment is attached for read and write access, and the process must /// have read and write permission for the segment. /// There is no notion of a write-only shared memory segment. SHM_RDONLY; - /// TODO: I have no clue at what this does - SHM_RND; } } From d07c9b84b2caaae556a2de0d5588c6b1d02c1213 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Sun, 24 Mar 2024 16:33:00 +0100 Subject: [PATCH 37/58] Update unit test --- src/sys/system_v/shm.rs | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 315e43f090..65c112a463 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -407,7 +407,7 @@ mod tests { const SHM_TEST: i32 = 1337; #[derive(Debug)] - /// Test struct used to store some data on the shared memory zone + /// Test struct used to store some data on the shared memory segment /// struct TestData { data: i64, @@ -415,34 +415,26 @@ mod tests { #[derive(Debug)] struct FixtureShm { - ipc: SharedMemory, + shm: Shm, + memory: SharedMemory, } impl FixtureShm { fn setup() -> Result { - let id = SharedMemory::::shmget( + let shm = Shm::::create_and_connect( SHM_TEST, - ShmgetFlag::IPC_CREAT | ShmgetFlag::IPC_EXCL, Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, )?; - Ok(Self { - ipc: SharedMemory::::new( - id, - None, - ShmatFlag::empty(), - Mode::empty(), - )?, - }) + let memory = shm.attach(ShmatFlag::empty())?; + Ok(Self { shm, memory }) } } impl Drop for FixtureShm { fn drop(&mut self) { - let _ = self - .ipc - .shmctl(ShmctlFlag::IPC_RMID, None, Mode::empty()) - .map_err(|_| { - panic!("Failed to delete the test shared memory zone") + let _ = + self.memory.shmctl(ShmctlFlag::IPC_RMID, None).map_err(|_| { + panic!("Failed to delete the test shared memory segment") }); } } @@ -460,7 +452,7 @@ mod tests { let _m = SHM_MTX.lock(); // Keep the IPC in scope, so we don't destroy it - let _ipc = FixtureShm::setup()?; + let _fixture = FixtureShm::setup()?; let expected = Errno::EEXIST; let actual = FixtureShm::setup().expect_err("Return EExist"); @@ -472,16 +464,11 @@ mod tests { fn create_ipc_and_get_value() -> Result<()> { let _m = SHM_MTX.lock(); - let mut sem = FixtureShm::setup()?; + let mut fixture = FixtureShm::setup()?; let expected = 0xDEADBEEF; - sem.ipc.data = expected; - - let actual = SharedMemory::::new( - sem.ipc.id, - None, - ShmatFlag::empty(), - Mode::empty(), - )?.data; + fixture.memory.data = expected; + + let actual = fixture.shm.attach(ShmatFlag::empty())?.data; assert_eq!(expected, actual); Ok(()) } From 5697ffd3f58ec0708671e81f622c8b3b07e9d81c Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Sun, 24 Mar 2024 16:35:47 +0100 Subject: [PATCH 38/58] Update imported function --- src/sys/system_v/shm.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 65c112a463..76eaef9c28 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -2,9 +2,10 @@ //! use std::{ + marker::PhantomData, mem::ManuallyDrop, ops::{Deref, DerefMut}, - ptr::{null, null_mut}, + ptr, }; use crate::Result; @@ -40,7 +41,7 @@ pub struct Shm { impl Shm { /// Attach to the current SystemV shared memory segment. -/// + /// /// To create a new segment, use [`Shm::create_and_connect`].\ /// If you need more customisation, use the unsafe version, /// [`Shm::shmget`], with the key [`ShmgetFlag::IPC_CREAT`]. From 3d70ae2f05067c3aa15d39c8f73dcc3ec5d20692 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Sun, 24 Mar 2024 16:42:24 +0100 Subject: [PATCH 39/58] Remove stable ptr_from_ref feature that isn't stable on Rust 1.69 --- src/sys/system_v/shm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 76eaef9c28..65b0e27fd7 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -266,7 +266,7 @@ impl SharedMemory { buf: Option<&mut shmid_ds>, ) -> Result { let buf_ptr: *mut shmid_ds = match buf { - Some(ptr) => ptr::from_mut(ptr), + Some(ptr) => ptr, None => ptr::null_mut(), }; Errno::result(unsafe { libc::shmctl(self.id, shm_cmd.bits(), buf_ptr) }) From 238fe627359dc16435c45171ac993c0f7ebb1298 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 18:48:13 +0100 Subject: [PATCH 40/58] Remove unused comment mark --- src/sys/system_v/shm.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 65b0e27fd7..8c4cfa88a4 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -1,5 +1,4 @@ //! Safe wrapper around a SystemV shared memory segment -//! use std::{ marker::PhantomData, From 0ff180a2eeeece6cddc48b7c05b8abed8f6e45f6 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 18:52:41 +0100 Subject: [PATCH 41/58] Add back `shmat` address. Use a raw pointer. --- src/sys/system_v/shm.rs | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 8c4cfa88a4..95fbea27d2 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -45,9 +45,6 @@ impl Shm { /// If you need more customisation, use the unsafe version, /// [`Shm::shmget`], with the key [`ShmgetFlag::IPC_CREAT`]. /// - /// Attaching a segment to a specific adress isn't supported. This is - /// because there is no way to create a void pointer on rust. - /// /// # Example /// /// ```no_run @@ -62,15 +59,21 @@ impl Shm { /// MY_KEY, /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, /// )?; - /// let mut shared_memory = mem_segment.attach(ShmatFlag::empty())?; + /// let mut shared_memory = mem_segment.attach(ptr::null(), ShmatFlag::empty())?; /// # Ok::<(), Errno>(()) /// ``` /// - pub fn attach(&self, shmat_flag: ShmatFlag) -> Result> { + pub fn attach( + &self, + shmaddr: *const c_void, + shmat_flag: ShmatFlag, + ) -> Result> { unsafe { Ok(SharedMemory:: { id: self.id, - shm: ManuallyDrop::new(Box::from_raw(self.shmat(shmat_flag)?)), + shm: ManuallyDrop::new(Box::from_raw( + self.shmat(shmaddr, shmat_flag)?, + )), }) } } @@ -166,9 +169,13 @@ impl Shm { /// For more information, see [`shmat(2)`]. /// /// [`shmat(2)`]: https://man7.org/linux/man-pages/man2/shmat.2.html - fn shmat(&self, shmat_flag: ShmatFlag) -> Result<*mut T> { + fn shmat( + &self, + shmaddr: *const c_void, + shmat_flag: ShmatFlag, + ) -> Result<*mut T> { Errno::result(unsafe { - libc::shmat(self.id, ptr::null(), shmat_flag.bits()) + libc::shmat(self.id, shmaddr, shmat_flag.bits()) }) .map(|ok| ok.cast::()) } @@ -329,12 +336,24 @@ libc_bitflags! { /// have execute permission on the segment. #[cfg(linux)] SHM_EXEC; + /// This flag specifies that the mapping of the segment should replace + /// any existing mapping in the range starting at shmaddr and + /// continuing for the size of the segment. + /// (Normally, an EINVAL error would result if a mapping already exists + /// in this address range.) + /// In this case, shmaddr must not be NULL. + #[cfg(target_os = "linux")] + SHM_REMAP; /// Attach the segment for read-only access. The process must have read /// permission for the segment. If this flag is not specified, the /// segment is attached for read and write access, and the process must /// have read and write permission for the segment. /// There is no notion of a write-only shared memory segment. SHM_RDONLY; + /// If shmaddr isn't NULL and SHM_RND is specified in shmflg, the + /// attach occurs at the address equal to shmaddr rounded down to the + /// nearest multiple of SHMLBA. + SHM_RND; } } From f5f2f59e0ad36843199d3f750a0db07458b41c53 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:00:41 +0100 Subject: [PATCH 42/58] Fix mismatched target os --- src/sys/system_v/shm.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 95fbea27d2..82e2af9b83 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -312,7 +312,7 @@ libc_bitflags!( /// Allocate the segment using "huge" pages. See the Linux kernel /// source file Documentation/admin-guide/mm/hugetlbpage.rst for /// further information. - #[cfg(linux)] + #[cfg(target_os = "linux")] SHM_HUGETLB; // TODO: Does not exist in libc/linux, but should? Maybe open an issue in their repo // SHM_HUGE_2MB; @@ -324,7 +324,7 @@ libc_bitflags!( /// segment. When swap space is not reserved one might get SIGSEGV upon /// a write if no physical memory is available. See also the discussion /// of the file /proc/sys/vm/overcommit_memory in proc(5). - #[cfg(linux)] + #[cfg(target_os = "linux")] SHM_NORESERVE; } ); @@ -334,7 +334,7 @@ libc_bitflags! { { /// Allow the contents of the segment to be executed. The caller must /// have execute permission on the segment. - #[cfg(linux)] + #[cfg(target_os = "linux")] SHM_EXEC; /// This flag specifies that the mapping of the segment should replace /// any existing mapping in the range starting at shmaddr and @@ -362,7 +362,7 @@ libc_bitflags!( pub struct ShmctlFlag: c_int { /// Returns the index of the highest used entry in the kernel's internal /// array recording information about all shared memory segment - #[cfg(linux)] + #[cfg(target_os = "linux")] IPC_INFO; /// Write the values of some members of the shmid_ds structure pointed /// to by buf to the kernel data structure associated with this shared @@ -408,10 +408,10 @@ libc_bitflags!( /// If a segment has been locked, then the (nonstandard) SHM_LOCKED /// flag of the shm_perm.mode field in the associated data structure /// retrieved by IPC_STAT will be set. - #[cfg(linux)] + #[cfg(target_os = "linux")] SHM_LOCK; /// Unlock the segment, allowing it to be swapped out. - #[cfg(linux)] + #[cfg(target_os = "linux")] SHM_UNLOCK; } ); From 2803e314ac4c577d2cd2f0c9f32f7c78bcd4d233 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:07:01 +0100 Subject: [PATCH 43/58] Fix wrong comment formatting --- src/sys/system_v/shm.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 82e2af9b83..188279e567 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -161,8 +161,8 @@ impl Shm { // -- Private -- - /// Attaches the System V shared memory segment identified by `shmid` to the - /// address space of the calling process. + /// Attaches the System V shared memory segment identified by a shmid to + /// the address space of the calling process. /// /// This is called automatically on [`Shm::attach`]. /// @@ -184,7 +184,7 @@ impl Shm { #[derive(Debug)] /// Safe wrapper around a SystemV shared memory segment data /// -/// This is a smart pointer, and so implement the [`Deref`] and [`DerefMut`] traits. +/// This is a smart pointer, and so implements the [`Deref`] and [`DerefMut`] traits. /// This means that you can work with the shared memory segment like you would with a [`Box`]. /// /// This type does not automatically destroy the shared memory segment, but @@ -238,8 +238,8 @@ impl Drop for SharedMemory { } impl SharedMemory { - /// Performs control operation specified by `cmd` on the System V shared - /// memory segment given by `shmid`. + /// Performs control operation specified by `cmd` on the current System V + /// shared memory segment. /// /// For more information, see [`shmctl(2)`]. /// @@ -280,7 +280,7 @@ impl SharedMemory { // -- Private -- - /// Performs the reverse of [`SharedMemory::shmat`], detaching the shared memory segment at + /// Performs the reverse of [`Shm::shmat`], detaching the shared memory segment at /// the given address from the address space of the calling process. /// /// This is called automatically on [`Drop`]. @@ -296,7 +296,7 @@ impl SharedMemory { } libc_bitflags!( - /// Valid flags for the third parameter of the function [`shmget`] + /// Valid flags for the third parameter of the function [`Shm::shmget`]. pub struct ShmgetFlag: c_int { /// A new shared memory segment is created if key has this value. From 35c2f4edf16bdc85e91a1b5053d66cbae272628c Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:17:36 +0100 Subject: [PATCH 44/58] Move `shmctl` function to `Shm`. This make it possible to delete a memory segment without attaching. --- src/sys/system_v/shm.rs | 78 ++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 188279e567..da03e6745c 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -109,6 +109,44 @@ impl Shm { }) } + /// Performs control operation specified by `cmd` on the current System V + /// shared memory segment. + /// + /// For more information, see [`shmctl(2)`]. + /// + /// # Example + /// + /// ## Deleting a shared memory segment + /// + /// ```no_run + /// # use nix::errno::Errno; + /// # use nix::sys::system_v::shm::*; + /// # use nix::sys::stat::Mode; + /// # + /// struct MyData(i64); + /// const MY_KEY: i32 = 1337; + /// + /// let mem_segment = Shm::::create_and_connect( + /// MY_KEY, + /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, + /// )?; + /// let _ = mem_segment.shmctl(ShmctlFlag::IPC_RMID, None)?; + /// # Ok::<(), Errno>(()) + /// ``` + /// + /// [`shmctl(2)`]: https://man7.org/linux/man-pages/man2/shmctl.2.html + pub fn shmctl( + &self, + shm_cmd: ShmctlFlag, + buf: Option<&mut shmid_ds>, + ) -> Result { + let buf_ptr: *mut shmid_ds = match buf { + Some(ptr) => ptr, + None => ptr::null_mut(), + }; + Errno::result(unsafe { libc::shmctl(self.id, shm_cmd.bits(), buf_ptr) }) + } + /// Creates and returns a new, or returns an existing, System V shared memory /// segment identifier. /// @@ -238,46 +276,6 @@ impl Drop for SharedMemory { } impl SharedMemory { - /// Performs control operation specified by `cmd` on the current System V - /// shared memory segment. - /// - /// For more information, see [`shmctl(2)`]. - /// - /// # Example - /// - /// ## Deleting a shared memory segment - /// - /// ```no_run - /// # use nix::errno::Errno; - /// # use nix::sys::system_v::shm::*; - /// # use nix::sys::stat::Mode; - /// # - /// struct MyData(i64); - /// const MY_KEY: i32 = 1337; - /// - /// let mem_segment = Shm::::create_and_connect( - /// MY_KEY, - /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, - /// )?; - /// let shared_memory = mem_segment.attach(ShmatFlag::empty())?; - /// - /// let _ = shared_memory.shmctl(ShmctlFlag::IPC_RMID, None)?; - /// # Ok::<(), Errno>(()) - /// ``` - /// - /// [`shmctl(2)`]: https://man7.org/linux/man-pages/man2/shmctl.2.html - pub fn shmctl( - &self, - shm_cmd: ShmctlFlag, - buf: Option<&mut shmid_ds>, - ) -> Result { - let buf_ptr: *mut shmid_ds = match buf { - Some(ptr) => ptr, - None => ptr::null_mut(), - }; - Errno::result(unsafe { libc::shmctl(self.id, shm_cmd.bits(), buf_ptr) }) - } - // -- Private -- /// Performs the reverse of [`Shm::shmat`], detaching the shared memory segment at From d05db61388ca88b77f0e7b205ecb962bfa1b5557 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:18:18 +0100 Subject: [PATCH 45/58] Update doctest --- src/sys/system_v/shm.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index da03e6745c..2f8d73f75b 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -18,6 +18,7 @@ use libc::{self, c_int, c_void, key_t, shmid_ds}; /// # Example /// /// ```no_run +/// # use std::ptr; /// # use nix::errno::Errno; /// # use nix::sys::system_v::shm::*; /// # use nix::sys::stat::Mode; @@ -29,7 +30,7 @@ use libc::{self, c_int, c_void, key_t, shmid_ds}; /// MY_KEY, /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, /// )?; -/// let mut shared_memory = mem_segment.attach(ShmatFlag::empty())?; +/// let mut shared_memory = mem_segment.attach(ptr::null(), ShmatFlag::empty())?; /// # Ok::<(), Errno>(()) /// ``` /// @@ -48,6 +49,7 @@ impl Shm { /// # Example /// /// ```no_run + /// # use std::ptr; /// # use nix::errno::Errno; /// # use nix::sys::system_v::shm::*; /// # use nix::sys::stat::Mode; @@ -233,6 +235,7 @@ impl Shm { /// # Example /// /// ```no_run +/// # use std::ptr; /// # use nix::errno::Errno; /// # use nix::sys::system_v::shm::*; /// # use nix::sys::stat::Mode; @@ -244,7 +247,7 @@ impl Shm { /// MY_KEY, /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, /// )?; -/// let mut shared_memory = mem_segment.attach(ShmatFlag::empty())?; +/// let mut shared_memory = mem_segment.attach(ptr::null(), ShmatFlag::empty())?; /// /// // This is writing to the stored [`MyData`] struct /// shared_memory.0 = 0xDEADBEEF; From ef1a839f6cb374b009e28b01d981c232822f7346 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:18:50 +0100 Subject: [PATCH 46/58] Remove `id` from `SharedMemory` don't need it now that `shmctl` isn't here --- src/sys/system_v/shm.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 2f8d73f75b..954e8fbc2a 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -72,7 +72,6 @@ impl Shm { ) -> Result> { unsafe { Ok(SharedMemory:: { - id: self.id, shm: ManuallyDrop::new(Box::from_raw( self.shmat(shmaddr, shmat_flag)?, )), @@ -255,7 +254,6 @@ impl Shm { /// ``` /// pub struct SharedMemory { - id: c_int, shm: ManuallyDrop>, } From e91efa39bb8e02e30edeb3432ca6b003142c94c0 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:52:23 +0100 Subject: [PATCH 47/58] Move test to independent file --- src/sys/system_v/shm.rs | 77 ----------------------------------------- test/sys/mod.rs | 8 +++++ test/sys/test_smh.rs | 69 ++++++++++++++++++++++++++++++++++++ test/test.rs | 2 ++ 4 files changed, 79 insertions(+), 77 deletions(-) create mode 100644 test/sys/test_smh.rs diff --git a/src/sys/system_v/shm.rs b/src/sys/system_v/shm.rs index 954e8fbc2a..ee4d3b00ac 100644 --- a/src/sys/system_v/shm.rs +++ b/src/sys/system_v/shm.rs @@ -414,80 +414,3 @@ libc_bitflags!( SHM_UNLOCK; } ); - -#[cfg(test)] -mod tests { - use super::*; - use parking_lot::Mutex; - - static SHM_MTX: Mutex<()> = Mutex::new(()); - - const SHM_TEST: i32 = 1337; - - #[derive(Debug)] - /// Test struct used to store some data on the shared memory segment - /// - struct TestData { - data: i64, - } - - #[derive(Debug)] - struct FixtureShm { - shm: Shm, - memory: SharedMemory, - } - - impl FixtureShm { - fn setup() -> Result { - let shm = Shm::::create_and_connect( - SHM_TEST, - Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, - )?; - let memory = shm.attach(ShmatFlag::empty())?; - Ok(Self { shm, memory }) - } - } - - impl Drop for FixtureShm { - fn drop(&mut self) { - let _ = - self.memory.shmctl(ShmctlFlag::IPC_RMID, None).map_err(|_| { - panic!("Failed to delete the test shared memory segment") - }); - } - } - - #[test] - fn create_ipc() -> Result<()> { - let _m = SHM_MTX.lock(); - - FixtureShm::setup()?; - Ok(()) - } - - #[test] - fn create_ipc_already_exist() -> Result<()> { - let _m = SHM_MTX.lock(); - - // Keep the IPC in scope, so we don't destroy it - let _fixture = FixtureShm::setup()?; - let expected = Errno::EEXIST; - let actual = FixtureShm::setup().expect_err("Return EExist"); - - assert_eq!(expected, actual); - Ok(()) - } - - #[test] - fn create_ipc_and_get_value() -> Result<()> { - let _m = SHM_MTX.lock(); - - let mut fixture = FixtureShm::setup()?; - let expected = 0xDEADBEEF; - fixture.memory.data = expected; - - let actual = fixture.shm.attach(ShmatFlag::empty())?.data; - assert_eq!(expected, actual); - Ok(()) - } -} diff --git a/test/sys/mod.rs b/test/sys/mod.rs index fb3f6be0e5..658110d523 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -25,6 +25,14 @@ mod test_mman; mod test_select; #[cfg(target_os = "linux")] mod test_signalfd; +#[cfg(all( + any( + bsd, + target_os = "linux", + ), + feature = "sysvipc", +))] +mod test_smh; #[cfg(not(any(target_os = "redox", target_os = "haiku")))] mod test_socket; #[cfg(not(any(target_os = "redox")))] diff --git a/test/sys/test_smh.rs b/test/sys/test_smh.rs new file mode 100644 index 0000000000..b65a3f115f --- /dev/null +++ b/test/sys/test_smh.rs @@ -0,0 +1,69 @@ +use nix::sys::shm::*; + +const SHM_TEST: i32 = 1337; + +#[derive(Debug, Default)] +/// Test struct used to store some data on the shared memory segment +/// +struct TestData { + data: i64, +} + +#[derive(Debug)] +struct FixtureShm { + shm: Shm, + memory: SharedMemory, +} + +impl FixtureShm { + fn setup() -> Result { + let shm = Shm::::create_and_connect( + SHM_TEST, + Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, + )?; + let memory = shm.attach(ptr::null(), ShmatFlag::empty())?; + Ok(Self { shm, memory }) + } +} + +impl Drop for FixtureShm { + fn drop(&mut self) { + let _ = self.shm.shmctl(ShmctlFlag::IPC_RMID, None).map_err(|_| { + panic!("Failed to delete the test shared memory segment") + }); + } +} + +#[test] +fn create_ipc() -> Result<()> { + let _m = SYSTEMV_MTX.lock(); + + FixtureShm::setup()?; + Ok(()) +} + +#[test] +fn create_ipc_already_exist() -> Result<()> { + let _m = SHM_MTX.lock(); + + // Keep the IPC in scope, so we don't destroy it + let _fixture = FixtureShm::setup()?; + let expected = Errno::EEXIST; + let actual = FixtureShm::setup().expect_err("Return EExist"); + + assert_eq!(expected, actual); + Ok(()) +} + +#[test] +fn create_ipc_and_get_value() -> Result<()> { + let _m = SHM_MTX.lock(); + + let mut fixture = FixtureShm::setup()?; + let expected = 0xDEADBEEF; + fixture.memory.data = expected; + + let actual = fixture.shm.attach(ptr::null(), ShmatFlag::empty())?.data; + assert_eq!(expected, actual); + Ok(()) +} diff --git a/test/test.rs b/test/test.rs index c7231426c2..8d032580a1 100644 --- a/test/test.rs +++ b/test/test.rs @@ -75,6 +75,8 @@ pub static KMOD_MTX: Mutex<()> = Mutex::new(()); pub static PTSNAME_MTX: Mutex<()> = Mutex::new(()); /// Any test that alters signal handling must grab this mutex. pub static SIGNAL_MTX: Mutex<()> = Mutex::new(()); +/// Any test that use SystemV must grab this mutex. +pub static SYSTEMV_MTX: Mutex<()> = Mutex::new(()); /// RAII object that restores a test's original directory on drop struct DirRestore<'a> { From d96d5ee7e8d5940d2bf981f852d3010ea0804f17 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:56:53 +0100 Subject: [PATCH 48/58] Follow Nix module convention --- src/sys/mod.rs | 13 +++++++------ src/sys/{system_v => }/sem.rs | 0 src/sys/{system_v => }/shm.rs | 0 src/sys/system_v/mod.rs | 6 ------ 4 files changed, 7 insertions(+), 12 deletions(-) rename src/sys/{system_v => }/sem.rs (100%) rename src/sys/{system_v => }/shm.rs (100%) delete mode 100644 src/sys/system_v/mod.rs diff --git a/src/sys/mod.rs b/src/sys/mod.rs index bbd503f0a0..d7d7e2468c 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -107,6 +107,13 @@ feature! { pub mod sendfile; } +#[cfg(any(bsd, target_os = "linux"))] +feature! { + #![feature = "sysvipc"] + pub mod sem; + pub mod shm; +} + pub mod signal; #[cfg(linux_android)] @@ -143,12 +150,6 @@ feature! { #[allow(missing_docs)] pub mod sysinfo; -#[cfg(any(bsd, target_os = "linux"))] -feature! { - #![feature = "system_v"] - pub mod system_v; -} - feature! { #![feature = "term"] #[allow(missing_docs)] diff --git a/src/sys/system_v/sem.rs b/src/sys/sem.rs similarity index 100% rename from src/sys/system_v/sem.rs rename to src/sys/sem.rs diff --git a/src/sys/system_v/shm.rs b/src/sys/shm.rs similarity index 100% rename from src/sys/system_v/shm.rs rename to src/sys/shm.rs diff --git a/src/sys/system_v/mod.rs b/src/sys/system_v/mod.rs deleted file mode 100644 index c194c632b5..0000000000 --- a/src/sys/system_v/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! Interact with SystemV inter-process communication API - -#[cfg(any(bsd, target_os = "linux"))] -pub mod sem; -#[cfg(any(bsd, target_os = "linux"))] -pub mod shm; From 6aa8740895a1241f364a412e3571a3afb372b91c Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:57:07 +0100 Subject: [PATCH 49/58] Update independent test import --- test/sys/test_smh.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/sys/test_smh.rs b/test/sys/test_smh.rs index b65a3f115f..ee31b543da 100644 --- a/test/sys/test_smh.rs +++ b/test/sys/test_smh.rs @@ -1,4 +1,11 @@ +use std::ptr; + +use nix::errno::Errno; use nix::sys::shm::*; +use nix::sys::stat::Mode; +use nix::Result; + +use crate::SYSTEMV_MTX; const SHM_TEST: i32 = 1337; @@ -44,7 +51,7 @@ fn create_ipc() -> Result<()> { #[test] fn create_ipc_already_exist() -> Result<()> { - let _m = SHM_MTX.lock(); + let _m = SYSTEMV_MTX.lock(); // Keep the IPC in scope, so we don't destroy it let _fixture = FixtureShm::setup()?; @@ -57,7 +64,7 @@ fn create_ipc_already_exist() -> Result<()> { #[test] fn create_ipc_and_get_value() -> Result<()> { - let _m = SHM_MTX.lock(); + let _m = SYSTEMV_MTX.lock(); let mut fixture = FixtureShm::setup()?; let expected = 0xDEADBEEF; From fd108e457373febf4dd5856986606ee17ca73b7a Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:57:34 +0100 Subject: [PATCH 50/58] Rename `system_v` feature to `sysvipc`, like Linux --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ac3cd34501..0c00d70959 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,7 @@ resource = [] sched = ["process"] signal = ["process"] socket = ["memoffset"] -system_v = ["fs"] +sysvipc = ["fs"] term = [] time = [] ucontext = ["signal"] From 2fde2c56bc1f06f80f2cd93fa12695e9a5324692 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 20:17:56 +0100 Subject: [PATCH 51/58] Update doctest import path --- src/sys/shm.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sys/shm.rs b/src/sys/shm.rs index ee4d3b00ac..7027d3be0d 100644 --- a/src/sys/shm.rs +++ b/src/sys/shm.rs @@ -20,7 +20,7 @@ use libc::{self, c_int, c_void, key_t, shmid_ds}; /// ```no_run /// # use std::ptr; /// # use nix::errno::Errno; -/// # use nix::sys::system_v::shm::*; +/// # use nix::sys::shm::*; /// # use nix::sys::stat::Mode; /// # /// struct MyData(i64); @@ -51,7 +51,7 @@ impl Shm { /// ```no_run /// # use std::ptr; /// # use nix::errno::Errno; - /// # use nix::sys::system_v::shm::*; + /// # use nix::sys::shm::*; /// # use nix::sys::stat::Mode; /// # /// struct MyData(i64); @@ -84,7 +84,7 @@ impl Shm { /// # Example /// ```no_run /// # use nix::errno::Errno; - /// # use nix::sys::system_v::shm::*; + /// # use nix::sys::shm::*; /// # use nix::sys::stat::Mode; /// # /// struct MyData(i64); @@ -121,7 +121,7 @@ impl Shm { /// /// ```no_run /// # use nix::errno::Errno; - /// # use nix::sys::system_v::shm::*; + /// # use nix::sys::shm::*; /// # use nix::sys::stat::Mode; /// # /// struct MyData(i64); @@ -167,7 +167,7 @@ impl Shm { /// /// ```no_run /// # use nix::errno::Errno; - /// # use nix::sys::system_v::shm::*; + /// # use nix::sys::shm::*; /// # use nix::sys::stat::Mode; /// # /// struct MyData(i64); @@ -236,7 +236,7 @@ impl Shm { /// ```no_run /// # use std::ptr; /// # use nix::errno::Errno; -/// # use nix::sys::system_v::shm::*; +/// # use nix::sys::shm::*; /// # use nix::sys::stat::Mode; /// # /// struct MyData(i64); From e531e8b40877a27c404c56c53a6a6bdfa9bd973d Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 20:23:19 +0100 Subject: [PATCH 52/58] Remove `IPC_PRIVATE` value from `ShmgetFlag`, because it wasn't actually a flag --- src/sys/shm.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sys/shm.rs b/src/sys/shm.rs index 7027d3be0d..96e8aa6478 100644 --- a/src/sys/shm.rs +++ b/src/sys/shm.rs @@ -298,8 +298,6 @@ libc_bitflags!( /// Valid flags for the third parameter of the function [`Shm::shmget`]. pub struct ShmgetFlag: c_int { - /// A new shared memory segment is created if key has this value. - IPC_PRIVATE; /// Create a new segment. /// If this flag is not used, then shmget() will find the segment /// associated with key and check to see if the user has permission From fee5acb3ea5ec142a2450be26e76f0492095de2b Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 20:40:06 +0100 Subject: [PATCH 53/58] Formatting parse --- test/sys/mod.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/test/sys/mod.rs b/test/sys/mod.rs index 658110d523..be0c2f4038 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -25,13 +25,7 @@ mod test_mman; mod test_select; #[cfg(target_os = "linux")] mod test_signalfd; -#[cfg(all( - any( - bsd, - target_os = "linux", - ), - feature = "sysvipc", -))] +#[cfg(all(any(bsd, target_os = "linux",), feature = "sysvipc",))] mod test_smh; #[cfg(not(any(target_os = "redox", target_os = "haiku")))] mod test_socket; From 52473cec0259ee41c63e8970a3381ad039ef7b6b Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Mon, 25 Mar 2024 20:41:19 +0100 Subject: [PATCH 54/58] Update comment of function that moved --- src/sys/shm.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sys/shm.rs b/src/sys/shm.rs index 96e8aa6478..dc96b09c55 100644 --- a/src/sys/shm.rs +++ b/src/sys/shm.rs @@ -44,7 +44,10 @@ impl Shm { /// /// To create a new segment, use [`Shm::create_and_connect`].\ /// If you need more customisation, use the unsafe version, - /// [`Shm::shmget`], with the key [`ShmgetFlag::IPC_CREAT`]. + /// [`Shm::shmget`], with the key [`ShmgetFlag::IPC_CREAT`].\ + /// + /// To delete a shared memory segment, use [`Shm::shmctl`], with the key [`ShmctlFlag::IPC_RMID`]. + /// /// /// # Example /// @@ -229,8 +232,6 @@ impl Shm { /// This type does not automatically destroy the shared memory segment, but /// only detach from it using RAII. /// -/// To delete a shared memory segment, use [`SharedMemory::shmctl`], with the key [`ShmctlFlag::IPC_RMID`]. -/// /// # Example /// /// ```no_run From d5267809374a324f91aa48df9269a18d49d1ffab Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Tue, 26 Mar 2024 23:41:46 +0100 Subject: [PATCH 55/58] Fix typo `test_smh` to `test_shm` --- test/sys/mod.rs | 2 +- test/sys/{test_smh.rs => test_shm.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename test/sys/{test_smh.rs => test_shm.rs} (100%) diff --git a/test/sys/mod.rs b/test/sys/mod.rs index be0c2f4038..57e7667bcf 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -26,7 +26,7 @@ mod test_select; #[cfg(target_os = "linux")] mod test_signalfd; #[cfg(all(any(bsd, target_os = "linux",), feature = "sysvipc",))] -mod test_smh; +mod test_shm; #[cfg(not(any(target_os = "redox", target_os = "haiku")))] mod test_socket; #[cfg(not(any(target_os = "redox")))] diff --git a/test/sys/test_smh.rs b/test/sys/test_shm.rs similarity index 100% rename from test/sys/test_smh.rs rename to test/sys/test_shm.rs From 400f71e8dec5cbcdd41bfe4dfb5ccda95497bd5c Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Wed, 27 Mar 2024 00:14:09 +0100 Subject: [PATCH 56/58] Add a test for unsafe `shmget`. Branch coverage is now 89.29% --- test/sys/test_shm.rs | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/test/sys/test_shm.rs b/test/sys/test_shm.rs index ee31b543da..3751df8fe1 100644 --- a/test/sys/test_shm.rs +++ b/test/sys/test_shm.rs @@ -42,7 +42,7 @@ impl Drop for FixtureShm { } #[test] -fn create_ipc() -> Result<()> { +fn create_shm() -> Result<()> { let _m = SYSTEMV_MTX.lock(); FixtureShm::setup()?; @@ -50,10 +50,10 @@ fn create_ipc() -> Result<()> { } #[test] -fn create_ipc_already_exist() -> Result<()> { +fn create_shm_already_exist() -> Result<()> { let _m = SYSTEMV_MTX.lock(); - // Keep the IPC in scope, so we don't destroy it + // Keep the fixture in scope, so we don't destroy it let _fixture = FixtureShm::setup()?; let expected = Errno::EEXIST; let actual = FixtureShm::setup().expect_err("Return EExist"); @@ -63,7 +63,7 @@ fn create_ipc_already_exist() -> Result<()> { } #[test] -fn create_ipc_and_get_value() -> Result<()> { +fn create_shm_and_get_value() -> Result<()> { let _m = SYSTEMV_MTX.lock(); let mut fixture = FixtureShm::setup()?; @@ -74,3 +74,26 @@ fn create_ipc_and_get_value() -> Result<()> { assert_eq!(expected, actual); Ok(()) } + +#[test] +fn connect_already_existing_shm() -> Result<()> { + let _m = SYSTEMV_MTX.lock(); + + let mut fixture = FixtureShm::setup()?; + let expected = 0xDEADBEEF; + fixture.memory.data = expected; + + let existing_mem_segment = unsafe { + Shm::::shmget( + SHM_TEST, + ShmgetFlag::empty(), + Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, + ) + }?; + let actual = existing_mem_segment + .attach(ptr::null(), ShmatFlag::empty())? + .data; + + assert_eq!(expected, actual); + Ok(()) +} From d619c4868a7dbecfafd74324db3f795185b9516c Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Wed, 27 Mar 2024 00:14:22 +0100 Subject: [PATCH 57/58] Improve code comment --- src/sys/shm.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/sys/shm.rs b/src/sys/shm.rs index dc96b09c55..f1501c335a 100644 --- a/src/sys/shm.rs +++ b/src/sys/shm.rs @@ -30,10 +30,10 @@ use libc::{self, c_int, c_void, key_t, shmid_ds}; /// MY_KEY, /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, /// )?; -/// let mut shared_memory = mem_segment.attach(ptr::null(), ShmatFlag::empty())?; +/// let shared_memory = mem_segment.attach(ptr::null(), ShmatFlag::empty())?; +/// // Do stuff with shared memory... /// # Ok::<(), Errno>(()) /// ``` -/// pub struct Shm { id: c_int, _phantom: PhantomData, @@ -67,7 +67,6 @@ impl Shm { /// let mut shared_memory = mem_segment.attach(ptr::null(), ShmatFlag::empty())?; /// # Ok::<(), Errno>(()) /// ``` - /// pub fn attach( &self, shmaddr: *const c_void, @@ -99,7 +98,6 @@ impl Shm { /// )?; /// # Ok::<(), Errno>(()) /// ``` - /// pub fn create_and_connect(key: key_t, mode: Mode) -> Result { let size = std::mem::size_of::(); // This is the main difference between this function and [`Shm::shmget`] @@ -176,13 +174,11 @@ impl Shm { /// struct MyData(i64); /// const MY_KEY: i32 = 1337; /// - /// unsafe { - /// let mem_segment = Shm::::shmget( - /// MY_KEY, - /// ShmgetFlag::empty(), - /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, - /// )?; - /// } + /// let mem_segment = unsafe { Shm::::shmget( + /// MY_KEY, + /// ShmgetFlag::empty(), + /// Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO, + /// )}?; /// # Ok::<(), Errno>(()) /// ``` /// @@ -251,6 +247,8 @@ impl Shm { /// /// // This is writing to the stored [`MyData`] struct /// shared_memory.0 = 0xDEADBEEF; +/// +/// // Detach here on shared_memory being dropped /// # Ok::<(), Errno>(()) /// ``` /// From ca0c5f9b0b6d2367368fae44b8e70af23237dc14 Mon Sep 17 00:00:00 2001 From: IzawGithub <125356732+IzawGithub@users.noreply.github.com> Date: Wed, 27 Mar 2024 00:17:33 +0100 Subject: [PATCH 58/58] Formatting parse --- test/sys/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/sys/mod.rs b/test/sys/mod.rs index 57e7667bcf..b952c9b152 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -23,10 +23,10 @@ mod test_ioctl; mod test_mman; #[cfg(not(target_os = "redox"))] mod test_select; -#[cfg(target_os = "linux")] -mod test_signalfd; #[cfg(all(any(bsd, target_os = "linux",), feature = "sysvipc",))] mod test_shm; +#[cfg(target_os = "linux")] +mod test_signalfd; #[cfg(not(any(target_os = "redox", target_os = "haiku")))] mod test_socket; #[cfg(not(any(target_os = "redox")))]