• 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 

Generated on Sat Jul 7 2012 15:29:08 for Ruby by  doxygen 1.7.1