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

[node源码解析] os.tmpdir() 与 os.homedir() #7

Open
billfeller opened this issue Nov 2, 2019 · 0 comments
Open

[node源码解析] os.tmpdir() 与 os.homedir() #7

billfeller opened this issue Nov 2, 2019 · 0 comments

Comments

@billfeller
Copy link
Owner

billfeller commented Nov 2, 2019

问题

在 lerna bootstrap 运行 postintall 任务脚本中,调用 os.tmpdir() 函数输出结果值会默认指向当前npm包项目目录,和通过 npm run postinstall 运行脚本任务返回正确的系统临时目录 /tmp 存在差异。

解决方法

通过使用 os.homedir() 替代 os.tmpdir() ,在二者运行时环境中返回值相同,来保证一致性。

根本原因

通过阅读源码,可以看到,以 Linux 环境为例, os.tmpdir() 会按顺序读取环境变量 TMPDIR | TMP | TEMP,否则默认返回为 /tmplerna bootstrap 会初始化环境变量 TMPDIR 为当前安装包项目根目录,导致 `os.tmpdir() 与 默认的 npm install postinstall 运行时不一致。

// lib/os.js
function tmpdir() {
  var path;
  if (isWindows) {
    path = process.env.TEMP ||
           process.env.TMP ||
           (process.env.SystemRoot || process.env.windir) + '\\temp';
    if (path.length > 1 && path.endsWith('\\') && !path.endsWith(':\\'))
      path = path.slice(0, -1);
  } else {
    path = safeGetenv('TMPDIR') ||
           safeGetenv('TMP') ||
           safeGetenv('TEMP') ||
           '/tmp';
    if (path.length > 1 && path.endsWith('/'))
      path = path.slice(0, -1);
  }

  return path;
}
tmpdir[Symbol.toPrimitive] = () => tmpdir();

同理,通过阅读 os.homdir() 统一读取 HOME 环境变量,只需要保证 lerna bootstrap 与 npm run postinstall 一致即可。

// lib/os.js
module.exports = {
  homedir: getHomeDirectory,
}

// src/node_os.cc
static void GetHomeDirectory(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);
  char buf[PATH_MAX];

  size_t len = sizeof(buf);
  const int err = uv_os_homedir(buf, &len);

  if (err) {
    CHECK_GE(args.Length(), 1);
    env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_homedir");
    return args.GetReturnValue().SetUndefined();
  }

  Local<String> home = String::NewFromUtf8(env->isolate(),
                                           buf,
                                           v8::NewStringType::kNormal,
                                           len).ToLocalChecked();
  args.GetReturnValue().Set(home);
}

// deps/uv/src/unix/core.c
int uv_os_homedir(char* buffer, size_t* size) {
  uv_passwd_t pwd;
  size_t len;
  int r;

  /* Check if the HOME environment variable is set first. The task of
     performing input validation on buffer and size is taken care of by
     uv_os_getenv(). */
  r = uv_os_getenv("HOME", buffer, size);

  if (r != UV_ENOENT)
    return r;

  /* HOME is not set, so call uv__getpwuid_r() */
  r = uv__getpwuid_r(&pwd);

  if (r != 0) {
    return r;
  }

  len = strlen(pwd.homedir);

  if (len >= *size) {
    *size = len + 1;
    uv_os_free_passwd(&pwd);
    return UV_ENOBUFS;
  }

  memcpy(buffer, pwd.homedir, len + 1);
  *size = len;
  uv_os_free_passwd(&pwd);

  return 0;
}


int uv_os_getenv(const char* name, char* buffer, size_t* size) {
  char* var;
  size_t len;

  if (name == NULL || buffer == NULL || size == NULL || *size == 0)
    return UV_EINVAL;

  var = getenv(name);

  if (var == NULL)
    return UV_ENOENT;

  len = strlen(var);

  if (len >= *size) {
    *size = len + 1;
    return UV_ENOBUFS;
  }

  memcpy(buffer, var, len + 1);
  *size = len;

  return 0;
}

测试用例

通过以下测试用例 os.test.js ,为了验证 os.tmpdir() os.homedir() 是否可以通过修改环境变量值,来获取不同的返回值。

/**
 * 以下测试用例运行于 linux 环境,Window  环境变量名略有区别,可同理参考
 */

const os = require('os');

// tmpdir()

// 默认输出
console.log('default tmpdir():', os.tmpdir());

// 动态修改输出
process.env.TEMP = '/tmp/wadezhan';
console.log('set process.env.TEMP:', os.tmpdir());

// homedir()

// 默认输出
console.log('default homedir():', os.homedir());

// 动态修改输出
// process.env.USERPROFILE = 'C:\\Users\\wadezhan\\AppData'; // window 环境对应环境变量
process.env.HOME = '/home/wadezhan';
console.log('set process.env.HOME:', os.homedir());

输出结果如下:

# node os.test.js 
default tmpdir(): /tmp
set process.env.TEMP: /tmp/wadezhan
default homedir(): /root
set process.env.HOME: /home/wadezhan

推荐阅读

  1. os.tmpdir()
  2. os.homdir()
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