Ruby 1.9.2p136(2010-12-25revision30365)

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

# ext/etc/etc.c

Go to the documentation of this file.

    00001 /************************************************
    00002
    00003   etc.c -
    00004
    00005   $Author: naruse $
    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 /* Returns the short user name of the currently logged in user.
    00044  * Unfortunately, it is often rather easy to fool getlogin().
    00045  * Avoid getlogin() for security-related purposes.
    00046  *
    00047  * e.g.
    00048  *   Etc.getlogin -> 'guest'
    00049  */
    00050 static VALUE
    00051 etc_getlogin(VALUE obj)
    00052 {
    00053     char *login;
    00054
    00055     rb_secure(4);
    00056 #ifdef HAVE_GETLOGIN
    00057     login = getlogin();
    00058     if (!login) login = getenv("USER");
    00059 #else
    00060     login = getenv("USER");
    00061 #endif
    00062
    00063     if (login)
    00064         return rb_tainted_str_new2(login);
    00065     return Qnil;
    00066 }
    00067
    00068 #if defined(HAVE_GETPWENT) || defined(HAVE_GETGRENT)
    00069 static VALUE
    00070 safe_setup_str(const char *str)
    00071 {
    00072     if (str == 0) str = "";
    00073     return rb_tainted_str_new2(str);
    00074 }
    00075 #endif
    00076
    00077 #ifdef HAVE_GETPWENT
    00078 static VALUE
    00079 setup_passwd(struct passwd *pwd)
    00080 {
    00081     if (pwd == 0) rb_sys_fail("/etc/passwd");
    00082     return rb_struct_new(sPasswd,
    00083                          safe_setup_str(pwd->pw_name),
    00084 #ifdef HAVE_ST_PW_PASSWD
    00085                          safe_setup_str(pwd->pw_passwd),
    00086 #endif
    00087                          UIDT2NUM(pwd->pw_uid),
    00088                          GIDT2NUM(pwd->pw_gid),
    00089 #ifdef HAVE_ST_PW_GECOS
    00090                          safe_setup_str(pwd->pw_gecos),
    00091 #endif
    00092                          safe_setup_str(pwd->pw_dir),
    00093                          safe_setup_str(pwd->pw_shell),
    00094 #ifdef HAVE_ST_PW_CHANGE
    00095                          INT2NUM(pwd->pw_change),
    00096 #endif
    00097 #ifdef HAVE_ST_PW_QUOTA
    00098                          INT2NUM(pwd->pw_quota),
    00099 #endif
    00100 #ifdef HAVE_ST_PW_AGE
    00101                          PW_AGE2VAL(pwd->pw_age),
    00102 #endif
    00103 #ifdef HAVE_ST_PW_CLASS
    00104                          safe_setup_str(pwd->pw_class),
    00105 #endif
    00106 #ifdef HAVE_ST_PW_COMMENT
    00107                          safe_setup_str(pwd->pw_comment),
    00108 #endif
    00109 #ifdef HAVE_ST_PW_EXPIRE
    00110                          INT2NUM(pwd->pw_expire),
    00111 #endif
    00112                          0              /*dummy*/
    00113         );
    00114 }
    00115 #endif
    00116
    00117 /* Returns the /etc/passwd information for the user with specified integer
    00118  * user id (uid).
    00119  *
    00120  * The information is returned as a Struct::Passwd; see getpwent above for
    00121  * details.
    00122  *
    00123  * e.g.  * Etc.getpwuid(0) -> #<struct Struct::Passwd name="root",
    00124  * passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
    00125  */
    00126 static VALUE
    00127 etc_getpwuid(int argc, VALUE *argv, VALUE obj)
    00128 {
    00129 #if defined(HAVE_GETPWENT)
    00130     VALUE id;
    00131     rb_uid_t uid;
    00132     struct passwd *pwd;
    00133
    00134     rb_secure(4);
    00135     if (rb_scan_args(argc, argv, "01", &id) == 1) {
    00136         uid = NUM2UIDT(id);
    00137     }
    00138     else {
    00139         uid = getuid();
    00140     }
    00141     pwd = getpwuid(uid);
    00142     if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %d", (int)uid);
    00143     return setup_passwd(pwd);
    00144 #else
    00145     return Qnil;
    00146 #endif
    00147 }
    00148
    00149 /* Returns the /etc/passwd information for the user with specified login name.
    00150  *
    00151  * The information is returned as a Struct::Passwd; see getpwent above for
    00152  * details.
    00153  *
    00154  * e.g.  * Etc.getpwnam('root') -> #<struct Struct::Passwd name="root",
    00155  * passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
    00156  */
    00157 static VALUE
    00158 etc_getpwnam(VALUE obj, VALUE nam)
    00159 {
    00160 #ifdef HAVE_GETPWENT
    00161     struct passwd *pwd;
    00162
    00163     SafeStringValue(nam);
    00164     pwd = getpwnam(RSTRING_PTR(nam));
    00165     if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %s", RSTRING_PTR(nam));
    00166     return setup_passwd(pwd);
    00167 #else
    00168     return Qnil;
    00169 #endif
    00170 }
    00171
    00172 #ifdef HAVE_GETPWENT
    00173 static int passwd_blocking = 0;
    00174 static VALUE
    00175 passwd_ensure(void)
    00176 {
    00177     passwd_blocking = (int)Qfalse;
    00178     return Qnil;
    00179 }
    00180
    00181 static VALUE
    00182 passwd_iterate(void)
    00183 {
    00184     struct passwd *pw;
    00185
    00186     setpwent();
    00187     while (pw = getpwent()) {
    00188         rb_yield(setup_passwd(pw));
    00189     }
    00190     endpwent();
    00191     return Qnil;
    00192 }
    00193
    00194 static void
    00195 each_passwd(void)
    00196 {
    00197     if (passwd_blocking) {
    00198         rb_raise(rb_eRuntimeError, "parallel passwd iteration");
    00199     }
    00200     passwd_blocking = (int)Qtrue;
    00201     rb_ensure(passwd_iterate, 0, passwd_ensure, 0);
    00202 }
    00203 #endif
    00204
    00205 /* Provides a convenient Ruby iterator which executes a block for each entry
    00206  * in the /etc/passwd file.
    00207  *
    00208  * The code block is passed an Struct::Passwd struct; see getpwent above for
    00209  * details.
    00210  *
    00211  * Example:
    00212  *
    00213  *     require 'etc'
    00214  *
    00215  *     Etc.passwd {|u|
    00216  *       puts u.name + " = " + u.gecos
    00217  *     }
    00218  *
    00219  */
    00220 static VALUE
    00221 etc_passwd(VALUE obj)
    00222 {
    00223 #ifdef HAVE_GETPWENT
    00224     struct passwd *pw;
    00225
    00226     rb_secure(4);
    00227     if (rb_block_given_p()) {
    00228         each_passwd();
    00229     }
    00230     else if (pw = getpwent()) {
    00231         return setup_passwd(pw);
    00232     }
    00233 #endif
    00234     return Qnil;
    00235 }
    00236
    00237 /* Iterates for each entry in the /etc/passwd file if a block is given.
    00238  * If no block is given, returns the enumerator.
    00239  *
    00240  * The code block is passed an Struct::Passwd struct; see getpwent above for
    00241  * details.
    00242  *
    00243  * Example:
    00244  *
    00245  *     require 'etc'
    00246  *
    00247  *     Etc::Passwd.each {|u|
    00248  *       puts u.name + " = " + u.gecos
    00249  *     }
    00250  *
    00251  *     Etc::Passwd.collect {|u| u.gecos}
    00252  *     Etc::Passwd.collect {|u| u.gecos}
    00253  *
    00254  */
    00255 static VALUE
    00256 etc_each_passwd(VALUE obj)
    00257 {
    00258 #ifdef HAVE_GETPWENT
    00259     RETURN_ENUMERATOR(obj, 0, 0);
    00260     each_passwd();
    00261 #endif
    00262     return obj;
    00263 }
    00264
    00265 /* Resets the process of reading the /etc/passwd file, so that the next call
    00266  * to getpwent will return the first entry again.
    00267  */
    00268 static VALUE
    00269 etc_setpwent(VALUE obj)
    00270 {
    00271 #ifdef HAVE_GETPWENT
    00272     setpwent();
    00273 #endif
    00274     return Qnil;
    00275 }
    00276
    00277 /* Ends the process of scanning through the /etc/passwd file begun with
    00278  * getpwent, and closes the file.
    00279  */
    00280 static VALUE
    00281 etc_endpwent(VALUE obj)
    00282 {
    00283 #ifdef HAVE_GETPWENT
    00284     endpwent();
    00285 #endif
    00286     return Qnil;
    00287 }
    00288
    00289 /* Returns an entry from the /etc/passwd file. The first time it is called it
    00290  * opens the file and returns the first entry; each successive call returns
    00291  * the next entry, or nil if the end of the file has been reached.
    00292  *
    00293  * To close the file when processing is complete, call endpwent.
    00294  *
    00295  * Each entry is returned as a Struct::Passwd:
    00296  *
    00297  * - Passwd#name contains the short login name of the user as a String.
    00298  *
    00299  * - Passwd#passwd contains the encrypted password of the user as a String.
    00300  *   an 'x' is returned if shadow passwords are in use. An '*' is returned
    00301  *   if the user cannot log in using a password.
    00302  *
    00303  * - Passwd#uid contains the integer user ID (uid) of the user.
    00304  *
    00305  * - Passwd#gid contains the integer group ID (gid) of the user's primary group.
    00306  *
    00307  * - Passwd#gecos contains a longer String description of the user, such as
    00308  *   a full name. Some Unix systems provide structured information in the
    00309  *   gecos field, but this is system-dependent.
    00310  *
    00311  * - Passwd#dir contains the path to the home directory of the user as a String.
    00312  *
    00313  * - Passwd#shell contains the path to the login shell of the user as a String.
    00314  */
    00315 static VALUE
    00316 etc_getpwent(VALUE obj)
    00317 {
    00318 #ifdef HAVE_GETPWENT
    00319     struct passwd *pw;
    00320
    00321     if (pw = getpwent()) {
    00322         return setup_passwd(pw);
    00323     }
    00324 #endif
    00325     return Qnil;
    00326 }
    00327
    00328 #ifdef HAVE_GETGRENT
    00329 static VALUE
    00330 setup_group(struct group *grp)
    00331 {
    00332     VALUE mem;
    00333     char **tbl;
    00334
    00335     mem = rb_ary_new();
    00336     tbl = grp->gr_mem;
    00337     while (*tbl) {
    00338         rb_ary_push(mem, safe_setup_str(*tbl));
    00339         tbl++;
    00340     }
    00341     return rb_struct_new(sGroup,
    00342                          safe_setup_str(grp->gr_name),
    00343 #ifdef HAVE_ST_GR_PASSWD
    00344                          safe_setup_str(grp->gr_passwd),
    00345 #endif
    00346                          GIDT2NUM(grp->gr_gid),
    00347                          mem);
    00348 }
    00349 #endif
    00350
    00351 /* Returns information about the group with specified integer group id (gid),
    00352  * as found in /etc/group.
    00353  *
    00354  * The information is returned as a Struct::Group; see getgrent above for
    00355  * details.
    00356  *
    00357  * e.g.  Etc.getgrgid(100) -> #<struct Struct::Group name="users", passwd="x",
    00358  * gid=100, mem=["meta", "root"]>
    00359  *
    00360  */
    00361 static VALUE
    00362 etc_getgrgid(int argc, VALUE *argv, VALUE obj)
    00363 {
    00364 #ifdef HAVE_GETGRENT
    00365     VALUE id;
    00366     gid_t gid;
    00367     struct group *grp;
    00368
    00369     rb_secure(4);
    00370     if (rb_scan_args(argc, argv, "01", &id) == 1) {
    00371         gid = NUM2GIDT(id);
    00372     }
    00373     else {
    00374         gid = getgid();
    00375     }
    00376     grp = getgrgid(gid);
    00377     if (grp == 0) rb_raise(rb_eArgError, "can't find group for %d", (int)gid);
    00378     return setup_group(grp);
    00379 #else
    00380     return Qnil;
    00381 #endif
    00382 }
    00383
    00384 /* Returns information about the group with specified String name, as found
    00385  * in /etc/group.
    00386  *
    00387  * The information is returned as a Struct::Group; see getgrent above for
    00388  * details.
    00389  *
    00390  * e.g.  Etc.getgrnam('users') -> #<struct Struct::Group name="users",
    00391  * passwd="x", gid=100, mem=["meta", "root"]>
    00392  *
    00393  */
    00394 static VALUE
    00395 etc_getgrnam(VALUE obj, VALUE nam)
    00396 {
    00397 #ifdef HAVE_GETGRENT
    00398     struct group *grp;
    00399
    00400     rb_secure(4);
    00401     SafeStringValue(nam);
    00402     grp = getgrnam(RSTRING_PTR(nam));
    00403     if (grp == 0) rb_raise(rb_eArgError, "can't find group for %s", RSTRING_PTR(nam));
    00404     return setup_group(grp);
    00405 #else
    00406     return Qnil;
    00407 #endif
    00408 }
    00409
    00410 #ifdef HAVE_GETGRENT
    00411 static int group_blocking = 0;
    00412 static VALUE
    00413 group_ensure(void)
    00414 {
    00415     group_blocking = (int)Qfalse;
    00416     return Qnil;
    00417 }
    00418
    00419 static VALUE
    00420 group_iterate(void)
    00421 {
    00422     struct group *pw;
    00423
    00424     setgrent();
    00425     while (pw = getgrent()) {
    00426         rb_yield(setup_group(pw));
    00427     }
    00428     endgrent();
    00429     return Qnil;
    00430 }
    00431
    00432 static void
    00433 each_group(void)
    00434 {
    00435     if (group_blocking) {
    00436         rb_raise(rb_eRuntimeError, "parallel group iteration");
    00437     }
    00438     group_blocking = (int)Qtrue;
    00439     rb_ensure(group_iterate, 0, group_ensure, 0);
    00440 }
    00441 #endif
    00442
    00443 /* Provides a convenient Ruby iterator which executes a block for each entry
    00444  * in the /etc/group file.
    00445  *
    00446  * The code block is passed an Struct::Group struct; see getgrent above for
    00447  * details.
    00448  *
    00449  * Example:
    00450  *
    00451  *     require 'etc'
    00452  *
    00453  *     Etc.group {|g|
    00454  *       puts g.name + ": " + g.mem.join(', ')
    00455  *     }
    00456  *
    00457  */
    00458 static VALUE
    00459 etc_group(VALUE obj)
    00460 {
    00461 #ifdef HAVE_GETGRENT
    00462     struct group *grp;
    00463
    00464     rb_secure(4);
    00465     if (rb_block_given_p()) {
    00466         each_group();
    00467     }
    00468     else if (grp = getgrent()) {
    00469         return setup_group(grp);
    00470     }
    00471 #endif
    00472     return Qnil;
    00473 }
    00474
    00475 #ifdef HAVE_GETGRENT
    00476 /* Iterates for each entry in the /etc/group file if a block is given.
    00477  * If no block is given, returns the enumerator.
    00478  *
    00479  * The code block is passed an Struct::Group struct; see getpwent above for
    00480  * details.
    00481  *
    00482  * Example:
    00483  *
    00484  *     require 'etc'
    00485  *
    00486  *     Etc::Group.each {|g|
    00487  *       puts g.name + ": " + g.mem.join(', ')
    00488  *     }
    00489  *
    00490  *     Etc::Group.collect {|g| g.name}
    00491  *     Etc::Group.select {|g| !g.mem.empty?}
    00492  *
    00493  */
    00494 static VALUE
    00495 etc_each_group(VALUE obj)
    00496 {
    00497     RETURN_ENUMERATOR(obj, 0, 0);
    00498     each_group();
    00499     return obj;
    00500 }
    00501 #endif
    00502
    00503 /* Resets the process of reading the /etc/group file, so that the next call
    00504  * to getgrent will return the first entry again.
    00505  */
    00506 static VALUE
    00507 etc_setgrent(VALUE obj)
    00508 {
    00509 #ifdef HAVE_GETGRENT
    00510     setgrent();
    00511 #endif
    00512     return Qnil;
    00513 }
    00514
    00515 /* Ends the process of scanning through the /etc/group file begun by
    00516  * getgrent, and closes the file.
    00517  */
    00518 static VALUE
    00519 etc_endgrent(VALUE obj)
    00520 {
    00521 #ifdef HAVE_GETGRENT
    00522     endgrent();
    00523 #endif
    00524     return Qnil;
    00525 }
    00526
    00527 /* Returns an entry from the /etc/group file. The first time it is called it
    00528  * opens the file and returns the first entry; each successive call returns
    00529  * the next entry, or nil if the end of the file has been reached.
    00530  *
    00531  * To close the file when processing is complete, call endgrent.
    00532  *
    00533  * Each entry is returned as a Struct::Group:
    00534  *
    00535  * - Group#name contains the name of the group as a String.
    00536  *
    00537  * - Group#passwd contains the encrypted password as a String. An 'x' is
    00538  *   returned if password access to the group is not available; an empty
    00539  *   string is returned if no password is needed to obtain membership of
    00540  *   the group.
    00541  *
    00542  * - Group#gid contains the group's numeric ID as an integer.
    00543  *
    00544  * - Group#mem is an Array of Strings containing the short login names of the
    00545  *   members of the group.
    00546  */
    00547 static VALUE
    00548 etc_getgrent(VALUE obj)
    00549 {
    00550 #ifdef HAVE_GETGRENT
    00551     struct group *gr;
    00552
    00553     if (gr = getgrent()) {
    00554         return setup_group(gr);
    00555     }
    00556 #endif
    00557     return Qnil;
    00558 }
    00559
    00560 #define numberof(array) (sizeof(array) / sizeof(*array))
    00561
    00562 #ifdef _WIN32
    00563 VALUE rb_w32_special_folder(int type);
    00564 UINT rb_w32_system_tmpdir(WCHAR *path, UINT len);
    00565 VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
    00566 #endif
    00567
    00568 /*
    00569  * Returns system configuration directory.
    00570  */
    00571 static VALUE
    00572 etc_sysconfdir(VALUE obj)
    00573 {
    00574 #ifdef _WIN32
    00575     return rb_w32_special_folder(CSIDL_COMMON_APPDATA);
    00576 #else
    00577     return rb_filesystem_str_new_cstr(SYSCONFDIR);
    00578 #endif
    00579 }
    00580
    00581 /*
    00582  * Returns system temporary directory.
    00583  */
    00584 static VALUE
    00585 etc_systmpdir(void)
    00586 {
    00587 #ifdef _WIN32
    00588     WCHAR path[_MAX_PATH];
    00589     UINT len = rb_w32_system_tmpdir(path, numberof(path));
    00590     if (!len) return Qnil;
    00591     return rb_w32_conv_from_wchar(path, rb_filesystem_encoding());
    00592 #else
    00593     return rb_filesystem_str_new_cstr("/tmp");
    00594 #endif
    00595 }
    00596
    00597 /*
    00598  * The etc module provides access to information from the running OS.
    00599  *
    00600  * Documented by mathew <meta@pobox.com>.
    00601  */
    00602 void
    00603 Init_etc(void)
    00604 {
    00605     VALUE mEtc;
    00606
    00607     mEtc = rb_define_module("Etc");
    00608     rb_define_module_function(mEtc, "getlogin", etc_getlogin, 0);
    00609
    00610     rb_define_module_function(mEtc, "getpwuid", etc_getpwuid, -1);
    00611     rb_define_module_function(mEtc, "getpwnam", etc_getpwnam, 1);
    00612     rb_define_module_function(mEtc, "setpwent", etc_setpwent, 0);
    00613     rb_define_module_function(mEtc, "endpwent", etc_endpwent, 0);
    00614     rb_define_module_function(mEtc, "getpwent", etc_getpwent, 0);
    00615     rb_define_module_function(mEtc, "passwd", etc_passwd, 0);
    00616
    00617     rb_define_module_function(mEtc, "getgrgid", etc_getgrgid, -1);
    00618     rb_define_module_function(mEtc, "getgrnam", etc_getgrnam, 1);
    00619     rb_define_module_function(mEtc, "group", etc_group, 0);
    00620     rb_define_module_function(mEtc, "setgrent", etc_setgrent, 0);
    00621     rb_define_module_function(mEtc, "endgrent", etc_endgrent, 0);
    00622     rb_define_module_function(mEtc, "getgrent", etc_getgrent, 0);
    00623     rb_define_module_function(mEtc, "sysconfdir", etc_sysconfdir, 0);
    00624     rb_define_module_function(mEtc, "systmpdir", etc_systmpdir, 0);
    00625
    00626     sPasswd =  rb_struct_define("Passwd",
    00627                                 "name", "passwd", "uid", "gid",
    00628 #ifdef HAVE_ST_PW_GECOS
    00629                                 "gecos",
    00630 #endif
    00631                                 "dir", "shell",
    00632 #ifdef HAVE_ST_PW_CHANGE
    00633                                 "change",
    00634 #endif
    00635 #ifdef HAVE_ST_PW_QUOTA
    00636                                 "quota",
    00637 #endif
    00638 #ifdef HAVE_ST_PW_AGE
    00639                                 "age",
    00640 #endif
    00641 #ifdef HAVE_ST_PW_CLASS
    00642                                 "uclass",
    00643 #endif
    00644 #ifdef HAVE_ST_PW_COMMENT
    00645                                 "comment",
    00646 #endif
    00647 #ifdef HAVE_ST_PW_EXPIRE
    00648                                 "expire",
    00649 #endif
    00650                                 NULL);
    00651     rb_define_const(mEtc, "Passwd", sPasswd);
    00652     rb_extend_object(sPasswd, rb_mEnumerable);
    00653     rb_define_singleton_method(sPasswd, "each", etc_each_passwd, 0);
    00654
    00655 #ifdef HAVE_GETGRENT
    00656     sGroup = rb_struct_define("Group", "name",
    00657 #ifdef HAVE_ST_GR_PASSWD
    00658                               "passwd",
    00659 #endif
    00660                               "gid", "mem", NULL);
    00661
    00662     rb_define_const(mEtc, "Group", sGroup);
    00663     rb_extend_object(sGroup, rb_mEnumerable);
    00664     rb_define_singleton_method(sGroup, "each", etc_each_group, 0);
    00665 #endif
    00666 }
    00667