diff options
Diffstat (limited to 'ext/etc')
| -rw-r--r-- | ext/etc/.cvsignore | 3 | ||||
| -rw-r--r-- | ext/etc/.document | 2 | ||||
| -rw-r--r-- | ext/etc/MANIFEST | 6 | ||||
| -rw-r--r-- | ext/etc/depend | 183 | ||||
| -rw-r--r-- | ext/etc/etc.c | 1170 | ||||
| -rw-r--r-- | ext/etc/etc.gemspec | 44 | ||||
| -rw-r--r-- | ext/etc/etc.txt | 72 | ||||
| -rw-r--r-- | ext/etc/etc.txt.ja | 72 | ||||
| -rw-r--r-- | ext/etc/extconf.rb | 61 | ||||
| -rw-r--r-- | ext/etc/mkconstants.rb | 352 |
10 files changed, 1669 insertions, 296 deletions
diff --git a/ext/etc/.cvsignore b/ext/etc/.cvsignore deleted file mode 100644 index 4088712231..0000000000 --- a/ext/etc/.cvsignore +++ /dev/null @@ -1,3 +0,0 @@ -Makefile -mkmf.log -*.def diff --git a/ext/etc/.document b/ext/etc/.document new file mode 100644 index 0000000000..9bbea23b92 --- /dev/null +++ b/ext/etc/.document @@ -0,0 +1,2 @@ +etc.c +constdefs.h diff --git a/ext/etc/MANIFEST b/ext/etc/MANIFEST deleted file mode 100644 index 62cf5be233..0000000000 --- a/ext/etc/MANIFEST +++ /dev/null @@ -1,6 +0,0 @@ -MANIFEST -etc.c -etc.txt -etc.txt.ja -depend -extconf.rb diff --git a/ext/etc/depend b/ext/etc/depend index ac706477b0..77fe56a6bf 100644 --- a/ext/etc/depend +++ b/ext/etc/depend @@ -1 +1,182 @@ -etc.o : etc.c $(hdrdir)/ruby.h $(topdir)/config.h $(hdrdir)/defines.h +constdefs.h : $(srcdir)/mkconstants.rb + @echo "generating constant definitions" + @$(RUBY) $(srcdir)/mkconstants.rb -o constdefs.h + +# AUTOGENERATED DEPENDENCIES START +etc.o: $(RUBY_EXTCONF_H) +etc.o: $(arch_hdrdir)/ruby/config.h +etc.o: $(hdrdir)/ruby.h +etc.o: $(hdrdir)/ruby/assert.h +etc.o: $(hdrdir)/ruby/atomic.h +etc.o: $(hdrdir)/ruby/backward.h +etc.o: $(hdrdir)/ruby/backward/2/assume.h +etc.o: $(hdrdir)/ruby/backward/2/attributes.h +etc.o: $(hdrdir)/ruby/backward/2/bool.h +etc.o: $(hdrdir)/ruby/backward/2/inttypes.h +etc.o: $(hdrdir)/ruby/backward/2/limits.h +etc.o: $(hdrdir)/ruby/backward/2/long_long.h +etc.o: $(hdrdir)/ruby/backward/2/stdalign.h +etc.o: $(hdrdir)/ruby/backward/2/stdarg.h +etc.o: $(hdrdir)/ruby/defines.h +etc.o: $(hdrdir)/ruby/encoding.h +etc.o: $(hdrdir)/ruby/intern.h +etc.o: $(hdrdir)/ruby/internal/abi.h +etc.o: $(hdrdir)/ruby/internal/anyargs.h +etc.o: $(hdrdir)/ruby/internal/arithmetic.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/char.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/double.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/int.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/long.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/short.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +etc.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +etc.o: $(hdrdir)/ruby/internal/assume.h +etc.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +etc.o: $(hdrdir)/ruby/internal/attr/artificial.h +etc.o: $(hdrdir)/ruby/internal/attr/cold.h +etc.o: $(hdrdir)/ruby/internal/attr/const.h +etc.o: $(hdrdir)/ruby/internal/attr/constexpr.h +etc.o: $(hdrdir)/ruby/internal/attr/deprecated.h +etc.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +etc.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +etc.o: $(hdrdir)/ruby/internal/attr/error.h +etc.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +etc.o: $(hdrdir)/ruby/internal/attr/forceinline.h +etc.o: $(hdrdir)/ruby/internal/attr/format.h +etc.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +etc.o: $(hdrdir)/ruby/internal/attr/noalias.h +etc.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +etc.o: $(hdrdir)/ruby/internal/attr/noexcept.h +etc.o: $(hdrdir)/ruby/internal/attr/noinline.h +etc.o: $(hdrdir)/ruby/internal/attr/nonnull.h +etc.o: $(hdrdir)/ruby/internal/attr/noreturn.h +etc.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +etc.o: $(hdrdir)/ruby/internal/attr/pure.h +etc.o: $(hdrdir)/ruby/internal/attr/restrict.h +etc.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +etc.o: $(hdrdir)/ruby/internal/attr/warning.h +etc.o: $(hdrdir)/ruby/internal/attr/weakref.h +etc.o: $(hdrdir)/ruby/internal/cast.h +etc.o: $(hdrdir)/ruby/internal/compiler_is.h +etc.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +etc.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +etc.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +etc.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +etc.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +etc.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +etc.o: $(hdrdir)/ruby/internal/compiler_since.h +etc.o: $(hdrdir)/ruby/internal/config.h +etc.o: $(hdrdir)/ruby/internal/constant_p.h +etc.o: $(hdrdir)/ruby/internal/core.h +etc.o: $(hdrdir)/ruby/internal/core/rarray.h +etc.o: $(hdrdir)/ruby/internal/core/rbasic.h +etc.o: $(hdrdir)/ruby/internal/core/rbignum.h +etc.o: $(hdrdir)/ruby/internal/core/rclass.h +etc.o: $(hdrdir)/ruby/internal/core/rdata.h +etc.o: $(hdrdir)/ruby/internal/core/rfile.h +etc.o: $(hdrdir)/ruby/internal/core/rhash.h +etc.o: $(hdrdir)/ruby/internal/core/robject.h +etc.o: $(hdrdir)/ruby/internal/core/rregexp.h +etc.o: $(hdrdir)/ruby/internal/core/rstring.h +etc.o: $(hdrdir)/ruby/internal/core/rstruct.h +etc.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +etc.o: $(hdrdir)/ruby/internal/ctype.h +etc.o: $(hdrdir)/ruby/internal/dllexport.h +etc.o: $(hdrdir)/ruby/internal/dosish.h +etc.o: $(hdrdir)/ruby/internal/encoding/coderange.h +etc.o: $(hdrdir)/ruby/internal/encoding/ctype.h +etc.o: $(hdrdir)/ruby/internal/encoding/encoding.h +etc.o: $(hdrdir)/ruby/internal/encoding/pathname.h +etc.o: $(hdrdir)/ruby/internal/encoding/re.h +etc.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +etc.o: $(hdrdir)/ruby/internal/encoding/string.h +etc.o: $(hdrdir)/ruby/internal/encoding/symbol.h +etc.o: $(hdrdir)/ruby/internal/encoding/transcode.h +etc.o: $(hdrdir)/ruby/internal/error.h +etc.o: $(hdrdir)/ruby/internal/eval.h +etc.o: $(hdrdir)/ruby/internal/event.h +etc.o: $(hdrdir)/ruby/internal/fl_type.h +etc.o: $(hdrdir)/ruby/internal/gc.h +etc.o: $(hdrdir)/ruby/internal/glob.h +etc.o: $(hdrdir)/ruby/internal/globals.h +etc.o: $(hdrdir)/ruby/internal/has/attribute.h +etc.o: $(hdrdir)/ruby/internal/has/builtin.h +etc.o: $(hdrdir)/ruby/internal/has/c_attribute.h +etc.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +etc.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +etc.o: $(hdrdir)/ruby/internal/has/extension.h +etc.o: $(hdrdir)/ruby/internal/has/feature.h +etc.o: $(hdrdir)/ruby/internal/has/warning.h +etc.o: $(hdrdir)/ruby/internal/intern/array.h +etc.o: $(hdrdir)/ruby/internal/intern/bignum.h +etc.o: $(hdrdir)/ruby/internal/intern/class.h +etc.o: $(hdrdir)/ruby/internal/intern/compar.h +etc.o: $(hdrdir)/ruby/internal/intern/complex.h +etc.o: $(hdrdir)/ruby/internal/intern/cont.h +etc.o: $(hdrdir)/ruby/internal/intern/dir.h +etc.o: $(hdrdir)/ruby/internal/intern/enum.h +etc.o: $(hdrdir)/ruby/internal/intern/enumerator.h +etc.o: $(hdrdir)/ruby/internal/intern/error.h +etc.o: $(hdrdir)/ruby/internal/intern/eval.h +etc.o: $(hdrdir)/ruby/internal/intern/file.h +etc.o: $(hdrdir)/ruby/internal/intern/hash.h +etc.o: $(hdrdir)/ruby/internal/intern/io.h +etc.o: $(hdrdir)/ruby/internal/intern/load.h +etc.o: $(hdrdir)/ruby/internal/intern/marshal.h +etc.o: $(hdrdir)/ruby/internal/intern/numeric.h +etc.o: $(hdrdir)/ruby/internal/intern/object.h +etc.o: $(hdrdir)/ruby/internal/intern/parse.h +etc.o: $(hdrdir)/ruby/internal/intern/proc.h +etc.o: $(hdrdir)/ruby/internal/intern/process.h +etc.o: $(hdrdir)/ruby/internal/intern/random.h +etc.o: $(hdrdir)/ruby/internal/intern/range.h +etc.o: $(hdrdir)/ruby/internal/intern/rational.h +etc.o: $(hdrdir)/ruby/internal/intern/re.h +etc.o: $(hdrdir)/ruby/internal/intern/ruby.h +etc.o: $(hdrdir)/ruby/internal/intern/select.h +etc.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +etc.o: $(hdrdir)/ruby/internal/intern/set.h +etc.o: $(hdrdir)/ruby/internal/intern/signal.h +etc.o: $(hdrdir)/ruby/internal/intern/sprintf.h +etc.o: $(hdrdir)/ruby/internal/intern/string.h +etc.o: $(hdrdir)/ruby/internal/intern/struct.h +etc.o: $(hdrdir)/ruby/internal/intern/thread.h +etc.o: $(hdrdir)/ruby/internal/intern/time.h +etc.o: $(hdrdir)/ruby/internal/intern/variable.h +etc.o: $(hdrdir)/ruby/internal/intern/vm.h +etc.o: $(hdrdir)/ruby/internal/interpreter.h +etc.o: $(hdrdir)/ruby/internal/iterator.h +etc.o: $(hdrdir)/ruby/internal/memory.h +etc.o: $(hdrdir)/ruby/internal/method.h +etc.o: $(hdrdir)/ruby/internal/module.h +etc.o: $(hdrdir)/ruby/internal/newobj.h +etc.o: $(hdrdir)/ruby/internal/scan_args.h +etc.o: $(hdrdir)/ruby/internal/special_consts.h +etc.o: $(hdrdir)/ruby/internal/static_assert.h +etc.o: $(hdrdir)/ruby/internal/stdalign.h +etc.o: $(hdrdir)/ruby/internal/stdbool.h +etc.o: $(hdrdir)/ruby/internal/stdckdint.h +etc.o: $(hdrdir)/ruby/internal/symbol.h +etc.o: $(hdrdir)/ruby/internal/value.h +etc.o: $(hdrdir)/ruby/internal/value_type.h +etc.o: $(hdrdir)/ruby/internal/variable.h +etc.o: $(hdrdir)/ruby/internal/warning_push.h +etc.o: $(hdrdir)/ruby/internal/xmalloc.h +etc.o: $(hdrdir)/ruby/io.h +etc.o: $(hdrdir)/ruby/missing.h +etc.o: $(hdrdir)/ruby/onigmo.h +etc.o: $(hdrdir)/ruby/oniguruma.h +etc.o: $(hdrdir)/ruby/ruby.h +etc.o: $(hdrdir)/ruby/st.h +etc.o: $(hdrdir)/ruby/subst.h +etc.o: constdefs.h +etc.o: etc.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/etc/etc.c b/ext/etc/etc.c index c42f279d58..8d50a96a62 100644 --- a/ext/etc/etc.c +++ b/ext/etc/etc.c @@ -3,12 +3,13 @@ etc.c - $Author$ - $Date$ created at: Tue Mar 22 18:39:19 JST 1994 ************************************************/ #include "ruby.h" +#include "ruby/encoding.h" +#include "ruby/io.h" #include <sys/types.h> #ifdef HAVE_UNISTD_H @@ -23,20 +24,98 @@ #include <grp.h> #endif -static VALUE sPasswd, sGroup; +#include <errno.h> -#ifndef _WIN32 -char *getenv(); +#ifdef HAVE_SYS_UTSNAME_H +#include <sys/utsname.h> #endif -char *getlogin(); +#ifdef HAVE_SCHED_GETAFFINITY +#include <sched.h> +#endif + +static VALUE sPasswd; +#ifdef HAVE_GETGRENT +static VALUE sGroup; +#endif + +#ifdef _WIN32 +#include <shlobj.h> +#ifndef CSIDL_COMMON_APPDATA +#define CSIDL_COMMON_APPDATA 35 +#endif +#define HAVE_UNAME 1 +#endif + +#ifdef STDC_HEADERS +# include <stdlib.h> +#else +# ifdef HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +RUBY_EXTERN char *getlogin(void); + +#define RUBY_ETC_VERSION "1.4.6" + +#define SYMBOL_LIT(str) ID2SYM(rb_intern_const(str "")) + +#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" + +#ifndef HAVE_RB_IO_DESCRIPTOR +static int +io_descriptor_fallback(VALUE io) +{ + rb_io_t *fptr; + GetOpenFile(io, fptr); + return fptr->fd; +} +#define rb_io_descriptor io_descriptor_fallback +#endif + +#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 <code>getpwuid(3)</code> for more detail. + * + * e.g. + * Etc.getlogin -> 'guest' + */ static VALUE -etc_getlogin(obj) - VALUE obj; +etc_getlogin(VALUE obj) { char *login; - rb_secure(4); #ifdef HAVE_GETLOGIN login = getlogin(); if (!login) login = getenv("USER"); @@ -44,152 +123,270 @@ etc_getlogin(obj) login = getenv("USER"); #endif - if (login) - return rb_tainted_str_new2(login); + 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(str) - const char *str; +safe_setup_str(const char *str) { if (str == 0) str = ""; - return rb_tainted_str_new2(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(pwd) - struct passwd *pwd; +setup_passwd(struct passwd *pwd) { if (pwd == 0) rb_sys_fail("/etc/passwd"); return rb_struct_new(sPasswd, - safe_setup_str(pwd->pw_name), -#ifdef HAVE_ST_PW_PASSWD + safe_setup_locale_str(pwd->pw_name), +#ifdef HAVE_STRUCT_PASSWD_PW_PASSWD safe_setup_str(pwd->pw_passwd), #endif - INT2FIX(pwd->pw_uid), - INT2FIX(pwd->pw_gid), -#ifdef HAVE_ST_PW_GECOS - safe_setup_str(pwd->pw_gecos), + UIDT2NUM(pwd->pw_uid), + GIDT2NUM(pwd->pw_gid), +#ifdef HAVE_STRUCT_PASSWD_PW_GECOS + safe_setup_locale_str(pwd->pw_gecos), #endif - safe_setup_str(pwd->pw_dir), - safe_setup_str(pwd->pw_shell), -#ifdef HAVE_ST_PW_CHANGE - INT2FIX(pwd->pw_change), + 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_ST_PW_QUOTA - INT2FIX(pwd->pw_quota), +#ifdef HAVE_STRUCT_PASSWD_PW_QUOTA + INT2NUM(pwd->pw_quota), #endif -#ifdef HAVE_ST_PW_AGE - INT2FIX(pwd->pw_age), +#ifdef HAVE_STRUCT_PASSWD_PW_AGE + PW_AGE2VAL(pwd->pw_age), #endif -#ifdef HAVE_ST_PW_CLASS - safe_setup_str(pwd->pw_class), +#ifdef HAVE_STRUCT_PASSWD_PW_CLASS + safe_setup_locale_str(pwd->pw_class), #endif -#ifdef HAVE_ST_PW_COMMENT - safe_setup_str(pwd->pw_comment), +#ifdef HAVE_STRUCT_PASSWD_PW_COMMENT + safe_setup_locale_str(pwd->pw_comment), #endif -#ifdef HAVE_ST_PW_EXPIRE - INT2FIX(pwd->pw_expire), +#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE + PW_TIME2VAL(pwd->pw_expire), #endif 0 /*dummy*/ ); } #endif +/* call-seq: + * getpwuid(uid) -> Etc::Passwd + * + * Returns the <tt>/etc/passwd</tt> information for the user with the given + * integer +uid+. + * + * The information is returned as a Passwd struct. + * + * If +uid+ is omitted, the value from <code>Passwd[:uid]</code> is returned + * instead. + * + * See the unix manpage for <code>getpwuid(3)</code> for more detail. + * + * *Example:* + * + * 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(argc, argv, obj) - int argc; - VALUE *argv; - VALUE obj; +etc_getpwuid(int argc, VALUE *argv, VALUE obj) { #if defined(HAVE_GETPWENT) VALUE id; - int uid; + rb_uid_t uid; struct passwd *pwd; - rb_secure(4); if (rb_scan_args(argc, argv, "01", &id) == 1) { - uid = NUM2INT(id); + uid = NUM2UIDT(id); } else { uid = getuid(); } pwd = getpwuid(uid); - if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %d", uid); + if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %d", (int)uid); return setup_passwd(pwd); -#else +#else return Qnil; #endif } +/* call-seq: + * getpwnam(name) -> Etc::Passwd + * + * Returns the <tt>/etc/passwd</tt> information for the user with specified + * login +name+. + * + * The information is returned as a Passwd struct. + * + * See the unix manpage for <code>getpwnam(3)</code> for more detail. + * + * *Example:* + * + * 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(obj, nam) - VALUE obj, nam; +etc_getpwnam(VALUE obj, VALUE nam) { #ifdef HAVE_GETPWENT struct passwd *pwd; + const char *p = StringValueCStr(nam); - SafeStringValue(nam); - pwd = getpwnam(RSTRING(nam)->ptr); - if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %s", RSTRING(nam)->ptr); + pwd = getpwnam(p); + if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, nam); return setup_passwd(pwd); -#else +#else return Qnil; #endif } #ifdef HAVE_GETPWENT -static int passwd_blocking = 0; +static rb_atomic_t passwd_blocking; static VALUE -passwd_ensure() +passwd_ensure(VALUE _) { - passwd_blocking = Qfalse; + endpwent(); + if (RUBY_ATOMIC_EXCHANGE(passwd_blocking, 0) != 1) { + rb_raise(rb_eRuntimeError, "unexpected passwd_blocking"); + } return Qnil; } static VALUE -passwd_iterate() +passwd_iterate(VALUE _) { struct passwd *pw; setpwent(); - while (pw = getpwent()) { + while ((pw = getpwent()) != 0) { rb_yield(setup_passwd(pw)); } - endpwent(); 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: + * passwd { |struct| block } + * passwd -> Etc::Passwd + * + * Provides a convenient Ruby iterator which executes a block for each entry + * in the <tt>/etc/passwd</tt> 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(obj) - VALUE obj; +etc_passwd(VALUE obj) { #ifdef HAVE_GETPWENT struct passwd *pw; - rb_secure(4); if (rb_block_given_p()) { - if (passwd_blocking) { - rb_raise(rb_eRuntimeError, "parallel passwd iteration"); - } - passwd_blocking = Qtrue; - rb_ensure(passwd_iterate, 0, passwd_ensure, 0); + each_passwd(); } - if (pw = getpwent()) { + else if ((pw = getpwent()) != 0) { return setup_passwd(pw); } #endif return Qnil; } +/* call-seq: + * Etc::Passwd.each { |struct| block } -> Etc::Passwd + * Etc::Passwd.each -> Enumerator + * + * Iterates for each entry in the <tt>/etc/passwd</tt> 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; +} + +/* call-seq: + * setpwent + * + * Resets the process of reading the <tt>/etc/passwd</tt> file, so that the + * next call to ::getpwent will return the first entry again. + */ static VALUE -etc_setpwent(obj) - VALUE obj; +etc_setpwent(VALUE obj) { #ifdef HAVE_GETPWENT setpwent(); @@ -197,9 +394,14 @@ etc_setpwent(obj) return Qnil; } +/* call-seq: + * endpwent + * + * Ends the process of scanning through the <tt>/etc/passwd</tt> file begun + * with ::getpwent, and closes the file. + */ static VALUE -etc_endpwent(obj) - VALUE obj; +etc_endpwent(VALUE obj) { #ifdef HAVE_GETPWENT endpwent(); @@ -207,14 +409,27 @@ etc_endpwent(obj) return Qnil; } +/* call-seq: + * getpwent -> Etc::Passwd + * + * Returns an entry from the <tt>/etc/passwd</tt> 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(obj) - VALUE obj; +etc_getpwent(VALUE obj) { #ifdef HAVE_GETPWENT struct passwd *pw; - if (pw = getpwent()) { + if ((pw = getpwent()) != 0) { return setup_passwd(pw); } #endif @@ -223,8 +438,7 @@ etc_getpwent(obj) #ifdef HAVE_GETGRENT static VALUE -setup_group(grp) - struct group *grp; +setup_group(struct group *grp) { VALUE mem; char **tbl; @@ -232,48 +446,82 @@ setup_group(grp) mem = rb_ary_new(); tbl = grp->gr_mem; while (*tbl) { - rb_ary_push(mem, safe_setup_str(*tbl)); + rb_ary_push(mem, safe_setup_locale_str(*tbl)); tbl++; } return rb_struct_new(sGroup, - safe_setup_str(grp->gr_name), -#ifdef HAVE_ST_GR_PASSWD + safe_setup_locale_str(grp->gr_name), +#ifdef HAVE_STRUCT_GROUP_GR_PASSWD safe_setup_str(grp->gr_passwd), #endif - INT2FIX(grp->gr_gid), + GIDT2NUM(grp->gr_gid), mem); } #endif +/* call-seq: + * getgrgid(group_id) -> Etc::Group + * + * Returns information about the group with specified integer +group_id+, + * as found in <tt>/etc/group</tt>. + * + * The information is returned as a Group struct. + * + * See the unix manpage for <code>getgrgid(3)</code> for more detail. + * + * *Example:* + * + * Etc.getgrgid(100) + * #=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]> + * + */ static VALUE -etc_getgrgid(obj, id) - VALUE obj, id; +etc_getgrgid(int argc, VALUE *argv, VALUE obj) { #ifdef HAVE_GETGRENT - int gid; + VALUE id; + gid_t gid; struct group *grp; - rb_secure(4); - gid = NUM2INT(id); + 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", 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) -> Etc::Group + * + * Returns information about the group with specified +name+, as found in + * <tt>/etc/group</tt>. + * + * The information is returned as a Group struct. + * + * See the unix manpage for <code>getgrnam(3)</code> for more detail. + * + * *Example:* + * + * Etc.getgrnam('users') + * #=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]> + * + */ static VALUE -etc_getgrnam(obj, nam) - VALUE obj, nam; +etc_getgrnam(VALUE obj, VALUE nam) { #ifdef HAVE_GETGRENT struct group *grp; + const char *p = StringValueCStr(nam); - rb_secure(4); - SafeStringValue(nam); - grp = getgrnam(RSTRING(nam)->ptr); - if (grp == 0) rb_raise(rb_eArgError, "can't find group for %s", RSTRING(nam)->ptr); + grp = getgrnam(p); + if (grp == 0) rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, nam); return setup_group(grp); #else return Qnil; @@ -281,53 +529,116 @@ etc_getgrnam(obj, nam) } #ifdef HAVE_GETGRENT -static int group_blocking = 0; +static rb_atomic_t group_blocking; static VALUE -group_ensure() +group_ensure(VALUE _) { - group_blocking = Qfalse; + endgrent(); + if (RUBY_ATOMIC_EXCHANGE(group_blocking, 0) != 1) { + rb_raise(rb_eRuntimeError, "unexpected group_blocking"); + } return Qnil; } static VALUE -group_iterate() +group_iterate(VALUE _) { struct group *pw; setgrent(); - while (pw = getgrent()) { + while ((pw = getgrent()) != 0) { rb_yield(setup_group(pw)); } - endgrent(); 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 +/* call-seq: + * group { |struct| block } + * group -> Etc::Group + * + * Provides a convenient Ruby iterator which executes a block for each entry + * in the <tt>/etc/group</tt> 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(obj) - VALUE obj; +etc_group(VALUE obj) { #ifdef HAVE_GETGRENT struct group *grp; - rb_secure(4); if (rb_block_given_p()) { - if (group_blocking) { - rb_raise(rb_eRuntimeError, "parallel group iteration"); - } - group_blocking = Qtrue; - rb_ensure(group_iterate, 0, group_ensure, 0); + each_group(); } - if (grp = getgrent()) { + else if ((grp = getgrent()) != 0) { return setup_group(grp); } #endif return Qnil; } +#ifdef HAVE_GETGRENT +/* call-seq: + * Etc::Group.each { |group| block } -> Etc::Group + * Etc::Group.each -> Enumerator + * + * Iterates for each entry in the <tt>/etc/group</tt> 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 + +/* call-seq: + * setgrent + * + * Resets the process of reading the <tt>/etc/group</tt> file, so that the + * next call to ::getgrent will return the first entry again. + */ static VALUE -etc_setgrent(obj) - VALUE obj; +etc_setgrent(VALUE obj) { #ifdef HAVE_GETGRENT setgrent(); @@ -335,9 +646,14 @@ etc_setgrent(obj) return Qnil; } +/* call-seq: + * endgrent + * + * Ends the process of scanning through the <tt>/etc/group</tt> file begun + * by ::getgrent, and closes the file. + */ static VALUE -etc_endgrent(obj) - VALUE obj; +etc_endgrent(VALUE obj) { #ifdef HAVE_GETGRENT endgrent(); @@ -345,27 +661,524 @@ etc_endgrent(obj) return Qnil; } +/* call-seq: + * getgrent -> Etc::Group + * + * Returns an entry from the <tt>/etc/group</tt> 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(obj) - VALUE obj; +etc_getgrent(VALUE obj) { #ifdef HAVE_GETGRENT struct group *gr; - if (gr = getgrent()) { + if ((gr = getgrent()) != 0) { return setup_group(gr); } #endif return Qnil; } -static VALUE mEtc; +#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); +#elif defined(LOAD_RELATIVE) +static inline VALUE +rbconfig(void) +{ + VALUE config; + rb_require("rbconfig"); + config = rb_const_get(rb_path2class("RbConfig"), rb_intern_const("CONFIG")); + Check_Type(config, T_HASH); + return config; +} +#endif +/* call-seq: + * sysconfdir -> String + * + * Returns system configuration directory. + * + * This is typically <code>"/etc"</code>, but is modified by the prefix used + * when Ruby was compiled. For example, if Ruby is built and installed in + * <tt>/usr/local</tt>, returns <code>"/usr/local/etc"</code> 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); +#elif defined(LOAD_RELATIVE) + return rb_hash_aref(rbconfig(), rb_str_new_lit("sysconfdir")); +#else + return rb_filesystem_str_new_cstr(SYSCONFDIR); +#endif +} + +/* call-seq: + * systmpdir -> String + * + * 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 +/* call-seq: + * uname -> hash + * + * 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, SYMBOL_LIT("sysname"), rb_str_new_cstr(sysname)); + release = rb_sprintf("%lu.%lu.%lu", v.dwMajorVersion, v.dwMinorVersion, v.dwBuildNumber); + rb_hash_aset(result, SYMBOL_LIT("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, SYMBOL_LIT("version"), version); + +# define GET_COMPUTER_NAME(ptr, plen) GetComputerNameExW(ComputerNameDnsFullyQualified, ptr, plen) + 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, SYMBOL_LIT("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, SYMBOL_LIT("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, SYMBOL_LIT("sysname"), rb_str_new_cstr(u.sysname)); + rb_hash_aset(result, SYMBOL_LIT("nodename"), rb_str_new_cstr(u.nodename)); + rb_hash_aset(result, SYMBOL_LIT("release"), rb_str_new_cstr(u.release)); + rb_hash_aset(result, SYMBOL_LIT("version"), rb_str_new_cstr(u.version)); + rb_hash_aset(result, SYMBOL_LIT("machine"), rb_str_new_cstr(u.machine)); +#endif + + return result; +} +#else +#define etc_uname rb_f_notimplement +#endif + +#ifdef HAVE_SYSCONF +/* call-seq: + * sysconf(name) -> Integer + * + * Returns system configuration variable using sysconf(). + * + * _name_ should be a constant under <code>Etc</code> which begins with <code>SC_</code>. + * + * 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 +/* call-seq: + * confstr(name) -> String + * + * Returns system configuration variable using confstr(). + * + * _name_ should be a constant under <code>Etc</code> which begins with <code>CS_</code>. + * + * 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 +/* call-seq: + * pathconf(name) -> Integer + * + * Returns pathname configuration variable using fpathconf(). + * + * _name_ should be a constant under <code>Etc</code> which begins with <code>PC_</code>. + * + * 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; + + name = NUM2INT(arg); + + errno = 0; + ret = fpathconf(rb_io_descriptor(io), 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 + +/* call-seq: + * nprocessors -> Integer + * + * 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 <tt>/etc</tt> directory on Unix systems. + * + * The information accessible consists of the information found in the + * <tt>/etc/passwd</tt> and <tt>/etc/group</tt> files, plus information + * about the system's temporary directory (<tt>/tmp</tt>) and configuration + * directory (<tt>/etc</tt>). + * + * 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() +Init_etc(void) { + VALUE mEtc; + mEtc = rb_define_module("Etc"); + /* The version */ + rb_define_const(mEtc, "VERSION", rb_str_new_cstr(RUBY_ETC_VERSION)); + init_constants(mEtc); + /* Ractor-safe methods */ +#ifdef HAVE_RB_EXT_RACTOR_SAFE + RB_EXT_RACTOR_SAFE(true); +#endif + 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); + + /* Non-Ractor-safe methods, see https://bugs.ruby-lang.org/issues/21115 */ +#ifdef HAVE_RB_EXT_RACTOR_SAFE + RB_EXT_RACTOR_SAFE(false); +#endif rb_define_module_function(mEtc, "getlogin", etc_getlogin, 0); rb_define_module_function(mEtc, "getpwuid", etc_getpwuid, -1); @@ -375,42 +1188,127 @@ Init_etc() 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, "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); - sPasswd = rb_struct_define("Passwd", - "name", "passwd", "uid", "gid", -#ifdef HAVE_ST_PW_GECOS - "gecos", + /* Uses RbConfig::CONFIG so does not work in a Ractor */ + rb_define_module_function(mEtc, "sysconfdir", etc_sysconfdir, 0); + + sPasswd = rb_struct_define_under(mEtc, "Passwd", + "name", +#ifdef HAVE_STRUCT_PASSWD_PW_PASSWD + "passwd", #endif - "dir", "shell", -#ifdef HAVE_ST_PW_CHANGE - "change", + "uid", + "gid", +#ifdef HAVE_STRUCT_PASSWD_PW_GECOS + "gecos", #endif -#ifdef HAVE_ST_PW_QUOTA - "quota", + "dir", + "shell", +#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE + "change", #endif -#ifdef HAVE_ST_PW_AGE - "age", +#ifdef HAVE_STRUCT_PASSWD_PW_QUOTA + "quota", #endif -#ifdef HAVE_ST_PW_CLASS - "uclass", +#ifdef HAVE_STRUCT_PASSWD_PW_AGE + "age", #endif -#ifdef HAVE_ST_PW_COMMENT - "comment", +#ifdef HAVE_STRUCT_PASSWD_PW_CLASS + "uclass", #endif -#ifdef HAVE_ST_PW_EXPIRE - "expire", +#ifdef HAVE_STRUCT_PASSWD_PW_COMMENT + "comment", #endif - NULL); - rb_global_variable(&sPasswd); +#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 <code>'x'</code> is returned if shadow passwords are in + * use. An <code>'*'</code> 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("Group", "name", "passwd", "gid", "mem", NULL); - rb_global_variable(&sGroup); + 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 <code>'x'</code> 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 + +#if defined(HAVE_GETPWENT) || defined(HAVE_GETGRENT) + (void)safe_setup_str; #endif } diff --git a/ext/etc/etc.gemspec b/ext/etc/etc.gemspec new file mode 100644 index 0000000000..0e9803dc62 --- /dev/null +++ b/ext/etc/etc.gemspec @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +version = ["", "ext/etc/"].find do |dir| + begin + break File.open(File.expand_path("../#{dir}/etc.c", __FILE__)) do |f| + f.gets "\n#define RUBY_ETC_VERSION " + f.gets[/"(.+)"/, 1] + end + rescue + next + end +end + +Gem::Specification.new do |spec| + spec.name = "etc" + spec.version = version + spec.authors = ["Yukihiro Matsumoto"] + spec.email = ["matz@ruby-lang.org"] + + spec.summary = %q{Provides access to information typically stored in UNIX /etc directory.} + spec.description = spec.summary + spec.homepage = "https://github.com/ruby/etc" + spec.licenses = ["Ruby", "BSD-2-Clause"] + + changelogs = Dir.glob("logs/ChangeLog-[1-9]*[^~]", base: __dir__) + spec.files = %w[ + BSDL + COPYING + README.md + ChangeLog + ext/etc/constdefs.h + ext/etc/etc.c + ext/etc/extconf.rb + ext/etc/mkconstants.rb + test/etc/test_etc.rb + ] + changelogs + spec.rdoc_options = ["--main", "README.md"] + spec.extra_rdoc_files = spec.files.grep_v(/\.{rb,[ch]}\z/) + spec.bindir = "exe" + spec.require_paths = ["lib"] + spec.extensions = %w{ext/etc/extconf.rb} + + spec.required_ruby_version = ">= 2.7.0" +end diff --git a/ext/etc/etc.txt b/ext/etc/etc.txt deleted file mode 100644 index 534790172c..0000000000 --- a/ext/etc/etc.txt +++ /dev/null @@ -1,72 +0,0 @@ -.\" etc.txt - -*- Indented-Text -*- created at: Fri Jul 14 00:47:15 JST 1995 - -** Etc(Module) - -The module to retrieve information under /etc directory. Available -only on UNIX platforms. All operations defined in this module are -module functions, so that you can include Etc module into your class. - -Module Function: - - getlogin - - returns login name of the user. It this fails, try getpwuid(). - - getpwnam(name) - - searches in /etc/passwd file (or equivalent database), and - returns password entry for the user. The return value is an - passwd structure, which has members described below. - - struct passwd - name # user name(string) - passwd # encrypted password(string) - uid # user ID(integer) - gid # group ID(integer) - gecos # gecos field(string) - dir # home directory(string) - shell # login shell(string) - # members below are optional - 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) - end - - See getpwnam(3) for detail. - - getpwuid([uid]) - - returns passwd entry for the specified user id. If uid is - ommitted, use the value from getuid(). See getpwuid(3) for - detail. - - getgrgid(gid) - - searches in /etc/group file (or equivalent database), and - returns group entry for the group id. The return value is an - group structure, which has members described below. - - struct group - name # group name(string) - passwd # group password(string) - gid # group ID(integer) - mem # array of the group member names - end - - See getgrgid(3) for detail. - - getgrnam(name) - - returns the group entry for the specified name. The return - value is the group structure. See getgrnam(3) for detail. - - group - - iterates over all group entries. - - passwd - - iterates over all passwd entries. diff --git a/ext/etc/etc.txt.ja b/ext/etc/etc.txt.ja deleted file mode 100644 index 2dddcfb036..0000000000 --- a/ext/etc/etc.txt.ja +++ /dev/null @@ -1,72 +0,0 @@ -.\" etc.txt.ja - -*- Indented-Text -*- created at: Fri Jul 14 00:47:15 JST 1995 - -** Etc(モジュール) - -/etcディレクトリ以下の情報を得るためのモジュール.クラスにインクルード -して使うこともできる. - -Module Function: - - getlogin - - 自分のlogin名を返す.これが失敗した場合はgetpwuid()を用いると - 良い. - - getpwnam(name) - - /etc/passwdファイル(あるいはDBMファイルやNISデータベース)を検 - 索し,nameの名前を持つpasswdエントリを返す.戻り値はpasswd構造 - 体で以下のメンバを持つ. - - struct passwd - name # ユーザ名(文字列) - passwd # パスワード(文字列) - uid # ユーザID(整数) - gid # グループID(整数) - gecos # gecosフィールド(文字列) - dir # ホームディレクトリ(文字列) - shell # ログインシェル(文字列) - # 以降のメンバはシステムによっては提供されない. - change # パスワード変更時間(整数) - quota # クォータ(整数) - age # エージ(整数) - class # ユーザアクセスクラス(文字列) - comment # コメント(文字列) - expire # アカウント有効期限(整数) - end - - 詳細はgetpwnam(3)を参照のこと. - - getpwuid([uid]) - - uidをユーザIDとするpasswdエントリを返す.戻り値はgetpwnam()と - 同様である.引数を省略した場合にはgetuid()の値を用いる.詳細は - getpwuid(3)を参照のこと. - - getgrgid(gid) - - /etc/groupファイル(あるいは…getpwnam参照)を検索し,gidをグルー - プIDとするグループエントリを返す.戻り値はgroup構造体で以下の - メンバを持つ. - - struct group - name # グループ名(文字列) - passwd # グループのパスワード(文字列) - gid # グループID(整数) - mem # グループメンバ名の配列 - end - - 詳細はgetgrgid(3)を参照のこと. - - getgrnam(name) - - nameという名前のグループエントリを返す.戻り値はgetgrgid()と同 - 様である.詳細はgetgrnam(3)を参照. - - group - - 全てのグループエントリを順にアクセスするためのイテレータ. - - passwd - - 全てのpasswdエントリを順にアクセスするためのイテレータ. diff --git a/ext/etc/extconf.rb b/ext/etc/extconf.rb index 16f2da352b..497303a4fa 100644 --- a/ext/etc/extconf.rb +++ b/ext/etc/extconf.rb @@ -1,18 +1,67 @@ +# frozen_string_literal: true require 'mkmf' +headers = [] +%w[sys/utsname.h].each {|h| + if have_header(h, headers) + headers << h + end +} have_library("sun", "getpwnam") # NIS (== YP) interface for IRIX 4 -a = have_func("getlogin") -b = have_func("getpwent") -c = have_func("getgrent") -if a or b or c +have_func("uname((struct utsname *)NULL)", headers) +have_func("getlogin") +if have_func("getpwent") have_struct_member('struct passwd', 'pw_gecos', 'pwd.h') have_struct_member('struct passwd', 'pw_change', 'pwd.h') have_struct_member('struct passwd', 'pw_quota', 'pwd.h') - have_struct_member('struct passwd', 'pw_age', 'pwd.h') + if have_struct_member('struct passwd', 'pw_age', 'pwd.h') + case what_type?('struct passwd', 'pw_age', 'pwd.h') + when "string" + f = "safe_setup_str" + when "long long" + f = "LL2NUM" + else + f = "INT2NUM" + end + $defs.push("-DPW_AGE2VAL="+f) + end have_struct_member('struct passwd', 'pw_class', 'pwd.h') have_struct_member('struct passwd', 'pw_comment', 'pwd.h') unless /cygwin/ === RUBY_PLATFORM have_struct_member('struct passwd', 'pw_expire', 'pwd.h') have_struct_member('struct passwd', 'pw_passwd', 'pwd.h') +end +if have_func("getgrent") have_struct_member('struct group', 'gr_passwd', 'grp.h') - create_makefile("etc") end + +if (sysconfdir = RbConfig::CONFIG["sysconfdir"] and + !RbConfig.expand(sysconfdir.dup, "prefix"=>"", "DESTDIR"=>"").empty?) + $defs.push("-DSYSCONFDIR=#{Shellwords.escape(sysconfdir.dump)}") +end + +have_func("sysconf") +have_func("confstr") +have_func("fpathconf") + +# for https://github.com/ruby/etc +srcdir = File.expand_path("..", __FILE__) +constdefs = "#{srcdir}/constdefs.h" +if !File.exist?(constdefs) + ruby = RbConfig.ruby + if File.file?(ruby) + ruby = [ruby] + else + require "shellwords" + ruby = Shellwords.split(ruby) + end + system(*ruby, "#{srcdir}/mkconstants.rb", "-o", constdefs) +end + +# TODO: remove when dropping 2.7 support, as exported since 3.0 +have_func('rb_deprecate_constant(Qnil, "None")') + +have_func("rb_io_descriptor", "ruby/io.h") + +$distcleanfiles << "constdefs.h" + +create_makefile("etc") diff --git a/ext/etc/mkconstants.rb b/ext/etc/mkconstants.rb new file mode 100644 index 0000000000..a766560a8a --- /dev/null +++ b/ext/etc/mkconstants.rb @@ -0,0 +1,352 @@ +# frozen_string_literal: true +require 'optparse' +require 'erb' + +C_ESC = { + "\\" => "\\\\", + '"' => '\"', + "\n" => '\n', +} + +0x00.upto(0x1f) {|ch| C_ESC[[ch].pack("C")] ||= "\\%03o" % ch } +0x7f.upto(0xff) {|ch| C_ESC[[ch].pack("C")] = "\\%03o" % ch } +C_ESC_PAT = Regexp.union(*C_ESC.keys) + +def c_str(str) + '"' + str.gsub(C_ESC_PAT) {|s| C_ESC[s]} + '"' +end + +opt = OptionParser.new + +opt.def_option('-h', 'help') { + puts opt + exit 0 +} + +opt_o = nil +opt.def_option('-o FILE', 'specify output file') {|filename| + opt_o = filename +} + +opt_H = nil +opt.def_option('-H FILE', 'specify output header file') {|filename| + opt_H = filename +} + +opt.parse! + +CONST_PREFIXES = { + 'SC' => 'for Etc.sysconf; See <tt>man sysconf</tt>', + 'CS' => 'for Etc.confstr; See <tt>man constr</tt>', + 'PC' => 'for IO#pathconf; See <tt>man fpathconf</tt>', +} + +h = {} +COMMENTS = {} + +DATA.each_line {|s| + next if /\A\s*(\#|\z)/ =~ s + name, default_value, comment = s.chomp.split(/\s+/, 3) + + default_value = nil if default_value == 'nil' + + if h.has_key? name + warn "#{$.}: warning: duplicate name: #{name}" + next + end + h[name] = default_value + if additional = CONST_PREFIXES[name[/\A_([A-Z]+)_/, 1]] + if comment&.match(/\w\z/) + comment << " " << additional + else + (comment ||= String.new) << " " << additional.sub(/\A\w/) {$&.upcase} + end + end + COMMENTS[name] = comment if comment +} +DEFS = h.to_a + +def each_const + DEFS.each {|name, default_value| + yield name, default_value + } +end + +def each_name(pat) + DEFS.each {|name, default_value| + next if pat !~ name + yield name + } +end + +erb_new = lambda do |src, trim| + ERB.new(src, trim_mode: trim) +end + +erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_decls") +% each_const {|name, default_value| +#if !defined(<%=name%>) +# if defined(HAVE_CONST_<%=name.upcase%>) +# define <%=name%> <%=name%> +%if default_value +# else +# define <%=name%> <%=default_value%> +%end +# endif +#endif +% } +EOS + +erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_defs") +% each_const {|name, default_value| +#if defined(<%=name%>) +% if comment = COMMENTS[name] + /* <%=comment%> */ +% end + rb_define_const(mod, <%=c_str name.sub(/\A_*/, '')%>, INTEGER2NUM(<%=name%>)); +#endif +% } +EOS + +header_result = erb_new.call(<<'EOS', '%').result(binding) +/* autogenerated file */ + +<%= gen_const_decls %> +EOS + +result = erb_new.call(<<'EOS', '%').result(binding) +/* autogenerated file */ + +#ifdef HAVE_LONG_LONG +#define INTEGER2NUM(n) \ + (FIXNUM_MAX < (n) ? ULL2NUM(n) : \ + FIXNUM_MIN > (LONG_LONG)(n) ? LL2NUM(n) : \ + LONG2FIX(n)) +#else +#define INTEGER2NUM(n) \ + (FIXNUM_MAX < (n) ? ULONG2NUM(n) : \ + FIXNUM_MIN > (long)(n) ? LONG2NUM(n) : \ + LONG2FIX(n)) +#endif + +static void +init_constants(VALUE mod) +{ +#if 0 + mod = rb_define_module("Etc"); +#endif +<%= gen_const_defs %> +} +EOS + +if opt_H + File.open(opt_H, 'w') {|f| + f << header_result + } +else + result = header_result + result +end + +if opt_o + File.open(opt_o, 'w') {|f| + f << result + } +else + $stdout << result +end + +__END__ +# SUSv4 +_SC_AIO_LISTIO_MAX +_SC_AIO_MAX +_SC_AIO_PRIO_DELTA_MAX +_SC_ARG_MAX +_SC_ATEXIT_MAX +_SC_BC_BASE_MAX +_SC_BC_DIM_MAX +_SC_BC_SCALE_MAX +_SC_BC_STRING_MAX +_SC_CHILD_MAX +_SC_CLK_TCK +_SC_COLL_WEIGHTS_MAX +_SC_DELAYTIMER_MAX +_SC_EXPR_NEST_MAX +_SC_HOST_NAME_MAX +_SC_IOV_MAX +_SC_LINE_MAX +_SC_LOGIN_NAME_MAX +_SC_NGROUPS_MAX +_SC_GETGR_R_SIZE_MAX +_SC_GETPW_R_SIZE_MAX +_SC_MQ_OPEN_MAX +_SC_MQ_PRIO_MAX +_SC_OPEN_MAX +_SC_ADVISORY_INFO +_SC_BARRIERS +_SC_ASYNCHRONOUS_IO +_SC_CLOCK_SELECTION +_SC_CPUTIME +_SC_FSYNC +_SC_IPV6 +_SC_JOB_CONTROL +_SC_MAPPED_FILES +_SC_MEMLOCK +_SC_MEMLOCK_RANGE +_SC_MEMORY_PROTECTION +_SC_MESSAGE_PASSING +_SC_MONOTONIC_CLOCK +_SC_PRIORITIZED_IO +_SC_PRIORITY_SCHEDULING +_SC_RAW_SOCKETS +_SC_READER_WRITER_LOCKS +_SC_REALTIME_SIGNALS +_SC_REGEXP +_SC_SAVED_IDS +_SC_SEMAPHORES +_SC_SHARED_MEMORY_OBJECTS +_SC_SHELL +_SC_SPAWN +_SC_SPIN_LOCKS +_SC_SPORADIC_SERVER +_SC_SS_REPL_MAX +_SC_SYNCHRONIZED_IO +_SC_THREAD_ATTR_STACKADDR +_SC_THREAD_ATTR_STACKSIZE +_SC_THREAD_CPUTIME +_SC_THREAD_PRIO_INHERIT +_SC_THREAD_PRIO_PROTECT +_SC_THREAD_PRIORITY_SCHEDULING +_SC_THREAD_PROCESS_SHARED +_SC_THREAD_ROBUST_PRIO_INHERIT +_SC_THREAD_ROBUST_PRIO_PROTECT +_SC_THREAD_SAFE_FUNCTIONS +_SC_THREAD_SPORADIC_SERVER +_SC_THREADS +_SC_TIMEOUTS +_SC_TIMERS +_SC_TRACE +_SC_TRACE_EVENT_FILTER +_SC_TRACE_EVENT_NAME_MAX +_SC_TRACE_INHERIT +_SC_TRACE_LOG +_SC_TRACE_NAME_MAX +_SC_TRACE_SYS_MAX +_SC_TRACE_USER_EVENT_MAX +_SC_TYPED_MEMORY_OBJECTS +_SC_VERSION +_SC_V7_ILP32_OFF32 +_SC_V7_ILP32_OFFBIG +_SC_V7_LP64_OFF64 +_SC_V7_LPBIG_OFFBIG +_SC_V6_ILP32_OFF32 +_SC_V6_ILP32_OFFBIG +_SC_V6_LP64_OFF64 +_SC_V6_LPBIG_OFFBIG +_SC_2_C_BIND +_SC_2_C_DEV +_SC_2_CHAR_TERM +_SC_2_FORT_DEV +_SC_2_FORT_RUN +_SC_2_LOCALEDEF +_SC_2_PBS +_SC_2_PBS_ACCOUNTING +_SC_2_PBS_CHECKPOINT +_SC_2_PBS_LOCATE +_SC_2_PBS_MESSAGE +_SC_2_PBS_TRACK +_SC_2_SW_DEV +_SC_2_UPE +_SC_2_VERSION +_SC_PAGE_SIZE +_SC_PAGESIZE +_SC_THREAD_DESTRUCTOR_ITERATIONS +_SC_THREAD_KEYS_MAX +_SC_THREAD_STACK_MIN +_SC_THREAD_THREADS_MAX +_SC_RE_DUP_MAX +_SC_RTSIG_MAX +_SC_SEM_NSEMS_MAX +_SC_SEM_VALUE_MAX +_SC_SIGQUEUE_MAX +_SC_STREAM_MAX +_SC_SYMLOOP_MAX +_SC_TIMER_MAX +_SC_TTY_NAME_MAX +_SC_TZNAME_MAX +_SC_XOPEN_CRYPT +_SC_XOPEN_ENH_I18N +_SC_XOPEN_REALTIME +_SC_XOPEN_REALTIME_THREADS +_SC_XOPEN_SHM +_SC_XOPEN_STREAMS +_SC_XOPEN_UNIX +_SC_XOPEN_UUCP +_SC_XOPEN_VERSION + +# non-standard +_SC_PHYS_PAGES +_SC_AVPHYS_PAGES +_SC_NPROCESSORS_CONF +_SC_NPROCESSORS_ONLN +_SC_CPUSET_SIZE + +# SUSv4 +_CS_PATH +_CS_POSIX_V7_ILP32_OFF32_CFLAGS +_CS_POSIX_V7_ILP32_OFF32_LDFLAGS +_CS_POSIX_V7_ILP32_OFF32_LIBS +_CS_POSIX_V7_ILP32_OFFBIG_CFLAGS +_CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS +_CS_POSIX_V7_ILP32_OFFBIG_LIBS +_CS_POSIX_V7_LP64_OFF64_CFLAGS +_CS_POSIX_V7_LP64_OFF64_LDFLAGS +_CS_POSIX_V7_LP64_OFF64_LIBS +_CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS +_CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS +_CS_POSIX_V7_LPBIG_OFFBIG_LIBS +_CS_POSIX_V7_THREADS_CFLAGS +_CS_POSIX_V7_THREADS_LDFLAGS +_CS_POSIX_V7_WIDTH_RESTRICTED_ENVS +_CS_V7_ENV +_CS_POSIX_V6_ILP32_OFF32_CFLAGS +_CS_POSIX_V6_ILP32_OFF32_LDFLAGS +_CS_POSIX_V6_ILP32_OFF32_LIBS +_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS +_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS +_CS_POSIX_V6_ILP32_OFFBIG_LIBS +_CS_POSIX_V6_LP64_OFF64_CFLAGS +_CS_POSIX_V6_LP64_OFF64_LDFLAGS +_CS_POSIX_V6_LP64_OFF64_LIBS +_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS +_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS +_CS_POSIX_V6_LPBIG_OFFBIG_LIBS +_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS +_CS_V6_ENV + +# non-standard +_CS_GNU_LIBC_VERSION +_CS_GNU_LIBPTHREAD_VERSION + +# SUSv4 +_PC_FILESIZEBITS +_PC_LINK_MAX +_PC_MAX_CANON +_PC_MAX_INPUT +_PC_NAME_MAX +_PC_PATH_MAX +_PC_PIPE_BUF +_PC_2_SYMLINKS +_PC_ALLOC_SIZE_MIN +_PC_REC_INCR_XFER_SIZE +_PC_REC_MAX_XFER_SIZE +_PC_REC_MIN_XFER_SIZE +_PC_REC_XFER_ALIGN +_PC_SYMLINK_MAX +_PC_CHOWN_RESTRICTED +_PC_NO_TRUNC +_PC_VDISABLE +_PC_ASYNC_IO +_PC_PRIO_IO +_PC_SYNC_IO +_PC_TIMESTAMP_RESOLUTION + |
