Ruby  2.0.0p247(2013-06-27revision41674)
ext/etc/etc.c
Go to the documentation of this file.
00001 /************************************************
00002 
00003   etc.c -
00004 
00005   $Author: nagachika $
00006   created at: Tue Mar 22 18:39:19 JST 1994
00007 
00008 ************************************************/
00009 
00010 #include "ruby.h"
00011 #include "ruby/encoding.h"
00012 
00013 #include <sys/types.h>
00014 #ifdef HAVE_UNISTD_H
00015 #include <unistd.h>
00016 #endif
00017 
00018 #ifdef HAVE_GETPWENT
00019 #include <pwd.h>
00020 #endif
00021 
00022 #ifdef HAVE_GETGRENT
00023 #include <grp.h>
00024 #endif
00025 
00026 static VALUE sPasswd;
00027 #ifdef HAVE_GETGRENT
00028 static VALUE sGroup;
00029 #endif
00030 
00031 #ifdef _WIN32
00032 #include <shlobj.h>
00033 #ifndef CSIDL_COMMON_APPDATA
00034 #define CSIDL_COMMON_APPDATA 35
00035 #endif
00036 #endif
00037 
00038 #ifndef _WIN32
00039 char *getenv();
00040 #endif
00041 char *getlogin();
00042 
00043 /* call-seq:
00044  *      getlogin        ->  String
00045  *
00046  * Returns the short user name of the currently logged in user.
00047  * Unfortunately, it is often rather easy to fool ::getlogin.
00048  *
00049  * Avoid ::getlogin for security-related purposes.
00050  *
00051  * If ::getlogin fails, try ::getpwuid.
00052  *
00053  * See the unix manpage for <code>getpwuid(3)</code> for more detail.
00054  *
00055  * e.g.
00056  *   Etc.getlogin -> 'guest'
00057  */
00058 static VALUE
00059 etc_getlogin(VALUE obj)
00060 {
00061     char *login;
00062 
00063     rb_secure(4);
00064 #ifdef HAVE_GETLOGIN
00065     login = getlogin();
00066     if (!login) login = getenv("USER");
00067 #else
00068     login = getenv("USER");
00069 #endif
00070 
00071     if (login)
00072         return rb_tainted_str_new2(login);
00073     return Qnil;
00074 }
00075 
00076 #if defined(HAVE_GETPWENT) || defined(HAVE_GETGRENT)
00077 static VALUE
00078 safe_setup_str(const char *str)
00079 {
00080     if (str == 0) str = "";
00081     return rb_tainted_str_new2(str);
00082 }
00083 #endif
00084 
00085 #ifdef HAVE_GETPWENT
00086 static VALUE
00087 setup_passwd(struct passwd *pwd)
00088 {
00089     if (pwd == 0) rb_sys_fail("/etc/passwd");
00090     return rb_struct_new(sPasswd,
00091                          safe_setup_str(pwd->pw_name),
00092 #ifdef HAVE_ST_PW_PASSWD
00093                          safe_setup_str(pwd->pw_passwd),
00094 #endif
00095                          UIDT2NUM(pwd->pw_uid),
00096                          GIDT2NUM(pwd->pw_gid),
00097 #ifdef HAVE_ST_PW_GECOS
00098                          safe_setup_str(pwd->pw_gecos),
00099 #endif
00100                          safe_setup_str(pwd->pw_dir),
00101                          safe_setup_str(pwd->pw_shell),
00102 #ifdef HAVE_ST_PW_CHANGE
00103                          INT2NUM(pwd->pw_change),
00104 #endif
00105 #ifdef HAVE_ST_PW_QUOTA
00106                          INT2NUM(pwd->pw_quota),
00107 #endif
00108 #ifdef HAVE_ST_PW_AGE
00109                          PW_AGE2VAL(pwd->pw_age),
00110 #endif
00111 #ifdef HAVE_ST_PW_CLASS
00112                          safe_setup_str(pwd->pw_class),
00113 #endif
00114 #ifdef HAVE_ST_PW_COMMENT
00115                          safe_setup_str(pwd->pw_comment),
00116 #endif
00117 #ifdef HAVE_ST_PW_EXPIRE
00118                          INT2NUM(pwd->pw_expire),
00119 #endif
00120                          0              /*dummy*/
00121         );
00122 }
00123 #endif
00124 
00125 /* call-seq:
00126  *      getpwuid(uid)   ->  Passwd
00127  *
00128  * Returns the /etc/passwd information for the user with the given integer +uid+.
00129  *
00130  * The information is returned as a Passwd struct.
00131  *
00132  * If +uid+ is omitted, the value from <code>Passwd[:uid]</code> is returned
00133  * instead.
00134  *
00135  * See the unix manpage for <code>getpwuid(3)</code> for more detail.
00136  *
00137  * === Example:
00138  *
00139  *      Etc.getpwuid(0)
00140  *      #=> #<struct Struct::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
00141  */
00142 static VALUE
00143 etc_getpwuid(int argc, VALUE *argv, VALUE obj)
00144 {
00145 #if defined(HAVE_GETPWENT)
00146     VALUE id;
00147     rb_uid_t uid;
00148     struct passwd *pwd;
00149 
00150     rb_secure(4);
00151     if (rb_scan_args(argc, argv, "01", &id) == 1) {
00152         uid = NUM2UIDT(id);
00153     }
00154     else {
00155         uid = getuid();
00156     }
00157     pwd = getpwuid(uid);
00158     if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %d", (int)uid);
00159     return setup_passwd(pwd);
00160 #else
00161     return Qnil;
00162 #endif
00163 }
00164 
00165 /* call-seq:
00166  *      getpwnam(name)  ->  Passwd
00167  *
00168  * Returns the /etc/passwd information for the user with specified login
00169  * +name+.
00170  *
00171  * The information is returned as a Passwd struct.
00172  *
00173  * See the unix manpage for <code>getpwnam(3)</code> for more detail.
00174  *
00175  * === Example:
00176  *
00177  *      Etc.getpwnam('root')
00178  *      #=> #<struct Struct::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
00179  */
00180 static VALUE
00181 etc_getpwnam(VALUE obj, VALUE nam)
00182 {
00183 #ifdef HAVE_GETPWENT
00184     struct passwd *pwd;
00185 
00186     SafeStringValue(nam);
00187     pwd = getpwnam(RSTRING_PTR(nam));
00188     if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, nam);
00189     return setup_passwd(pwd);
00190 #else
00191     return Qnil;
00192 #endif
00193 }
00194 
00195 #ifdef HAVE_GETPWENT
00196 static int passwd_blocking = 0;
00197 static VALUE
00198 passwd_ensure(void)
00199 {
00200     endpwent();
00201     passwd_blocking = (int)Qfalse;
00202     return Qnil;
00203 }
00204 
00205 static VALUE
00206 passwd_iterate(void)
00207 {
00208     struct passwd *pw;
00209 
00210     setpwent();
00211     while (pw = getpwent()) {
00212         rb_yield(setup_passwd(pw));
00213     }
00214     return Qnil;
00215 }
00216 
00217 static void
00218 each_passwd(void)
00219 {
00220     if (passwd_blocking) {
00221         rb_raise(rb_eRuntimeError, "parallel passwd iteration");
00222     }
00223     passwd_blocking = (int)Qtrue;
00224     rb_ensure(passwd_iterate, 0, passwd_ensure, 0);
00225 }
00226 #endif
00227 
00228 /* call-seq:
00229  *      Etc.passwd { |struct| block }   ->  Passwd
00230  *      Etc.passwd                      ->  Passwd
00231  *
00232  * Provides a convenient Ruby iterator which executes a block for each entry
00233  * in the /etc/passwd file.
00234  *
00235  * The code block is passed an Passwd struct.
00236  *
00237  * See ::getpwent above for details.
00238  *
00239  * Example:
00240  *
00241  *     require 'etc'
00242  *
00243  *     Etc.passwd {|u|
00244  *       puts u.name + " = " + u.gecos
00245  *     }
00246  *
00247  */
00248 static VALUE
00249 etc_passwd(VALUE obj)
00250 {
00251 #ifdef HAVE_GETPWENT
00252     struct passwd *pw;
00253 
00254     rb_secure(4);
00255     if (rb_block_given_p()) {
00256         each_passwd();
00257     }
00258     else if (pw = getpwent()) {
00259         return setup_passwd(pw);
00260     }
00261 #endif
00262     return Qnil;
00263 }
00264 
00265 /* call-seq:
00266  *      Etc::Passwd.each { |struct| block }     ->  Passwd
00267  *      Etc::Passwd.each                        ->  Enumerator
00268  *
00269  * Iterates for each entry in the /etc/passwd file if a block is given.
00270  *
00271  * If no block is given, returns the Enumerator.
00272  *
00273  * The code block is passed an Passwd struct.
00274  *
00275  * See ::getpwent above for details.
00276  *
00277  * Example:
00278  *
00279  *     require 'etc'
00280  *
00281  *     Etc::Passwd.each {|u|
00282  *       puts u.name + " = " + u.gecos
00283  *     }
00284  *
00285  *     Etc::Passwd.collect {|u| u.gecos}
00286  *     Etc::Passwd.collect {|u| u.gecos}
00287  *
00288  */
00289 static VALUE
00290 etc_each_passwd(VALUE obj)
00291 {
00292 #ifdef HAVE_GETPWENT
00293     RETURN_ENUMERATOR(obj, 0, 0);
00294     each_passwd();
00295 #endif
00296     return obj;
00297 }
00298 
00299 /* Resets the process of reading the /etc/passwd file, so that the next call
00300  * to ::getpwent will return the first entry again.
00301  */
00302 static VALUE
00303 etc_setpwent(VALUE obj)
00304 {
00305 #ifdef HAVE_GETPWENT
00306     setpwent();
00307 #endif
00308     return Qnil;
00309 }
00310 
00311 /* Ends the process of scanning through the /etc/passwd file begun with
00312  * ::getpwent, and closes the file.
00313  */
00314 static VALUE
00315 etc_endpwent(VALUE obj)
00316 {
00317 #ifdef HAVE_GETPWENT
00318     endpwent();
00319 #endif
00320     return Qnil;
00321 }
00322 
00323 /* Returns an entry from the /etc/passwd file.
00324  *
00325  * The first time it is called it opens the file and returns the first entry;
00326  * each successive call returns the next entry, or +nil+ if the end of the file
00327  * has been reached.
00328  *
00329  * To close the file when processing is complete, call ::endpwent.
00330  *
00331  * Each entry is returned as a Passwd struct.
00332  *
00333  */
00334 static VALUE
00335 etc_getpwent(VALUE obj)
00336 {
00337 #ifdef HAVE_GETPWENT
00338     struct passwd *pw;
00339 
00340     if (pw = getpwent()) {
00341         return setup_passwd(pw);
00342     }
00343 #endif
00344     return Qnil;
00345 }
00346 
00347 #ifdef HAVE_GETGRENT
00348 static VALUE
00349 setup_group(struct group *grp)
00350 {
00351     VALUE mem;
00352     char **tbl;
00353 
00354     mem = rb_ary_new();
00355     tbl = grp->gr_mem;
00356     while (*tbl) {
00357         rb_ary_push(mem, safe_setup_str(*tbl));
00358         tbl++;
00359     }
00360     return rb_struct_new(sGroup,
00361                          safe_setup_str(grp->gr_name),
00362 #ifdef HAVE_ST_GR_PASSWD
00363                          safe_setup_str(grp->gr_passwd),
00364 #endif
00365                          GIDT2NUM(grp->gr_gid),
00366                          mem);
00367 }
00368 #endif
00369 
00370 /* call-seq:
00371  *      getgrgid(group_id)  ->  Group
00372  *
00373  * Returns information about the group with specified integer +group_id+,
00374  * as found in /etc/group.
00375  *
00376  * The information is returned as a Group struct.
00377  *
00378  * See the unix manpage for <code>getgrgid(3)</code> for more detail.
00379  *
00380  * === Example:
00381  *
00382  *      Etc.getgrgid(100)
00383  *      #=> #<struct Struct::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
00384  *
00385  */
00386 static VALUE
00387 etc_getgrgid(int argc, VALUE *argv, VALUE obj)
00388 {
00389 #ifdef HAVE_GETGRENT
00390     VALUE id;
00391     gid_t gid;
00392     struct group *grp;
00393 
00394     rb_secure(4);
00395     if (rb_scan_args(argc, argv, "01", &id) == 1) {
00396         gid = NUM2GIDT(id);
00397     }
00398     else {
00399         gid = getgid();
00400     }
00401     grp = getgrgid(gid);
00402     if (grp == 0) rb_raise(rb_eArgError, "can't find group for %d", (int)gid);
00403     return setup_group(grp);
00404 #else
00405     return Qnil;
00406 #endif
00407 }
00408 
00409 /* call-seq:
00410  *      getgrnam(name)  ->  Group
00411  *
00412  * Returns information about the group with specified +name+, as found in
00413  * /etc/group.
00414  *
00415  * The information is returned as a Group struct.
00416  *
00417  * See the unix manpage for <code>getgrnam(3)</code> for more detail.
00418  *
00419  * === Example:
00420  *
00421  *      Etc.getgrnam('users')
00422  *      #=> #<struct Struct::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
00423  *
00424  */
00425 static VALUE
00426 etc_getgrnam(VALUE obj, VALUE nam)
00427 {
00428 #ifdef HAVE_GETGRENT
00429     struct group *grp;
00430 
00431     rb_secure(4);
00432     SafeStringValue(nam);
00433     grp = getgrnam(RSTRING_PTR(nam));
00434     if (grp == 0) rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, nam);
00435     return setup_group(grp);
00436 #else
00437     return Qnil;
00438 #endif
00439 }
00440 
00441 #ifdef HAVE_GETGRENT
00442 static int group_blocking = 0;
00443 static VALUE
00444 group_ensure(void)
00445 {
00446     endgrent();
00447     group_blocking = (int)Qfalse;
00448     return Qnil;
00449 }
00450 
00451 
00452 static VALUE
00453 group_iterate(void)
00454 {
00455     struct group *pw;
00456 
00457     setgrent();
00458     while (pw = getgrent()) {
00459         rb_yield(setup_group(pw));
00460     }
00461     return Qnil;
00462 }
00463 
00464 static void
00465 each_group(void)
00466 {
00467     if (group_blocking) {
00468         rb_raise(rb_eRuntimeError, "parallel group iteration");
00469     }
00470     group_blocking = (int)Qtrue;
00471     rb_ensure(group_iterate, 0, group_ensure, 0);
00472 }
00473 #endif
00474 
00475 /* Provides a convenient Ruby iterator which executes a block for each entry
00476  * in the /etc/group file.
00477  *
00478  * The code block is passed an Group struct.
00479  *
00480  * See ::getgrent above for details.
00481  *
00482  * Example:
00483  *
00484  *     require 'etc'
00485  *
00486  *     Etc.group {|g|
00487  *       puts g.name + ": " + g.mem.join(', ')
00488  *     }
00489  *
00490  */
00491 static VALUE
00492 etc_group(VALUE obj)
00493 {
00494 #ifdef HAVE_GETGRENT
00495     struct group *grp;
00496 
00497     rb_secure(4);
00498     if (rb_block_given_p()) {
00499         each_group();
00500     }
00501     else if (grp = getgrent()) {
00502         return setup_group(grp);
00503     }
00504 #endif
00505     return Qnil;
00506 }
00507 
00508 #ifdef HAVE_GETGRENT
00509 /* call-seq:
00510  *      Etc::Group.each { |group| block }   ->  obj
00511  *      Etc::Group.each                     ->  Enumerator
00512  *
00513  * Iterates for each entry in the /etc/group file if a block is given.
00514  *
00515  * If no block is given, returns the Enumerator.
00516  *
00517  * The code block is passed a Group struct.
00518  *
00519  * Example:
00520  *
00521  *     require 'etc'
00522  *
00523  *     Etc::Group.each {|g|
00524  *       puts g.name + ": " + g.mem.join(', ')
00525  *     }
00526  *
00527  *     Etc::Group.collect {|g| g.name}
00528  *     Etc::Group.select {|g| !g.mem.empty?}
00529  *
00530  */
00531 static VALUE
00532 etc_each_group(VALUE obj)
00533 {
00534     RETURN_ENUMERATOR(obj, 0, 0);
00535     each_group();
00536     return obj;
00537 }
00538 #endif
00539 
00540 /* Resets the process of reading the /etc/group file, so that the next call
00541  * to ::getgrent will return the first entry again.
00542  */
00543 static VALUE
00544 etc_setgrent(VALUE obj)
00545 {
00546 #ifdef HAVE_GETGRENT
00547     setgrent();
00548 #endif
00549     return Qnil;
00550 }
00551 
00552 /* Ends the process of scanning through the /etc/group file begun by
00553  * ::getgrent, and closes the file.
00554  */
00555 static VALUE
00556 etc_endgrent(VALUE obj)
00557 {
00558 #ifdef HAVE_GETGRENT
00559     endgrent();
00560 #endif
00561     return Qnil;
00562 }
00563 
00564 /* Returns an entry from the /etc/group file.
00565  *
00566  * The first time it is called it opens the file and returns the first entry;
00567  * each successive call returns the next entry, or +nil+ if the end of the file
00568  * has been reached.
00569  *
00570  * To close the file when processing is complete, call ::endgrent.
00571  *
00572  * Each entry is returned as a Group struct
00573  */
00574 static VALUE
00575 etc_getgrent(VALUE obj)
00576 {
00577 #ifdef HAVE_GETGRENT
00578     struct group *gr;
00579 
00580     if (gr = getgrent()) {
00581         return setup_group(gr);
00582     }
00583 #endif
00584     return Qnil;
00585 }
00586 
00587 #define numberof(array) (sizeof(array) / sizeof(*(array)))
00588 
00589 #ifdef _WIN32
00590 VALUE rb_w32_special_folder(int type);
00591 UINT rb_w32_system_tmpdir(WCHAR *path, UINT len);
00592 VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
00593 #endif
00594 
00595 /*
00596  * Returns system configuration directory.
00597  *
00598  * This is typically "/etc", but is modified by the prefix used when Ruby was
00599  * compiled. For example, if Ruby is built and installed in /usr/local, returns
00600  * "/usr/local/etc".
00601  */
00602 static VALUE
00603 etc_sysconfdir(VALUE obj)
00604 {
00605 #ifdef _WIN32
00606     return rb_w32_special_folder(CSIDL_COMMON_APPDATA);
00607 #else
00608     return rb_filesystem_str_new_cstr(SYSCONFDIR);
00609 #endif
00610 }
00611 
00612 /*
00613  * Returns system temporary directory; typically "/tmp".
00614  */
00615 static VALUE
00616 etc_systmpdir(void)
00617 {
00618     VALUE tmpdir;
00619 #ifdef _WIN32
00620     WCHAR path[_MAX_PATH];
00621     UINT len = rb_w32_system_tmpdir(path, numberof(path));
00622     if (!len) return Qnil;
00623     tmpdir = rb_w32_conv_from_wchar(path, rb_filesystem_encoding());
00624 #else
00625     tmpdir = rb_filesystem_str_new_cstr("/tmp");
00626 #endif
00627     FL_UNSET(tmpdir, FL_TAINT|FL_UNTRUSTED);
00628     return tmpdir;
00629 }
00630 
00631 /*
00632  * The Etc module provides access to information typically stored in
00633  * files in the /etc directory on Unix systems.
00634  *
00635  * The information accessible consists of the information found in the
00636  * /etc/passwd and /etc/group files, plus information about the system's
00637  * temporary directory (/tmp) and configuration directory (/etc).
00638  *
00639  * The Etc module provides a more reliable way to access information about
00640  * the logged in user than environment variables such as +$USER+.
00641  *
00642  * == Example:
00643  *
00644  *     require 'etc'
00645  *
00646  *     login = Etc.getlogin
00647  *     info = Etc.getpwnam(login)
00648  *     username = info.gecos.split(/,/).first
00649  *     puts "Hello #{username}, I see your login name is #{login}"
00650  *
00651  * Note that the methods provided by this module are not always secure.
00652  * It should be used for informational purposes, and not for security.
00653  *
00654  * All operations defined in this module are class methods, so that you can
00655  * include the Etc module into your class.
00656  */
00657 void
00658 Init_etc(void)
00659 {
00660     VALUE mEtc;
00661 
00662     mEtc = rb_define_module("Etc");
00663     rb_define_module_function(mEtc, "getlogin", etc_getlogin, 0);
00664 
00665     rb_define_module_function(mEtc, "getpwuid", etc_getpwuid, -1);
00666     rb_define_module_function(mEtc, "getpwnam", etc_getpwnam, 1);
00667     rb_define_module_function(mEtc, "setpwent", etc_setpwent, 0);
00668     rb_define_module_function(mEtc, "endpwent", etc_endpwent, 0);
00669     rb_define_module_function(mEtc, "getpwent", etc_getpwent, 0);
00670     rb_define_module_function(mEtc, "passwd", etc_passwd, 0);
00671 
00672     rb_define_module_function(mEtc, "getgrgid", etc_getgrgid, -1);
00673     rb_define_module_function(mEtc, "getgrnam", etc_getgrnam, 1);
00674     rb_define_module_function(mEtc, "group", etc_group, 0);
00675     rb_define_module_function(mEtc, "setgrent", etc_setgrent, 0);
00676     rb_define_module_function(mEtc, "endgrent", etc_endgrent, 0);
00677     rb_define_module_function(mEtc, "getgrent", etc_getgrent, 0);
00678     rb_define_module_function(mEtc, "sysconfdir", etc_sysconfdir, 0);
00679     rb_define_module_function(mEtc, "systmpdir", etc_systmpdir, 0);
00680 
00681     sPasswd =  rb_struct_define("Passwd",
00682                                 "name", "passwd", "uid", "gid",
00683 #ifdef HAVE_ST_PW_GECOS
00684                                 "gecos",
00685 #endif
00686                                 "dir", "shell",
00687 #ifdef HAVE_ST_PW_CHANGE
00688                                 "change",
00689 #endif
00690 #ifdef HAVE_ST_PW_QUOTA
00691                                 "quota",
00692 #endif
00693 #ifdef HAVE_ST_PW_AGE
00694                                 "age",
00695 #endif
00696 #ifdef HAVE_ST_PW_CLASS
00697                                 "uclass",
00698 #endif
00699 #ifdef HAVE_ST_PW_COMMENT
00700                                 "comment",
00701 #endif
00702 #ifdef HAVE_ST_PW_EXPIRE
00703                                 "expire",
00704 #endif
00705                                 NULL);
00706     /* Define-const: Passwd
00707      *
00708      * Passwd is a Struct that contains the following members:
00709      *
00710      * name::
00711      *      contains the short login name of the user as a String.
00712      * passwd::
00713      *      contains the encrypted password of the user as a String.
00714      *      an 'x' is returned if shadow passwords are in use. An '*' is returned
00715      *      if the user cannot log in using a password.
00716      * uid::
00717      *      contains the integer user ID (uid) of the user.
00718      * gid::
00719      *      contains the integer group ID (gid) of the user's primary group.
00720      * dir::
00721      *      contains the path to the home directory of the user as a String.
00722      * shell::
00723      *      contains the path to the login shell of the user as a String.
00724      *
00725      * === The following members below are optional, and must be compiled with special flags:
00726      *
00727      * gecos::
00728      *     contains a longer String description of the user, such as
00729      *     a full name. Some Unix systems provide structured information in the
00730      *     gecos field, but this is system-dependent.
00731      *     must be compiled with +HAVE_ST_PW_GECOS+
00732      * change::
00733      *     password change time(integer) must be compiled with +HAVE_ST_PW_CHANGE+
00734      * quota::
00735      *     quota value(integer) must be compiled with +HAVE_ST_PW_QUOTA+
00736      * age::
00737      *     password age(integer) must be compiled with +HAVE_ST_PW_AGE+
00738      * class::
00739      *     user access class(string) must be compiled with +HAVE_ST_PW_CLASS+
00740      * comment::
00741      *     comment(string) must be compiled with +HAVE_ST_PW_COMMENT+
00742      * expire::
00743      *      account expiration time(integer) must be compiled with +HAVE_ST_PW_EXPIRE+
00744      */
00745     rb_define_const(mEtc, "Passwd", sPasswd);
00746     rb_extend_object(sPasswd, rb_mEnumerable);
00747     rb_define_singleton_method(sPasswd, "each", etc_each_passwd, 0);
00748 
00749 #ifdef HAVE_GETGRENT
00750     sGroup = rb_struct_define("Group", "name",
00751 #ifdef HAVE_ST_GR_PASSWD
00752                               "passwd",
00753 #endif
00754                               "gid", "mem", NULL);
00755 
00756     /* Define-const: Group
00757      *
00758      * Group is a Struct that is only available when compiled with +HAVE_GETGRENT+.
00759      *
00760      * The struct contains the following members:
00761      *
00762      * name::
00763      *      contains the name of the group as a String.
00764      * passwd::
00765      *      contains the encrypted password as a String. An 'x' is
00766      *      returned if password access to the group is not available; an empty
00767      *      string is returned if no password is needed to obtain membership of
00768      *      the group.
00769      *
00770      *      Must be compiled with +HAVE_ST_GR_PASSWD+.
00771      * gid::
00772      *      contains the group's numeric ID as an integer.
00773      * mem::
00774      *      is an Array of Strings containing the short login names of the
00775      *      members of the group.
00776      */
00777     rb_define_const(mEtc, "Group", sGroup);
00778     rb_extend_object(sGroup, rb_mEnumerable);
00779     rb_define_singleton_method(sGroup, "each", etc_each_group, 0);
00780 #endif
00781 }
00782