模組 Etc

Etc 模組提供存取通常儲存在 Unix 系統上 /etc 目錄中檔案的資訊。

可存取的資訊包含在 /etc/passwd/etc/group 檔案中找到的資訊,以及有關系統暫存目錄 (/tmp) 和組態目錄 (/etc) 的資訊。

Etc 模組提供比環境變數(例如 +$USER+)更可靠的方式來存取已登入使用者的資訊。

範例:

require 'etc'

login = Etc.getlogin
info = Etc.getpwnam(login)
username = info.gecos.split(/,/).first
puts "Hello #{username}, I see your login name is #{login}"

請注意,此模組提供的方法並不總是安全的。它應該用於提供資訊,而不是用於安全性。

在此模組中定義的所有操作都是類別方法,因此您可以將 Etc 模組包含到您的類別中。

常數

VERSION

公開類別方法

confstr(p1) 按一下以切換來源

使用 confstr() 傳回系統組態變數。

name 應為 Etc 下的常數,其開頭為 CS_

傳回值為字串或 nil。nil 表示沒有組態定義的值。(confstr() 傳回 0 但 errno 未設定。)

Etc.confstr(Etc::CS_PATH) #=> "/bin:/usr/bin"

# GNU/Linux
Etc.confstr(Etc::CS_GNU_LIBC_VERSION) #=> "glibc 2.18"
Etc.confstr(Etc::CS_GNU_LIBPTHREAD_VERSION) #=> "NPTL 2.18"
static VALUE
etc_confstr(VALUE obj, VALUE arg)
{
    int name;
    char localbuf[128], *buf = localbuf;
    size_t bufsize = sizeof(localbuf), ret;
    VALUE tmp;

    name = NUM2INT(arg);

    errno = 0;
    ret = confstr(name, buf, bufsize);
    if (bufsize < ret) {
        bufsize = ret;
        buf = ALLOCV_N(char, tmp, bufsize);
        errno = 0;
        ret = confstr(name, buf, bufsize);
    }
    if (bufsize < ret)
        rb_bug("required buffer size for confstr() changed dynamically.");
    if (ret == 0) {
        if (errno == 0) /* no configuration-defined value */
            return Qnil;
        rb_sys_fail("confstr");
    }
    return rb_str_new_cstr(buf);
}
endgrent() 按一下以切換來源

結束由 ::getgrent 開始的掃描 /etc/group 檔案的程序,並關閉檔案。

static VALUE
etc_endgrent(VALUE obj)
{
#ifdef HAVE_GETGRENT
    endgrent();
#endif
    return Qnil;
}
endpwent() 按一下以切換來源

結束由 ::getpwent 開始的掃描 /etc/passwd 檔案的程序,並關閉檔案。

static VALUE
etc_endpwent(VALUE obj)
{
#ifdef HAVE_GETPWENT
    endpwent();
#endif
    return Qnil;
}
getgrent() 按一下以切換來源

傳回 /etc/group 檔案中的項目。

第一次呼叫時,它會開啟檔案並傳回第一個項目;每次後續呼叫都會傳回下一個項目,或在到達檔案結尾時傳回 nil

若要在處理完成時關閉檔案,請呼叫 ::endgrent

每個項目都傳回為 Group 結構

static VALUE
etc_getgrent(VALUE obj)
{
#ifdef HAVE_GETGRENT
    struct group *gr;

    if ((gr = getgrent()) != 0) {
        return setup_group(gr);
    }
#endif
    return Qnil;
}
getgrgid(group_id) → Group 按一下以切換來源

傳回在 /etc/group 中找到的具有指定整數 group_id 的群組的資訊。

資訊以 Group 結構傳回。

有關更多詳細資訊,請參閱 getgrgid(3) 的 unix 手冊頁。

範例:

Etc.getgrgid(100)
#=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
static VALUE
etc_getgrgid(int argc, VALUE *argv, VALUE obj)
{
#ifdef HAVE_GETGRENT
    VALUE id;
    gid_t gid;
    struct group *grp;

    if (rb_scan_args(argc, argv, "01", &id) == 1) {
        gid = NUM2GIDT(id);
    }
    else {
        gid = getgid();
    }
    grp = getgrgid(gid);
    if (grp == 0) rb_raise(rb_eArgError, "can't find group for %d", (int)gid);
    return setup_group(grp);
#else
    return Qnil;
#endif
}
getgrnam(name) → Group 按一下以切換來源

傳回在 /etc/group 中找到的具有指定 name 的群組資訊。

資訊以 Group 結構傳回。

有關更多詳細資訊,請參閱 getgrnam(3) 的 unix 手冊頁。

範例:

