]> git.lizzy.rs Git - rust.git/blob - src/tools/qemu-test-server/src/main.rs
Rollup merge of #39604 - est31:i128_tests, r=alexcrichton
[rust.git] / src / tools / qemu-test-server / src / main.rs
1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /// This is a small server which is intended to run inside of an emulator. This
12 /// server pairs with the `qemu-test-client` program in this repository. The
13 /// `qemu-test-client` connects to this server over a TCP socket and performs
14 /// work such as:
15 ///
16 /// 1. Pushing shared libraries to the server
17 /// 2. Running tests through the server
18 ///
19 /// The server supports running tests concurrently and also supports tests
20 /// themselves having support libraries. All data over the TCP sockets is in a
21 /// basically custom format suiting our needs.
22
23 use std::fs::{self, File, Permissions};
24 use std::io::prelude::*;
25 use std::io::{self, BufReader};
26 use std::net::{TcpListener, TcpStream};
27 use std::os::unix::prelude::*;
28 use std::sync::{Arc, Mutex};
29 use std::path::Path;
30 use std::str;
31 use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
32 use std::thread;
33 use std::process::{Command, Stdio};
34
35 macro_rules! t {
36     ($e:expr) => (match $e {
37         Ok(e) => e,
38         Err(e) => panic!("{} failed with {}", stringify!($e), e),
39     })
40 }
41
42 static TEST: AtomicUsize = ATOMIC_USIZE_INIT;
43
44 fn main() {
45     println!("starting test server");
46     let listener = t!(TcpListener::bind("10.0.2.15:12345"));
47     println!("listening!");
48
49     let work = Path::new("/tmp/work");
50     t!(fs::create_dir_all(work));
51
52     let lock = Arc::new(Mutex::new(()));
53
54     for socket in listener.incoming() {
55         let mut socket = t!(socket);
56         let mut buf = [0; 4];
57         t!(socket.read_exact(&mut buf));
58         if &buf[..] == b"ping" {
59             t!(socket.write_all(b"pong"));
60         } else if &buf[..] == b"push" {
61             handle_push(socket, work);
62         } else if &buf[..] == b"run " {
63             let lock = lock.clone();
64             thread::spawn(move || handle_run(socket, work, &lock));
65         } else {
66             panic!("unknown command {:?}", buf);
67         }
68     }
69 }
70
71 fn handle_push(socket: TcpStream, work: &Path) {
72     let mut reader = BufReader::new(socket);
73     let mut filename = Vec::new();
74     t!(reader.read_until(0, &mut filename));
75     filename.pop(); // chop off the 0
76     let filename = t!(str::from_utf8(&filename));
77
78     let path = work.join(filename);
79     t!(io::copy(&mut reader, &mut t!(File::create(&path))));
80     t!(fs::set_permissions(&path, Permissions::from_mode(0o755)));
81 }
82
83 struct RemoveOnDrop<'a> {
84     inner: &'a Path,
85 }
86
87 impl<'a> Drop for RemoveOnDrop<'a> {
88     fn drop(&mut self) {
89         t!(fs::remove_dir_all(self.inner));
90     }
91 }
92
93 fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) {
94     let mut arg = Vec::new();
95     let mut reader = BufReader::new(socket);
96
97     // Allocate ourselves a directory that we'll delete when we're done to save
98     // space.
99     let n = TEST.fetch_add(1, Ordering::SeqCst);
100     let path = work.join(format!("test{}", n));
101     let exe = path.join("exe");
102     t!(fs::create_dir(&path));
103     let _a = RemoveOnDrop { inner: &path };
104
105     // First up we'll get a list of arguments delimited with 0 bytes. An empty
106     // argument means that we're done.
107     let mut cmd = Command::new(&exe);
108     while t!(reader.read_until(0, &mut arg)) > 1 {
109         cmd.arg(t!(str::from_utf8(&arg[..arg.len() - 1])));
110         arg.truncate(0);
111     }
112
113     // Next we'll get a bunch of env vars in pairs delimited by 0s as well
114     arg.truncate(0);
115     while t!(reader.read_until(0, &mut arg)) > 1 {
116         let key_len = arg.len() - 1;
117         let val_len = t!(reader.read_until(0, &mut arg)) - 1;
118         {
119             let key = &arg[..key_len];
120             let val = &arg[key_len + 1..][..val_len];
121             let key = t!(str::from_utf8(key));
122             let val = t!(str::from_utf8(val));
123             cmd.env(key, val);
124         }
125         arg.truncate(0);
126     }
127
128     // The section of code from here down to where we drop the lock is going to
129     // be a critical section for us. On Linux you can't execute a file which is
130     // open somewhere for writing, as you'll receive the error "text file busy".
131     // Now here we never have the text file open for writing when we spawn it,
132     // so why do we still need a critical section?
133     //
134     // Process spawning first involves a `fork` on Unix, which clones all file
135     // descriptors into the child process. This means that it's possible for us
136     // to open the file for writing (as we're downloading it), then some other
137     // thread forks, then we close the file and try to exec. At that point the
138     // other thread created a child process with the file open for writing, and
139     // we attempt to execute it, so we get an error.
140     //
141     // This race is resolve by ensuring that only one thread can writ ethe file
142     // and spawn a child process at once. Kinda an unfortunate solution, but we
143     // don't have many other choices with this sort of setup!
144     //
145     // In any case the lock is acquired here, before we start writing any files.
146     // It's then dropped just after we spawn the child. That way we don't lock
147     // the execution of the child, just the creation of its files.
148     let lock = lock.lock();
149
150     // Next there's a list of dynamic libraries preceded by their filenames.
151     arg.truncate(0);
152     while t!(reader.read_until(0, &mut arg)) > 1 {
153         let dst = path.join(t!(str::from_utf8(&arg[..arg.len() - 1])));
154         let amt = read_u32(&mut reader) as u64;
155         t!(io::copy(&mut reader.by_ref().take(amt),
156                     &mut t!(File::create(&dst))));
157         t!(fs::set_permissions(&dst, Permissions::from_mode(0o755)));
158         arg.truncate(0);
159     }
160
161     // Finally we'll get the binary. The other end will tell us how big the
162     // binary is and then we'll download it all to the exe path we calculated
163     // earlier.
164     let amt = read_u32(&mut reader) as u64;
165     t!(io::copy(&mut reader.by_ref().take(amt),
166                 &mut t!(File::create(&exe))));
167     t!(fs::set_permissions(&exe, Permissions::from_mode(0o755)));
168
169     // Support libraries were uploaded to `work` earlier, so make sure that's
170     // in `LD_LIBRARY_PATH`. Also include our own current dir which may have
171     // had some libs uploaded.
172     cmd.env("LD_LIBRARY_PATH",
173             format!("{}:{}", work.display(), path.display()));
174
175     // Spawn the child and ferry over stdout/stderr to the socket in a framed
176     // fashion (poor man's style)
177     let mut child = t!(cmd.stdin(Stdio::null())
178                           .stdout(Stdio::piped())
179                           .stderr(Stdio::piped())
180                           .spawn());
181     drop(lock);
182     let mut stdout = child.stdout.take().unwrap();
183     let mut stderr = child.stderr.take().unwrap();
184     let socket = Arc::new(Mutex::new(reader.into_inner()));
185     let socket2 = socket.clone();
186     let thread = thread::spawn(move || my_copy(&mut stdout, 0, &*socket2));
187     my_copy(&mut stderr, 1, &*socket);
188     thread.join().unwrap();
189
190     // Finally send over the exit status.
191     let status = t!(child.wait());
192     let (which, code) = match status.code() {
193         Some(n) => (0, n),
194         None => (1, status.signal().unwrap()),
195     };
196     t!(socket.lock().unwrap().write_all(&[
197         which,
198         (code >> 24) as u8,
199         (code >> 16) as u8,
200         (code >>  8) as u8,
201         (code >>  0) as u8,
202     ]));
203 }
204
205 fn my_copy(src: &mut Read, which: u8, dst: &Mutex<Write>) {
206     let mut b = [0; 1024];
207     loop {
208         let n = t!(src.read(&mut b));
209         let mut dst = dst.lock().unwrap();
210         t!(dst.write_all(&[
211             which,
212             (n >> 24) as u8,
213             (n >> 16) as u8,
214             (n >>  8) as u8,
215             (n >>  0) as u8,
216         ]));
217         if n > 0 {
218             t!(dst.write_all(&b[..n]));
219         } else {
220             break
221         }
222     }
223 }
224
225 fn read_u32(r: &mut Read) -> u32 {
226     let mut len = [0; 4];
227     t!(r.read_exact(&mut len));
228     ((len[0] as u32) << 24) |
229     ((len[1] as u32) << 16) |
230     ((len[2] as u32) <<  8) |
231     ((len[3] as u32) <<  0)
232 }