janitor 0.1.0

perform cleanup after a program exits
//! perform cleanup after its parent process has exited
//!
//! existing in a seperate process allows it to always perform cleanup,
//! regardless of how the invoking process exited.
//! this includes things that are not usually handlable,
//! like SIGKILL.
//!
//! to know what cleanup to perform, janitor reads from standard input.
//! each line is a directive, with the first byte identifying its type:
//! - `d`: delete a directory (and its contents)
//! - `f`: delete a file (doesn't follow symlinks)
//! - `#`: ignored


use std::io;
use std::fs;
use std::path::PathBuf;

use libc::daemon;

fn is_alive(pid: i32) -> bool {
	// signal 0 means don't actually send a signal,
	// just make sure the process exists.
	unsafe { libc::kill(pid, 0) == 0 }
}

fn main() {
	let parent = std::os::unix::process::parent_id() as i32;
	// the rust standard library currently lacks any equivelent
	unsafe { daemon(1, 1); }
	let mut dirs_to_delete: Vec<PathBuf> = vec![];
	let mut files_to_delete: Vec<PathBuf> = vec![];
	while is_alive(parent) {
		let stdin = io::stdin();
		// this will read past end of file, which is required to allow a
		// named fifo to be written to multiple times by a shell script.
		for line in stdin.lines() {
			match line {
				Err(e) => if e.kind() == std::io::ErrorKind::BrokenPipe {
					// signals that the calling process has exited.
					break;
				} else {
					eprintln!("janitor: unexpected error reading from stdin: {}", e);
					break;
				}
				Ok(ln) => {
					if ln.is_empty() {
						continue;
					}
					let arg = ln[1..].to_string();
					match ln.bytes().nth(0).unwrap() {
						b'#' => {},
						b'f' => files_to_delete.push(arg.into()),
						b'd' => dirs_to_delete.push(arg.into()),
						_ => eprintln!("janitor: unsure how to process {:?}", &ln),
					}
				}
			}
		}
	}
	for dir in &dirs_to_delete {
		// ignore error, the process may have deleted it itself
		let _ = fs::remove_dir_all(dir);
	}
	for f in &files_to_delete {
		let _ = fs::remove_file(f);
	}
}