From d6e56fd843895356769774aebe28b0206ea86d35 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Thu, 7 Apr 2016 13:45:40 -0400 Subject: [PATCH] os: add userInfo() method os.userInfo() calls libuv's uv_os_get_passwd() function. It returns an object containing the current effective user's username, uid, gid, shell, and home directory. On Windows, the uid and gid are -1, and the shell is null. Refs: https://github.com/nodejs/node/issues/5582 PR-URL: https://github.com/nodejs/node/pull/6104 Reviewed-By: James M Snell --- doc/api/os.markdown | 16 ++++++++++ lib/os.js | 1 + src/env.h | 4 +++ src/node_os.cc | 69 ++++++++++++++++++++++++++++++++++++++++ test/parallel/test-os.js | 24 ++++++++++++++ 5 files changed, 114 insertions(+) diff --git a/doc/api/os.markdown b/doc/api/os.markdown index ea4f40ba79..43e71460c2 100644 --- a/doc/api/os.markdown +++ b/doc/api/os.markdown @@ -182,5 +182,21 @@ on OS X and `'Windows_NT'` on Windows. Returns the system uptime in seconds. +## os.userInfo([options]) + +* `options` {Object} + * `encoding` {String} Character encoding used to interpret resulting strings. + If `encoding` is set to `'buffer'`, the `username`, `shell`, and `homedir` + values will be `Buffer` instances. (Default: 'utf8') + +Returns a subset of the password file entry for the current effective user. The +returned object includes the `username`, `uid`, `gid`, `shell`, and `homedir`. +On Windows, the `uid` and `gid` fields are `-1`, and `shell` is `null`. + +The value of `homedir` returned by `userInfo()` comes directly from the +operating system. This differs from the result of `os.homedir()`, which queries +several environment variables for the home directory before falling back to the +operating system response. + [`process.arch`]: process.html#process_process_arch [`process.platform`]: process.html#process_process_platform diff --git a/lib/os.js b/lib/os.js index 42ece99a7d..d065449da8 100644 --- a/lib/os.js +++ b/lib/os.js @@ -14,6 +14,7 @@ exports.type = binding.getOSType; exports.release = binding.getOSRelease; exports.networkInterfaces = binding.getInterfaceAddresses; exports.homedir = binding.getHomeDirectory; +exports.userInfo = binding.getUserInfo; exports.arch = function() { diff --git a/src/env.h b/src/env.h index 0590a8a434..9b117e1de0 100644 --- a/src/env.h +++ b/src/env.h @@ -94,6 +94,7 @@ namespace node { V(exchange_string, "exchange") \ V(idle_string, "idle") \ V(irq_string, "irq") \ + V(encoding_string, "encoding") \ V(enter_string, "enter") \ V(env_pairs_string, "envPairs") \ V(env_string, "env") \ @@ -121,6 +122,7 @@ namespace node { V(handle_string, "handle") \ V(heap_total_string, "heapTotal") \ V(heap_used_string, "heapUsed") \ + V(homedir_string, "homedir") \ V(hostmaster_string, "hostmaster") \ V(ignore_string, "ignore") \ V(immediate_callback_string, "_immediateCallback") \ @@ -206,6 +208,7 @@ namespace node { V(service_string, "service") \ V(servername_string, "servername") \ V(session_id_string, "sessionId") \ + V(shell_string, "shell") \ V(signal_string, "signal") \ V(size_string, "size") \ V(sni_context_err_string, "Invalid SNI context") \ @@ -235,6 +238,7 @@ namespace node { V(uid_string, "uid") \ V(unknown_string, "") \ V(user_string, "user") \ + V(username_string, "username") \ V(uv_string, "uv") \ V(valid_from_string, "valid_from") \ V(valid_to_string, "valid_to") \ diff --git a/src/node_os.cc b/src/node_os.cc index f432702792..9a995b3112 100644 --- a/src/node_os.cc +++ b/src/node_os.cc @@ -2,6 +2,7 @@ #include "v8.h" #include "env.h" #include "env-inl.h" +#include "string_bytes.h" #include #include @@ -32,6 +33,7 @@ using v8::Context; using v8::FunctionCallbackInfo; using v8::Integer; using v8::Local; +using v8::Null; using v8::Number; using v8::Object; using v8::String; @@ -290,6 +292,72 @@ static void GetHomeDirectory(const FunctionCallbackInfo& args) { } +static void GetUserInfo(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + uv_passwd_t pwd; + enum encoding encoding; + + if (args[0]->IsObject()) { + Local options = args[0].As(); + Local encoding_opt = options->Get(env->encoding_string()); + encoding = ParseEncoding(env->isolate(), encoding_opt, UTF8); + } else { + encoding = UTF8; + } + + const int err = uv_os_get_passwd(&pwd); + + if (err) { + return env->ThrowUVException(err, "uv_os_get_passwd"); + } + + Local uid = Number::New(env->isolate(), pwd.uid); + Local gid = Number::New(env->isolate(), pwd.gid); + Local username = StringBytes::Encode(env->isolate(), + pwd.username, + encoding); + Local homedir = StringBytes::Encode(env->isolate(), + pwd.homedir, + encoding); + Local shell; + + if (pwd.shell == NULL) + shell = Null(env->isolate()); + else + shell = StringBytes::Encode(env->isolate(), pwd.shell, encoding); + + uv_os_free_passwd(&pwd); + + if (username.IsEmpty()) { + return env->ThrowUVException(UV_EINVAL, + "uv_os_get_passwd", + "Invalid character encoding for username"); + } + + if (homedir.IsEmpty()) { + return env->ThrowUVException(UV_EINVAL, + "uv_os_get_passwd", + "Invalid character encoding for homedir"); + } + + if (shell.IsEmpty()) { + return env->ThrowUVException(UV_EINVAL, + "uv_os_get_passwd", + "Invalid character encoding for shell"); + } + + Local entry = Object::New(env->isolate()); + + entry->Set(env->uid_string(), uid); + entry->Set(env->gid_string(), gid); + entry->Set(env->username_string(), username); + entry->Set(env->homedir_string(), homedir); + entry->Set(env->shell_string(), shell); + + args.GetReturnValue().Set(entry); +} + + void Initialize(Local target, Local unused, Local context) { @@ -304,6 +372,7 @@ void Initialize(Local target, env->SetMethod(target, "getOSRelease", GetOSRelease); env->SetMethod(target, "getInterfaceAddresses", GetInterfaceAddresses); env->SetMethod(target, "getHomeDirectory", GetHomeDirectory); + env->SetMethod(target, "getUserInfo", GetUserInfo); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "isBigEndian"), Boolean::New(env->isolate(), IsBigEndian())); } diff --git a/test/parallel/test-os.js b/test/parallel/test-os.js index 15fd189f9b..dcf6bcfbe9 100644 --- a/test/parallel/test-os.js +++ b/test/parallel/test-os.js @@ -125,3 +125,27 @@ if (common.isWindows && process.env.USERPROFILE) { assert.ok(os.homedir().indexOf(path.sep) !== -1); process.env.HOME = home; } + +const pwd = os.userInfo(); +const pwdBuf = os.userInfo({ encoding: 'buffer' }); + +if (common.isWindows) { + assert.strictEqual(pwd.uid, -1); + assert.strictEqual(pwd.gid, -1); + assert.strictEqual(pwd.shell, null); + assert.strictEqual(pwdBuf.uid, -1); + assert.strictEqual(pwdBuf.gid, -1); + assert.strictEqual(pwdBuf.shell, null); +} else { + assert.strictEqual(typeof pwd.uid, 'number'); + assert.strictEqual(typeof pwd.gid, 'number'); + assert.notStrictEqual(pwd.shell.indexOf(path.sep), -1); + assert.strictEqual(pwd.uid, pwdBuf.uid); + assert.strictEqual(pwd.gid, pwdBuf.gid); + assert.strictEqual(pwd.shell, pwdBuf.shell.toString('utf8')); +} + +assert.strictEqual(typeof pwd.username, 'string'); +assert.notStrictEqual(pwd.homedir.indexOf(path.sep), -1); +assert.strictEqual(pwd.username, pwdBuf.username.toString('utf8')); +assert.strictEqual(pwd.homedir, pwdBuf.homedir.toString('utf8'));