1/************************************************ 2 3 etc.c - 4 5 $Author: nagachika $ 6 created at: Tue Mar 22 18:39:19 JST 1994 7 8************************************************/ 9 10#include "ruby.h" 11#include "ruby/encoding.h" 12 13#include <sys/types.h> 14#ifdef HAVE_UNISTD_H 15#include <unistd.h> 16#endif 17 18#ifdef HAVE_GETPWENT 19#include <pwd.h> 20#endif 21 22#ifdef HAVE_GETGRENT 23#include <grp.h> 24#endif 25 26static VALUE sPasswd; 27#ifdef HAVE_GETGRENT 28static VALUE sGroup; 29#endif 30 31#ifdef _WIN32 32#include <shlobj.h> 33#ifndef CSIDL_COMMON_APPDATA 34#define CSIDL_COMMON_APPDATA 35 35#endif 36#endif 37 38#ifndef _WIN32 39char *getenv(); 40#endif 41char *getlogin(); 42 43/* call-seq: 44 * getlogin -> String 45 * 46 * Returns the short user name of the currently logged in user. 47 * Unfortunately, it is often rather easy to fool ::getlogin. 48 * 49 * Avoid ::getlogin for security-related purposes. 50 * 51 * If ::getlogin fails, try ::getpwuid. 52 * 53 * See the unix manpage for <code>getpwuid(3)</code> for more detail. 54 * 55 * e.g. 56 * Etc.getlogin -> 'guest' 57 */ 58static VALUE 59etc_getlogin(VALUE obj) 60{ 61 char *login; 62 63 rb_secure(4); 64#ifdef HAVE_GETLOGIN 65 login = getlogin(); 66 if (!login) login = getenv("USER"); 67#else 68 login = getenv("USER"); 69#endif 70 71 if (login) 72 return rb_tainted_str_new2(login); 73 return Qnil; 74} 75 76#if defined(HAVE_GETPWENT) || defined(HAVE_GETGRENT) 77static VALUE 78safe_setup_str(const char *str) 79{ 80 if (str == 0) str = ""; 81 return rb_tainted_str_new2(str); 82} 83#endif 84 85#ifdef HAVE_GETPWENT 86static VALUE 87setup_passwd(struct passwd *pwd) 88{ 89 if (pwd == 0) rb_sys_fail("/etc/passwd"); 90 return rb_struct_new(sPasswd, 91 safe_setup_str(pwd->pw_name), 92#ifdef HAVE_ST_PW_PASSWD 93 safe_setup_str(pwd->pw_passwd), 94#endif 95 UIDT2NUM(pwd->pw_uid), 96 GIDT2NUM(pwd->pw_gid), 97#ifdef HAVE_ST_PW_GECOS 98 safe_setup_str(pwd->pw_gecos), 99#endif 100 safe_setup_str(pwd->pw_dir), 101 safe_setup_str(pwd->pw_shell), 102#ifdef HAVE_ST_PW_CHANGE 103 INT2NUM(pwd->pw_change), 104#endif 105#ifdef HAVE_ST_PW_QUOTA 106 INT2NUM(pwd->pw_quota), 107#endif 108#ifdef HAVE_ST_PW_AGE 109 PW_AGE2VAL(pwd->pw_age), 110#endif 111#ifdef HAVE_ST_PW_CLASS 112 safe_setup_str(pwd->pw_class), 113#endif 114#ifdef HAVE_ST_PW_COMMENT 115 safe_setup_str(pwd->pw_comment), 116#endif 117#ifdef HAVE_ST_PW_EXPIRE 118 INT2NUM(pwd->pw_expire), 119#endif 120 0 /*dummy*/ 121 ); 122} 123#endif 124 125/* call-seq: 126 * getpwuid(uid) -> Passwd 127 * 128 * Returns the /etc/passwd information for the user with the given integer +uid+. 129 * 130 * The information is returned as a Passwd struct. 131 * 132 * If +uid+ is omitted, the value from <code>Passwd[:uid]</code> is returned 133 * instead. 134 * 135 * See the unix manpage for <code>getpwuid(3)</code> for more detail. 136 * 137 * === Example: 138 * 139 * Etc.getpwuid(0) 140 * #=> #<struct Struct::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash"> 141 */ 142static VALUE 143etc_getpwuid(int argc, VALUE *argv, VALUE obj) 144{ 145#if defined(HAVE_GETPWENT) 146 VALUE id; 147 rb_uid_t uid; 148 struct passwd *pwd; 149 150 rb_secure(4); 151 if (rb_scan_args(argc, argv, "01", &id) == 1) { 152 uid = NUM2UIDT(id); 153 } 154 else { 155 uid = getuid(); 156 } 157 pwd = getpwuid(uid); 158 if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %d", (int)uid); 159 return setup_passwd(pwd); 160#else 161 return Qnil; 162#endif 163} 164 165/* call-seq: 166 * getpwnam(name) -> Passwd 167 * 168 * Returns the /etc/passwd information for the user with specified login 169 * +name+. 170 * 171 * The information is returned as a Passwd struct. 172 * 173 * See the unix manpage for <code>getpwnam(3)</code> for more detail. 174 * 175 * === Example: 176 * 177 * Etc.getpwnam('root') 178 * #=> #<struct Struct::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash"> 179 */ 180static VALUE 181etc_getpwnam(VALUE obj, VALUE nam) 182{ 183#ifdef HAVE_GETPWENT 184 struct passwd *pwd; 185 186 SafeStringValue(nam); 187 pwd = getpwnam(RSTRING_PTR(nam)); 188 if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, nam); 189 return setup_passwd(pwd); 190#else 191 return Qnil; 192#endif 193} 194 195#ifdef HAVE_GETPWENT 196static int passwd_blocking = 0; 197static VALUE 198passwd_ensure(void) 199{ 200 endpwent(); 201 passwd_blocking = (int)Qfalse; 202 return Qnil; 203} 204 205static VALUE 206passwd_iterate(void) 207{ 208 struct passwd *pw; 209 210 setpwent(); 211 while (pw = getpwent()) { 212 rb_yield(setup_passwd(pw)); 213 } 214 return Qnil; 215} 216 217static void 218each_passwd(void) 219{ 220 if (passwd_blocking) { 221 rb_raise(rb_eRuntimeError, "parallel passwd iteration"); 222 } 223 passwd_blocking = (int)Qtrue; 224 rb_ensure(passwd_iterate, 0, passwd_ensure, 0); 225} 226#endif 227 228/* call-seq: 229 * Etc.passwd { |struct| block } -> Passwd 230 * Etc.passwd -> Passwd 231 * 232 * Provides a convenient Ruby iterator which executes a block for each entry 233 * in the /etc/passwd file. 234 * 235 * The code block is passed an Passwd struct. 236 * 237 * See ::getpwent above for details. 238 * 239 * Example: 240 * 241 * require 'etc' 242 * 243 * Etc.passwd {|u| 244 * puts u.name + " = " + u.gecos 245 * } 246 * 247 */ 248static VALUE 249etc_passwd(VALUE obj) 250{ 251#ifdef HAVE_GETPWENT 252 struct passwd *pw; 253 254 rb_secure(4); 255 if (rb_block_given_p()) { 256 each_passwd(); 257 } 258 else if (pw = getpwent()) { 259 return setup_passwd(pw); 260 } 261#endif 262 return Qnil; 263} 264 265/* call-seq: 266 * Etc::Passwd.each { |struct| block } -> Passwd 267 * Etc::Passwd.each -> Enumerator 268 * 269 * Iterates for each entry in the /etc/passwd file if a block is given. 270 * 271 * If no block is given, returns the Enumerator. 272 * 273 * The code block is passed an Passwd struct. 274 * 275 * See ::getpwent above for details. 276 * 277 * Example: 278 * 279 * require 'etc' 280 * 281 * Etc::Passwd.each {|u| 282 * puts u.name + " = " + u.gecos 283 * } 284 * 285 * Etc::Passwd.collect {|u| u.gecos} 286 * Etc::Passwd.collect {|u| u.gecos} 287 * 288 */ 289static VALUE 290etc_each_passwd(VALUE obj) 291{ 292#ifdef HAVE_GETPWENT 293 RETURN_ENUMERATOR(obj, 0, 0); 294 each_passwd(); 295#endif 296 return obj; 297} 298 299/* Resets the process of reading the /etc/passwd file, so that the next call 300 * to ::getpwent will return the first entry again. 301 */ 302static VALUE 303etc_setpwent(VALUE obj) 304{ 305#ifdef HAVE_GETPWENT 306 setpwent(); 307#endif 308 return Qnil; 309} 310 311/* Ends the process of scanning through the /etc/passwd file begun with 312 * ::getpwent, and closes the file. 313 */ 314static VALUE 315etc_endpwent(VALUE obj) 316{ 317#ifdef HAVE_GETPWENT 318 endpwent(); 319#endif 320 return Qnil; 321} 322 323/* Returns an entry from the /etc/passwd file. 324 * 325 * The first time it is called it opens the file and returns the first entry; 326 * each successive call returns the next entry, or +nil+ if the end of the file 327 * has been reached. 328 * 329 * To close the file when processing is complete, call ::endpwent. 330 * 331 * Each entry is returned as a Passwd struct. 332 * 333 */ 334static VALUE 335etc_getpwent(VALUE obj) 336{ 337#ifdef HAVE_GETPWENT 338 struct passwd *pw; 339 340 if (pw = getpwent()) { 341 return setup_passwd(pw); 342 } 343#endif 344 return Qnil; 345} 346 347#ifdef HAVE_GETGRENT 348static VALUE 349setup_group(struct group *grp) 350{ 351 VALUE mem; 352 char **tbl; 353 354 mem = rb_ary_new(); 355 tbl = grp->gr_mem; 356 while (*tbl) { 357 rb_ary_push(mem, safe_setup_str(*tbl)); 358 tbl++; 359 } 360 return rb_struct_new(sGroup, 361 safe_setup_str(grp->gr_name), 362#ifdef HAVE_ST_GR_PASSWD 363 safe_setup_str(grp->gr_passwd), 364#endif 365 GIDT2NUM(grp->gr_gid), 366 mem); 367} 368#endif 369 370/* call-seq: 371 * getgrgid(group_id) -> Group 372 * 373 * Returns information about the group with specified integer +group_id+, 374 * as found in /etc/group. 375 * 376 * The information is returned as a Group struct. 377 * 378 * See the unix manpage for <code>getgrgid(3)</code> for more detail. 379 * 380 * === Example: 381 * 382 * Etc.getgrgid(100) 383 * #=> #<struct Struct::Group name="users", passwd="x", gid=100, mem=["meta", "root"]> 384 * 385 */ 386static VALUE 387etc_getgrgid(int argc, VALUE *argv, VALUE obj) 388{ 389#ifdef HAVE_GETGRENT 390 VALUE id; 391 gid_t gid; 392 struct group *grp; 393 394 rb_secure(4); 395 if (rb_scan_args(argc, argv, "01", &id) == 1) { 396 gid = NUM2GIDT(id); 397 } 398 else { 399 gid = getgid(); 400 } 401 grp = getgrgid(gid); 402 if (grp == 0) rb_raise(rb_eArgError, "can't find group for %d", (int)gid); 403 return setup_group(grp); 404#else 405 return Qnil; 406#endif 407} 408 409/* call-seq: 410 * getgrnam(name) -> Group 411 * 412 * Returns information about the group with specified +name+, as found in 413 * /etc/group. 414 * 415 * The information is returned as a Group struct. 416 * 417 * See the unix manpage for <code>getgrnam(3)</code> for more detail. 418 * 419 * === Example: 420 * 421 * Etc.getgrnam('users') 422 * #=> #<struct Struct::Group name="users", passwd="x", gid=100, mem=["meta", "root"]> 423 * 424 */ 425static VALUE 426etc_getgrnam(VALUE obj, VALUE nam) 427{ 428#ifdef HAVE_GETGRENT 429 struct group *grp; 430 431 rb_secure(4); 432 SafeStringValue(nam); 433 grp = getgrnam(RSTRING_PTR(nam)); 434 if (grp == 0) rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, nam); 435 return setup_group(grp); 436#else 437 return Qnil; 438#endif 439} 440 441#ifdef HAVE_GETGRENT 442static int group_blocking = 0; 443static VALUE 444group_ensure(void) 445{ 446 endgrent(); 447 group_blocking = (int)Qfalse; 448 return Qnil; 449} 450 451 452static VALUE 453group_iterate(void) 454{ 455 struct group *pw; 456 457 setgrent(); 458 while (pw = getgrent()) { 459 rb_yield(setup_group(pw)); 460 } 461 return Qnil; 462} 463 464static void 465each_group(void) 466{ 467 if (group_blocking) { 468 rb_raise(rb_eRuntimeError, "parallel group iteration"); 469 } 470 group_blocking = (int)Qtrue; 471 rb_ensure(group_iterate, 0, group_ensure, 0); 472} 473#endif 474 475/* Provides a convenient Ruby iterator which executes a block for each entry 476 * in the /etc/group file. 477 * 478 * The code block is passed an Group struct. 479 * 480 * See ::getgrent above for details. 481 * 482 * Example: 483 * 484 * require 'etc' 485 * 486 * Etc.group {|g| 487 * puts g.name + ": " + g.mem.join(', ') 488 * } 489 * 490 */ 491static VALUE 492etc_group(VALUE obj) 493{ 494#ifdef HAVE_GETGRENT 495 struct group *grp; 496 497 rb_secure(4); 498 if (rb_block_given_p()) { 499 each_group(); 500 } 501 else if (grp = getgrent()) { 502 return setup_group(grp); 503 } 504#endif 505 return Qnil; 506} 507 508#ifdef HAVE_GETGRENT 509/* call-seq: 510 * Etc::Group.each { |group| block } -> obj 511 * Etc::Group.each -> Enumerator 512 * 513 * Iterates for each entry in the /etc/group file if a block is given. 514 * 515 * If no block is given, returns the Enumerator. 516 * 517 * The code block is passed a Group struct. 518 * 519 * Example: 520 * 521 * require 'etc' 522 * 523 * Etc::Group.each {|g| 524 * puts g.name + ": " + g.mem.join(', ') 525 * } 526 * 527 * Etc::Group.collect {|g| g.name} 528 * Etc::Group.select {|g| !g.mem.empty?} 529 * 530 */ 531static VALUE 532etc_each_group(VALUE obj) 533{ 534 RETURN_ENUMERATOR(obj, 0, 0); 535 each_group(); 536 return obj; 537} 538#endif 539 540/* Resets the process of reading the /etc/group file, so that the next call 541 * to ::getgrent will return the first entry again. 542 */ 543static VALUE 544etc_setgrent(VALUE obj) 545{ 546#ifdef HAVE_GETGRENT 547 setgrent(); 548#endif 549 return Qnil; 550} 551 552/* Ends the process of scanning through the /etc/group file begun by 553 * ::getgrent, and closes the file. 554 */ 555static VALUE 556etc_endgrent(VALUE obj) 557{ 558#ifdef HAVE_GETGRENT 559 endgrent(); 560#endif 561 return Qnil; 562} 563 564/* Returns an entry from the /etc/group file. 565 * 566 * The first time it is called it opens the file and returns the first entry; 567 * each successive call returns the next entry, or +nil+ if the end of the file 568 * has been reached. 569 * 570 * To close the file when processing is complete, call ::endgrent. 571 * 572 * Each entry is returned as a Group struct 573 */ 574static VALUE 575etc_getgrent(VALUE obj) 576{ 577#ifdef HAVE_GETGRENT 578 struct group *gr; 579 580 if (gr = getgrent()) { 581 return setup_group(gr); 582 } 583#endif 584 return Qnil; 585} 586 587#define numberof(array) (sizeof(array) / sizeof(*(array))) 588 589#ifdef _WIN32 590VALUE rb_w32_special_folder(int type); 591UINT rb_w32_system_tmpdir(WCHAR *path, UINT len); 592VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc); 593#endif 594 595/* 596 * Returns system configuration directory. 597 * 598 * This is typically "/etc", but is modified by the prefix used when Ruby was 599 * compiled. For example, if Ruby is built and installed in /usr/local, returns 600 * "/usr/local/etc". 601 */ 602static VALUE 603etc_sysconfdir(VALUE obj) 604{ 605#ifdef _WIN32 606 return rb_w32_special_folder(CSIDL_COMMON_APPDATA); 607#else 608 return rb_filesystem_str_new_cstr(SYSCONFDIR); 609#endif 610} 611 612/* 613 * Returns system temporary directory; typically "/tmp". 614 */ 615static VALUE 616etc_systmpdir(void) 617{ 618 VALUE tmpdir; 619#ifdef _WIN32 620 WCHAR path[_MAX_PATH]; 621 UINT len = rb_w32_system_tmpdir(path, numberof(path)); 622 if (!len) return Qnil; 623 tmpdir = rb_w32_conv_from_wchar(path, rb_filesystem_encoding()); 624#else 625 #ifndef MAXPATHLEN 626 #define MAXPATHLEN 1024 627 #endif 628 char path[MAXPATHLEN]; 629 size_t len = 0; 630 631 len = confstr(_CS_DARWIN_USER_TEMP_DIR, path, sizeof(path)); 632 if (len > 0) { 633 tmpdir = rb_filesystem_str_new_cstr(path); 634 } else { 635 tmpdir = rb_filesystem_str_new_cstr("/tmp"); 636 } 637#endif 638 FL_UNSET(tmpdir, FL_TAINT|FL_UNTRUSTED); 639 return tmpdir; 640} 641 642/* 643 * The Etc module provides access to information typically stored in 644 * files in the /etc directory on Unix systems. 645 * 646 * The information accessible consists of the information found in the 647 * /etc/passwd and /etc/group files, plus information about the system's 648 * temporary directory (/tmp) and configuration directory (/etc). 649 * 650 * The Etc module provides a more reliable way to access information about 651 * the logged in user than environment variables such as +$USER+. 652 * 653 * == Example: 654 * 655 * require 'etc' 656 * 657 * login = Etc.getlogin 658 * info = Etc.getpwnam(login) 659 * username = info.gecos.split(/,/).first 660 * puts "Hello #{username}, I see your login name is #{login}" 661 * 662 * Note that the methods provided by this module are not always secure. 663 * It should be used for informational purposes, and not for security. 664 * 665 * All operations defined in this module are class methods, so that you can 666 * include the Etc module into your class. 667 */ 668void 669Init_etc(void) 670{ 671 VALUE mEtc; 672 673 mEtc = rb_define_module("Etc"); 674 rb_define_module_function(mEtc, "getlogin", etc_getlogin, 0); 675 676 rb_define_module_function(mEtc, "getpwuid", etc_getpwuid, -1); 677 rb_define_module_function(mEtc, "getpwnam", etc_getpwnam, 1); 678 rb_define_module_function(mEtc, "setpwent", etc_setpwent, 0); 679 rb_define_module_function(mEtc, "endpwent", etc_endpwent, 0); 680 rb_define_module_function(mEtc, "getpwent", etc_getpwent, 0); 681 rb_define_module_function(mEtc, "passwd", etc_passwd, 0); 682 683 rb_define_module_function(mEtc, "getgrgid", etc_getgrgid, -1); 684 rb_define_module_function(mEtc, "getgrnam", etc_getgrnam, 1); 685 rb_define_module_function(mEtc, "group", etc_group, 0); 686 rb_define_module_function(mEtc, "setgrent", etc_setgrent, 0); 687 rb_define_module_function(mEtc, "endgrent", etc_endgrent, 0); 688 rb_define_module_function(mEtc, "getgrent", etc_getgrent, 0); 689 rb_define_module_function(mEtc, "sysconfdir", etc_sysconfdir, 0); 690 rb_define_module_function(mEtc, "systmpdir", etc_systmpdir, 0); 691 692 sPasswd = rb_struct_define("Passwd", 693 "name", "passwd", "uid", "gid", 694#ifdef HAVE_ST_PW_GECOS 695 "gecos", 696#endif 697 "dir", "shell", 698#ifdef HAVE_ST_PW_CHANGE 699 "change", 700#endif 701#ifdef HAVE_ST_PW_QUOTA 702 "quota", 703#endif 704#ifdef HAVE_ST_PW_AGE 705 "age", 706#endif 707#ifdef HAVE_ST_PW_CLASS 708 "uclass", 709#endif 710#ifdef HAVE_ST_PW_COMMENT 711 "comment", 712#endif 713#ifdef HAVE_ST_PW_EXPIRE 714 "expire", 715#endif 716 NULL); 717 /* Define-const: Passwd 718 * 719 * Passwd is a Struct that contains the following members: 720 * 721 * name:: 722 * contains the short login name of the user as a String. 723 * passwd:: 724 * contains the encrypted password of the user as a String. 725 * an 'x' is returned if shadow passwords are in use. An '*' is returned 726 * if the user cannot log in using a password. 727 * uid:: 728 * contains the integer user ID (uid) of the user. 729 * gid:: 730 * contains the integer group ID (gid) of the user's primary group. 731 * dir:: 732 * contains the path to the home directory of the user as a String. 733 * shell:: 734 * contains the path to the login shell of the user as a String. 735 * 736 * === The following members below are optional, and must be compiled with special flags: 737 * 738 * gecos:: 739 * contains a longer String description of the user, such as 740 * a full name. Some Unix systems provide structured information in the 741 * gecos field, but this is system-dependent. 742 * must be compiled with +HAVE_ST_PW_GECOS+ 743 * change:: 744 * password change time(integer) must be compiled with +HAVE_ST_PW_CHANGE+ 745 * quota:: 746 * quota value(integer) must be compiled with +HAVE_ST_PW_QUOTA+ 747 * age:: 748 * password age(integer) must be compiled with +HAVE_ST_PW_AGE+ 749 * class:: 750 * user access class(string) must be compiled with +HAVE_ST_PW_CLASS+ 751 * comment:: 752 * comment(string) must be compiled with +HAVE_ST_PW_COMMENT+ 753 * expire:: 754 * account expiration time(integer) must be compiled with +HAVE_ST_PW_EXPIRE+ 755 */ 756 rb_define_const(mEtc, "Passwd", sPasswd); 757 rb_extend_object(sPasswd, rb_mEnumerable); 758 rb_define_singleton_method(sPasswd, "each", etc_each_passwd, 0); 759 760#ifdef HAVE_GETGRENT 761 sGroup = rb_struct_define("Group", "name", 762#ifdef HAVE_ST_GR_PASSWD 763 "passwd", 764#endif 765 "gid", "mem", NULL); 766 767 /* Define-const: Group 768 * 769 * Group is a Struct that is only available when compiled with +HAVE_GETGRENT+. 770 * 771 * The struct contains the following members: 772 * 773 * name:: 774 * contains the name of the group as a String. 775 * passwd:: 776 * contains the encrypted password as a String. An 'x' is 777 * returned if password access to the group is not available; an empty 778 * string is returned if no password is needed to obtain membership of 779 * the group. 780 * 781 * Must be compiled with +HAVE_ST_GR_PASSWD+. 782 * gid:: 783 * contains the group's numeric ID as an integer. 784 * mem:: 785 * is an Array of Strings containing the short login names of the 786 * members of the group. 787 */ 788 rb_define_const(mEtc, "Group", sGroup); 789 rb_extend_object(sGroup, rb_mEnumerable); 790 rb_define_singleton_method(sGroup, "each", etc_each_group, 0); 791#endif 792} 793