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