Etc.getgrnam('users')
#=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
static VALUE
etc_getgrnam(VALUE obj, VALUE nam)
{
#ifdef HAVE_GETGRENT
    struct group *grp;
    const char *p = StringValueCStr(nam);

    grp = getgrnam(p);
    if (grp == 0) rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, nam);
    return setup_group(grp);
#else
    return Qnil;
#endif
}
getlogin → String 按一下以切換來源

傳回目前已登入使用者的簡短使用者名稱。很不幸地,通常很容易欺騙 ::getlogin

請勿將 ::getlogin 用於與安全性相關的目的。

如果 ::getlogin 失敗,請嘗試 ::getpwuid

有關更多詳細資訊,請參閱 getpwuid(3) 的 unix 手冊頁。

例如

Etc.getlogin -> 'guest'
static VALUE
etc_getlogin(VALUE obj)
{
    char *login;

#ifdef HAVE_GETLOGIN
    login = getlogin();
    if (!login) login = getenv("USER");
#else
    login = getenv("USER");
#endif

    if (login) {
#ifdef _WIN32
        rb_encoding *extenc = rb_utf8_encoding();
#else
        rb_encoding *extenc = rb_locale_encoding();
#endif
        return rb_external_str_new_with_enc(login, strlen(login), extenc);
    }

    return Qnil;
}
getpwent() 按一下以切換來源

/etc/passwd 檔案傳回一個項目。

第一次呼叫時,它會開啟檔案並傳回第一個項目;每次後續呼叫都會傳回下一個項目,或在到達檔案結尾時傳回 nil

若要關閉檔案(處理完成後),請呼叫 ::endpwent

每個項目都以 Passwd 結構傳回。

static VALUE
etc_getpwent(VALUE obj)
{
#ifdef HAVE_GETPWENT
    struct passwd *pw;

    if ((pw = getpwent()) != 0) {
        return setup_passwd(pw);
    }
#endif
    return Qnil;
}
getpwnam(name) → Passwd 按一下以切換來源

傳回具有指定登入 name 的使用者的 /etc/passwd 資訊。

資訊以 Passwd 結構傳回。

有關更多詳細資訊,請參閱 getpwnam(3) 的 unix 手冊頁。

範例:

Etc.getpwnam('root')
#=> #<struct Etc::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
static VALUE
etc_getpwnam(VALUE obj, VALUE nam)
{
#ifdef HAVE_GETPWENT
    struct passwd *pwd;
    const char *p = StringValueCStr(nam);

    pwd = getpwnam(p);
    if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, nam);
    return setup_passwd(pwd);
#else
    return Qnil;
#endif
}
getpwuid(uid) → Passwd 按一下以切換來源

傳回具有指定整數 uid 的使用者的 /etc/passwd 資訊。

資訊以 Passwd 結構傳回。

如果省略 uid,則會傳回 Passwd[:uid] 的值。

有關更多詳細資訊,請參閱 getpwuid(3) 的 unix 手冊頁。

範例:

Etc.getpwuid(0)
#=> #<struct Etc::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
static VALUE
etc_getpwuid(int argc, VALUE *argv, VALUE obj)
{
#if defined(HAVE_GETPWENT)
    VALUE id;
    rb_uid_t uid;
    struct passwd *pwd;

    if (rb_scan_args(argc, argv, "01", &id) == 1) {
        uid = NUM2UIDT(id);
    }
    else {
        uid = getuid();
    }
    pwd = getpwuid(uid);
    if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %d", (int)uid);
    return setup_passwd(pwd);
#else
    return Qnil;
#endif
}
group() 按一下以切換來源

提供一個便利的 Ruby 迭代器,它會為 /etc/group 檔案中的每個項目執行一個區塊。

程式區塊傳遞 Group 結構。

有關詳細資訊,請參閱上方的 ::getgrent

範例

require 'etc'

Etc.group {|g|
  puts g.name + ": " + g.mem.join(', ')
}
static VALUE
etc_group(VALUE obj)
{
#ifdef HAVE_GETGRENT
    struct group *grp;

    if (rb_block_given_p()) {
        each_group();
    }
    else if ((grp = getgrent()) != 0) {
        return setup_group(grp);
    }
#endif
    return Qnil;
}
nprocessors() 按一下以切換來源

傳回線上處理器的數量。

結果預計為使用所有可用處理器的程序數量。

此方法使用下列方式實作

  • sched_getaffinity(): Linux

  • sysconf(_SC_NPROCESSORS_ONLN): GNU/Linux、NetBSD、FreeBSD、OpenBSD、DragonFly BSD、OpenIndiana、Mac OS X、AIX

範例

require 'etc'
p Etc.nprocessors #=> 4

結果可能小於實際 CPU 數量,特別是當 Ruby 程序繫結到特定 CPU 時。這預計可獲得更佳的平行處理。

範例:(Linux)

