air_sys_types/process.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 fondamentaux de la famille `process`.
6//!
7//! Cf. `docs/specs/layer-0/family-process.md` et `air-sys-types.md`.
8//!
9//! `Pid` et `Tid` sont deux newtypes **distincts** sur `NonZeroI32` afin
10//! d'empêcher par typage la confusion entre un identifiant de processus et un
11//! identifiant de thread (application directe du Principe 7 et de la
12//! convention 1 de l'ADR-021). Le reste du module héberge les types liés à
13//! `clone3` (création de processus / thread) et `waitid` (attente
14//! d'événement sur un enfant).
15
16use core::num::NonZeroI32;
17use core::sync::atomic::AtomicU32;
18
19use bitflags::bitflags;
20
21use crate::fd::{BorrowedFd, OwnedFd};
22use crate::signal::Signal;
23
24/// Identifiant kernel d'un processus (`pid_t`).
25///
26/// Garanti strictement positif par le kernel Linux : un PID nul ne désigne
27/// jamais un processus réel (cf. `getpid(2)`). Le wrapper `NonZeroI32` encode
28/// cet invariant dans le type, ce qui permet à `Option<Pid>` d'occuper la
29/// même taille que `Pid`. La convention 1 de l'ADR-021 exploite cette
30/// propriété pour remplacer les sentinelles kernel (`0` = « processus
31/// courant ») par `None`.
32#[repr(transparent)]
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
34pub struct Pid(NonZeroI32);
35
36impl Pid {
37 /// Construit un `Pid` à partir d'une valeur déjà validée.
38 ///
39 /// Réservé aux producteurs qui ont déjà la garantie que la valeur vient
40 /// du kernel et est strictement positive (par exemple le wrapper
41 /// `getpid` de la couche 0).
42 #[must_use]
43 #[inline]
44 pub const fn from_nonzero(raw: NonZeroI32) -> Self {
45 Self(raw)
46 }
47
48 /// Tente de construire un `Pid` à partir d'un `i32` brut.
49 ///
50 /// Retourne `None` si la valeur est négative ou nulle, deux cas qui
51 /// n'identifient pas un processus valide.
52 #[must_use]
53 #[inline]
54 pub const fn try_from_raw(raw: i32) -> Option<Self> {
55 match NonZeroI32::new(raw) {
56 Some(nz) if nz.get() > 0 => Some(Self(nz)),
57 _ => None,
58 }
59 }
60
61 /// Retourne la représentation brute (`i32`) du PID.
62 ///
63 /// L'expose pour l'interfaçage avec les wrappers de syscalls et les
64 /// affichages humains ; éviter de manipuler des `i32` nus dans le
65 /// reste du code (convention « newtypes systématiques »).
66 #[must_use]
67 #[inline]
68 pub const fn as_raw(self) -> i32 {
69 self.0.get()
70 }
71}
72
73/// Identifiant kernel d'un thread (TID, `tid_t`).
74///
75/// Sur Linux, chaque thread possède un TID distinct. Le TID du thread
76/// principal d'un processus est égal au PID du processus (invariant
77/// `gettid() == getpid()` pour le thread principal).
78///
79/// Type **distinct** de [`Pid`] : on ne peut pas confondre les deux par
80/// inadvertance.
81#[repr(transparent)]
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
83pub struct Tid(NonZeroI32);
84
85impl Tid {
86 /// Construit un `Tid` à partir d'une valeur déjà validée.
87 #[must_use]
88 #[inline]
89 pub const fn from_nonzero(raw: NonZeroI32) -> Self {
90 Self(raw)
91 }
92
93 /// Tente de construire un `Tid` à partir d'un `i32` brut.
94 ///
95 /// Retourne `None` si la valeur est négative ou nulle.
96 #[must_use]
97 #[inline]
98 pub const fn try_from_raw(raw: i32) -> Option<Self> {
99 match NonZeroI32::new(raw) {
100 Some(nz) if nz.get() > 0 => Some(Self(nz)),
101 _ => None,
102 }
103 }
104
105 /// Retourne la représentation brute (`i32`) du TID.
106 #[must_use]
107 #[inline]
108 pub const fn as_raw(self) -> i32 {
109 self.0.get()
110 }
111}
112
113// ─────────────────────────────────────────────────────────────────────────
114// Identifiants d'utilisateur / de groupe (`uid_t` / `gid_t`).
115//
116// Spec : `docs/specs/layer-0/family-process-privsep.md` §1.
117//
118// Contrairement à `Pid`/`Tid` (toujours > 0), **`0` est une valeur valide**
119// (root) : on n'utilise donc pas `NonZeroI32` mais un newtype `repr(transparent)`
120// sur `u32` (= `uid_t`/`gid_t`). Newtypes typés systématiques (ADR-029 : jamais
121// d'`u32` brut pour un identifiant ; `Uid` et `Gid` sont **distincts** pour
122// empêcher toute confusion par typage).
123// ─────────────────────────────────────────────────────────────────────────
124
125/// Identifiant d'utilisateur (`uid_t`). `0` = root (valeur valide).
126///
127/// La valeur `(uid_t)-1` (`u32::MAX`) est **réservée** par le kernel comme
128/// sentinelle « inchangé » de `setresuid` ; elle est exposée typée via
129/// `Option<Uid>` (`None`), jamais comme un `Uid` de production.
130#[repr(transparent)]
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
132pub struct Uid(u32);
133
134impl Uid {
135 /// L'utilisateur root (`uid == 0`).
136 pub const ROOT: Self = Self(0);
137
138 /// Construit un `Uid` à partir d'une valeur brute `uid_t`.
139 #[must_use]
140 #[inline]
141 pub const fn from_raw(raw: u32) -> Self {
142 Self(raw)
143 }
144
145 /// Retourne la représentation brute (`uid_t`) de l'identifiant.
146 #[must_use]
147 #[inline]
148 pub const fn as_raw(self) -> u32 {
149 self.0
150 }
151}
152
153/// Identifiant de groupe (`gid_t`). `0` = groupe root (valeur valide).
154///
155/// Voir [`Uid`] pour la sentinelle `(gid_t)-1` (`None` de `setresgid`).
156#[repr(transparent)]
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
158pub struct Gid(u32);
159
160impl Gid {
161 /// Le groupe root (`gid == 0`).
162 pub const ROOT: Self = Self(0);
163
164 /// Construit un `Gid` à partir d'une valeur brute `gid_t`.
165 #[must_use]
166 #[inline]
167 pub const fn from_raw(raw: u32) -> Self {
168 Self(raw)
169 }
170
171 /// Retourne la représentation brute (`gid_t`) de l'identifiant.
172 #[must_use]
173 #[inline]
174 pub const fn as_raw(self) -> u32 {
175 self.0
176 }
177}
178
179/// Triplet d'identité UID **réel / effectif / sauvegardé** (`getresuid`).
180///
181/// Le *saved-set-uid* est la composante qui rend une réduction de privilèges
182/// **irréversible** : si `real == effective == saved == non-root`, le processus
183/// ne peut plus regagner root (cf. `setresuid`, `family-process-privsep.md` §3).
184#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185pub struct ResUid {
186 /// UID réel.
187 pub real: Uid,
188 /// UID effectif (gouverne les contrôles d'accès).
189 pub effective: Uid,
190 /// UID sauvegardé (*saved-set-uid*).
191 pub saved: Uid,
192}
193
194/// Triplet d'identité GID **réel / effectif / sauvegardé** (`getresgid`).
195#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196pub struct ResGid {
197 /// GID réel.
198 pub real: Gid,
199 /// GID effectif.
200 pub effective: Gid,
201 /// GID sauvegardé (*saved-set-gid*).
202 pub saved: Gid,
203}
204
205// ─────────────────────────────────────────────────────────────────────────
206// Création de processus : types associés à `clone3`.
207// Spec : `docs/specs/layer-0/family-process.md` sous-section 2.
208// ─────────────────────────────────────────────────────────────────────────
209
210/// File descriptor RAII qui référence un processus par pidfd.
211///
212/// Ferme automatiquement le FD sous-jacent à la destruction. Reste valide
213/// même après la mort du processus référencé (le FD ne se transfère pas à
214/// un éventuel nouveau processus qui recyclerait le PID).
215#[derive(Debug)]
216pub struct PidFd(OwnedFd);
217
218impl PidFd {
219 /// Construit un `PidFd` à partir d'un `OwnedFd` déjà ouvert sur un
220 /// processus par le kernel (typiquement via `pidfd_open` ou `clone3`
221 /// avec `CLONE_PIDFD`).
222 #[must_use]
223 #[inline]
224 pub const fn from_owned_fd(fd: OwnedFd) -> Self {
225 Self(fd)
226 }
227
228 /// Retourne une vue empruntée du FD sous-jacent.
229 #[must_use]
230 #[inline]
231 pub fn as_fd(&self) -> BorrowedFd<'_> {
232 use crate::fd::AsFd;
233 self.0.as_fd()
234 }
235
236 /// Consomme le `PidFd` et restitue le `OwnedFd` sous-jacent.
237 #[must_use]
238 #[inline]
239 pub fn into_fd(self) -> OwnedFd {
240 self.0
241 }
242}
243
244bitflags! {
245 /// Drapeaux de `clone3` (cf. `linux/sched.h`).
246 ///
247 /// Représentation `u64` pour accepter les bits Linux ≥ 5.7 qui dépassent
248 /// 32 bits (`CLEAR_SIGHAND`, `INTO_CGROUP`, `NEWTIME`).
249 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
250 pub struct CloneFlags: u64 {
251 /// Partage l'espace d'adressage avec le parent (création de thread).
252 const VM = 0x0000_0100;
253 /// Partage la table FS (`umask`, `cwd`, `root`).
254 const FS = 0x0000_0200;
255 /// Partage la table des FD ouverts.
256 const FILES = 0x0000_0400;
257 /// Partage la table des dispositions de signaux.
258 const SIGHAND = 0x0000_0800;
259 /// Demande au kernel de créer un pidfd vers l'enfant.
260 const PIDFD = 0x0000_1000;
261 /// L'enfant peut être tracé par `ptrace`.
262 const PTRACE = 0x0000_2000;
263 /// Suspend l'appelant jusqu'à `execve`/`exit` de l'enfant (vfork).
264 const VFORK = 0x0000_4000;
265 /// Le parent de l'enfant est le parent du parent (sibling).
266 const PARENT = 0x0000_8000;
267 /// L'enfant est dans le même groupe de threads que l'appelant.
268 const THREAD = 0x0001_0000;
269 /// L'enfant est dans un nouveau mount namespace.
270 const NEWNS = 0x0002_0000;
271 /// Partage les sémaphores System V.
272 const SYSVSEM = 0x0004_0000;
273 /// Initialise le `tls` de l'enfant avec la valeur fournie.
274 const SETTLS = 0x0008_0000;
275 /// Écrit le TID enfant dans `parent_tid` (mémoire du parent).
276 const PARENT_SETTID = 0x0010_0000;
277 /// `CLEAR_TID` à la mort de l'enfant.
278 const CHILD_CLEARTID = 0x0020_0000;
279 /// Enfant détaché (le parent n'est pas SIGCHLD-notifié).
280 const DETACHED = 0x0040_0000;
281 /// Désactive le traçage hérité par `ptrace`.
282 const UNTRACED = 0x0080_0000;
283 /// Écrit le TID enfant dans `child_tid` (mémoire de l'enfant).
284 const CHILD_SETTID = 0x0100_0000;
285 /// Nouveau cgroup namespace.
286 const NEWCGROUP = 0x0200_0000;
287 /// Nouveau UTS namespace (hostname).
288 const NEWUTS = 0x0400_0000;
289 /// Nouveau IPC namespace.
290 const NEWIPC = 0x0800_0000;
291 /// Nouveau user namespace.
292 const NEWUSER = 0x1000_0000;
293 /// Nouveau PID namespace.
294 const NEWPID = 0x2000_0000;
295 /// Nouveau network namespace.
296 const NEWNET = 0x4000_0000;
297 /// Partage le contexte I/O du parent.
298 const IO = 0x8000_0000;
299 /// Réinitialise toutes les dispositions de signal de l'enfant.
300 const CLEAR_SIGHAND = 0x1_0000_0000;
301 /// Place l'enfant dans le cgroup référencé par l'`fd` fourni.
302 const INTO_CGROUP = 0x2_0000_0000;
303 /// Nouveau time namespace (offsets `CLOCK_MONOTONIC`/`BOOTTIME`).
304 const NEWTIME = 0x4_0000_0000;
305 }
306}
307
308bitflags! {
309 /// Drapeaux de [`pidfd_open`](../../air_sys_syscall/process/fn.pidfd_open.html).
310 ///
311 /// La spec couche 0 (`docs/specs/layer-0/family-process.md`) ne définit
312 /// qu'une seule valeur valide (`NONBLOCK`) ; les autres bits sont
313 /// réservés et le kernel retourne `EINVAL` s'ils sont positionnés.
314 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
315 pub struct PidFdOpenFlags: u32 {
316 /// Le pidfd est créé en mode non-bloquant : `poll`/`epoll` sur
317 /// ce FD retournera immédiatement (utile pour de la surveillance
318 /// asynchrone). Valeur ABI `O_NONBLOCK = 0x800`.
319 const NONBLOCK = 0x800;
320 }
321}
322
323bitflags! {
324 /// Drapeaux de [`execveat`](../../air_sys_syscall/process/fn.execveat.html).
325 ///
326 /// Variante *at préférée d'`execve` (ADR-021 : « variantes modernes
327 /// préférées »). Correspond aux constantes `AT_*` de `<fcntl.h>`.
328 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
329 pub struct ExecveatFlags: i32 {
330 /// `AT_SYMLINK_NOFOLLOW` : si le composant final du chemin est un lien
331 /// symbolique, ne pas le suivre (échoue avec `ELOOP`).
332 const SYMLINK_NOFOLLOW = 0x100;
333 /// `AT_EMPTY_PATH` : `path` est vide (`c""`) et l'exécutable est
334 /// désigné **directement** par le `dirfd` (un fd ouvert sur le binaire,
335 /// typiquement avec `O_PATH`). Évite toute race sur le chemin
336 /// filesystem entre l'ouverture et l'exec.
337 const EMPTY_PATH = 0x1000;
338 }
339}
340
341bitflags! {
342 /// Drapeaux de [`dup3`](../../air_sys_syscall/process/fn.dup3.html).
343 ///
344 /// `dup3(2)` (et non `dup2`) impose de passer les drapeaux explicitement ;
345 /// le seul drapeau valide est `O_CLOEXEC`. Contrairement à `dup2`, `dup3`
346 /// échoue avec `EINVAL` si `old == new` — pas de no-op silencieux.
347 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
348 pub struct Dup3Flags: i32 {
349 /// `O_CLOEXEC` : le nouveau descripteur est marqué close-on-exec
350 /// (fermé automatiquement lors d'un `execve`/`execveat` ultérieur).
351 /// Valeur ABI `0o2000000`, identique sur x86_64 et aarch64.
352 const CLOEXEC = 0o2_000_000;
353 }
354}
355
356/// Spécification d'une pile pour la création d'un thread (`CLONE_VM`).
357///
358/// Le pointeur `addr` désigne l'**adresse de base** (octet le plus bas) d'une
359/// région mémoire valide d'au moins `size` octets, propriété de l'appelant
360/// (typiquement issue d'un `mmap` anonyme). Le kernel positionne le pointeur
361/// de pile de l'enfant au **sommet** de la région (`addr + size`) ; sur les
362/// deux architectures Air (x86_64, aarch64) la pile croît vers le bas.
363///
364/// **Répartition des rôles.** La création de thread se fait via
365/// [`clone_thread`](../../air_sys_syscall/process/fn.clone_thread.html), qui
366/// **exige** `Some(StackSpecification)` et valide en amont l'alignement
367/// (`addr` et `size` multiples de 16) et la taille minimale. Le wrapper de fork
368/// [`clone3`](../../air_sys_syscall/process/fn.clone3.html) — dont le modèle de
369/// retour `CloneResult::Child` est *unsound* sur une pile neuve — **rejette**
370/// au contraire `Some(StackSpecification)` avec
371/// [`Errno::EINVAL`](crate::errno::Errno::EINVAL) (cf. la doc de `clone_thread`
372/// pour la justification kernel : un retour Rust normal sur une pile neuve lit
373/// des ordures).
374#[derive(Debug, Clone, Copy)]
375pub struct StackSpecification {
376 /// Adresse de base de la pile.
377 pub addr: *mut u8,
378 /// Taille de la pile, en octets.
379 pub size: usize,
380}
381
382/// Emplacement mémoire où le kernel écrit (`CLONE_PARENT_SETTID` /
383/// `CLONE_CHILD_SETTID`) ou efface (`CLONE_CHILD_CLEARTID`) le TID d'un enfant
384/// créé par `clone3`.
385///
386/// Encapsule l'adresse d'un **mot 32 bits partagé** ([`AtomicU32`]) — un
387/// `tid_t` côté kernel — plutôt qu'un pointeur nu (convention 1 de l'ADR-021 :
388/// pas de pointeur brut non typé exposé). Le pattern de *join* de thread :
389///
390/// - `CLONE_PARENT_SETTID` + `parent_tid = &mot` → le kernel écrit le TID dans
391/// le mot, **dans le contexte du parent**, avant le retour de `clone3` (sans
392/// course avec une terminaison précoce de l'enfant) ;
393/// - `CLONE_CHILD_CLEARTID` + `child_tid = &mot` → à la mort du thread, le
394/// kernel remet le mot à `0` **et** effectue un `FUTEX_WAKE` dessus. Le parent
395/// joint donc en bouclant sur `futex_wait(&mot, tid)` jusqu'à lire `0`.
396///
397/// Le mot doit rester **vivant** (et l'`AtomicU32` non déplacé) jusqu'à la fin
398/// du *join* ; cette garantie incombe à l'appelant de la fonction `unsafe`
399/// `clone_thread`. Construire un `TidReceiver` est en soi inoffensif (on ne fait
400/// qu'enregistrer une adresse).
401#[derive(Debug, Clone, Copy)]
402pub struct TidReceiver {
403 /// Adresse du mot 32 bits (`tid_t`) partagé avec le kernel.
404 word: *mut u32,
405}
406
407impl TidReceiver {
408 /// Construit un récepteur à partir d'un mot atomique partagé vivant.
409 ///
410 /// On ne capture que l'**adresse** du mot ; sa validité au moment de l'appel
411 /// `clone3`/`clone_thread` (et jusqu'au *join*) relève du contrat `unsafe`
412 /// de ces fonctions.
413 #[must_use]
414 #[inline]
415 pub fn new(word: &AtomicU32) -> Self {
416 Self {
417 word: word.as_ptr(),
418 }
419 }
420
421 /// Retourne le pointeur brut du mot, pour le marshalling vers la structure
422 /// `clone_args` du kernel (réservé aux wrappers de la couche 0).
423 #[must_use]
424 #[inline]
425 pub fn as_ptr(self) -> *mut u32 {
426 self.word
427 }
428}
429
430/// Arguments pour `clone3`.
431///
432/// La spec couche 0 (`docs/specs/layer-0/family-process.md`) liste neuf
433/// champs (flags, pidfd, child_tid, parent_tid, exit_signal, stack, tls,
434/// set_tid, cgroup). Ce type en expose six : les trois du fork classique
435/// (`flags`, `exit_signal`, `stack`) **plus** les trois requis par la
436/// création de thread (`child_tid`, `parent_tid`, `tls`). Les restants
437/// (`pidfd` en tant que récepteur typé, `set_tid`, `cgroup`) seront ajoutés au
438/// fil des syscalls qui les exigent. L'évolution est strictement additive :
439/// aucun champ existant n'est destiné à être renommé ou retiré.
440///
441/// Pour demander la création d'un pidfd sur l'enfant, positionner le bit
442/// [`CloneFlags::PIDFD`] ; le `PidFd` résultant est retourné dans la
443/// variante [`CloneResult::Parent`].
444///
445/// **Création de thread.** Renseigner `stack`, `child_tid`/`parent_tid` (pour
446/// le *join* via `CLONE_CHILD_CLEARTID`) et éventuellement `tls`, puis appeler
447/// [`clone_thread`](../../air_sys_syscall/process/fn.clone_thread.html) (et non
448/// `clone3`, qui reste dédié au fork).
449#[derive(Debug, Clone, Default)]
450pub struct CloneArgs {
451 /// Drapeaux de comportement (`CLONE_*`).
452 pub flags: CloneFlags,
453 /// Signal à délivrer au parent à la mort de l'enfant. `Some(SIGCHLD)`
454 /// est le choix standard pour `clone3` style fork. **Doit être `None`**
455 /// pour un thread (`CLONE_THREAD` interdit tout signal de sortie).
456 pub exit_signal: Option<Signal>,
457 /// Pile pour `CLONE_VM` (création de thread). `None` pour le fork classique
458 /// (rejeté par `clone3` si renseigné) ; `Some(_)` est **requis** par
459 /// `clone_thread`.
460 pub stack: Option<StackSpecification>,
461 /// Récepteur du TID enfant côté **enfant** (`CLONE_CHILD_SETTID` /
462 /// `CLONE_CHILD_CLEARTID`). Pour le *join* de thread, le pointer sur le
463 /// même mot que [`Self::parent_tid`] et positionner `CLONE_CHILD_CLEARTID`.
464 pub child_tid: Option<TidReceiver>,
465 /// Récepteur du TID enfant côté **parent** (`CLONE_PARENT_SETTID`). Écrit
466 /// par le kernel avant le retour de `clone3`, sans course.
467 pub parent_tid: Option<TidReceiver>,
468 /// Adresse du bloc TLS de l'enfant (`CLONE_SETTLS`). `None` = pas de TLS
469 /// dédié (l'enfant hérite alors du registre TLS du parent : n'utiliser
470 /// **aucune** variable `thread_local` côté enfant dans ce cas). La spec
471 /// couche 0 type ce champ en `u64` (adresse brute) — voir
472 /// `docs/specs/layer-0/family-process.md`.
473 pub tls: Option<u64>,
474}
475
476/// Résultat de `clone3`.
477///
478/// Distingue sans ambiguïté le côté parent (avec le PID enfant et,
479/// optionnellement, un pidfd) du côté enfant (qui n'a rien d'autre à
480/// connaître que sa propre identité, accessible via `getpid`/`gettid`).
481#[derive(Debug)]
482pub enum CloneResult {
483 /// Retour côté parent.
484 Parent {
485 /// PID du nouvel enfant.
486 child_pid: Pid,
487 /// `PidFd` si [`CloneFlags::PIDFD`] était positionné, `None` sinon.
488 child_pidfd: Option<PidFd>,
489 },
490 /// Retour côté enfant.
491 ///
492 /// L'enfant doit typiquement exécuter ses traitements puis terminer
493 /// (`exit_group`, ou — dans les tests Air — `std::process::exit` qui
494 /// flush les compteurs LLVM de couverture). Voir le pattern recommandé
495 /// dans la spec.
496 Child,
497}
498
499// ─────────────────────────────────────────────────────────────────────────
500// Attente d'événements : types associés à `waitid`.
501// Spec : `docs/specs/layer-0/family-process.md` sous-section 3.
502// ─────────────────────────────────────────────────────────────────────────
503
504/// Cible d'un appel `waitid`.
505///
506/// L'enum encode à la fois `idtype` et `id` du syscall, en évitant les
507/// combinaisons invalides (`P_PIDFD` avec un id qui n'est pas un fd, etc.).
508#[derive(Debug, Clone, Copy)]
509pub enum WaitTarget<'fd> {
510 /// `P_ALL` — n'importe quel enfant.
511 AnyChild,
512 /// `P_PID` — un enfant désigné par son PID.
513 Pid(Pid),
514 /// `P_PGID` — n'importe quel processus du groupe désigné.
515 ProcessGroup(Pid),
516 /// `P_PGID` avec `id = 0` — le groupe de processus du caller.
517 AnyProcessGroup,
518 /// `P_PIDFD` — l'enfant référencé par le pidfd.
519 PidFd(BorrowedFd<'fd>),
520}
521
522bitflags! {
523 /// Options de `waitid` (`W*` flags).
524 ///
525 /// Au moins un parmi [`Self::EXITED`], [`Self::STOPPED`],
526 /// [`Self::CONTINUED`] doit être positionné, sinon le kernel n'a aucun
527 /// événement à rapporter et retourne `EINVAL`. Le wrapper laisse cette
528 /// validation au kernel.
529 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
530 pub struct WaitOptions: i32 {
531 /// `WNOHANG` — ne pas bloquer si aucun événement n'est disponible.
532 const NOHANG = 1;
533 /// `WSTOPPED` — rapporter les arrêts (`SIGSTOP`/`SIGTSTP`/…).
534 const STOPPED = 2;
535 /// `WEXITED` — rapporter les terminaisons (normal, par signal).
536 const EXITED = 4;
537 /// `WCONTINUED` — rapporter les reprises (`SIGCONT`).
538 const CONTINUED = 8;
539 /// `WNOWAIT` — ne pas consommer l'événement (peek).
540 const NOWAIT = 0x0100_0000;
541 }
542}
543
544/// État rapporté par `waitid` pour un événement enfant.
545#[derive(Debug, Clone, Copy)]
546pub struct WaitStatus {
547 /// PID de l'enfant concerné.
548 pub pid: Pid,
549 /// UID effectif de l'enfant au moment de l'événement.
550 pub uid: u32,
551 /// Type d'événement (terminaison, signal, arrêt, reprise, …).
552 pub event: WaitEvent,
553}
554
555/// Type d'événement remonté par `waitid`.
556///
557/// Le mapping kernel `si_code` → variante :
558/// - `CLD_EXITED` (1) → [`Self::Exited`]
559/// - `CLD_KILLED` (2) → [`Self::Killed { core_dumped: false }`](Self::Killed)
560/// - `CLD_DUMPED` (3) → [`Self::Killed { core_dumped: true }`](Self::Killed)
561/// - `CLD_TRAPPED` (4) → [`Self::Trapped`]
562/// - `CLD_STOPPED` (5) → [`Self::Stopped`]
563/// - `CLD_CONTINUED` (6) → [`Self::Continued`]
564#[derive(Debug, Clone, Copy, PartialEq, Eq)]
565pub enum WaitEvent {
566 /// L'enfant a terminé normalement (`exit_group` / retour de `main`).
567 Exited {
568 /// Code de sortie (low 8 bits du `exit_group(status)`).
569 code: i32,
570 },
571 /// L'enfant a été terminé par un signal.
572 Killed {
573 /// Signal fatal.
574 signal: Signal,
575 /// Vrai si le kernel a généré un fichier `core` (`CLD_DUMPED`).
576 core_dumped: bool,
577 },
578 /// L'enfant a été arrêté (`SIGSTOP`/`SIGTSTP`).
579 Stopped {
580 /// Signal d'arrêt.
581 signal: Signal,
582 },
583 /// L'enfant a été repris (`SIGCONT`).
584 Continued,
585 /// `ptrace` trap.
586 Trapped {
587 /// Signal de trap.
588 signal: Signal,
589 },
590}
591
592// ─────────────────────────────────────────────────────────────────────────
593// prctl : modes exposés (sous-section 5 de family-process.md).
594// ─────────────────────────────────────────────────────────────────────────
595
596/// Mode `dumpable` du processus (`prctl(PR_GET_DUMPABLE | PR_SET_DUMPABLE)`).
597///
598/// Contrôle si le processus peut être « dumpé » : produit un core, peut être
599/// attaché par ptrace, peut voir ses pages mémoire via `/proc/PID/mem`, etc.
600#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
601#[non_exhaustive]
602pub enum DumpableMode {
603 /// `SUID_DUMP_DISABLE` (0) : non-dumpable. État après `execve` d'un
604 /// binaire SUID/SGID/cap-augmenté.
605 NotDumpable = 0,
606 /// `SUID_DUMP_USER` (1) : dumpable par le propriétaire du processus.
607 /// Valeur par défaut pour la majorité des processus.
608 Dumpable = 1,
609 /// `SUID_DUMP_ROOT` (2, déprécié) : dumpable uniquement par root.
610 /// Conservé pour compatibilité historique ; les kernels récents le
611 /// traitent essentiellement comme `Dumpable`.
612 SuidDumpable = 2,
613}
614
615// ─────────────────────────────────────────────────────────────────────────
616// rlimits (sous-section 6 de family-process.md).
617// ─────────────────────────────────────────────────────────────────────────
618
619/// Type de ressource pour `getrlimit`/`setrlimit`/`prlimit`.
620///
621/// Valeurs `RLIMIT_*` extraites de `asm-generic/resource.h` (identiques
622/// `x86_64` et `aarch64` — ABI kernel stable hors archs hors ADR-014).
623#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
624#[non_exhaustive]
625pub enum Resource {
626 /// `RLIMIT_CPU` (0) : temps CPU max en secondes.
627 Cpu = 0,
628 /// `RLIMIT_FSIZE` (1) : taille max d'un fichier (octets).
629 FileSize = 1,
630 /// `RLIMIT_DATA` (2) : taille max du segment data.
631 Data = 2,
632 /// `RLIMIT_STACK` (3) : taille max de la pile.
633 Stack = 3,
634 /// `RLIMIT_CORE` (4) : taille max d'un core dump.
635 Core = 4,
636 /// `RLIMIT_RSS` (5) : taille max du RSS (largement ignoré depuis 2.4).
637 Rss = 5,
638 /// `RLIMIT_NPROC` (6) : nombre max de processus pour l'UID réel.
639 NProc = 6,
640 /// `RLIMIT_NOFILE` (7) : numéro max + 1 de FD ouvrable.
641 NoFile = 7,
642 /// `RLIMIT_MEMLOCK` (8) : taille max de mémoire `mlock()`'ée.
643 MemLock = 8,
644 /// `RLIMIT_AS` (9) : taille max de l'espace d'adressage virtuel.
645 As = 9,
646 /// `RLIMIT_LOCKS` (10) : nombre max de verrous `flock()` / `fcntl()`.
647 Locks = 10,
648 /// `RLIMIT_SIGPENDING` (11) : nombre max de signaux pendants en file.
649 SigPending = 11,
650 /// `RLIMIT_MSGQUEUE` (12) : octets max alloués aux POSIX message queues.
651 MsgQueue = 12,
652 /// `RLIMIT_NICE` (13) : niveau `nice` ceiling (offset depuis 20).
653 Nice = 13,
654 /// `RLIMIT_RTPRIO` (14) : priorité temps-réel ceiling.
655 RtPrio = 14,
656 /// `RLIMIT_RTTIME` (15) : temps CPU max (µs) en politique RT sans bloquer.
657 RtTime = 15,
658}
659
660/// Valeur d'une limite de ressource. `Infinity` correspond à
661/// `RLIM_INFINITY` (`u64::MAX` sur LP64).
662#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
663pub enum RlimitValue {
664 /// Limite finie.
665 Finite(u64),
666 /// `RLIM_INFINITY` côté kernel — pas de limite.
667 Infinity,
668}
669
670/// Paire (soft, hard) de limites de ressource.
671///
672/// La soft limit est celle réellement appliquée. La hard limit est le
673/// plafond que la soft limit ne peut excéder ; baisser la hard limit est
674/// possible pour tout processus, la relever exige `CAP_SYS_RESOURCE`.
675#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
676pub struct Rlimit {
677 /// Limite douce (effectivement appliquée).
678 pub soft: RlimitValue,
679 /// Limite dure (plafond pour la soft limit).
680 pub hard: RlimitValue,
681}
682
683// ─────────────────────────────────────────────────────────────────────────
684// Capabilities (sous-section 7 de family-process.md).
685// ─────────────────────────────────────────────────────────────────────────
686
687/// Capability Linux — permission fine remplaçant le modèle binaire
688/// root/non-root.
689///
690/// **Ensemble complet.** Les **41** capabilities du noyau (`CAP_CHOWN` = 0 …
691/// `CAP_CHECKPOINT_RESTORE` = 40 = `CAP_LAST_CAP` sur Linux ≥ 5.9) sont **toutes**
692/// nommées (ADR-126, `couche-0-v1.14`). Un consommateur peut donc désigner
693/// n'importe quelle capability — en particulier pour **réduire un bounding set à un
694/// sous-ensemble exact** (privsep, ADR-124), ce que le stub partiel initial (20/41)
695/// ne permettait pas.
696///
697/// **Valeurs numériques.** Tirées de `include/uapi/linux/capability.h` ;
698/// identiques `x86_64` et `aarch64` (ABI kernel stable hors archs hors
699/// ADR-014).
700///
701/// **Note de discipline (mot haut).** Les valeurs 32-40 (`CAP_MAC_OVERRIDE` …
702/// `CAP_CHECKPOINT_RESTORE`) occupent le **mot haut** (bits 32-63) du
703/// `CapabilityMask` (`u64`) et donc le second mot `u32` de l'ABI `capget`/`capset`.
704/// La conversion lo/hi est faite par les helpers d'`air-sys-syscall`
705/// (`mask_to_words`/`words_to_mask`) et testée explicitement (masque à bit ≥ 32)
706/// pour catcher une éventuelle inversion lo/hi.
707#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
708#[non_exhaustive]
709#[repr(u32)]
710pub enum Capability {
711 /// `CAP_CHOWN` (0) : changer l'ownership d'un fichier arbitrairement.
712 Chown = 0,
713 /// `CAP_DAC_OVERRIDE` (1) : passer outre les vérifications DAC.
714 DacOverride = 1,
715 /// `CAP_DAC_READ_SEARCH` (2) : lire/parcourir tout fichier/répertoire.
716 DacReadSearch = 2,
717 /// `CAP_FOWNER` (3) : opérations habituellement réservées au propriétaire.
718 Fowner = 3,
719 /// `CAP_FSETID` (4) : ne pas vider SUID/SGID sur modification.
720 Fsetid = 4,
721 /// `CAP_KILL` (5) : envoyer signal à n'importe quel processus.
722 Kill = 5,
723 /// `CAP_SETGID` (6) : manipulation arbitraire des GID.
724 Setgid = 6,
725 /// `CAP_SETUID` (7) : manipulation arbitraire des UID.
726 Setuid = 7,
727 /// `CAP_SETPCAP` (8) : transfert/retrait de capabilities (dont `PR_CAPBSET_DROP`).
728 Setpcap = 8,
729 /// `CAP_LINUX_IMMUTABLE` (9) : poser les attributs `IMMUTABLE`/`APPEND`.
730 LinuxImmutable = 9,
731 /// `CAP_NET_BIND_SERVICE` (10) : bind sur ports < 1024.
732 NetBindService = 10,
733 /// `CAP_NET_BROADCAST` (11) : broadcast et écoute multicast.
734 NetBroadcast = 11,
735 /// `CAP_NET_ADMIN` (12) : configuration réseau (interfaces, routage…).
736 NetAdmin = 12,
737 /// `CAP_NET_RAW` (13) : sockets raw et packet.
738 NetRaw = 13,
739 /// `CAP_IPC_LOCK` (14) : verrouiller de la mémoire (`mlock`, `SHM_LOCK`).
740 IpcLock = 14,
741 /// `CAP_IPC_OWNER` (15) : passer outre les vérifications de propriété IPC.
742 IpcOwner = 15,
743 /// `CAP_SYS_MODULE` (16) : load/unload de modules kernel.
744 SysModule = 16,
745 /// `CAP_SYS_RAWIO` (17) : I/O ports, `/dev/mem`, etc.
746 SysRawio = 17,
747 /// `CAP_SYS_CHROOT` (18) : `chroot(2)`.
748 SysChroot = 18,
749 /// `CAP_SYS_PTRACE` (19) : `ptrace` arbitraire.
750 SysPtrace = 19,
751 /// `CAP_SYS_PACCT` (20) : `acct(2)` (comptabilité des processus).
752 SysPacct = 20,
753 /// `CAP_SYS_ADMIN` (21) : « capability fourre-tout » système.
754 SysAdmin = 21,
755 /// `CAP_SYS_BOOT` (22) : `reboot(2)` et `kexec_load(2)`.
756 SysBoot = 22,
757 /// `CAP_SYS_NICE` (23) : priorités CPU et politiques RT/IO.
758 SysNice = 23,
759 /// `CAP_SYS_RESOURCE` (24) : relever les hard limits et autres ceilings.
760 SysResource = 24,
761 /// `CAP_SYS_TIME` (25) : horloge système et RTC.
762 SysTime = 25,
763 /// `CAP_SYS_TTY_CONFIG` (26) : configuration TTY et `vhangup(2)`.
764 SysTtyConfig = 26,
765 /// `CAP_MKNOD` (27) : créer des fichiers spéciaux via `mknod(2)`.
766 Mknod = 27,
767 /// `CAP_LEASE` (28) : poser des baux (`fcntl(F_SETLEASE)`).
768 Lease = 28,
769 /// `CAP_AUDIT_WRITE` (29) : écrire des enregistrements dans le journal d'audit.
770 AuditWrite = 29,
771 /// `CAP_AUDIT_CONTROL` (30) : configurer/désactiver l'audit du kernel.
772 AuditControl = 30,
773 /// `CAP_SETFCAP` (31) : poser des file-capabilities.
774 Setfcap = 31,
775 /// `CAP_MAC_OVERRIDE` (32) : passer outre le MAC (LSM Smack…).
776 MacOverride = 32,
777 /// `CAP_MAC_ADMIN` (33) : administrer la politique MAC.
778 MacAdmin = 33,
779 /// `CAP_SYSLOG` (34) : opérations `syslog(2)` privilégiées.
780 Syslog = 34,
781 /// `CAP_WAKE_ALARM` (35) : armer un réveil qui sort le système de veille.
782 WakeAlarm = 35,
783 /// `CAP_BLOCK_SUSPEND` (36) : empêcher la mise en veille du système.
784 BlockSuspend = 36,
785 /// `CAP_AUDIT_READ` (37) : lire le journal d'audit via socket multicast.
786 AuditRead = 37,
787 /// `CAP_PERFMON` (38) : monitoring de performance (`perf_event_open`…).
788 Perfmon = 38,
789 /// `CAP_BPF` (39) : opérations `bpf(2)` privilégiées.
790 Bpf = 39,
791 /// `CAP_CHECKPOINT_RESTORE` (40) : checkpoint/restore (`CAP_LAST_CAP`).
792 CheckpointRestore = 40,
793}
794
795impl Capability {
796 /// Retourne l'index ABI kernel (valeur numérique de `CAP_*`).
797 #[must_use]
798 #[inline]
799 pub const fn as_raw(self) -> u32 {
800 self as u32
801 }
802}
803
804/// Bitmask des capabilities.
805///
806/// Représentation interne `u64` : le bit `i` est positionné ssi la
807/// `Capability` de valeur numérique `i` est dans le masque. Les Linux v3
808/// capabilities (kernel ≥ 2.6.25) utilisent 64 bits côté kernel, exposés
809/// par l'ABI `capget`/`capset` sous forme de deux mots `u32` (bits 0-31
810/// puis bits 32-63) ; la conversion est réalisée par les helpers du
811/// wrapper `air-sys-syscall`.
812#[repr(transparent)]
813#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
814pub struct CapabilityMask(u64);
815
816impl CapabilityMask {
817 /// Masque vide (aucune capability).
818 #[must_use]
819 #[inline]
820 pub const fn empty() -> Self {
821 Self(0)
822 }
823
824 /// Construit un masque depuis sa représentation brute u64.
825 #[must_use]
826 #[inline]
827 pub const fn from_bits(bits: u64) -> Self {
828 Self(bits)
829 }
830
831 /// Représentation brute u64 du masque.
832 #[must_use]
833 #[inline]
834 pub const fn bits(self) -> u64 {
835 self.0
836 }
837
838 /// Vrai si `cap` est dans le masque.
839 #[must_use]
840 #[inline]
841 pub const fn contains(self, cap: Capability) -> bool {
842 (self.0 & (1_u64 << cap.as_raw())) != 0
843 }
844
845 /// Retourne un nouveau masque avec `cap` positionné.
846 #[must_use]
847 #[inline]
848 pub const fn with(self, cap: Capability) -> Self {
849 Self(self.0 | (1_u64 << cap.as_raw()))
850 }
851
852 /// Retourne un nouveau masque avec `cap` retiré.
853 #[must_use]
854 #[inline]
855 pub const fn without(self, cap: Capability) -> Self {
856 Self(self.0 & !(1_u64 << cap.as_raw()))
857 }
858}
859
860/// Triple des ensembles de capabilities pour un thread.
861///
862/// Trois ensembles indépendants :
863/// - `effective` : capabilities **actuellement utilisables** par le thread.
864/// - `permitted` : capabilities que le thread **peut activer** dans
865/// `effective` (super-ensemble de `effective` typiquement).
866/// - `inheritable` : capabilities **transmises** à travers `execve(2)`.
867#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
868pub struct CapabilitySet {
869 /// Capabilities actuellement actives.
870 pub effective: CapabilityMask,
871 /// Capabilities activables dans `effective`.
872 pub permitted: CapabilityMask,
873 /// Capabilities transmises à `execve(2)`.
874 pub inheritable: CapabilityMask,
875}
876
877/// Cible d'un appel `capget`/`capset`.
878///
879/// Per ADR-021 convention 1, l'invocation « pour soi-même » est exprimée
880/// par la variante dédiée [`CapabilityTarget::CurrentThread`] plutôt que par une
881/// sentinelle kernel `pid == 0`.
882#[derive(Debug, Clone, Copy)]
883pub enum CapabilityTarget {
884 /// Thread courant (équivalent kernel `pid == 0`).
885 CurrentThread,
886 /// Thread désigné par son TID Linux.
887 Thread(Tid),
888 /// Processus désigné par son PID (premier thread du groupe).
889 Process(Pid),
890}
891
892#[cfg(test)]
893mod tests;
894
895// ─────────────────────────────────────────────────────────────────────────
896// Ressources — `getrusage(2)` (ADR-051, re-sceau couche-0-v1.7)
897// ─────────────────────────────────────────────────────────────────────────
898
899/// `struct timeval` du kernel (`time_t tv_sec`, `suseconds_t tv_usec`), tel que
900/// rempli par `getrusage(2)`. Layout `#[repr(C)]` fidèle, cibles 64-bit.
901#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
902#[repr(C)]
903pub struct Timeval {
904 /// Secondes.
905 pub tv_sec: i64,
906 /// Microsecondes (`[0, 1_000_000)`).
907 pub tv_usec: i64,
908}
909
910/// Cible d'une mesure [`Rusage`] pour `getrusage(2)`. Re-présente `RUSAGE_*` en
911/// enum typé (pas de constante magique exposée — ADR-021).
912#[derive(Debug, Clone, Copy, PartialEq, Eq)]
913pub enum RusageWho {
914 /// Le processus appelant (`RUSAGE_SELF`).
915 SelfProcess,
916 /// Tous les enfants terminés **et attendus** (`RUSAGE_CHILDREN`).
917 Children,
918 /// Le thread appelant (`RUSAGE_THREAD`).
919 Thread,
920}
921
922/// `struct rusage` du kernel : statistiques de ressources (`getrusage(2)`).
923///
924/// Layout `#[repr(C)]` **fidèle au kernel** (2 `timeval` + 14 `long`), cibles
925/// 64-bit (ADR-014). Plusieurs champs ne sont **pas maintenus** par Linux (legacy
926/// BSD) et restent à `0` — on les expose tels quels (transparence, kernel = bible).
927#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
928#[repr(C)]
929pub struct Rusage {
930 /// Temps CPU passé en mode **utilisateur**.
931 pub ru_utime: Timeval,
932 /// Temps CPU passé en mode **noyau**.
933 pub ru_stime: Timeval,
934 /// Taille mémoire résidente **maximale** (Kio).
935 pub ru_maxrss: i64,
936 /// Mémoire partagée (non maintenu par Linux).
937 pub ru_ixrss: i64,
938 /// Mémoire data non partagée (non maintenu).
939 pub ru_idrss: i64,
940 /// Mémoire pile non partagée (non maintenu).
941 pub ru_isrss: i64,
942 /// Fautes de page **mineures** (sans I/O).
943 pub ru_minflt: i64,
944 /// Fautes de page **majeures** (avec I/O).
945 pub ru_majflt: i64,
946 /// Swaps (non maintenu).
947 pub ru_nswap: i64,
948 /// Lectures bloc en entrée.
949 pub ru_inblock: i64,
950 /// Écritures bloc en sortie.
951 pub ru_oublock: i64,
952 /// Messages IPC envoyés (non maintenu).
953 pub ru_msgsnd: i64,
954 /// Messages IPC reçus (non maintenu).
955 pub ru_msgrcv: i64,
956 /// Signaux reçus (non maintenu).
957 pub ru_nsignals: i64,
958 /// Changements de contexte **volontaires**.
959 pub ru_nvcsw: i64,
960 /// Changements de contexte **involontaires**.
961 pub ru_nivcsw: i64,
962}
963
964#[cfg(test)]
965mod rusage_types_tests;