]> git.proxmox.com Git - rustc.git/blob - src/tools/qemu-test-client/src/main.rs
New upstream version 1.17.0+dfsg1
[rustc.git] / src / tools / qemu-test-client / 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 client program intended to pair with `qemu-test-server` in
12 /// this repository. This client connects to the server over TCP and is used to
13 /// push artifacts and run tests on the server instead of locally.
14 ///
15 /// Here is also where we bake in the support to spawn the QEMU emulator as
16 /// well.
17
18 use std::env;
19 use std::fs::File;
20 use std::io::prelude::*;
21 use std::io::{self, BufWriter};
22 use std::net::TcpStream;
23 use std::path::Path;
24 use std::process::{Command, Stdio};
25 use std::thread;
26 use std::time::Duration;
27
28 macro_rules! t {
29 ($e:expr) => (match $e {
30 Ok(e) => e,
31 Err(e) => panic!("{} failed with {}", stringify!($e), e),
32 })
33 }
34
35 fn main() {
36 let mut args = env::args().skip(1);
37
38 match &args.next().unwrap()[..] {
39 "spawn-emulator" => {
40 spawn_emulator(Path::new(&args.next().unwrap()),
41 Path::new(&args.next().unwrap()))
42 }
43 "push" => {
44 push(Path::new(&args.next().unwrap()))
45 }
46 "run" => {
47 run(args.next().unwrap(), args.collect())
48 }
49 cmd => panic!("unknown command: {}", cmd),
50 }
51 }
52
53 fn spawn_emulator(rootfs: &Path, tmpdir: &Path) {
54 // Generate a new rootfs image now that we've updated the test server
55 // executable. This is the equivalent of:
56 //
57 // find $rootfs -print 0 | cpio --null -o --format=newc > rootfs.img
58 let rootfs_img = tmpdir.join("rootfs.img");
59 let mut cmd = Command::new("cpio");
60 cmd.arg("--null")
61 .arg("-o")
62 .arg("--format=newc")
63 .stdin(Stdio::piped())
64 .stdout(Stdio::piped())
65 .current_dir(rootfs);
66 let mut child = t!(cmd.spawn());
67 let mut stdin = child.stdin.take().unwrap();
68 let rootfs = rootfs.to_path_buf();
69 thread::spawn(move || add_files(&mut stdin, &rootfs, &rootfs));
70 t!(io::copy(&mut child.stdout.take().unwrap(),
71 &mut t!(File::create(&rootfs_img))));
72 assert!(t!(child.wait()).success());
73
74 // Start up the emulator, in the background
75 let mut cmd = Command::new("qemu-system-arm");
76 cmd.arg("-M").arg("vexpress-a15")
77 .arg("-m").arg("1024")
78 .arg("-kernel").arg("/tmp/zImage")
79 .arg("-initrd").arg(&rootfs_img)
80 .arg("-dtb").arg("/tmp/vexpress-v2p-ca15-tc1.dtb")
81 .arg("-append").arg("console=ttyAMA0 root=/dev/ram rdinit=/sbin/init init=/sbin/init")
82 .arg("-nographic")
83 .arg("-redir").arg("tcp:12345::12345");
84 t!(cmd.spawn());
85
86 // Wait for the emulator to come online
87 loop {
88 let dur = Duration::from_millis(100);
89 if let Ok(mut client) = TcpStream::connect("127.0.0.1:12345") {
90 t!(client.set_read_timeout(Some(dur)));
91 t!(client.set_write_timeout(Some(dur)));
92 if client.write_all(b"ping").is_ok() {
93 let mut b = [0; 4];
94 if client.read_exact(&mut b).is_ok() {
95 break
96 }
97 }
98 }
99 thread::sleep(dur);
100 }
101
102 fn add_files(w: &mut Write, root: &Path, cur: &Path) {
103 for entry in t!(cur.read_dir()) {
104 let entry = t!(entry);
105 let path = entry.path();
106 let to_print = path.strip_prefix(root).unwrap();
107 t!(write!(w, "{}\u{0}", to_print.to_str().unwrap()));
108 if t!(entry.file_type()).is_dir() {
109 add_files(w, root, &path);
110 }
111 }
112 }
113 }
114
115 fn push(path: &Path) {
116 let client = t!(TcpStream::connect("127.0.0.1:12345"));
117 let mut client = BufWriter::new(client);
118 t!(client.write_all(b"push"));
119 t!(client.write_all(path.file_name().unwrap().to_str().unwrap().as_bytes()));
120 t!(client.write_all(&[0]));
121 let mut file = t!(File::open(path));
122 t!(io::copy(&mut file, &mut client));
123 t!(client.flush());
124 println!("done pushing {:?}", path);
125 }
126
127 fn run(files: String, args: Vec<String>) {
128 let client = t!(TcpStream::connect("127.0.0.1:12345"));
129 let mut client = BufWriter::new(client);
130 t!(client.write_all(b"run "));
131
132 // Send over the args
133 for arg in args {
134 t!(client.write_all(arg.as_bytes()));
135 t!(client.write_all(&[0]));
136 }
137 t!(client.write_all(&[0]));
138
139 // Send over env vars
140 for (k, v) in env::vars() {
141 if k != "PATH" && k != "LD_LIBRARY_PATH" {
142 t!(client.write_all(k.as_bytes()));
143 t!(client.write_all(&[0]));
144 t!(client.write_all(v.as_bytes()));
145 t!(client.write_all(&[0]));
146 }
147 }
148 t!(client.write_all(&[0]));
149
150 // Send over support libraries
151 let mut files = files.split(':');
152 let exe = files.next().unwrap();
153 for file in files.map(Path::new) {
154 t!(client.write_all(file.file_name().unwrap().to_str().unwrap().as_bytes()));
155 t!(client.write_all(&[0]));
156 send(&file, &mut client);
157 }
158 t!(client.write_all(&[0]));
159
160 // Send over the client executable as the last piece
161 send(exe.as_ref(), &mut client);
162
163 println!("uploaded {:?}, waiting for result", exe);
164
165 // Ok now it's time to read all the output. We're receiving "frames"
166 // representing stdout/stderr, so we decode all that here.
167 let mut header = [0; 5];
168 let mut stderr_done = false;
169 let mut stdout_done = false;
170 let mut client = t!(client.into_inner());
171 let mut stdout = io::stdout();
172 let mut stderr = io::stderr();
173 while !stdout_done || !stderr_done {
174 t!(client.read_exact(&mut header));
175 let amt = ((header[1] as u64) << 24) |
176 ((header[2] as u64) << 16) |
177 ((header[3] as u64) << 8) |
178 ((header[4] as u64) << 0);
179 if header[0] == 0 {
180 if amt == 0 {
181 stdout_done = true;
182 } else {
183 t!(io::copy(&mut (&mut client).take(amt), &mut stdout));
184 t!(stdout.flush());
185 }
186 } else {
187 if amt == 0 {
188 stderr_done = true;
189 } else {
190 t!(io::copy(&mut (&mut client).take(amt), &mut stderr));
191 t!(stderr.flush());
192 }
193 }
194 }
195
196 // Finally, read out the exit status
197 let mut status = [0; 5];
198 t!(client.read_exact(&mut status));
199 let code = ((status[1] as i32) << 24) |
200 ((status[2] as i32) << 16) |
201 ((status[3] as i32) << 8) |
202 ((status[4] as i32) << 0);
203 if status[0] == 0 {
204 std::process::exit(code);
205 } else {
206 println!("died due to signal {}", code);
207 std::process::exit(3);
208 }
209 }
210
211 fn send(path: &Path, dst: &mut Write) {
212 let mut file = t!(File::open(&path));
213 let amt = t!(file.metadata()).len();
214 t!(dst.write_all(&[
215 (amt >> 24) as u8,
216 (amt >> 16) as u8,
217 (amt >> 8) as u8,
218 (amt >> 0) as u8,
219 ]));
220 t!(io::copy(&mut file, dst));
221 }