linux$ taskset 0x3 ./ruby -retc -e "p Etc.nprocessors"  #=> 2
static VALUE
etc_nprocessors(VALUE obj)
{
    long ret;

#if !defined(_WIN32)

#if defined(HAVE_SCHED_GETAFFINITY) && defined(CPU_ALLOC)
    int ncpus;

    ncpus = etc_nprocessors_affin();
    if (ncpus != -1) {
        return INT2NUM(ncpus);
    }
    /* fallback to _SC_NPROCESSORS_ONLN */
#endif

    errno = 0;
    ret = sysconf(_SC_NPROCESSORS_ONLN);
    if (ret == -1) {
        rb_sys_fail("sysconf(_SC_NPROCESSORS_ONLN)");
    }
#else
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    ret = (long)si.dwNumberOfProcessors;
#endif
    return LONG2NUM(ret);
}
passwd { |struct| block } → Passwd 按一下以切換來源
passwd → Passwd

提供一個方便的 Ruby 迭代器,可針對 /etc/passwd 檔案中的每個項目執行區塊。

程式區塊傳遞 Passwd 結構。

有關詳細資訊,請參閱上方的 ::getpwent

範例

require 'etc'

Etc.passwd {|u|
  puts u.name + " = " + u.gecos
}
static VALUE
etc_passwd(VALUE obj)
{
#ifdef HAVE_GETPWENT
    struct passwd *pw;

    if (rb_block_given_p()) {
        each_passwd();
    }
    else if ((pw = getpwent()) != 0) {
        return setup_passwd(pw);
    }
#endif
    return Qnil;
}
setgrent() 按一下以切換來源

重設讀取 /etc/group 檔案的程序,以便下一次呼叫 ::getgrent 會再次傳回第一個項目。

static VALUE
etc_setgrent(VALUE obj)
{
#ifdef HAVE_GETGRENT
    setgrent();
#endif
    return Qnil;
}
setpwent() 按一下以切換來源

重設讀取 /etc/passwd 檔案的程序,以便下一次呼叫 ::getpwent 會再次傳回第一個項目。

static VALUE
etc_setpwent(VALUE obj)
{
#ifdef HAVE_GETPWENT
    setpwent();
#endif
    return Qnil;
}
sysconf(p1) 按一下以切換來源

使用 sysconf() 傳回系統組態變數。

name 應為 Etc 下的常數,且以 SC_ 開頭。

傳回值為整數或 nil。nil 表示不確定的限制。(sysconf() 傳回 -1,但 errno 未設定。)

Etc.sysconf(Etc::SC_ARG_MAX) #=> 2097152
Etc.sysconf(Etc::SC_LOGIN_NAME_MAX) #=> 256
static VALUE
etc_sysconf(VALUE obj, VALUE arg)
{
    int name;
    long ret;

    name = NUM2INT(arg);

    errno = 0;
    ret = sysconf(name);
    if (ret == -1) {
        if (errno == 0) /* no limit */
            return Qnil;
        rb_sys_fail("sysconf");
    }
    return LONG2NUM(ret);
}
sysconfdir() 按一下以切換來源

傳回系統組態目錄。

這通常是 "/etc",但會由編譯 Ruby 時使用的前綴修改。例如,如果 Ruby 建置並安裝在 /usr/local,則會在 Windows 以外的平台傳回 "/usr/local/etc"

在 Windows 上,這總是傳回系統提供的目錄。

static VALUE
etc_sysconfdir(VALUE obj)
{
#ifdef _WIN32
    return rb_w32_special_folder(CSIDL_COMMON_APPDATA);
#else
    return rb_filesystem_str_new_cstr(SYSCONFDIR);
#endif
}
systmpdir() 按一下以切換來源

傳回系統暫存目錄;通常是 “/tmp”。

static VALUE
etc_systmpdir(VALUE _)
{
    VALUE tmpdir;
#ifdef _WIN32
    WCHAR path[_MAX_PATH];
    UINT len = rb_w32_system_tmpdir(path, numberof(path));
    if (!len) return Qnil;
    tmpdir = rb_w32_conv_from_wchar(path, rb_filesystem_encoding());
#else
    const char default_tmp[] = "/tmp";
    const char *tmpstr = default_tmp;
    size_t tmplen = strlen(default_tmp);
# if defined _CS_DARWIN_USER_TEMP_DIR
    #ifndef MAXPATHLEN
    #define MAXPATHLEN 1024
    #endif
    char path[MAXPATHLEN];
    size_t len;
    len = confstr(_CS_DARWIN_USER_TEMP_DIR, path, sizeof(path));
    if (len > 0) {
        tmpstr = path;
        tmplen = len - 1;
        if (len > sizeof(path)) tmpstr = 0;
    }
# endif
    tmpdir = rb_filesystem_str_new(tmpstr, tmplen);
# if defined _CS_DARWIN_USER_TEMP_DIR
    if (!tmpstr) {
        confstr(_CS_DARWIN_USER_TEMP_DIR, RSTRING_PTR(tmpdir), len);
    }
# endif
#endif
#ifndef RB_PASS_KEYWORDS
    /* untaint on Ruby < 2.7 */
    FL_UNSET(tmpdir, FL_TAINT);
#endif
    return tmpdir;
}
uname() 按一下以切換來源

傳回 uname 系統呼叫取得的系統資訊。

傳回值至少有 5 個鍵值的雜湊。

:sysname, :nodename, :release, :version, :machine

範例

require 'etc'
require 'pp'

pp Etc.uname
#=> {:sysname=>"Linux",
#    :nodename=>"boron",
#    :release=>"2.6.18-6-xen-686",
#    :version=>"#1 SMP Thu Nov 5 19:54:42 UTC 2009",
#    :machine=>"i686"}
static VALUE
etc_uname(VALUE obj)
{
#ifdef _WIN32
    OSVERSIONINFOW v;
    SYSTEM_INFO s;
    const char *sysname, *mach;
    VALUE result, release, version;
    VALUE vbuf, nodename = Qnil;
    DWORD len = 0;
    WCHAR *buf;

    v.dwOSVersionInfoSize = sizeof(v);
    if (!GetVersionExW(&v))
        rb_sys_fail("GetVersionEx");

    result = rb_hash_new();
    switch (v.dwPlatformId) {
      case VER_PLATFORM_WIN32s:
        sysname = "Win32s";
        break;
      case VER_PLATFORM_WIN32_NT:
        sysname = "Windows_NT";
        break;
      case VER_PLATFORM_WIN32_WINDOWS:
      default:
        sysname = "Windows";
        break;
    }
    rb_hash_aset(result, ID2SYM(rb_intern("sysname")), rb_str_new_cstr(sysname));
    release = rb_sprintf("%lu.%lu.%lu", v.dwMajorVersion, v.dwMinorVersion, v.dwBuildNumber);
    rb_hash_aset(result, ID2SYM(rb_intern("release")), release);
    version = rb_sprintf("%s Version %"PRIsVALUE": %"PRIsVALUE, sysname, release,
                         rb_w32_conv_from_wchar(v.szCSDVersion, rb_utf8_encoding()));
    rb_hash_aset(result, ID2SYM(rb_intern("version")), version);

# if defined _MSC_VER && _MSC_VER < 1300
#   define GET_COMPUTER_NAME(ptr, plen) GetComputerNameW(ptr, plen)
# else
#   define GET_COMPUTER_NAME(ptr, plen) GetComputerNameExW(ComputerNameDnsFullyQualified, ptr, plen)
# endif
    GET_COMPUTER_NAME(NULL, &len);
    buf = ALLOCV_N(WCHAR, vbuf, len);
    if (GET_COMPUTER_NAME(buf, &len)) {
        nodename = rb_w32_conv_from_wchar(buf, rb_utf8_encoding());
    }
    ALLOCV_END(vbuf);
    if (NIL_P(nodename)) nodename = rb_str_new(0, 0);
    rb_hash_aset(result, ID2SYM(rb_intern("nodename")), nodename);

# ifndef PROCESSOR_ARCHITECTURE_AMD64
#   define PROCESSOR_ARCHITECTURE_AMD64 9
# endif
# ifndef PROCESSOR_ARCHITECTURE_INTEL
#   define PROCESSOR_ARCHITECTURE_INTEL 0
# endif
    GetSystemInfo(&s);
    switch (s.wProcessorArchitecture) {
      case PROCESSOR_ARCHITECTURE_AMD64:
        mach = "x64";
        break;
      case PROCESSOR_ARCHITECTURE_ARM:
        mach = "ARM";
        break;
      case PROCESSOR_ARCHITECTURE_INTEL:
        mach = "x86";
        break;
      default:
        mach = "unknown";
        break;
    }

    rb_hash_aset(result, ID2SYM(rb_intern("machine")), rb_str_new_cstr(mach));
#else
    struct utsname u;
    int ret;
    VALUE result;

    ret = uname(&u);
    if (ret == -1)
        rb_sys_fail("uname");

    result = rb_hash_new();
    rb_hash_aset(result, ID2SYM(rb_intern("sysname")), rb_str_new_cstr(u.sysname));
    rb_hash_aset(result, ID2SYM(rb_intern("nodename")), rb_str_new_cstr(u.nodename));
    rb_hash_aset(result, ID2SYM(rb_intern("release")), rb_str_new_cstr(u.release));
    rb_hash_aset(result, ID2SYM(rb_intern("version")), rb_str_new_cstr(u.version));
    rb_hash_aset(result, ID2SYM(rb_intern("machine")), rb_str_new_cstr(u.machine));
#endif

    return result;
}