/************************************************ etc.c - $Author$ created at: Tue Mar 22 18:39:19 JST 1994 ************************************************/ #include "ruby.h" #include "ruby/encoding.h" #include "ruby/io.h" #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_GETPWENT #include #endif #ifdef HAVE_GETGRENT #include #endif #include #ifdef HAVE_SYS_UTSNAME_H #include #endif #ifdef HAVE_SCHED_GETAFFINITY #include #endif static VALUE sPasswd; #ifdef HAVE_GETGRENT static VALUE sGroup; #endif #ifdef _WIN32 #include #ifndef CSIDL_COMMON_APPDATA #define CSIDL_COMMON_APPDATA 35 #endif #define HAVE_UNAME 1 #endif #ifdef STDC_HEADERS # include #else # ifdef HAVE_STDLIB_H # include # endif #endif char *getlogin(); #define RUBY_ETC_VERSION "1.4.0" #ifdef HAVE_RB_DEPRECATE_CONSTANT void rb_deprecate_constant(VALUE mod, const char *name); #else # define rb_deprecate_constant(mod,name) ((void)(mod),(void)(name)) #endif #include "constdefs.h" #ifdef HAVE_RUBY_ATOMIC_H # include "ruby/atomic.h" #else typedef int rb_atomic_t; # define RUBY_ATOMIC_CAS(var, oldval, newval) \ ((var) == (oldval) ? ((var) = (newval), (oldval)) : (var)) # define RUBY_ATOMIC_EXCHANGE(var, newval) \ atomic_exchange(&var, newval) static inline rb_atomic_t atomic_exchange(volatile rb_atomic_t *var, rb_atomic_t newval) { rb_atomic_t oldval = *var; *var = newval; return oldval; } #endif /* call-seq: * getlogin -> String * * Returns the short user name of the currently logged in user. * Unfortunately, it is often rather easy to fool ::getlogin. * * Avoid ::getlogin for security-related purposes. * * If ::getlogin fails, try ::getpwuid. * * See the unix manpage for getpwuid(3) for more detail. * * e.g. * 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; } #if defined(HAVE_GETPWENT) || defined(HAVE_GETGRENT) static VALUE safe_setup_str(const char *str) { if (str == 0) str = ""; return rb_str_new2(str); } static VALUE safe_setup_locale_str(const char *str) { if (str == 0) str = ""; return rb_locale_str_new_cstr(str); } static VALUE safe_setup_filesystem_str(const char *str) { if (str == 0) str = ""; return rb_filesystem_str_new_cstr(str); } #endif #ifdef HAVE_GETPWENT # ifdef __APPLE__ # define PW_TIME2VAL(t) INT2NUM((int)(t)) # else # define PW_TIME2VAL(t) TIMET2NUM(t) # endif static VALUE setup_passwd(struct passwd *pwd) { if (pwd == 0) rb_sys_fail("/etc/passwd"); return rb_struct_new(sPasswd, safe_setup_locale_str(pwd->pw_name), #ifdef HAVE_STRUCT_PASSWD_PW_PASSWD safe_setup_str(pwd->pw_passwd), #endif UIDT2NUM(pwd->pw_uid), GIDT2NUM(pwd->pw_gid), #ifdef HAVE_STRUCT_PASSWD_PW_GECOS safe_setup_locale_str(pwd->pw_gecos), #endif safe_setup_filesystem_str(pwd->pw_dir), safe_setup_filesystem_str(pwd->pw_shell), #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE PW_TIME2VAL(pwd->pw_change), #endif #ifdef HAVE_STRUCT_PASSWD_PW_QUOTA INT2NUM(pwd->pw_quota), #endif #ifdef HAVE_STRUCT_PASSWD_PW_AGE PW_AGE2VAL(pwd->pw_age), #endif #ifdef HAVE_STRUCT_PASSWD_PW_CLASS safe_setup_locale_str(pwd->pw_class), #endif #ifdef HAVE_STRUCT_PASSWD_PW_COMMENT safe_setup_locale_str(pwd->pw_comment), #endif #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE PW_TIME2VAL(pwd->pw_expire), #endif 0 /*dummy*/ ); } #endif /* call-seq: * getpwuid(uid) -> Passwd * * Returns the /etc/passwd information for the user with the given * integer +uid+. * * The information is returned as a Passwd struct. * * If +uid+ is omitted, the value from Passwd[:uid] is returned * instead. * * See the unix manpage for getpwuid(3) for more detail. * * === Example: * * Etc.getpwuid(0) * #=> # */ 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 } /* call-seq: * getpwnam(name) -> Passwd * * Returns the /etc/passwd information for the user with specified * login +name+. * * The information is returned as a Passwd struct. * * See the unix manpage for getpwnam(3) for more detail. * * === Example: * * Etc.getpwnam('root') * #=> # */ 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 } #ifdef HAVE_GETPWENT static rb_atomic_t passwd_blocking; static VALUE passwd_ensure(VALUE _) { endpwent(); if (RUBY_ATOMIC_EXCHANGE(passwd_blocking, 0) != 1) { rb_raise(rb_eRuntimeError, "unexpected passwd_blocking"); } return Qnil; } static VALUE passwd_iterate(VALUE _) { struct passwd *pw; setpwent(); while ((pw = getpwent()) != 0) { rb_yield(setup_passwd(pw)); } return Qnil; } static void each_passwd(void) { if (RUBY_ATOMIC_CAS(passwd_blocking, 0, 1)) { rb_raise(rb_eRuntimeError, "parallel passwd iteration"); } rb_ensure(passwd_iterate, 0, passwd_ensure, 0); } #endif /* call-seq: * Etc.passwd { |struct| block } -> Passwd * Etc.passwd -> Passwd * * Provides a convenient Ruby iterator which executes a block for each entry * in the /etc/passwd file. * * The code block is passed an Passwd struct. * * See ::getpwent above for details. * * Example: * * 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; } /* call-seq: * Etc::Passwd.each { |struct| block } -> Passwd * Etc::Passwd.each -> Enumerator * * Iterates for each entry in the /etc/passwd file if a block is * given. * * If no block is given, returns the Enumerator. * * The code block is passed an Passwd struct. * * See Etc.getpwent above for details. * * Example: * * require 'etc' * * Etc::Passwd.each {|u| * puts u.name + " = " + u.gecos * } * * Etc::Passwd.collect {|u| u.gecos} * Etc::Passwd.collect {|u| u.gecos} * */ static VALUE etc_each_passwd(VALUE obj) { #ifdef HAVE_GETPWENT RETURN_ENUMERATOR(obj, 0, 0); each_passwd(); #endif return obj; } /* Resets the process of reading the /etc/passwd file, so that the * next call to ::getpwent will return the first entry again. */ static VALUE etc_setpwent(VALUE obj) { #ifdef HAVE_GETPWENT setpwent(); #endif return Qnil; } /* Ends the process of scanning through the /etc/passwd file begun * with ::getpwent, and closes the file. */ static VALUE etc_endpwent(VALUE obj) { #ifdef HAVE_GETPWENT endpwent(); #endif return Qnil; } /* Returns an entry from the /etc/passwd file. * * The first time it is called it opens the file and returns the first entry; * each successive call returns the next entry, or +nil+ if the end of the file * has been reached. * * To close the file when processing is complete, call ::endpwent. * * Each entry is returned as a Passwd struct. * */ static VALUE etc_getpwent(VALUE obj) { #ifdef HAVE_GETPWENT struct passwd *pw; if ((pw = getpwent()) != 0) { return setup_passwd(pw); } #endif return Qnil; } #ifdef HAVE_GETGRENT static VALUE setup_group(struct group *grp) { VALUE mem; char **tbl; mem = rb_ary_new(); tbl = grp->gr_mem; while (*tbl) { rb_ary_push(mem, safe_setup_locale_str(*tbl)); tbl++; } return rb_struct_new(sGroup, safe_setup_locale_str(grp->gr_name), #ifdef HAVE_STRUCT_GROUP_GR_PASSWD safe_setup_str(grp->gr_passwd), #endif GIDT2NUM(grp->gr_gid), mem); } #endif /* call-seq: * getgrgid(group_id) -> Group * * Returns information about the group with specified integer +group_id+, * as found in /etc/group. * * The information is returned as a Group struct. * * See the unix manpage for getgrgid(3) for more detail. * * === Example: * * Etc.getgrgid(100) * #=> # * */ 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 } /* call-seq: * getgrnam(name) -> Group * * Returns information about the group with specified +name+, as found in * /etc/group. * * The information is returned as a Group struct. * * See the unix manpage for getgrnam(3) for more detail. * * === Example: * * Etc.getgrnam('users') * #=> # * */ 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 } #ifdef HAVE_GETGRENT static rb_atomic_t group_blocking; static VALUE group_ensure(VALUE _) { endgrent(); if (RUBY_ATOMIC_EXCHANGE(group_blocking, 0) != 1) { rb_raise(rb_eRuntimeError, "unexpected group_blocking"); } return Qnil; } static VALUE group_iterate(VALUE _) { struct group *pw; setgrent(); while ((pw = getgrent()) != 0) { rb_yield(setup_group(pw)); } return Qnil; } static void each_group(void) { if (RUBY_ATOMIC_CAS(group_blocking, 0, 1)) { rb_raise(rb_eRuntimeError, "parallel group iteration"); } rb_ensure(group_iterate, 0, group_ensure, 0); } #endif /* Provides a convenient Ruby iterator which executes a block for each entry * in the /etc/group file. * * The code block is passed an Group struct. * * See ::getgrent above for details. * * Example: * * 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; } #ifdef HAVE_GETGRENT /* call-seq: * Etc::Group.each { |group| block } -> obj * Etc::Group.each -> Enumerator * * Iterates for each entry in the /etc/group file if a block is * given. * * If no block is given, returns the Enumerator. * * The code block is passed a Group struct. * * Example: * * require 'etc' * * Etc::Group.each {|g| * puts g.name + ": " + g.mem.join(', ') * } * * Etc::Group.collect {|g| g.name} * Etc::Group.select {|g| !g.mem.empty?} * */ static VALUE etc_each_group(VALUE obj) { RETURN_ENUMERATOR(obj, 0, 0); each_group(); return obj; } #endif /* Resets the process of reading the /etc/group file, so that the * next call to ::getgrent will return the first entry again. */ static VALUE etc_setgrent(VALUE obj) { #ifdef HAVE_GETGRENT setgrent(); #endif return Qnil; } /* Ends the process of scanning through the /etc/group file begun * by ::getgrent, and closes the file. */ static VALUE etc_endgrent(VALUE obj) { #ifdef HAVE_GETGRENT endgrent(); #endif return Qnil; } /* Returns an entry from the /etc/group file. * * The first time it is called it opens the file and returns the first entry; * each successive call returns the next entry, or +nil+ if the end of the file * has been reached. * * To close the file when processing is complete, call ::endgrent. * * Each entry is returned as a Group struct */ static VALUE etc_getgrent(VALUE obj) { #ifdef HAVE_GETGRENT struct group *gr; if ((gr = getgrent()) != 0) { return setup_group(gr); } #endif return Qnil; } #define numberof(array) (sizeof(array) / sizeof(*(array))) #ifdef _WIN32 VALUE rb_w32_special_folder(int type); UINT rb_w32_system_tmpdir(WCHAR *path, UINT len); VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc); #endif /* * Returns system configuration directory. * * This is typically "/etc", but is modified by the prefix used * when Ruby was compiled. For example, if Ruby is built and installed in * /usr/local, returns "/usr/local/etc" on other * platforms than Windows. * * On Windows, this always returns the directory provided by the system. */ 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 } /* * Returns system temporary directory; typically "/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; } #ifdef HAVE_UNAME /* * Returns the system information obtained by uname system call. * * The return value is a hash which has 5 keys at least: * :sysname, :nodename, :release, :version, :machine * * Example: * * 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; } #else #define etc_uname rb_f_notimplement #endif #ifdef HAVE_SYSCONF /* * Returns system configuration variable using sysconf(). * * _name_ should be a constant under Etc which begins with SC_. * * The return value is an integer or nil. * nil means indefinite limit. (sysconf() returns -1 but errno is not set.) * * 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); } #else #define etc_sysconf rb_f_notimplement #endif #ifdef HAVE_CONFSTR /* * Returns system configuration variable using confstr(). * * _name_ should be a constant under Etc which begins with CS_. * * The return value is a string or nil. * nil means no configuration-defined value. (confstr() returns 0 but errno is not set.) * * 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); } #else #define etc_confstr rb_f_notimplement #endif #ifdef HAVE_FPATHCONF /* * Returns pathname configuration variable using fpathconf(). * * _name_ should be a constant under Etc which begins with PC_. * * The return value is an integer or nil. * nil means indefinite limit. (fpathconf() returns -1 but errno is not set.) * * require 'etc' * IO.pipe {|r, w| * p w.pathconf(Etc::PC_PIPE_BUF) #=> 4096 * } * */ static VALUE io_pathconf(VALUE io, VALUE arg) { int name; long ret; rb_io_t *fptr; name = NUM2INT(arg); GetOpenFile(io, fptr); errno = 0; ret = fpathconf(fptr->fd, name); if (ret == -1) { if (errno == 0) /* no limit */ return Qnil; rb_sys_fail("fpathconf"); } return LONG2NUM(ret); } #else #define io_pathconf rb_f_notimplement #endif #if (defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)) || defined(_WIN32) #if defined(HAVE_SCHED_GETAFFINITY) && defined(CPU_ALLOC) static int etc_nprocessors_affin(void) { cpu_set_t *cpuset, cpuset_buff[1024 / sizeof(cpu_set_t)]; size_t size; int ret; int n; CPU_ZERO_S(sizeof(cpuset_buff), cpuset_buff); /* * XXX: * man page says CPU_ALLOC takes number of cpus. But it is not accurate * explanation. sched_getaffinity() returns EINVAL if cpuset bitmap is * smaller than kernel internal bitmap. * That said, sched_getaffinity() can fail when a kernel have sparse bitmap * even if cpuset bitmap is larger than number of cpus. * The precious way is to use /sys/devices/system/cpu/online. But there are * two problems, * - Costly calculation * It is a minor issue, but possibly kill a benefit of a parallel processing. * - No guarantee to exist /sys/devices/system/cpu/online * This is an issue especially when using Linux containers. * So, we use hardcode number for a workaround. Current linux kernel * (Linux 3.17) support 8192 cpus at maximum. Then 16384 must be enough. */ for (n=64; n <= 16384; n *= 2) { size = CPU_ALLOC_SIZE(n); if (size >= sizeof(cpuset_buff)) { cpuset = xcalloc(1, size); if (!cpuset) return -1; } else { cpuset = cpuset_buff; } ret = sched_getaffinity(0, size, cpuset); if (ret == 0) { /* On success, count number of cpus. */ ret = CPU_COUNT_S(size, cpuset); } if (size >= sizeof(cpuset_buff)) { xfree(cpuset); } if (ret > 0 || errno != EINVAL) { return ret; } } return ret; } #endif /* * Returns the number of online processors. * * The result is intended as the number of processes to * use all available processors. * * This method is implemented using: * - sched_getaffinity(): Linux * - sysconf(_SC_NPROCESSORS_ONLN): GNU/Linux, NetBSD, FreeBSD, OpenBSD, DragonFly BSD, OpenIndiana, Mac OS X, AIX * * Example: * * require 'etc' * p Etc.nprocessors #=> 4 * * The result might be smaller number than physical cpus especially when ruby * process is bound to specific cpus. This is intended for getting better * parallel processing. * * Example: (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); } #else #define etc_nprocessors rb_f_notimplement #endif /* * The Etc module provides access to information typically stored in * files in the /etc directory on Unix systems. * * The information accessible consists of the information found in the * /etc/passwd and /etc/group files, plus information * about the system's temporary directory (/tmp) and configuration * directory (/etc). * * The Etc module provides a more reliable way to access information about * the logged in user than environment variables such as +$USER+. * * == Example: * * require 'etc' * * login = Etc.getlogin * info = Etc.getpwnam(login) * username = info.gecos.split(/,/).first * puts "Hello #{username}, I see your login name is #{login}" * * Note that the methods provided by this module are not always secure. * It should be used for informational purposes, and not for security. * * All operations defined in this module are class methods, so that you can * include the Etc module into your class. */ void Init_etc(void) { VALUE mEtc; #ifdef HAVE_RB_EXT_RACTOR_SAFE RB_EXT_RACTOR_SAFE(true); #endif mEtc = rb_define_module("Etc"); rb_define_const(mEtc, "VERSION", rb_str_new_cstr(RUBY_ETC_VERSION)); init_constants(mEtc); rb_define_module_function(mEtc, "getlogin", etc_getlogin, 0); rb_define_module_function(mEtc, "getpwuid", etc_getpwuid, -1); rb_define_module_function(mEtc, "getpwnam", etc_getpwnam, 1); rb_define_module_function(mEtc, "setpwent", etc_setpwent, 0); rb_define_module_function(mEtc, "endpwent", etc_endpwent, 0); rb_define_module_function(mEtc, "getpwent", etc_getpwent, 0); rb_define_module_function(mEtc, "passwd", etc_passwd, 0); rb_define_module_function(mEtc, "getgrgid", etc_getgrgid, -1); rb_define_module_function(mEtc, "getgrnam", etc_getgrnam, 1); rb_define_module_function(mEtc, "group", etc_group, 0); rb_define_module_function(mEtc, "setgrent", etc_setgrent, 0); rb_define_module_function(mEtc, "endgrent", etc_endgrent, 0); rb_define_module_function(mEtc, "getgrent", etc_getgrent, 0); rb_define_module_function(mEtc, "sysconfdir", etc_sysconfdir, 0); rb_define_module_function(mEtc, "systmpdir", etc_systmpdir, 0); rb_define_module_function(mEtc, "uname", etc_uname, 0); rb_define_module_function(mEtc, "sysconf", etc_sysconf, 1); rb_define_module_function(mEtc, "confstr", etc_confstr, 1); rb_define_method(rb_cIO, "pathconf", io_pathconf, 1); rb_define_module_function(mEtc, "nprocessors", etc_nprocessors, 0); sPasswd = rb_struct_define_under(mEtc, "Passwd", "name", #ifdef HAVE_STRUCT_PASSWD_PW_PASSWD "passwd", #endif "uid", "gid", #ifdef HAVE_STRUCT_PASSWD_PW_GECOS "gecos", #endif "dir", "shell", #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE "change", #endif #ifdef HAVE_STRUCT_PASSWD_PW_QUOTA "quota", #endif #ifdef HAVE_STRUCT_PASSWD_PW_AGE "age", #endif #ifdef HAVE_STRUCT_PASSWD_PW_CLASS "uclass", #endif #ifdef HAVE_STRUCT_PASSWD_PW_COMMENT "comment", #endif #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE "expire", #endif NULL); #if 0 /* * Passwd is a placeholder Struct for user database on Unix systems. * * === The struct contains the following members * * name:: * contains the short login name of the user as a String. * passwd:: * contains the encrypted password of the user as a String. * an 'x' is returned if shadow passwords are in * use. An '*' is returned if the user cannot * log in using a password. * uid:: * contains the integer user ID (uid) of the user. * gid:: * contains the integer group ID (gid) of the user's primary group. * dir:: * contains the path to the home directory of the user as a String. * shell:: * contains the path to the login shell of the user as a String. * * === The following members below are system-dependent * * gecos:: * contains a longer String description of the user, such as * a full name. Some Unix systems provide structured information in the * gecos field, but this is system-dependent. * change:: * password change time(integer). * quota:: * quota value(integer). * age:: * password age(integer). * class:: * user access class(string). * comment:: * comment(string). * expire:: * account expiration time(integer). */ sPasswd = rb_define_class_under(mEtc, "Passwd", rb_cStruct); #endif rb_extend_object(sPasswd, rb_mEnumerable); rb_define_singleton_method(sPasswd, "each", etc_each_passwd, 0); #ifdef HAVE_GETGRENT sGroup = rb_struct_define_under(mEtc, "Group", "name", #ifdef HAVE_STRUCT_GROUP_GR_PASSWD "passwd", #endif "gid", "mem", NULL); #if 0 /* * Group is a placeholder Struct for user group database on Unix systems. * * === The struct contains the following members * * name:: * contains the name of the group as a String. * passwd:: * contains the encrypted password as a String. An 'x' is * returned if password access to the group is not available; an empty * string is returned if no password is needed to obtain membership of * the group. * This is system-dependent. * gid:: * contains the group's numeric ID as an integer. * mem:: * is an Array of Strings containing the short login names of the * members of the group. */ sGroup = rb_define_class_under(mEtc, "Group", rb_cStruct); #endif rb_extend_object(sGroup, rb_mEnumerable); rb_define_singleton_method(sGroup, "each", etc_each_group, 0); #endif }