diff --git a/doc/api/os.md b/doc/api/os.md index 9f2aa0390cc56a..cdac68de0f775f 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 28bb83e558c426..2bada838184488 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -51,6 +51,7 @@ const { const { codes: { + ERR_INVALID_FD, ERR_NO_CRYPTO, ERR_NO_TYPESCRIPT, ERR_UNKNOWN_SIGNAL, @@ -898,9 +899,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 5e53879bd6d5aa..34b228c4edb12a 100644 --- a/lib/os.js +++ b/lib/os.js @@ -40,7 +40,7 @@ const { }, hideStackFrames, } = require('internal/errors'); -const { getCIDR } = require('internal/util'); +const { getCIDR, guessHandleType: guessFileDescriptorType } = require('internal/util'); const { validateInt32 } = require('internal/validators'); const { @@ -329,6 +329,7 @@ module.exports = { uptime: getUptime, version: getOSVersion, machine: getMachine, + guessFileDescriptorType, }; ObjectFreeze(constants.signals); diff --git a/src/node_util.cc b/src/node_util.cc index fbfda9c1551e07..acd57d96e9826c 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -215,7 +215,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) { Local context = isolate->GetCurrentContext(); int fd; if (!args[0]->Int32Value(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(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..0efb4780aa9934 --- /dev/null +++ b/test/pseudo-tty/test-os-guessFileDescriptorType.js @@ -0,0 +1,28 @@ +'use strict'; + +require('../common'); +const assert = require('node:assert'); +const { guessFileDescriptorType } = require('os'); + +assert.strictEqual(guessFileDescriptorType(0), 'TTY'); +assert.strictEqual(guessFileDescriptorType(1), 'TTY'); +assert.strictEqual(guessFileDescriptorType(2), 'TTY'); + +assert.strictEqual(guessFileDescriptorType(55555), 'UNKNOWN'); +assert.strictEqual(guessFileDescriptorType(2 ** 31 - 1), 'UNKNOWN'); + +[ + -1, + 1.1, + '1', + [], + {}, + () => {}, + 2 ** 31, + true, + false, + 1n, + Symbol(), + undefined, + null, +].forEach((val) => assert.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