Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider replacing the implementation with just the ctermid syscall #1

Open
AlexTMjugador opened this issue Feb 2, 2022 · 0 comments

Comments

@AlexTMjugador
Copy link

The POSIX standard defines the ctermid C function to get the file path of the process controlling terminal. All Unix-like OSes are mostly POSIX compliant, so this function can be assumed to be available on every Unix-like target.

Therefore, this crate could be refactored to just be a nice, safe facade to that C function, to save quite a bit of complexity with build scripts and be even more cross-platform than it currently is.

For a project of mine, I've came up with the following function that you can use as you like:

/// Returns a file path to the controlling terminal of this process. If this
/// process has no controlling terminal or an error happens, `None` will
/// be returned.
fn ctermid() -> Option<String> {
	use std::ffi::CString;
	use std::os::raw::{c_char, c_uchar};

	extern "C" {
		/// `char* ctermid(char* s)`, from `#include <stdio.h>`.
		///
		/// Documentation: <https://pubs.opengroup.org/onlinepubs/9699919799/functions/ctermid.html>
		fn ctermid(s: *mut c_char) -> *mut c_char;
	}

	// SAFETY: system calls are unsafe. ctermid is required by the standard to populate the passed
	// pointer with a valid string, always. If some error happens, then the string is empty, but the
	// pointer is valid. Because we bring our own buffer, safe Rust is in full control of the data
	// lifetime. L_ctermid is 9 on Linux, but allocating space for 256 characters should guarantee
	// that the buffer size is always greater than L_ctermid. c_uchar has the same memory layout
	// than c_char and are interchangeable
	#[allow(unsafe_code)]
	let path = unsafe {
		let mut path_buf = vec![0 as c_uchar; 256];

		ctermid(path_buf.as_mut_ptr() as *mut c_char);

		// CString requires that the Vec exactly contains a C string, with no NUL bytes
		let mut found_nul = false;
		path_buf.retain(|byte| {
			let is_not_first_nul = *byte != 0 && !found_nul;
			found_nul = found_nul || *byte == 0;
			is_not_first_nul
		});

		CString::new(path_buf)
	};

	path.ok()
		.and_then(|path_cstring| path_cstring.into_string().ok())
		.filter(|path| !path.is_empty())
}

Thank you for making this useful library, by the way!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant