Skip to content

Commit

Permalink
3.1.0 Add functions to get/set the close-on-exec flag
Browse files Browse the repository at this point in the history
pid2 needs to set the close-on-exec flag on a certain file descriptor,
but node doesn't have an API to achieve this. The only other node module
that provides the `fcntl(2)` binding doesn't play well with esbuild, so
we need to recruit the help of our friend here.

This change adds two functions to get/set the close-on-exec flag on a
file descriptor.
  • Loading branch information
lhchavez committed Jul 9, 2024
1 parent 3195048 commit 5893968
Show file tree
Hide file tree
Showing 7 changed files with 328 additions and 223 deletions.
4 changes: 3 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export interface Size {
cols: number
rows: number
}
export class Pty {
export function setCloseOnExec(fd: number, cloexec: boolean): void
export function getCloseOnExec(fd: number): boolean
export declare class Pty {
/** The pid of the forked process. */
pid: number
constructor(opts: PtyOptions)
Expand Down
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { Pty } = nativeBinding
const { Pty, setCloseOnExec, getCloseOnExec } = nativeBinding

module.exports.Pty = Pty
module.exports.setCloseOnExec = setCloseOnExec
module.exports.getCloseOnExec = getCloseOnExec
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@replit/ruspty",
"version": "3.0.5",
"version": "3.1.0",
"main": "dist/wrapper.js",
"types": "dist/wrapper.d.ts",
"author": "Szymon Kaliski <[email protected]>",
Expand Down
51 changes: 50 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFun
use napi::Status::GenericFailure;
use napi::{self, Env};
use nix::errno::Errno;
use nix::fcntl::{fcntl, FcntlArg, FdFlag};
use nix::poll::{poll, PollFd, PollFlags, PollTimeout};
use nix::pty::{openpty, Winsize};
use nix::sys::termios::{self, SetArg};
Expand Down Expand Up @@ -200,7 +201,7 @@ impl Pty {
thread::spawn(move || {
let wait_result = child.wait();

// try to wait for the controller fd to be fully read
// try to wait for the controller fd to be fully read
poll_controller_fd_until_read(raw_controller_fd);

// we don't drop the controller fd immediately
Expand Down Expand Up @@ -302,3 +303,51 @@ impl Pty {
Ok(())
}
}

#[napi]
#[allow(dead_code)]
fn set_close_on_exec(fd: i32, cloexec: bool) -> Result<(), napi::Error> {
let mut flags = match fcntl(fd as RawFd, FcntlArg::F_GETFD) {
Ok(flags) => FdFlag::from_bits_truncate(flags),
Err(err) => {
return Err(napi::Error::new(
GenericFailure,
format!("fcntl F_GETFD: {}", err,),
));
}
};
if cloexec {
if flags.contains(FdFlag::FD_CLOEXEC) {
// It's already there!
return Ok(());
}
flags.insert(FdFlag::FD_CLOEXEC);
} else {
if !flags.contains(FdFlag::FD_CLOEXEC) {
// It's already removed!
return Ok(());
}
flags.remove(FdFlag::FD_CLOEXEC);
}

if let Err(err) = fcntl(fd as RawFd, FcntlArg::F_SETFD(flags)) {
return Err(napi::Error::new(
GenericFailure,
format!("fcntl F_SETFD: {}", err,),
));
};

Ok(())
}

#[napi]
#[allow(dead_code)]
fn get_close_on_exec(fd: i32) -> Result<bool, napi::Error> {
match fcntl(fd as RawFd, FcntlArg::F_GETFD) {
Ok(flags) => Ok(FdFlag::from_bits_truncate(flags).contains(FdFlag::FD_CLOEXEC)),
Err(err) => Err(napi::Error::new(
GenericFailure,
format!("fcntl F_GETFD: {}", err,),
)),
}
}
Loading

0 comments on commit 5893968

Please sign in to comment.