starnix_task_command/
lib.rs1#![warn(missing_docs)]
6
7use flyweights::FlyByteStr;
10use std::ops::Range;
11
12#[derive(Clone, Eq, Hash, PartialEq)]
17pub struct TaskCommand {
18 name: FlyByteStr,
19 linux_name_range: Option<Range<usize>>,
20}
21
22impl TaskCommand {
23 pub fn new(name: &[u8]) -> Self {
26 let name = if let Some(idx) = memchr::memchr(b'\0', name) { &name[..idx] } else { name };
27 Self { name: FlyByteStr::new(name), linux_name_range: None }
28 }
29
30 pub fn from_path_bytes(path: &[u8]) -> Self {
32 let basename =
33 if let Some(idx) = memchr::memrchr(b'/', path) { &path[idx + 1..] } else { path };
34 Self::new(basename)
35 }
36
37 pub fn comm_name(&self) -> &[u8] {
39 let bytes = self.linux_name_bytes();
40 &bytes[..std::cmp::min(bytes.len(), 15)]
41 }
42
43 pub fn prctl_name(&self) -> [u8; 16] {
46 let mut name = [0u8; 16];
47 let comm = self.comm_name();
48 name[..comm.len()].copy_from_slice(comm);
49 name
50 }
51
52 pub fn as_bytes(&self) -> &[u8] {
54 self.name.as_bytes()
55 }
56
57 fn linux_name_bytes(&self) -> &[u8] {
59 if let Some(range) = &self.linux_name_range {
60 &self.name.as_bytes()[range.clone()]
61 } else {
62 self.name.as_bytes()
63 }
64 }
65
66 pub fn try_embed(&self, other: &TaskCommand) -> Option<Self> {
69 use bstr::ByteSlice;
70 self.name.as_bytes().find(other.linux_name_bytes()).map(|offset| Self {
71 name: self.name.clone(),
72 linux_name_range: Some(offset..offset + other.linux_name_bytes().len()),
73 })
74 }
75}
76
77impl Default for TaskCommand {
78 fn default() -> Self {
79 Self::new(b"")
80 }
81}
82
83impl std::fmt::Debug for TaskCommand {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 self.name.fmt(f)
86 }
87}
88
89impl std::fmt::Display for TaskCommand {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 self.name.fmt(f)
92 }
93}
94
95impl PartialOrd for TaskCommand {
96 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
97 Some(self.cmp(other))
98 }
99}
100
101impl Ord for TaskCommand {
102 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
105 self.name.cmp(&other.name)
106 }
107}
108
109impl Into<FlyByteStr> for TaskCommand {
110 fn into(self) -> FlyByteStr {
111 self.name
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_new() {
121 assert_eq!(TaskCommand::new(b"foo").as_bytes(), b"foo");
122 assert_eq!(TaskCommand::new(b"foo\0bar").as_bytes(), b"foo");
123 }
124
125 #[test]
126 fn test_from_path_bytes() {
127 assert_eq!(TaskCommand::from_path_bytes(b"/foo/bar").as_bytes(), b"bar");
128 assert_eq!(TaskCommand::from_path_bytes(b"bar").as_bytes(), b"bar");
129 assert_eq!(TaskCommand::from_path_bytes(b"/bar").as_bytes(), b"bar");
130 }
131
132 #[test]
133 fn test_comm_name() {
134 assert_eq!(TaskCommand::new(b"short").comm_name(), b"short");
135 assert_eq!(TaskCommand::new(b"0123456789abcdef").comm_name(), b"0123456789abcde");
136 assert_eq!(TaskCommand::new(b"0123456789abcdefg").comm_name(), b"0123456789abcde");
137 }
138
139 #[test]
140 fn test_prctl_name() {
141 assert_eq!(TaskCommand::new(b"short").prctl_name(), *b"short\0\0\0\0\0\0\0\0\0\0\0");
142 assert_eq!(TaskCommand::new(b"0123456789abcdef").prctl_name(), *b"0123456789abcde\0");
143 assert_eq!(TaskCommand::new(b"0123456789abcdefg").prctl_name(), *b"0123456789abcde\0");
144 }
145
146 #[test]
147 fn test_prctl_name_16_bytes() {
148 let name = b"0123456789abcdef"; assert_eq!(TaskCommand::new(name).prctl_name(), *b"0123456789abcde\0");
150 assert_eq!(TaskCommand::new(name).comm_name(), b"0123456789abcde"); }
152
153 #[test]
154 fn test_debug() {
155 assert_eq!(format!("{:?}", TaskCommand::new(b"foo")), "\"foo\"");
156 }
157
158 #[test]
159 fn test_display() {
160 assert_eq!(TaskCommand::new(b"foo").to_string(), "foo");
161 }
162
163 #[test]
164 fn test_sniffing() {
165 let argv0 = TaskCommand::new(b"/path/to/binary");
166 let short = TaskCommand::new(b"binary");
167 let embedded = argv0.try_embed(&short).expect("should embed");
168 assert_eq!(embedded.as_bytes(), b"/path/to/binary");
169 assert_eq!(embedded.comm_name(), b"binary");
170
171 let other = TaskCommand::new(b"other");
172 assert!(argv0.try_embed(&other).is_none());
173 }
174
175 #[test]
176 fn test_comm_name_sniffed() {
177 let long_argv0 = TaskCommand::new(b"/path/to/short_name_with_suffix");
178 let short_name = TaskCommand::new(b"short_name");
179 let embedded = long_argv0.try_embed(&short_name).expect("should embed");
180 assert_eq!(embedded.comm_name(), b"short_name");
182 }
183}