diff --git a/doc/api/os.md b/doc/api/os.md index 625d395e056786..572c3756c78acf 100644 --- a/doc/api/os.md +++ b/doc/api/os.md @@ -508,6 +508,32 @@ On POSIX systems, the operating system release is determined by calling available, `GetVersionExW()` will be used. See for more information. +## `os.guessFileDescriptorType(fd)` + + + +* `fd` {integer} The file descriptor number to try and guess the type of. + +* Returns: {string|null} + +Returns the type of the file descriptor passed in, or `null` if the provided file descriptor +is invalid. +A common use case for this function is checking whether standard input is passed into your process, +and if it is, if it can be consumed by the process. For example, on Unix systems, if the type is `TTY`, it means +you can prompt the user for new data while the process is running, and if it's `FILE` or `PIPE`, it means there is data +available, but you shouldn't try to prompt for more. + +Currently, the following types for a file descriptor can be returned: + +* `'TCP'` +* `'TTY'` +* `'UDP'` +* `'FILE'` +* `'PIPE'` +* `'UNKNOWN'` + ## OS constants The following constants are exported by `os.constants`. diff --git a/lib/internal/util.js b/lib/internal/util.js index 180ca49b3207eb..c1c374fbcb5ce0 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -48,6 +48,7 @@ const { const { codes: { + ERR_INVALID_FD, ERR_NO_CRYPTO, ERR_NO_TYPESCRIPT, ERR_UNKNOWN_SIGNAL, @@ -876,9 +877,14 @@ function getCIDR(address, netmask, family) { } const handleTypes = ['TCP', 'TTY', 'UDP', 'FILE', 'PIPE', 'UNKNOWN']; + function guessHandleType(fd) { + if (typeof fd !== 'number' || fd >> 0 !== fd || fd < 0) { + throw new ERR_INVALID_FD(fd); + } + const type = _guessHandleType(fd); - return handleTypes[type]; + return handleTypes[type] || type; } class WeakReference { diff --git a/lib/os.js b/lib/os.js index c44147f0e1170d..d63c1ee319c6e0 100644 --- a/lib/os.js +++ b/lib/os.js @@ -39,7 +39,7 @@ const { }, hideStackFrames, } = require('internal/errors'); -const { getCIDR } = require('internal/util'); +const { getCIDR, guessHandleType: guessFileDescriptorType } = require('internal/util'); const { validateInt32 } = require('internal/validators'); const { @@ -328,6 +328,7 @@ module.exports = { uptime: getUptime, version: getOSVersion, machine: getMachine, + guessFileDescriptorType, }; ObjectDefineProperties(module.exports, { diff --git a/src/node_util.cc b/src/node_util.cc index 85ef1c205e0d50..b5b88b87ac4993 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -216,7 +216,10 @@ static uint32_t GetUVHandleTypeCode(const uv_handle_type type) { case UV_UNKNOWN_HANDLE: return 5; default: - ABORT(); + // For an unhandled handle type, we want to return `UNKNOWN` instead of + // `null` since the type is "known" by UV, just not exposed further to + // JS land + return 5; } } @@ -224,7 +227,12 @@ static void GuessHandleType(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); int fd; if (!args[0]->Int32Value(env->context()).To(&fd)) return; - CHECK_GE(fd, 0); + + // If the provided file descriptor is not valid, we return null + if (fd < 0) [[unlikely]] { + args.GetReturnValue().Set(v8::Null(env->isolate())); + return; + } uv_handle_type t = uv_guess_handle(fd); args.GetReturnValue().Set(GetUVHandleTypeCode(t)); diff --git a/test/pseudo-tty/test-os-guessFileDescriptorType.js b/test/pseudo-tty/test-os-guessFileDescriptorType.js new file mode 100644 index 00000000000000..b927e7165f160a --- /dev/null +++ b/test/pseudo-tty/test-os-guessFileDescriptorType.js @@ -0,0 +1,28 @@ +'use strict'; + +require('../common'); +const { strictEqual, throws } = require('assert'); +const { guessFileDescriptorType } = require('os'); + +strictEqual(guessFileDescriptorType(0), 'TTY', 'stdin reported to not be a tty, but it is'); +strictEqual(guessFileDescriptorType(1), 'TTY', 'stdout reported to not be a tty, but it is'); +strictEqual(guessFileDescriptorType(2), 'TTY', 'stderr reported to not be a tty, but it is'); + +strictEqual(guessFileDescriptorType(55555), 'UNKNOWN', '55555 reported to be a handle, but it is not'); +strictEqual(guessFileDescriptorType(2 ** 31 - 1), 'UNKNOWN', '2^31-1 reported to be a handle, but it is not'); + +[ + -1, + 1.1, + '1', + [], + {}, + () => {}, + 2 ** 31, + true, + false, + 1n, + Symbol(), + undefined, + null, +].forEach((val) => throws(() => guessFileDescriptorType(val), { code: 'ERR_INVALID_FD' })); diff --git a/test/pseudo-tty/test-os-guessFileDescriptorType.out b/test/pseudo-tty/test-os-guessFileDescriptorType.out new file mode 100644 index 00000000000000..e69de29bb2d1d6