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