socket_config 0.1.1

Set up sockets according to command line option or configuration file
Documentation
use cfg_if::cfg_if;
use crate::convert::SocketState;
use once_cell::sync::Lazy;
use socket2::Socket;
use std::{
	convert::Infallible,
	env,
	fs,
	io,
	os::{
		fd::AsRawFd,
		unix::fs::FileTypeExt,
	},
	path::Path,
	process,
};

pub use std::os::fd::{
	BorrowedFd as BorrowedSocket,
	OwnedFd as OwnedSocket,
	RawFd as RawSocket,
};

type Pid = u32;

pub const SD_LISTEN_FDS_START: RawSocket = 3;

pub static SD_LISTEN_FDS_END: Lazy<Option<RawSocket>> = Lazy::new(|| {
	let expected_pid: Pid =
		env::var("LISTEN_PID")
		.ok()?
		.parse()
		.ok()?;

	let actual_pid: Pid = process::id();

	if actual_pid != expected_pid {
		return None;
	}

	let total_listen_fds =
		env::var("LISTEN_FDS")
		.ok()?
		.parse()
		.ok()
		.filter(|count| *count >= 1)?;

	let listen_fds_end = SD_LISTEN_FDS_START.saturating_add(total_listen_fds);

	Some(listen_fds_end)
});

pub fn make_socket_inheritable(
	socket: &Socket,
	inheritable: bool,
) -> io::Result<RawSocket> {
	socket.set_cloexec(!inheritable)?;
	Ok(socket.as_raw_fd())
}

pub fn is_unix_socket(path: &Path) -> io::Result<bool> {
	fs::symlink_metadata(path)
	.map(|metadata| metadata.file_type().is_socket())
}

pub fn startup_socket_api() {}

pub fn get_stdin_as_socket() -> Result<RawSocket, Infallible> {
	Ok(0)
}

pub(crate) fn get_socket_state(socket: &Socket) -> io::Result<SocketState> {
	let r#type = socket.r#type()?;

	cfg_if! {
		if #[cfg(any(
			target_os = "android",
			target_os = "freebsd",
			target_os = "fuchsia",
			target_os = "linux",
		))] {
			let protocol = socket.protocol()?;
		}
		else {
			let protocol = None;
		}
	}

	cfg_if! {
		if #[cfg(any(
			target_os = "aix",
			target_os = "android",
			target_os = "freebsd",
			target_os = "fuchsia",
			target_os = "linux",
		))] {
			let is_listening = Some(socket.is_listener()?);
		}
		else {
			let is_listening = None;
		}
	}

	Ok(SocketState { r#type, protocol, is_listening })
}