1 // Copyright 2017 Mozilla Foundation
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use crate::mock_command::{CommandChild, RunCommand};
16 use blake3::Hasher as blake3_Hasher;
17 use byteorder::{BigEndian, ByteOrder};
18 use futures::{future, Future};
19 use futures_03::executor::ThreadPool;
20 use futures_03::future::TryFutureExt;
21 use futures_03::task;
22 use serde::Serialize;
23 use std::ffi::{OsStr, OsString};
24 use std::fs::File;
25 use std::hash::Hasher;
26 use std::io::prelude::*;
27 use std::path::{Path, PathBuf};
28 use std::process::{self, Stdio};
29 use std::time;
30 use std::time::Duration;
31
32 use crate::errors::*;
33
34 pub trait SpawnExt: task::SpawnExt {
spawn_fn<F, T>(&self, f: F) -> SFuture<T> where F: FnOnce() -> Result<T> + std::marker::Send + 'static, T: std::marker::Send + 'static,35 fn spawn_fn<F, T>(&self, f: F) -> SFuture<T>
36 where
37 F: FnOnce() -> Result<T> + std::marker::Send + 'static,
38 T: std::marker::Send + 'static,
39 {
40 self.spawn_with_handle(async move { f() })
41 .map(|f| Box::new(f.compat()) as _)
42 .unwrap_or_else(f_err)
43 }
44 }
45
46 impl<S: task::SpawnExt + ?Sized> SpawnExt for S {}
47
48 #[derive(Clone)]
49 pub struct Digest {
50 inner: blake3_Hasher,
51 }
52
53 impl Digest {
new() -> Digest54 pub fn new() -> Digest {
55 Digest {
56 inner: blake3_Hasher::new(),
57 }
58 }
59
60 /// Calculate the BLAKE3 digest of the contents of `path`, running
61 /// the actual hash computation on a background thread in `pool`.
file<T>(path: T, pool: &ThreadPool) -> SFuture<String> where T: AsRef<Path>,62 pub fn file<T>(path: T, pool: &ThreadPool) -> SFuture<String>
63 where
64 T: AsRef<Path>,
65 {
66 Self::reader(path.as_ref().to_owned(), pool)
67 }
68
69 /// Calculate the BLAKE3 digest of the contents read from `reader`.
reader_sync<R: Read>(mut reader: R) -> Result<String>70 pub fn reader_sync<R: Read>(mut reader: R) -> Result<String> {
71 let mut m = Digest::new();
72 // A buffer of 128KB should give us the best performance.
73 // See https://eklitzke.org/efficient-file-copying-on-linux.
74 let mut buffer = [0; 128 * 1024];
75 loop {
76 let count = reader.read(&mut buffer[..])?;
77 if count == 0 {
78 break;
79 }
80 m.update(&buffer[..count]);
81 }
82 Ok(m.finish())
83 }
84
85 /// Calculate the BLAKE3 digest of the contents of `path`, running
86 /// the actual hash computation on a background thread in `pool`.
reader(path: PathBuf, pool: &ThreadPool) -> SFuture<String>87 pub fn reader(path: PathBuf, pool: &ThreadPool) -> SFuture<String> {
88 Box::new(pool.spawn_fn(move || -> Result<_> {
89 let reader = File::open(&path)
90 .with_context(|| format!("Failed to open file for hashing: {:?}", path))?;
91 Digest::reader_sync(reader)
92 }))
93 }
94
update(&mut self, bytes: &[u8])95 pub fn update(&mut self, bytes: &[u8]) {
96 self.inner.update(bytes);
97 }
98
finish(self) -> String99 pub fn finish(self) -> String {
100 hex(self.inner.finalize().as_bytes())
101 }
102 }
103
104 impl Default for Digest {
default() -> Self105 fn default() -> Self {
106 Self::new()
107 }
108 }
109
hex(bytes: &[u8]) -> String110 pub fn hex(bytes: &[u8]) -> String {
111 let mut s = String::with_capacity(bytes.len() * 2);
112 for &byte in bytes {
113 s.push(hex(byte & 0xf));
114 s.push(hex((byte >> 4) & 0xf));
115 }
116 return s;
117
118 fn hex(byte: u8) -> char {
119 match byte {
120 0..=9 => (b'0' + byte) as char,
121 _ => (b'a' + byte - 10) as char,
122 }
123 }
124 }
125
126 /// Calculate the digest of each file in `files` on background threads in
127 /// `pool`.
hash_all(files: &[PathBuf], pool: &ThreadPool) -> SFuture<Vec<String>>128 pub fn hash_all(files: &[PathBuf], pool: &ThreadPool) -> SFuture<Vec<String>> {
129 let start = time::Instant::now();
130 let count = files.len();
131 let pool = pool.clone();
132 Box::new(
133 future::join_all(
134 files
135 .iter()
136 .map(move |f| Digest::file(f, &pool))
137 .collect::<Vec<_>>(),
138 )
139 .map(move |hashes| {
140 trace!(
141 "Hashed {} files in {}",
142 count,
143 fmt_duration_as_secs(&start.elapsed())
144 );
145 hashes
146 }),
147 )
148 }
149
150 /// Format `duration` as seconds with a fractional component.
fmt_duration_as_secs(duration: &Duration) -> String151 pub fn fmt_duration_as_secs(duration: &Duration) -> String {
152 format!("{}.{:03} s", duration.as_secs(), duration.subsec_millis())
153 }
154
155 /// If `input`, write it to `child`'s stdin while also reading `child`'s stdout and stderr, then wait on `child` and return its status and output.
156 ///
157 /// This was lifted from `std::process::Child::wait_with_output` and modified
158 /// to also write to stdin.
wait_with_input_output<T>(mut child: T, input: Option<Vec<u8>>) -> SFuture<process::Output> where T: CommandChild + 'static,159 fn wait_with_input_output<T>(mut child: T, input: Option<Vec<u8>>) -> SFuture<process::Output>
160 where
161 T: CommandChild + 'static,
162 {
163 use tokio_io::io::{read_to_end, write_all};
164 let stdin = input.and_then(|i| {
165 child
166 .take_stdin()
167 .map(|stdin| write_all(stdin, i).fcontext("failed to write stdin"))
168 });
169 let stdout = child
170 .take_stdout()
171 .map(|io| read_to_end(io, Vec::new()).fcontext("failed to read stdout"));
172 let stderr = child
173 .take_stderr()
174 .map(|io| read_to_end(io, Vec::new()).fcontext("failed to read stderr"));
175
176 // Finish writing stdin before waiting, because waiting drops stdin.
177 let status = Future::and_then(stdin, |io| {
178 drop(io);
179 child.wait().fcontext("failed to wait for child")
180 });
181
182 Box::new(status.join3(stdout, stderr).map(|(status, out, err)| {
183 let stdout = out.map(|p| p.1);
184 let stderr = err.map(|p| p.1);
185 process::Output {
186 status,
187 stdout: stdout.unwrap_or_default(),
188 stderr: stderr.unwrap_or_default(),
189 }
190 }))
191 }
192
193 /// Run `command`, writing `input` to its stdin if it is `Some` and return the exit status and output.
194 ///
195 /// If the command returns a non-successful exit status, an error of `SccacheError::ProcessError`
196 /// will be returned containing the process output.
run_input_output<C>( mut command: C, input: Option<Vec<u8>>, ) -> impl Future<Item = process::Output, Error = Error> where C: RunCommand,197 pub fn run_input_output<C>(
198 mut command: C,
199 input: Option<Vec<u8>>,
200 ) -> impl Future<Item = process::Output, Error = Error>
201 where
202 C: RunCommand,
203 {
204 let child = command
205 .no_console()
206 .stdin(if input.is_some() {
207 Stdio::piped()
208 } else {
209 Stdio::inherit()
210 })
211 .stdout(Stdio::piped())
212 .stderr(Stdio::piped())
213 .spawn();
214
215 child.and_then(|child| {
216 wait_with_input_output(child, input).and_then(|output| {
217 if output.status.success() {
218 f_ok(output)
219 } else {
220 f_err(ProcessError(output))
221 }
222 })
223 })
224 }
225
226 /// Write `data` to `writer` with bincode serialization, prefixed by a `u32` length.
write_length_prefixed_bincode<W, S>(mut writer: W, data: S) -> Result<()> where W: Write, S: Serialize,227 pub fn write_length_prefixed_bincode<W, S>(mut writer: W, data: S) -> Result<()>
228 where
229 W: Write,
230 S: Serialize,
231 {
232 let bytes = bincode::serialize(&data)?;
233 let mut len = [0; 4];
234 BigEndian::write_u32(&mut len, bytes.len() as u32);
235 writer.write_all(&len)?;
236 writer.write_all(&bytes)?;
237 writer.flush()?;
238 Ok(())
239 }
240
241 pub trait OsStrExt {
starts_with(&self, s: &str) -> bool242 fn starts_with(&self, s: &str) -> bool;
split_prefix(&self, s: &str) -> Option<OsString>243 fn split_prefix(&self, s: &str) -> Option<OsString>;
244 }
245
246 #[cfg(unix)]
247 use std::os::unix::ffi::OsStrExt as _OsStrExt;
248
249 #[cfg(unix)]
250 impl OsStrExt for OsStr {
starts_with(&self, s: &str) -> bool251 fn starts_with(&self, s: &str) -> bool {
252 self.as_bytes().starts_with(s.as_bytes())
253 }
254
split_prefix(&self, s: &str) -> Option<OsString>255 fn split_prefix(&self, s: &str) -> Option<OsString> {
256 let bytes = self.as_bytes();
257 if bytes.starts_with(s.as_bytes()) {
258 Some(OsStr::from_bytes(&bytes[s.len()..]).to_owned())
259 } else {
260 None
261 }
262 }
263 }
264
265 #[cfg(windows)]
266 use std::os::windows::ffi::{OsStrExt as _OsStrExt, OsStringExt};
267
268 #[cfg(windows)]
269 impl OsStrExt for OsStr {
starts_with(&self, s: &str) -> bool270 fn starts_with(&self, s: &str) -> bool {
271 // Attempt to interpret this OsStr as utf-16. This is a pretty "poor
272 // man's" implementation, however, as it only handles a subset of
273 // unicode characters in `s`. Currently that's sufficient, though, as
274 // we're only calling `starts_with` with ascii string literals.
275 let u16s = self.encode_wide();
276 let mut utf8 = s.chars();
277
278 for codepoint in u16s {
279 let to_match = match utf8.next() {
280 Some(ch) => ch,
281 None => return true,
282 };
283
284 let to_match = to_match as u32;
285 let codepoint = codepoint as u32;
286
287 // UTF-16 encodes codepoints < 0xd7ff as just the raw value as a
288 // u16, and that's all we're matching against. If the codepoint in
289 // `s` is *over* this value then just assume it's not in `self`.
290 //
291 // If `to_match` is the same as the `codepoint` coming out of our
292 // u16 iterator we keep going, otherwise we've found a mismatch.
293 if to_match < 0xd7ff {
294 if to_match != codepoint {
295 return false;
296 }
297 } else {
298 return false;
299 }
300 }
301
302 // If we ran out of characters to match, then the strings should be
303 // equal, otherwise we've got more data to match in `s` so we didn't
304 // start with `s`
305 utf8.next().is_none()
306 }
307
split_prefix(&self, s: &str) -> Option<OsString>308 fn split_prefix(&self, s: &str) -> Option<OsString> {
309 // See comments in the above implementation for what's going on here
310 let mut u16s = self.encode_wide().peekable();
311 let mut utf8 = s.chars();
312
313 while let Some(&codepoint) = u16s.peek() {
314 let to_match = match utf8.next() {
315 Some(ch) => ch,
316 None => {
317 let codepoints = u16s.collect::<Vec<_>>();
318 return Some(OsString::from_wide(&codepoints));
319 }
320 };
321
322 let to_match = to_match as u32;
323 let codepoint = codepoint as u32;
324
325 if to_match < 0xd7ff {
326 if to_match != codepoint {
327 return None;
328 }
329 } else {
330 return None;
331 }
332 u16s.next();
333 }
334
335 if utf8.next().is_none() {
336 Some(OsString::new())
337 } else {
338 None
339 }
340 }
341 }
342
343 pub struct HashToDigest<'a> {
344 pub digest: &'a mut Digest,
345 }
346
347 impl<'a> Hasher for HashToDigest<'a> {
write(&mut self, bytes: &[u8])348 fn write(&mut self, bytes: &[u8]) {
349 self.digest.update(bytes)
350 }
351
finish(&self) -> u64352 fn finish(&self) -> u64 {
353 panic!("not supposed to be called");
354 }
355 }
356
357 /// Turns a slice of environment var tuples into the type expected by Command::envs.
ref_env(env: &[(OsString, OsString)]) -> impl Iterator<Item = (&OsString, &OsString)>358 pub fn ref_env(env: &[(OsString, OsString)]) -> impl Iterator<Item = (&OsString, &OsString)> {
359 env.iter().map(|&(ref k, ref v)| (k, v))
360 }
361
362 #[cfg(feature = "hyperx")]
363 pub use self::http_extension::{HeadersExt, RequestExt};
364
365 #[cfg(feature = "hyperx")]
366 mod http_extension {
367 use http::header::HeaderValue;
368 use std::fmt;
369
370 pub trait HeadersExt {
set<H>(&mut self, header: H) where H: hyperx::header::Header + fmt::Display371 fn set<H>(&mut self, header: H)
372 where
373 H: hyperx::header::Header + fmt::Display;
374
get_hyperx<H>(&self) -> Option<H> where H: hyperx::header::Header375 fn get_hyperx<H>(&self) -> Option<H>
376 where
377 H: hyperx::header::Header;
378 }
379
380 impl HeadersExt for http::HeaderMap {
set<H>(&mut self, header: H) where H: hyperx::header::Header + fmt::Display,381 fn set<H>(&mut self, header: H)
382 where
383 H: hyperx::header::Header + fmt::Display,
384 {
385 self.insert(
386 H::header_name(),
387 HeaderValue::from_shared(header.to_string().into()).unwrap(),
388 );
389 }
390
get_hyperx<H>(&self) -> Option<H> where H: hyperx::header::Header,391 fn get_hyperx<H>(&self) -> Option<H>
392 where
393 H: hyperx::header::Header,
394 {
395 http::HeaderMap::get(self, H::header_name())
396 .and_then(|header| H::parse_header(&header.as_bytes().into()).ok())
397 }
398 }
399
400 pub trait RequestExt {
set_header<H>(self, header: H) -> Self where H: hyperx::header::Header + fmt::Display401 fn set_header<H>(self, header: H) -> Self
402 where
403 H: hyperx::header::Header + fmt::Display;
404 }
405
406 impl RequestExt for http::request::Builder {
set_header<H>(mut self, header: H) -> Self where H: hyperx::header::Header + fmt::Display,407 fn set_header<H>(mut self, header: H) -> Self
408 where
409 H: hyperx::header::Header + fmt::Display,
410 {
411 self.header(
412 H::header_name(),
413 HeaderValue::from_shared(header.to_string().into()).unwrap(),
414 );
415 self
416 }
417 }
418
419 impl RequestExt for http::response::Builder {
set_header<H>(mut self, header: H) -> Self where H: hyperx::header::Header + fmt::Display,420 fn set_header<H>(mut self, header: H) -> Self
421 where
422 H: hyperx::header::Header + fmt::Display,
423 {
424 self.header(
425 H::header_name(),
426 HeaderValue::from_shared(header.to_string().into()).unwrap(),
427 );
428 self
429 }
430 }
431
432 #[cfg(feature = "reqwest")]
433 impl RequestExt for ::reqwest::r#async::RequestBuilder {
set_header<H>(self, header: H) -> Self where H: hyperx::header::Header + fmt::Display,434 fn set_header<H>(self, header: H) -> Self
435 where
436 H: hyperx::header::Header + fmt::Display,
437 {
438 self.header(
439 H::header_name(),
440 HeaderValue::from_shared(header.to_string().into()).unwrap(),
441 )
442 }
443 }
444
445 #[cfg(feature = "reqwest")]
446 impl RequestExt for ::reqwest::RequestBuilder {
set_header<H>(self, header: H) -> Self where H: hyperx::header::Header + fmt::Display,447 fn set_header<H>(self, header: H) -> Self
448 where
449 H: hyperx::header::Header + fmt::Display,
450 {
451 self.header(
452 H::header_name(),
453 HeaderValue::from_shared(header.to_string().into()).unwrap(),
454 )
455 }
456 }
457 }
458
459 /// Pipe `cmd`'s stdio to `/dev/null`, unless a specific env var is set.
460 #[cfg(not(windows))]
daemonize() -> Result<()>461 pub fn daemonize() -> Result<()> {
462 use daemonize::Daemonize;
463 use std::env;
464 use std::mem;
465
466 match env::var("SCCACHE_NO_DAEMON") {
467 Ok(ref val) if val == "1" => {}
468 _ => {
469 Daemonize::new().start().context("failed to daemonize")?;
470 }
471 }
472
473 static mut PREV_SIGSEGV: *mut libc::sigaction = 0 as *mut _;
474 static mut PREV_SIGBUS: *mut libc::sigaction = 0 as *mut _;
475 static mut PREV_SIGILL: *mut libc::sigaction = 0 as *mut _;
476
477 // We don't have a parent process any more once we've reached this point,
478 // which means that no one's probably listening for our exit status.
479 // In order to assist with debugging crashes of the server we configure our
480 // rlimit to allow runtime dumps and we also install a signal handler for
481 // segfaults which at least prints out what just happened.
482 unsafe {
483 match env::var("SCCACHE_ALLOW_CORE_DUMPS") {
484 Ok(ref val) if val == "1" => {
485 let rlim = libc::rlimit {
486 rlim_cur: libc::RLIM_INFINITY,
487 rlim_max: libc::RLIM_INFINITY,
488 };
489 libc::setrlimit(libc::RLIMIT_CORE, &rlim);
490 }
491 _ => {}
492 }
493
494 PREV_SIGSEGV = Box::into_raw(Box::new(mem::zeroed::<libc::sigaction>()));
495 PREV_SIGBUS = Box::into_raw(Box::new(mem::zeroed::<libc::sigaction>()));
496 PREV_SIGILL = Box::into_raw(Box::new(mem::zeroed::<libc::sigaction>()));
497 let mut new: libc::sigaction = mem::zeroed();
498 new.sa_sigaction = handler as usize;
499 new.sa_flags = libc::SA_SIGINFO | libc::SA_RESTART;
500 libc::sigaction(libc::SIGSEGV, &new, &mut *PREV_SIGSEGV);
501 libc::sigaction(libc::SIGBUS, &new, &mut *PREV_SIGBUS);
502 libc::sigaction(libc::SIGILL, &new, &mut *PREV_SIGILL);
503 }
504
505 return Ok(());
506
507 extern "C" fn handler(
508 signum: libc::c_int,
509 _info: *mut libc::siginfo_t,
510 _ptr: *mut libc::c_void,
511 ) {
512 use std::fmt::{Result, Write};
513
514 struct Stderr;
515
516 impl Write for Stderr {
517 fn write_str(&mut self, s: &str) -> Result {
518 unsafe {
519 let bytes = s.as_bytes();
520 libc::write(libc::STDERR_FILENO, bytes.as_ptr() as *const _, bytes.len());
521 Ok(())
522 }
523 }
524 }
525
526 unsafe {
527 let _ = writeln!(Stderr, "signal {} received", signum);
528
529 // Configure the old handler and then resume the program. This'll
530 // likely go on to create a runtime dump if one's configured to be
531 // created.
532 match signum {
533 libc::SIGBUS => libc::sigaction(signum, &*PREV_SIGBUS, std::ptr::null_mut()),
534 libc::SIGILL => libc::sigaction(signum, &*PREV_SIGILL, std::ptr::null_mut()),
535 _ => libc::sigaction(signum, &*PREV_SIGSEGV, std::ptr::null_mut()),
536 };
537 }
538 }
539 }
540
541 /// This is a no-op on Windows.
542 #[cfg(windows)]
daemonize() -> Result<()>543 pub fn daemonize() -> Result<()> {
544 Ok(())
545 }
546
547 #[cfg(test)]
548 mod tests {
549 use super::OsStrExt;
550 use std::ffi::{OsStr, OsString};
551
552 #[test]
simple_starts_with()553 fn simple_starts_with() {
554 let a: &OsStr = "foo".as_ref();
555 assert!(a.starts_with(""));
556 assert!(a.starts_with("f"));
557 assert!(a.starts_with("fo"));
558 assert!(a.starts_with("foo"));
559 assert!(!a.starts_with("foo2"));
560 assert!(!a.starts_with("b"));
561 assert!(!a.starts_with("b"));
562
563 let a: &OsStr = "".as_ref();
564 assert!(!a.starts_with("a"))
565 }
566
567 #[test]
simple_strip_prefix()568 fn simple_strip_prefix() {
569 let a: &OsStr = "foo".as_ref();
570
571 assert_eq!(a.split_prefix(""), Some(OsString::from("foo")));
572 assert_eq!(a.split_prefix("f"), Some(OsString::from("oo")));
573 assert_eq!(a.split_prefix("fo"), Some(OsString::from("o")));
574 assert_eq!(a.split_prefix("foo"), Some(OsString::from("")));
575 assert_eq!(a.split_prefix("foo2"), None);
576 assert_eq!(a.split_prefix("b"), None);
577 }
578 }
579