1pub use crate::{
4 apply_process_priority, assign_child_to_windows_job, cancel_capture_reader,
5 canonical_environment_pairs, capture_reader_done, compat_shell_command, configure_exact_trace,
6 configure_process_command, configure_process_command_for_bounded_owner_death,
7 configure_sync_contained_command, configure_sync_daemon_command,
8 configure_sync_daemon_command_with_inheritance, configure_trampoline_command,
9 current_executable_build_id, exact_trace_capability, exit_code, exit_signal,
10 monitor_console_windows, parent_has_console, prepare_capture_reader, send_interrupt,
11 set_process_name, shell_command, soft_terminate_process_group, spawn_sync, spawn_sync_daemon,
12 spawn_sync_daemon_with_inheritance, start_attached_descendant_monitor,
13 start_descendant_monitor, start_exact_trace, sync_child_native_handle, trampoline_exit_code,
14 unix_mark_extra_fds_close_on_exec, CaptureCancellation, PlatformCaptureReaders,
15 PlatformStdChild, TracedChild, WindowsJobHandle,
16};
17
18#[cfg(feature = "async-process")]
19pub use crate::{
20 PlatformChild, PlatformEmergencySignal, PlatformLifecycle, PlatformOutput, PlatformStdin,
21 SpawnSpec, StreamMode,
22};
23
24#[cfg(feature = "process-inspection")]
25pub use crate::{kill_tree, process_snapshot, process_snapshot_for_pid};
26
27#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
29pub struct ProcessCommandConfig {
30 pub creation_flags: Option<u32>,
31 pub create_process_group: bool,
32 pub nice: Option<i32>,
33 pub address_space_limit_bytes: Option<u64>,
34}
35
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
43pub struct DaemonExecInheritance {
44 descriptor: i32,
45}
46
47impl DaemonExecInheritance {
48 #[allow(dead_code)]
52 pub(crate) fn preserving_descriptor(descriptor: i32) -> Self {
53 Self { descriptor }
54 }
55
56 #[allow(dead_code)]
57 pub(crate) fn descriptor(self) -> i32 {
58 self.descriptor
59 }
60}
61
62#[derive(Clone, Debug, Eq, PartialEq)]
64pub struct ExactTraceCapability {
65 pub available: bool,
66 pub backend: &'static str,
67 pub reason: &'static str,
68 pub non_invasive_backend: &'static str,
69 pub non_invasive_grade: NonInvasiveObservationGrade,
70}
71
72#[derive(Clone, Copy, Debug, Eq, PartialEq)]
73pub enum NonInvasiveObservationGrade {
74 KernelNotification,
75 KernelHintReconciled,
76 SnapshotInferred,
77}
78
79#[derive(Clone, Debug, Default, Eq, PartialEq)]
81pub struct TraceOriginArtifact {
82 pub origin_pid: u32,
83 pub thread_id: u32,
84 pub architecture: String,
85 pub register_format: String,
86 pub executable: Option<std::path::PathBuf>,
87 pub registers: Vec<u8>,
88 pub stack_pointer: Option<u64>,
89 pub instruction_pointer: Option<u64>,
90 pub stack: Vec<u8>,
91 pub truncated: bool,
92 pub module_map: Vec<u8>,
93 pub module_map_truncated: bool,
94}
95
96#[derive(Clone, Debug, Eq, PartialEq)]
98pub struct ExactTraceEvent {
99 pub sequence: u64,
100 pub pid: u32,
101 pub parent_pid: Option<u32>,
102 pub parent_start_key: Option<u64>,
103 pub start_key: Option<u64>,
104 pub timestamp: std::time::SystemTime,
105 pub kind: ExactTraceEventKind,
106 pub executable: Option<std::path::PathBuf>,
107 pub argv: Option<Vec<std::ffi::OsString>>,
108 pub origin: Option<TraceOriginArtifact>,
109}
110
111#[derive(Clone, Debug, Eq, PartialEq)]
112pub enum ExactTraceEventKind {
113 Spawn,
114 Exec,
115 Exit {
116 exit_code: Option<i32>,
117 signal: Option<i32>,
118 raw_status: i64,
119 },
120 Loss {
121 reason: String,
122 },
123}
124
125#[derive(Clone, Copy, Debug, Eq, PartialEq)]
127pub enum DescendantEvent {
128 Started {
129 pid: u32,
130 parent_pid: Option<u32>,
136 },
137 Exited(u32),
138 Completed,
141}
142
143pub struct DescendantMonitorStop {
145 stopped: std::sync::atomic::AtomicBool,
146 mutex: std::sync::Mutex<()>,
147 wake: std::sync::Condvar,
148}
149
150impl DescendantMonitorStop {
151 pub fn new() -> Self {
153 Self {
154 stopped: std::sync::atomic::AtomicBool::new(false),
155 mutex: std::sync::Mutex::new(()),
156 wake: std::sync::Condvar::new(),
157 }
158 }
159
160 pub fn is_stopped(&self) -> bool {
162 self.stopped.load(std::sync::atomic::Ordering::Acquire)
163 }
164
165 pub fn stop(&self) {
167 let _guard = self.mutex.lock().unwrap_or_else(|error| error.into_inner());
168 if !self.stopped.swap(true, std::sync::atomic::Ordering::AcqRel) {
169 self.wake.notify_all();
170 }
171 }
172
173 pub fn wait_timeout(&self, timeout: std::time::Duration) -> bool {
175 if self.is_stopped() {
176 return true;
177 }
178 let guard = self.mutex.lock().unwrap_or_else(|error| error.into_inner());
179 if self.is_stopped() {
180 return true;
181 }
182 let (_guard, _wait_result) = self
183 .wake
184 .wait_timeout(guard, timeout)
185 .unwrap_or_else(|error| error.into_inner());
186 self.is_stopped()
187 }
188}
189
190impl Default for DescendantMonitorStop {
191 fn default() -> Self {
192 Self::new()
193 }
194}
195
196#[derive(Clone, Copy)]
198pub enum CaptureStream {
199 Stdout,
200 Stderr,
201}
202
203#[derive(Debug, Clone)]
205pub struct ConsoleWindowInfo {
206 pub pid: u32,
207 pub title: String,
208 pub hwnd: u64,
209}
210
211#[derive(Clone, Copy, Debug, Eq, PartialEq)]
215pub struct ProcessSnapshot {
216 pub pid: u32,
217 pub parent_pid: u32,
218 pub start_time_a: u64,
219 pub start_time_b: u64,
220}
221
222#[derive(Clone, Debug, Eq, PartialEq)]
227pub enum SyncEnvironment {
228 Inherit,
230 Explicit(Vec<(std::ffi::OsString, std::ffi::OsString)>),
232}
233
234pub struct SpawnStdio<'a> {
240 pub stdin: StdioSource<'a>,
242 pub stdout: StdioSource<'a>,
244 pub stderr: StdioSource<'a>,
246 pub drain_timeout: Option<std::time::Duration>,
248 pub show_console: bool,
250}
251
252impl Default for SpawnStdio<'_> {
253 fn default() -> Self {
254 Self {
255 stdin: StdioSource::Null,
256 stdout: StdioSource::Parent,
257 stderr: StdioSource::Parent,
258 drain_timeout: Some(std::time::Duration::from_secs(2)),
259 show_console: false,
260 }
261 }
262}
263
264pub struct DaemonStdio<'a> {
270 pub stdout: DaemonStdioSource<'a>,
272 pub stderr: DaemonStdioSource<'a>,
274}
275
276impl Default for DaemonStdio<'_> {
277 fn default() -> Self {
278 Self {
279 stdout: DaemonStdioSource::Null,
280 stderr: DaemonStdioSource::Null,
281 }
282 }
283}
284
285pub enum DaemonStdioSource<'a> {
287 Null,
289 File(&'a std::fs::File),
291}
292
293pub enum StdioSource<'a> {
295 Null,
297 Parent,
299 File(&'a std::fs::File),
301 Pipe,
303}
304
305pub struct DaemonChild {
307 pub(crate) pid: u32,
308 pub(crate) inner: Box<dyn DaemonChildControl>,
309}
310
311pub(crate) trait DaemonChildControl:
312 Send + Sync + std::panic::UnwindSafe + std::panic::RefUnwindSafe
313{
314 fn kill(&mut self) -> std::io::Result<()>;
315 fn wait(&mut self) -> std::io::Result<i32>;
316 fn try_wait(&mut self) -> std::io::Result<Option<i32>>;
317}
318
319impl DaemonChild {
320 pub fn id(&self) -> u32 {
322 self.pid
323 }
324
325 pub fn kill(&mut self) -> std::io::Result<()> {
327 self.inner.kill()
328 }
329
330 pub fn wait(&mut self) -> std::io::Result<i32> {
332 self.inner.wait()
333 }
334
335 pub fn try_wait(&mut self) -> std::io::Result<Option<i32>> {
337 self.inner.try_wait()
338 }
339}
340
341pub struct SpawnedChild {
345 pub(crate) kill_on_drop: bool,
346 pub stdin: Option<std::process::ChildStdin>,
348 pub stdout: Option<std::process::ChildStdout>,
350 pub stderr: Option<std::process::ChildStderr>,
352 pub(crate) pid: u32,
353 pub(crate) inner: Box<dyn SpawnedChildControl>,
354}
355
356pub trait SpawnedChildControl:
362 Send + Sync + std::panic::UnwindSafe + std::panic::RefUnwindSafe
363{
364 fn kill(&mut self) -> std::io::Result<()>;
365 fn wait(&mut self) -> std::io::Result<i32>;
366 fn try_wait(&mut self) -> std::io::Result<Option<i32>>;
367 fn shutdown(&mut self);
368 #[cfg(feature = "independent-spawn")]
369 fn retain_exit_identity(&mut self) {}
370 #[cfg(feature = "independent-spawn")]
371 fn detach(&mut self) -> std::io::Result<()> {
372 Ok(())
373 }
374 #[cfg(feature = "independent-spawn")]
375 fn kill_tree(&mut self) -> std::io::Result<()> {
376 self.kill()
377 }
378}
379
380impl SpawnedChild {
381 pub fn from_parts(
389 pid: u32,
390 stdin: Option<std::process::ChildStdin>,
391 stdout: Option<std::process::ChildStdout>,
392 stderr: Option<std::process::ChildStderr>,
393 inner: Box<dyn SpawnedChildControl>,
394 ) -> Self {
395 Self {
396 kill_on_drop: true,
397 stdin,
398 stdout,
399 stderr,
400 pid,
401 inner,
402 }
403 }
404
405 #[cfg(feature = "independent-spawn")]
406 pub(crate) fn retain_exit_identity(&mut self) {
407 self.inner.retain_exit_identity();
408 }
409 #[cfg(feature = "independent-spawn")]
410 pub(crate) fn commit_detached(&mut self) -> std::io::Result<()> {
411 self.inner.detach()?;
412 self.kill_on_drop = false;
413 Ok(())
414 }
415 #[cfg(feature = "independent-spawn")]
416 pub(crate) fn kill_tree(&mut self) -> std::io::Result<()> {
417 self.inner.kill_tree()
418 }
419 pub fn id(&self) -> u32 {
421 self.pid
422 }
423
424 pub fn kill(&mut self) -> std::io::Result<()> {
426 self.inner.kill()
427 }
428
429 pub fn wait(&mut self) -> std::io::Result<i32> {
431 self.inner.wait()
432 }
433
434 pub fn try_wait(&mut self) -> std::io::Result<Option<i32>> {
436 self.inner.try_wait()
437 }
438}
439
440impl Drop for SpawnedChild {
441 fn drop(&mut self) {
442 if self.kill_on_drop {
443 self.inner.shutdown();
444 }
445 }
446}
447
448#[derive(Clone, Copy)]
449pub enum ObserverScope {
450 SystemWide,
451 LaunchedProcessTree,
452}
453#[derive(Clone, Copy)]
454pub enum ObserverCategory {
455 File,
456 Network,
457 Process,
458}
459#[derive(Clone, Copy)]
460pub enum ObserverSupport {
461 Supported,
462 Partial,
463 Unavailable,
464}
465#[derive(Clone, Copy)]
466pub struct ObserverBackend {
467 pub support: ObserverSupport,
468 pub backend: &'static str,
469 pub reason: &'static str,
470}
471pub use crate::{
472 process_observer_backend as observer_backend, process_read_argv as read_process_argv,
473 process_read_cmdline as read_process_cmdline,
474 process_read_file_handles as read_process_file_handles,
475};
476
477#[derive(Debug, Clone, Copy, PartialEq, Eq)]
479pub enum UnixSignalKind {
480 Interrupt,
481 Terminate,
482 Kill,
483}
484
485pub use crate::{
486 unix_set_priority, unix_signal_process, unix_signal_process_group, unix_signal_raw,
487};
488
489#[derive(Debug, Clone, Copy, PartialEq, Eq)]
497pub enum OwnerDeathCleanup {
498 OwnerDeathSignal,
500 KillOnOwnerHandleClose,
502 AlreadyContained,
504 SupervisorRequired,
506 Unsupported,
508}
509
510#[derive(Debug, Clone, Copy, PartialEq, Eq)]
518pub enum OwnerDeathCleanupStage {
519 RequestSignal,
521 CreateContainer,
523 JoinContainer,
525}
526
527#[derive(Debug)]
529pub struct OwnerDeathCleanupError {
530 pub stage: OwnerDeathCleanupStage,
532 pub source: std::io::Error,
534}
535
536impl std::fmt::Display for OwnerDeathCleanupError {
537 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
538 write!(f, "{:?}: {}", self.stage, self.source)
539 }
540}
541
542impl std::error::Error for OwnerDeathCleanupError {
543 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
544 Some(&self.source)
545 }
546}
547
548pub use crate::{
549 process_install_owner_death_cleanup as install_owner_death_cleanup,
550 process_owner_death_cleanup_target as owner_death_cleanup_target,
551};
552
553#[derive(Debug, Clone, Copy, PartialEq, Eq)]
560pub enum ProcessInspectErrorKind {
561 InvalidPid,
563 NotFound,
565 Unsupported,
567 Host,
569}
570
571#[derive(Debug)]
573pub struct ProcessInspectError {
574 pub kind: ProcessInspectErrorKind,
576 pub source: std::io::Error,
578}
579
580impl ProcessInspectError {
581 pub fn last_os_error(kind: ProcessInspectErrorKind) -> Self {
583 Self {
584 kind,
585 source: std::io::Error::last_os_error(),
586 }
587 }
588
589 pub fn stated(kind: ProcessInspectErrorKind, message: &str) -> Self {
591 Self {
592 kind,
593 source: std::io::Error::other(message.to_string()),
594 }
595 }
596}
597
598impl std::fmt::Display for ProcessInspectError {
599 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
600 write!(f, "{:?}: {}", self.kind, self.source)
601 }
602}
603
604impl std::error::Error for ProcessInspectError {
605 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
606 Some(&self.source)
607 }
608}
609
610pub use crate::{
611 process_executable_path as executable_path, process_fault_code_name as fault_code_name,
612 process_force_kill as force_kill, process_same_executable_path as same_executable_path,
613 process_signal_terminate as signal_terminate, ProcessLiveness,
614};
615
616pub struct ShutdownRequest {
628 flag: &'static std::sync::atomic::AtomicBool,
629}
630
631impl ShutdownRequest {
632 pub fn watching(flag: &'static std::sync::atomic::AtomicBool) -> Self {
643 Self { flag }
644 }
645
646 pub fn requested(&self) -> bool {
652 self.flag.load(std::sync::atomic::Ordering::Relaxed)
653 }
654}
655
656impl std::fmt::Debug for ShutdownRequest {
657 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
658 f.debug_struct("ShutdownRequest")
659 .field("requested", &self.requested())
660 .finish()
661 }
662}
663
664pub use crate::process_install_shutdown_request_handler as install_shutdown_request_handler;
665
666pub use crate::{
678 process_can_replace_current_image as can_replace_current_image,
679 process_replace_current_image as replace_current_image,
680};
681
682#[derive(Clone, Copy, Debug, Eq, PartialEq)]
688#[non_exhaustive]
689pub enum LoadedImageFormat {
690 Pe,
692 Elf,
695 MachO,
698}
699
700#[derive(Clone, Debug)]
705pub struct LoadedImage {
706 pub format: LoadedImageFormat,
708 pub header_address: u64,
711 pub image_size: u64,
713 pub slide: i64,
715 pub path: Option<String>,
717 pub mapped_ranges: Vec<std::ops::Range<u64>>,
719 pub executable_ranges: Vec<std::ops::Range<u64>>,
721 pub elf_load_bias: Option<u64>,
724 pub build_id: Option<Vec<u8>>,
726 #[allow(dead_code)]
729 pub(crate) backing_file: Option<LoadedImageBackingFile>,
730}
731
732#[derive(Clone, Debug, Eq, PartialEq)]
737#[allow(dead_code)]
738pub(crate) struct LoadedImageBackingFile {
739 pub(crate) device_major: u64,
740 pub(crate) device_minor: u64,
741 pub(crate) inode: String,
742}
743
744pub use crate::{
752 process_loaded_images as loaded_images,
753 process_open_loaded_image_file as open_loaded_image_file,
754};
755
756#[cfg(test)]
757mod loaded_image_tests {
758 #[inline(never)]
760 fn landmark() -> u64 {
761 std::hint::black_box(0x0974_0004_u64)
762 }
763
764 #[test]
765 fn the_image_holding_this_code_is_reported_with_the_hosts_format() {
766 let address = landmark as fn() -> u64 as usize as u64;
767 assert_eq!(landmark(), 0x0974_0004);
768 let images = super::loaded_images().expect("enumerate loaded images");
769 assert!(!images.is_empty(), "the loader reported no images");
770 let expected = match std::env::consts::OS {
771 "windows" => super::LoadedImageFormat::Pe,
772 "macos" => super::LoadedImageFormat::MachO,
773 _ => super::LoadedImageFormat::Elf,
774 };
775 assert!(images.iter().all(|image| image.format == expected));
776 let owner = images
779 .iter()
780 .find(|image| {
781 image
782 .mapped_ranges
783 .iter()
784 .any(|range| range.contains(&address))
785 || (image.header_address..image.header_address.saturating_add(image.image_size))
786 .contains(&address)
787 })
788 .or_else(|| {
789 images
790 .iter()
791 .filter(|image| image.format == super::LoadedImageFormat::MachO)
792 .filter(|image| image.header_address <= address)
793 .max_by_key(|image| image.header_address)
794 });
795 let owner = owner.unwrap_or_else(|| panic!("no loaded image covers {address:#x}"));
796 assert!(owner.path.is_some(), "owning image reported no path");
797 assert!(
798 super::open_loaded_image_file(owner).is_some(),
799 "the file behind the owning image could not be reopened: {:?}",
800 owner.path
801 );
802 }
803}
804
805#[cfg(test)]
806mod exit_signal_tests {
807 #[test]
810 fn a_normal_exit_reports_no_signal() {
811 let status = std::process::ExitStatus::default();
812 assert!(status.success());
813 assert_eq!(super::exit_signal(&status), None);
814 }
815}