Skip to main content

air_sys_types/
system.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Types de la famille `system` — informations machine, entropie.
6//!
7//! Cf. `docs/specs/layer-0/family-system.md`.
8
9use alloc::ffi::CString;
10use core::time::Duration;
11
12use bitflags::bitflags;
13
14/// Nombre de CPU logiques adressables par un [`CpuSet`] (`CPU_SETSIZE` glibc).
15pub const CPU_SETSIZE: usize = 1024;
16
17/// Nombre de mots `u64` du masque ([`CPU_SETSIZE`] bits).
18const CPUSET_WORDS: usize = CPU_SETSIZE / 64;
19
20/// Masque d'affinité CPU — miroir de `cpu_set_t` (`CPU_SETSIZE` bits, 128
21/// octets), affinité des workers io-wq (`IORING_REGISTER_IOWQ_AFF`) et, à
22/// terme, `sched_setaffinity(2)` (cf. `family-system`).
23///
24/// Construit vide ; les CPU sont ajoutés un à un. Les indices **hors borne**
25/// (`≥ CPU_SETSIZE`) sont **ignorés sans panique** (les accesseurs renvoient
26/// `false`) — zéro présomption (Principe 3), jamais d'indexation qui panique.
27#[repr(C)]
28#[derive(Clone, Copy, PartialEq, Eq)]
29pub struct CpuSet {
30    /// Bitmap des CPU (LSB du mot 0 = CPU 0), layout `cpu_set_t`.
31    bits: [u64; CPUSET_WORDS],
32}
33
34impl CpuSet {
35    /// Masque **vide** (aucun CPU sélectionné).
36    #[must_use]
37    pub const fn new() -> Self {
38        Self {
39            bits: [0; CPUSET_WORDS],
40        }
41    }
42
43    /// Position `(mot, décalage de bit)` d'un CPU, ou `None` si hors borne. Le
44    /// décalage reste `usize` : un `u64 << usize` est licite, pas de cast lossy.
45    const fn locate(cpu: usize) -> Option<(usize, usize)> {
46        if cpu >= CPU_SETSIZE {
47            return None;
48        }
49        // `cpu < CPU_SETSIZE` ⇒ `cpu / 64 < CPUSET_WORDS` et `cpu % 64 < 64`.
50        Some((cpu / 64, cpu % 64))
51    }
52
53    /// Ajoute `cpu` au masque. Retourne `false` (sans rien changer) si `cpu` est
54    /// hors borne (`≥ CPU_SETSIZE`).
55    pub fn set(&mut self, cpu: usize) -> bool {
56        match Self::locate(cpu) {
57            Some((word, bit)) => {
58                // `locate` garantit `word < CPUSET_WORDS` ⇒ `get_mut` est `Some`
59                // (l'`expect` est structurellement inatteignable).
60                *self
61                    .bits
62                    .get_mut(word)
63                    .expect("word < CPUSET_WORDS (borné par locate)") |= 1u64 << bit;
64                true
65            }
66            None => false,
67        }
68    }
69
70    /// Retire `cpu` du masque. Retourne `false` si `cpu` est hors borne.
71    pub fn clear(&mut self, cpu: usize) -> bool {
72        match Self::locate(cpu) {
73            Some((word, bit)) => {
74                *self
75                    .bits
76                    .get_mut(word)
77                    .expect("word < CPUSET_WORDS (borné par locate)") &= !(1u64 << bit);
78                true
79            }
80            None => false,
81        }
82    }
83
84    /// `true` si `cpu` est dans le masque (`false` si hors borne).
85    #[must_use]
86    pub fn contains(&self, cpu: usize) -> bool {
87        match Self::locate(cpu) {
88            Some((word, bit)) => self.bits.get(word).is_some_and(|w| w & (1u64 << bit) != 0),
89            None => false,
90        }
91    }
92
93    /// Nombre de CPU sélectionnés (popcount du masque).
94    #[must_use]
95    pub fn count(&self) -> u32 {
96        self.bits.iter().map(|w| w.count_ones()).sum()
97    }
98
99    /// `true` si aucun CPU n'est sélectionné.
100    #[must_use]
101    pub fn is_empty(&self) -> bool {
102        self.bits.iter().all(|&w| w == 0)
103    }
104
105    /// Vue octets du masque (layout `cpu_set_t`), pour le passage au kernel
106    /// (`IORING_REGISTER_IOWQ_AFF` lit `nr_args` octets de cumpask).
107    #[must_use]
108    pub fn as_bytes(&self) -> &[u8] {
109        // SAFETY: `bits` est un `[u64; N]` `#[repr(C)]` densément peuplé ; toute
110        // configuration de bits est un motif d'octets valide (pas de padding,
111        // pas d'invariant de validité sur `u8`). La tranche est liée à `&self`.
112        unsafe {
113            core::slice::from_raw_parts(
114                core::ptr::from_ref(self).cast::<u8>(),
115                core::mem::size_of::<Self>(),
116            )
117        }
118    }
119
120    /// Vue octets **mutable** du masque (layout `cpu_set_t`), pour **remplir** le masque
121    /// depuis une source d'octets (p. ex. le `cpu_set_t *` reçu par la face libc
122    /// `sched_setaffinity`). Symétrique d'[`as_bytes`](Self::as_bytes). Additif ADR-085.
123    #[must_use]
124    pub fn as_mut_bytes(&mut self) -> &mut [u8] {
125        // SAFETY: `bits` est un `[u64; N]` `#[repr(C)]` densément peuplé (aucun padding,
126        // aucun invariant de validité sur `u8`) ⇒ tout motif d'octets écrit est valide.
127        // La tranche est liée à `&mut self` (exclusivité garantie par le borrow checker).
128        unsafe {
129            core::slice::from_raw_parts_mut(
130                core::ptr::from_mut(self).cast::<u8>(),
131                core::mem::size_of::<Self>(),
132            )
133        }
134    }
135}
136
137impl Default for CpuSet {
138    fn default() -> Self {
139        Self::new()
140    }
141}
142
143impl core::fmt::Debug for CpuSet {
144    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
145        // N'affiche que les CPU sélectionnés (le masque brut, 1024 bits, serait
146        // illisible).
147        f.debug_struct("CpuSet")
148            .field("selected_count", &self.count())
149            .finish()
150    }
151}
152
153/// Informations machine retournées par `uname(2)`.
154///
155/// Chaque champ est une chaîne C-compatible convertie en [`CString`]
156/// propriétaire. Le contenu est défini par le kernel ; aucun des champs
157/// n'est garanti UTF-8.
158#[derive(Debug, Clone)]
159pub struct UtsName {
160    /// Nom du système d'exploitation (typiquement `"Linux"`).
161    pub sysname: CString,
162    /// Nom de nœud réseau du système (hostname).
163    pub nodename: CString,
164    /// Version du kernel (ex. `"5.15.0-91-generic"`).
165    pub release: CString,
166    /// Informations de build du kernel.
167    pub version: CString,
168    /// Architecture matérielle (ex. `"x86_64"`, `"aarch64"`).
169    pub machine: CString,
170    /// Nom de domaine NIS (Linux-spécifique). Vide si non défini.
171    pub domainname: CString,
172}
173
174/// Statistiques globales du système retournées par `sysinfo(2)`.
175///
176/// Les valeurs de RAM et de swap sont exprimées en **octets** — le wrapper
177/// applique la multiplication par `mem_unit` en interne avant de
178/// construire ce type. Les `load_average` (1, 5 et 15 minutes) sont
179/// normalisés en `f64`.
180#[derive(Debug, Clone)]
181pub struct SystemInfo {
182    /// Durée de fonctionnement du système depuis le boot.
183    pub uptime: Duration,
184    /// Charge moyenne sur 1, 5 et 15 minutes (valeurs en nombre de processus).
185    pub load_average: [f64; 3],
186    /// Mémoire RAM totale (octets).
187    pub total_ram: u64,
188    /// Mémoire RAM libre (octets).
189    pub free_ram: u64,
190    /// Mémoire partagée (octets).
191    pub shared_ram: u64,
192    /// Mémoire en cache tampon (octets).
193    pub buffer_ram: u64,
194    /// Espace swap total (octets).
195    pub total_swap: u64,
196    /// Espace swap libre (octets).
197    pub free_swap: u64,
198    /// Nombre courant de processus.
199    pub processes: u16,
200    /// Total de mémoire high (zones >896 Mo sur x86 32 bits ; 0 sur 64 bits).
201    pub total_high: u64,
202    /// Mémoire high libre.
203    pub free_high: u64,
204    /// Unité de granularité mémoire du kernel (toujours 1 après conversion).
205    pub mem_unit: u32,
206}
207
208bitflags! {
209    /// Drapeaux pour `getrandom(2)` (cf. `linux/random.h`).
210    ///
211    /// En l'absence de flag, `getrandom` utilise le pool `urandom` et
212    /// bloque jusqu'à ce qu'il soit initialisé après le boot. C'est le
213    /// comportement recommandé pour 99 % des usages cryptographiques.
214    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
215    pub struct GetrandomFlags: u32 {
216        /// Retourne [`crate::Errno::EAGAIN`] immédiatement si l'entropie
217        /// n'est pas encore disponible, au lieu de bloquer.
218        const NONBLOCK = 1;
219        /// Utilise le pool `random` (bloquant quand l'entropie est faible).
220        /// Cryptographiquement équivalent à `urandom` sur Linux moderne ;
221        /// à éviter sauf contrainte spécifique.
222        const RANDOM   = 2;
223        /// Retourne immédiatement même si le pool n'est pas initialisé.
224        /// **Réservé aux usages non-cryptographiques.** La qualité de
225        /// l'entropie n'est pas garantie avant l'initialisation du pool.
226        const INSECURE = 4;
227    }
228}
229
230#[cfg(test)]
231mod cpuset_tests;