artiq/artiq/firmware/runtime/mgmt.rs

171 lines
6.2 KiB
Rust

use board::boot;
use std::io::{self, Read, Write};
use log::{self, LevelFilter};
use logger_artiq::BufferLogger;
use sched::Io;
use sched::{TcpListener, TcpStream};
use proto::WriteExt;
use mgmt_proto::*;
use profiler;
fn check_magic(stream: &mut TcpStream) -> io::Result<()> {
const MAGIC: &'static [u8] = b"ARTIQ management\n";
let mut magic: [u8; 17] = [0; 17];
stream.read_exact(&mut magic)?;
if magic != MAGIC {
Err(io::Error::new(io::ErrorKind::InvalidData, "unrecognized magic"))
} else {
Ok(())
}
}
fn worker(io: &Io, stream: &mut TcpStream) -> io::Result<()> {
check_magic(stream)?;
info!("new connection from {}", stream.remote_endpoint());
loop {
match Request::read_from(stream)? {
Request::GetLog => {
BufferLogger::with(|logger| {
let mut buffer = io.until_ok(|| logger.buffer())?;
Reply::LogContent(buffer.extract()).write_to(stream)
})?;
}
Request::ClearLog => {
BufferLogger::with(|logger| -> io::Result<()> {
let mut buffer = io.until_ok(|| logger.buffer())?;
Ok(buffer.clear())
})?;
Reply::Success.write_to(stream)?;
}
Request::PullLog => {
BufferLogger::with(|logger| -> io::Result<()> {
loop {
// Do this *before* acquiring the buffer, since that sets the log level
// to OFF.
let log_level = log::max_level();
let mut buffer = io.until_ok(|| logger.buffer())?;
if buffer.is_empty() { continue }
stream.write_string(buffer.extract())?;
if log_level == LevelFilter::Trace {
// Hold exclusive access over the logger until we get positive
// acknowledgement; otherwise we get an infinite loop of network
// trace messages being transmitted and causing more network
// trace messages to be emitted.
//
// Any messages unrelated to this management socket that arrive
// while it is flushed are lost, but such is life.
stream.flush()?;
}
// Clear the log *after* flushing the network buffers, or we're just
// going to resend all the trace messages on the next iteration.
buffer.clear();
}
})?;
}
Request::SetLogFilter(level) => {
info!("changing log level to {}", level);
log::set_max_level(level);
Reply::Success.write_to(stream)?;
}
Request::SetUartLogFilter(level) => {
info!("changing UART log level to {}", level);
BufferLogger::with(|logger|
logger.set_uart_log_level(level));
Reply::Success.write_to(stream)?;
}
Request::StartProfiler { interval_us, hits_size, edges_size } => {
match profiler::start(interval_us as u64,
hits_size as usize, edges_size as usize) {
Ok(()) => Reply::Success.write_to(stream)?,
Err(()) => Reply::Unavailable.write_to(stream)?
}
}
Request::StopProfiler => {
profiler::stop();
Reply::Success.write_to(stream)?;
}
Request::GetProfile => {
profiler::pause(|profile| {
let profile = match profile {
None => return Reply::Unavailable.write_to(stream),
Some(profile) => profile
};
Reply::Profile.write_to(stream)?;
{
let hits = profile.hits();
stream.write_u32(hits.len() as u32)?;
for (&addr, &count) in hits.iter() {
stream.write_u32(addr.as_raw() as u32)?;
stream.write_u32(count)?;
}
}
{
let edges = profile.edges();
stream.write_u32(edges.len() as u32)?;
for (&(caller, callee), &count) in edges.iter() {
stream.write_u32(caller.as_raw() as u32)?;
stream.write_u32(callee.as_raw() as u32)?;
stream.write_u32(count)?;
}
}
Ok(())
})?;
}
Request::Hotswap(firmware) => {
profiler::stop();
warn!("hotswapping firmware");
Reply::RebootImminent.write_to(stream)?;
stream.close()?;
stream.flush()?;
unsafe { boot::hotswap(&firmware) }
}
Request::Reboot => {
Reply::RebootImminent.write_to(stream)?;
stream.close()?;
warn!("restarting");
unsafe { boot::reset() }
}
Request::DebugAllocator =>
unsafe { println!("{}", ::ALLOC) },
};
}
}
pub fn thread(io: Io) {
let listener = TcpListener::new(&io, 8192);
listener.listen(1380).expect("mgmt: cannot listen");
info!("management interface active");
loop {
let stream = listener.accept().expect("mgmt: cannot accept").into_handle();
io.spawn(4096, move |io| {
let mut stream = TcpStream::from_handle(&io, stream);
match worker(&io, &mut stream) {
Ok(()) => (),
Err(ref err) if err.kind() == io::ErrorKind::UnexpectedEof => (),
Err(ref err) if err.kind() == io::ErrorKind::WriteZero => (),
Err(err) => error!("aborted: {}", err)
}
});
}
}