pub use privsep::{process::Child, Config, Error};
use privsep_derive::Privsep;
#[derive(Debug, Privsep)]
#[username = "nobody"]
pub enum Privsep {
Parent,
Hello,
#[connect(Hello)]
Child,
}
mod parent {
use crate::{Error, Privsep};
use nix::sys::wait::{waitpid, WaitStatus};
use privsep::{
net::Fd,
process::{daemon, Parent},
};
use privsep_log::{info, warn};
use std::{net::TcpListener, os::unix::io::IntoRawFd, sync::Arc, time::Duration};
use tokio::{
signal::unix::{signal, SignalKind},
time::sleep,
};
pub async fn main<const N: usize>(
parent: Parent<N>,
config: privsep::Config,
) -> Result<(), Error> {
let _guard = privsep_log::async_logger(&parent.to_string(), config.foreground)
.await
.map_err(|err| Error::GeneralError(Box::new(err)))?;
let parent = Arc::new(parent);
info!("Hello, parent!");
let mut sigchld = signal(SignalKind::child())?;
let fd = TcpListener::bind("127.0.0.1:80")
.ok()
.map(|stream| stream.into_raw_fd())
.map(Fd::from);
if !config.foreground {
daemon(true, false)?;
}
for id in Privsep::PROCESS_IDS
.iter()
.filter(|id| **id != Privsep::PARENT_ID)
{
parent[*id]
.send_message(23u32.into(), fd.as_ref(), &())
.await?;
}
loop {
tokio::select! {
_ = sigchld.recv() => {
match waitpid(None, None) {
Ok(WaitStatus::Exited(pid, status)) => {
warn!("Child {} exited with status {}", pid, status);
break Ok(());
}
status => {
warn!("Child exited with error: {:?}", status);
break Err(Error::Terminated("child process"));
}
}
}
message = parent[Privsep::CHILD_ID].recv_message::<()>() => {
match message? {
None => break Err(Error::Terminated(Privsep::Child.as_static_str())),
Some((message, _, _)) => {
info!(
"received message {:?}", message;
"source" => Privsep::Child.as_ref(),
);
sleep(Duration::from_secs(1)).await;
parent[Privsep::CHILD_ID].send_message(message, fd.as_ref(), &()).await?;
}
}
}
message = parent[Privsep::HELLO_ID].recv_message::<()>() => {
match message? {
None => break Err(Error::Terminated(Privsep::Hello.as_static_str())),
Some((message, _, _)) => {
info!(
"received hello message {:?}", message;
"source" => Privsep::Hello.as_ref(),
);
sleep(Duration::from_secs(1)).await;
parent[Privsep::HELLO_ID].send_message(message, fd.as_ref(), &()).await?;
}
}
}
}
}
}
}
mod child {
use crate::{Error, Privsep};
use privsep::process::Child;
use privsep_log::{debug, info, warn};
use std::{sync::Arc, time::Duration};
use tokio::time::{interval, sleep};
pub async fn main<const N: usize>(
child: Child<N>,
config: privsep::Config,
) -> Result<(), Error> {
let _guard = privsep_log::async_logger(&child.to_string(), config.foreground)
.await
.map_err(|err| Error::GeneralError(Box::new(err)))?;
let child = Arc::new(child);
info!("Hello, child {}!", child);
tokio::spawn(async {
let mut interval = interval(Duration::from_secs(3));
loop {
interval.tick().await;
debug!("tick");
}
});
if let Err(err) = child[Privsep::HELLO_ID]
.send_message(42u32.into(), None, &())
.await
{
warn!("failed to send message to sibling: {}", err);
}
loop {
tokio::select! {
message = child[Privsep::PARENT_ID].recv_message::<()>() => {
match message? {
Some((message, _, _)) => {
info!(
"received message {:?}", message;
"source" => Privsep::Parent.as_ref(),
);
sleep(Duration::from_secs(1)).await;
if let Err(err) = child[Privsep::PARENT_ID]
.send_message(message, None, &())
.await
{
warn!("failed to send message: {}", err);
}
}
None => break Err(Error::Terminated(Privsep::Parent.as_static_str())),
}
}
message = child[Privsep::HELLO_ID].recv_message::<()>() => {
match message? {
Some((message, _, _)) => {
info!(
"received hello message {:?}", message;
"source" => Privsep::Hello.as_ref(),
);
sleep(Duration::from_secs(1)).await;
if let Err(err) = child[Privsep::HELLO_ID]
.send_message(message, None, &())
.await
{
warn!("failed to send message: {}", err);
}
}
None => break Err(Error::Terminated(Privsep::Hello.as_static_str())),
}
}
}
}
}
}
mod hello {
use crate::{Error, Privsep};
use privsep::process::Child;
use privsep_log::{debug, info, warn};
use std::{sync::Arc, time::Duration};
use tokio::time::{interval, sleep};
pub async fn main<const N: usize>(
child: Child<N>,
config: privsep::Config,
) -> Result<(), Error> {
let _guard = privsep_log::async_logger(&child.to_string(), config.foreground)
.await
.map_err(|err| Error::GeneralError(Box::new(err)))?;
let child = Arc::new(child);
info!("Hello, child {}!", child);
tokio::spawn(async {
let mut interval = interval(Duration::from_secs(3));
loop {
interval.tick().await;
debug!("tick");
}
});
loop {
tokio::select! {
message = child[Privsep::PARENT_ID].recv_message::<()>() => {
match message? {
Some((message, _, _)) => {
info!(
"received message {:?}", message;
"source" => Privsep::Parent.as_ref(),
);
sleep(Duration::from_secs(1)).await;
if let Err(err) = child[Privsep::PARENT_ID]
.send_message(message, None, &())
.await
{
warn!("failed to send message: {}", err);
}
}
None => break Err(Error::Terminated(Privsep::Parent.as_static_str())),
}
}
message = child[Privsep::CHILD_ID].recv_message::<()>() => {
match message? {
Some((message, _, _)) => {
info!(
"received message {:?}", message;
"source" => Privsep::Child.as_ref(),
);
sleep(Duration::from_secs(1)).await;
if let Err(err) = child[Privsep::CHILD_ID]
.send_message(message, None, &())
.await
{
warn!("failed to send message: {}", err);
}
}
None => break Err(Error::Terminated(Privsep::Child.as_static_str())),
}
}
}
}
}
}
#[tokio::main]
async fn main() {
let config = Config {
foreground: true,
log_level: Some("debug".to_string()),
..Default::default()
};
if let Err(err) = Privsep::main(config).await {
eprintln!("Error: {}", err);
}
}