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.
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.
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
16 /// 1. Pushing shared libraries to the server
17 /// 2. Running tests through the server
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.
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};
31 use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
33 use std::process::{Command, Stdio};
36 ($e:expr) => (match $e {
38 Err(e) => panic!("{} failed with {}", stringify!($e), e),
42 static TEST: AtomicUsize = ATOMIC_USIZE_INIT;
45 println!("starting test server");
46 let listener = t!(TcpListener::bind("10.0.2.15:12345"));
47 println!("listening!");
49 let work = Path::new("/tmp/work");
50 t!(fs::create_dir_all(work));
52 let lock = Arc::new(Mutex::new(()));
54 for socket in listener.incoming() {
55 let mut socket = t!(socket);
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));
66 panic!("unknown command {:?}", buf);
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));
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)));
83 struct RemoveOnDrop<'a> {
87 impl<'a> Drop for RemoveOnDrop<'a> {
89 t!(fs::remove_dir_all(self.inner));
93 fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) {
94 let mut arg = Vec::new();
95 let mut reader = BufReader::new(socket);
97 // Allocate ourselves a directory that we'll delete when we're done to save
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 };
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])));
113 // Next we'll get a bunch of env vars in pairs delimited by 0s as well
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;
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));
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?
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.
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!
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();
150 // Next there's a list of dynamic libraries preceded by their filenames.
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)));
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
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)));
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()));
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())
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();
190 // Finally send over the exit status.
191 let status = t!(child.wait());
192 let (which, code) = match status.code() {
194 None => (1, status.signal().unwrap()),
196 t!(socket.lock().unwrap().write_all(&[
205 fn my_copy(src: &mut Read, which: u8, dst: &Mutex<Write>) {
206 let mut b = [0; 1024];
208 let n = t!(src.read(&mut b));
209 let mut dst = dst.lock().unwrap();
218 t!(dst.write_all(&b[..n]));
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)