use std::ffi::CString;
use std::io::{Read, Write};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::net::UnixStream;
use std::process::ExitCode;
#[cfg(target_os = "linux")]
const SOCKET_PREFIX: &str = "\0powerline-ipc-";
#[cfg(not(target_os = "linux"))]
const SOCKET_PREFIX: &str = "/tmp/powerline-ipc-";
fn default_socket_path() -> String {
let uid = unsafe { libc::getuid() };
format!("{}{}", SOCKET_PREFIX, uid)
}
fn main() -> ExitCode {
let mut argv: Vec<String> = std::env::args().collect();
let address = if argv.len() > 3 && argv[1] == "--socket" {
let s = argv[2].clone();
argv.drain(1..=2);
s
} else {
default_socket_path()
};
if argv.len() < 2 {
println!("Must provide at least one argument.");
return ExitCode::from(1);
}
let connect_addr = if let Some(rest) = address.strip_prefix('\0') {
#[cfg(target_os = "linux")]
{
use std::os::linux::net::SocketAddrExt;
let addr = std::os::unix::net::SocketAddr::from_abstract_name(rest.as_bytes())
.unwrap_or_else(|_| std::os::unix::net::SocketAddr::from_pathname("").unwrap());
UnixStream::connect_addr(&addr)
}
#[cfg(not(target_os = "linux"))]
{
let _ = rest;
UnixStream::connect(&address)
}
} else {
UnixStream::connect(&address)
};
let mut stream = match connect_addr {
Ok(s) => s,
Err(_) => {
return exec_render_fallback(&argv);
}
};
let num_args = format!("{:x}", argv.len() - 1);
if stream.write_all(num_args.as_bytes()).is_err() {
return write_failed();
}
if stream.write_all(b"\0").is_err() {
return write_failed();
}
for arg in &argv[1..] {
if stream.write_all(arg.as_bytes()).is_err() {
return write_failed();
}
if stream.write_all(b"\0").is_err() {
return write_failed();
}
}
if let Ok(cwd) = std::env::current_dir() {
if stream.write_all(cwd.as_os_str().as_bytes()).is_err() {
return write_failed();
}
}
if stream.write_all(b"\0").is_err() {
return write_failed();
}
for (k, v) in std::env::vars_os() {
let mut item: Vec<u8> = Vec::with_capacity(k.len() + 1 + v.len() + 1);
item.extend_from_slice(k.as_bytes());
item.push(b'=');
item.extend_from_slice(v.as_bytes());
item.push(0);
if stream.write_all(&item).is_err() {
return write_failed();
}
}
if stream.write_all(b"\0\0").is_err() {
return write_failed();
}
let mut stdout = std::io::stdout().lock();
let mut buf = [0u8; 4096];
loop {
match stream.read(&mut buf) {
Ok(0) => break,
Ok(n) => {
if stdout.write_all(&buf[..n]).is_err() {
return ExitCode::from(1);
}
}
Err(_) => return ExitCode::from(1),
}
}
ExitCode::SUCCESS
}
fn exec_render_fallback(argv: &[String]) -> ExitCode {
let prog = CString::new("powerline-render").expect("nul in program name");
let mut c_args: Vec<CString> = vec![prog.clone()];
for a in &argv[1..] {
c_args.push(match CString::new(a.as_str()) {
Ok(c) => c,
Err(_) => return ExitCode::from(1),
});
}
let mut ptrs: Vec<*const libc::c_char> = c_args.iter().map(|c| c.as_ptr()).collect();
ptrs.push(std::ptr::null());
unsafe {
libc::execvp(prog.as_ptr(), ptrs.as_ptr());
}
eprintln!("powerline: execvp(powerline-render) failed");
ExitCode::from(1)
}
fn write_failed() -> ExitCode {
eprintln!("powerline: write() to daemon failed");
ExitCode::from(1)
}