Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)
etc.c
Go to the documentation of this file.
1/************************************************
2
3 etc.c -
4
5 $Author$
6 created at: Tue Mar 22 18:39:19 JST 1994
7
8************************************************/
9
10#include "ruby.h"
11#include "ruby/encoding.h"
12#include "ruby/io.h"
13
14#include <sys/types.h>
15#ifdef HAVE_UNISTD_H
16#include <unistd.h>
17#endif
18
19#ifdef HAVE_GETPWENT
20#include <pwd.h>
21#endif
22
23#ifdef HAVE_GETGRENT
24#include <grp.h>
25#endif
26
27#include <errno.h>
28
29#ifdef HAVE_SYS_UTSNAME_H
30#include <sys/utsname.h>
31#endif
32
33#ifdef HAVE_SCHED_GETAFFINITY
34#include <sched.h>
35#endif
36
37static VALUE sPasswd;
38#ifdef HAVE_GETGRENT
39static VALUE sGroup;
40#endif
41
42#ifdef _WIN32
43#include <shlobj.h>
44#ifndef CSIDL_COMMON_APPDATA
45#define CSIDL_COMMON_APPDATA 35
46#endif
47#define HAVE_UNAME 1
48#endif
49
50#ifndef _WIN32
51char *getenv();
52#endif
53char *getlogin();
54
55#define RUBY_ETC_VERSION "1.3.0"
56
57#ifdef HAVE_RB_DEPRECATE_CONSTANT
58void rb_deprecate_constant(VALUE mod, const char *name);
59#else
60# define rb_deprecate_constant(mod,name) ((void)(mod),(void)(name))
61#endif
62
63#include "constdefs.h"
64
65#ifdef HAVE_RUBY_ATOMIC_H
66# include "ruby/atomic.h"
67#else
68typedef int rb_atomic_t;
69# define RUBY_ATOMIC_CAS(var, oldval, newval) \
70 ((var) == (oldval) ? ((var) = (newval), (oldval)) : (var))
71# define RUBY_ATOMIC_EXCHANGE(var, newval) \
72 atomic_exchange(&var, newval)
73static inline rb_atomic_t
74atomic_exchange(volatile rb_atomic_t *var, rb_atomic_t newval)
75{
76 rb_atomic_t oldval = *var;
77 *var = newval;
78 return oldval;
79}
80#endif
81
82/* call-seq:
83 * getlogin -> String
84 *
85 * Returns the short user name of the currently logged in user.
86 * Unfortunately, it is often rather easy to fool ::getlogin.
87 *
88 * Avoid ::getlogin for security-related purposes.
89 *
90 * If ::getlogin fails, try ::getpwuid.
91 *
92 * See the unix manpage for <code>getpwuid(3)</code> for more detail.
93 *
94 * e.g.
95 * Etc.getlogin -> 'guest'
96 */
97static VALUE
98etc_getlogin(VALUE obj)
99{
100 char *login;
101
102#ifdef HAVE_GETLOGIN
103 login = getlogin();
104 if (!login) login = getenv("USER");
105#else
106 login = getenv("USER");
107#endif
108
109 if (login) {
110#ifdef _WIN32
111 rb_encoding *extenc = rb_utf8_encoding();
112#else
114#endif
115 return rb_external_str_new_with_enc(login, strlen(login), extenc);
116 }
117
118 return Qnil;
119}
120
121#if defined(HAVE_GETPWENT) || defined(HAVE_GETGRENT)
122static VALUE
123safe_setup_str(const char *str)
124{
125 if (str == 0) str = "";
126 return rb_str_new2(str);
127}
128
129static VALUE
130safe_setup_locale_str(const char *str)
131{
132 if (str == 0) str = "";
134}
135
136static VALUE
137safe_setup_filesystem_str(const char *str)
138{
139 if (str == 0) str = "";
141}
142#endif
143
144#ifdef HAVE_GETPWENT
145# ifdef __APPLE__
146# define PW_TIME2VAL(t) INT2NUM((int)(t))
147# else
148# define PW_TIME2VAL(t) TIMET2NUM(t)
149# endif
150
151static VALUE
152setup_passwd(struct passwd *pwd)
153{
154 if (pwd == 0) rb_sys_fail("/etc/passwd");
155 return rb_struct_new(sPasswd,
156 safe_setup_locale_str(pwd->pw_name),
157#ifdef HAVE_STRUCT_PASSWD_PW_PASSWD
158 safe_setup_str(pwd->pw_passwd),
159#endif
160 UIDT2NUM(pwd->pw_uid),
161 GIDT2NUM(pwd->pw_gid),
162#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
163 safe_setup_locale_str(pwd->pw_gecos),
164#endif
165 safe_setup_filesystem_str(pwd->pw_dir),
166 safe_setup_filesystem_str(pwd->pw_shell),
167#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE
168 PW_TIME2VAL(pwd->pw_change),
169#endif
170#ifdef HAVE_STRUCT_PASSWD_PW_QUOTA
171 INT2NUM(pwd->pw_quota),
172#endif
173#ifdef HAVE_STRUCT_PASSWD_PW_AGE
174 PW_AGE2VAL(pwd->pw_age),
175#endif
176#ifdef HAVE_STRUCT_PASSWD_PW_CLASS
177 safe_setup_locale_str(pwd->pw_class),
178#endif
179#ifdef HAVE_STRUCT_PASSWD_PW_COMMENT
180 safe_setup_locale_str(pwd->pw_comment),
181#endif
182#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
183 PW_TIME2VAL(pwd->pw_expire),
184#endif
185 0 /*dummy*/
186 );
187}
188#endif
189
190/* call-seq:
191 * getpwuid(uid) -> Passwd
192 *
193 * Returns the /etc/passwd information for the user with the given integer +uid+.
194 *
195 * The information is returned as a Passwd struct.
196 *
197 * If +uid+ is omitted, the value from <code>Passwd[:uid]</code> is returned
198 * instead.
199 *
200 * See the unix manpage for <code>getpwuid(3)</code> for more detail.
201 *
202 * === Example:
203 *
204 * Etc.getpwuid(0)
205 * #=> #<struct Etc::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
206 */
207static VALUE
208etc_getpwuid(int argc, VALUE *argv, VALUE obj)
209{
210#if defined(HAVE_GETPWENT)
211 VALUE id;
212 rb_uid_t uid;
213 struct passwd *pwd;
214
215 if (rb_scan_args(argc, argv, "01", &id) == 1) {
216 uid = NUM2UIDT(id);
217 }
218 else {
219 uid = getuid();
220 }
221 pwd = getpwuid(uid);
222 if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %d", (int)uid);
223 return setup_passwd(pwd);
224#else
225 return Qnil;
226#endif
227}
228
229/* call-seq:
230 * getpwnam(name) -> Passwd
231 *
232 * Returns the /etc/passwd information for the user with specified login
233 * +name+.
234 *
235 * The information is returned as a Passwd struct.
236 *
237 * See the unix manpage for <code>getpwnam(3)</code> for more detail.
238 *
239 * === Example:
240 *
241 * Etc.getpwnam('root')
242 * #=> #<struct Etc::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
243 */
244static VALUE
245etc_getpwnam(VALUE obj, VALUE nam)
246{
247#ifdef HAVE_GETPWENT
248 struct passwd *pwd;
249 const char *p = StringValueCStr(nam);
250
251 pwd = getpwnam(p);
252 if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, nam);
253 return setup_passwd(pwd);
254#else
255 return Qnil;
256#endif
257}
258
259#ifdef HAVE_GETPWENT
260static rb_atomic_t passwd_blocking;
261static VALUE
262passwd_ensure(VALUE _)
263{
264 endpwent();
265 if (RUBY_ATOMIC_EXCHANGE(passwd_blocking, 0) != 1) {
266 rb_raise(rb_eRuntimeError, "unexpected passwd_blocking");
267 }
268 return Qnil;
269}
270
271static VALUE
272passwd_iterate(VALUE _)
273{
274 struct passwd *pw;
275
276 setpwent();
277 while ((pw = getpwent()) != 0) {
278 rb_yield(setup_passwd(pw));
279 }
280 return Qnil;
281}
282
283static void
284each_passwd(void)
285{
286 if (RUBY_ATOMIC_CAS(passwd_blocking, 0, 1)) {
287 rb_raise(rb_eRuntimeError, "parallel passwd iteration");
288 }
289 rb_ensure(passwd_iterate, 0, passwd_ensure, 0);
290}
291#endif
292
293/* call-seq:
294 * Etc.passwd { |struct| block } -> Passwd
295 * Etc.passwd -> Passwd
296 *
297 * Provides a convenient Ruby iterator which executes a block for each entry
298 * in the /etc/passwd file.
299 *
300 * The code block is passed an Passwd struct.
301 *
302 * See ::getpwent above for details.
303 *
304 * Example:
305 *
306 * require 'etc'
307 *
308 * Etc.passwd {|u|
309 * puts u.name + " = " + u.gecos
310 * }
311 *
312 */
313static VALUE
314etc_passwd(VALUE obj)
315{
316#ifdef HAVE_GETPWENT
317 struct passwd *pw;
318
319 if (rb_block_given_p()) {
320 each_passwd();
321 }
322 else if ((pw = getpwent()) != 0) {
323 return setup_passwd(pw);
324 }
325#endif
326 return Qnil;
327}
328
329/* call-seq:
330 * Etc::Passwd.each { |struct| block } -> Passwd
331 * Etc::Passwd.each -> Enumerator
332 *
333 * Iterates for each entry in the /etc/passwd file if a block is given.
334 *
335 * If no block is given, returns the Enumerator.
336 *
337 * The code block is passed an Passwd struct.
338 *
339 * See ::getpwent above for details.
340 *
341 * Example:
342 *
343 * require 'etc'
344 *
345 * Etc::Passwd.each {|u|
346 * puts u.name + " = " + u.gecos
347 * }
348 *
349 * Etc::Passwd.collect {|u| u.gecos}
350 * Etc::Passwd.collect {|u| u.gecos}
351 *
352 */
353static VALUE
354etc_each_passwd(VALUE obj)
355{
356#ifdef HAVE_GETPWENT
357 RETURN_ENUMERATOR(obj, 0, 0);
358 each_passwd();
359#endif
360 return obj;
361}
362
363/* Resets the process of reading the /etc/passwd file, so that the next call
364 * to ::getpwent will return the first entry again.
365 */
366static VALUE
367etc_setpwent(VALUE obj)
368{
369#ifdef HAVE_GETPWENT
370 setpwent();
371#endif
372 return Qnil;
373}
374
375/* Ends the process of scanning through the /etc/passwd file begun with
376 * ::getpwent, and closes the file.
377 */
378static VALUE
379etc_endpwent(VALUE obj)
380{
381#ifdef HAVE_GETPWENT
382 endpwent();
383#endif
384 return Qnil;
385}
386
387/* Returns an entry from the /etc/passwd file.
388 *
389 * The first time it is called it opens the file and returns the first entry;
390 * each successive call returns the next entry, or +nil+ if the end of the file
391 * has been reached.
392 *
393 * To close the file when processing is complete, call ::endpwent.
394 *
395 * Each entry is returned as a Passwd struct.
396 *
397 */
398static VALUE
399etc_getpwent(VALUE obj)
400{
401#ifdef HAVE_GETPWENT
402 struct passwd *pw;
403
404 if ((pw = getpwent()) != 0) {
405 return setup_passwd(pw);
406 }
407#endif
408 return Qnil;
409}
410
411#ifdef HAVE_GETGRENT
412static VALUE
413setup_group(struct group *grp)
414{
415 VALUE mem;
416 char **tbl;
417
418 mem = rb_ary_new();
419 tbl = grp->gr_mem;
420 while (*tbl) {
421 rb_ary_push(mem, safe_setup_locale_str(*tbl));
422 tbl++;
423 }
424 return rb_struct_new(sGroup,
425 safe_setup_locale_str(grp->gr_name),
426#ifdef HAVE_STRUCT_GROUP_GR_PASSWD
427 safe_setup_str(grp->gr_passwd),
428#endif
429 GIDT2NUM(grp->gr_gid),
430 mem);
431}
432#endif
433
434/* call-seq:
435 * getgrgid(group_id) -> Group
436 *
437 * Returns information about the group with specified integer +group_id+,
438 * as found in /etc/group.
439 *
440 * The information is returned as a Group struct.
441 *
442 * See the unix manpage for <code>getgrgid(3)</code> for more detail.
443 *
444 * === Example:
445 *
446 * Etc.getgrgid(100)
447 * #=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
448 *
449 */
450static VALUE
451etc_getgrgid(int argc, VALUE *argv, VALUE obj)
452{
453#ifdef HAVE_GETGRENT
454 VALUE id;
455 gid_t gid;
456 struct group *grp;
457
458 if (rb_scan_args(argc, argv, "01", &id) == 1) {
459 gid = NUM2GIDT(id);
460 }
461 else {
462 gid = getgid();
463 }
464 grp = getgrgid(gid);
465 if (grp == 0) rb_raise(rb_eArgError, "can't find group for %d", (int)gid);
466 return setup_group(grp);
467#else
468 return Qnil;
469#endif
470}
471
472/* call-seq:
473 * getgrnam(name) -> Group
474 *
475 * Returns information about the group with specified +name+, as found in
476 * /etc/group.
477 *
478 * The information is returned as a Group struct.
479 *
480 * See the unix manpage for <code>getgrnam(3)</code> for more detail.
481 *
482 * === Example:
483 *
484 * Etc.getgrnam('users')
485 * #=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
486 *
487 */
488static VALUE
489etc_getgrnam(VALUE obj, VALUE nam)
490{
491#ifdef HAVE_GETGRENT
492 struct group *grp;
493 const char *p = StringValueCStr(nam);
494
495 grp = getgrnam(p);
496 if (grp == 0) rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, nam);
497 return setup_group(grp);
498#else
499 return Qnil;
500#endif
501}
502
503#ifdef HAVE_GETGRENT
504static rb_atomic_t group_blocking;
505static VALUE
506group_ensure(VALUE _)
507{
508 endgrent();
509 if (RUBY_ATOMIC_EXCHANGE(group_blocking, 0) != 1) {
510 rb_raise(rb_eRuntimeError, "unexpected group_blocking");
511 }
512 return Qnil;
513}
514
515
516static VALUE
517group_iterate(VALUE _)
518{
519 struct group *pw;
520
521 setgrent();
522 while ((pw = getgrent()) != 0) {
523 rb_yield(setup_group(pw));
524 }
525 return Qnil;
526}
527
528static void
529each_group(void)
530{
531 if (RUBY_ATOMIC_CAS(group_blocking, 0, 1)) {
532 rb_raise(rb_eRuntimeError, "parallel group iteration");
533 }
534 rb_ensure(group_iterate, 0, group_ensure, 0);
535}
536#endif
537
538/* Provides a convenient Ruby iterator which executes a block for each entry
539 * in the /etc/group file.
540 *
541 * The code block is passed an Group struct.
542 *
543 * See ::getgrent above for details.
544 *
545 * Example:
546 *
547 * require 'etc'
548 *
549 * Etc.group {|g|
550 * puts g.name + ": " + g.mem.join(', ')
551 * }
552 *
553 */
554static VALUE
555etc_group(VALUE obj)
556{
557#ifdef HAVE_GETGRENT
558 struct group *grp;
559
560 if (rb_block_given_p()) {
561 each_group();
562 }
563 else if ((grp = getgrent()) != 0) {
564 return setup_group(grp);
565 }
566#endif
567 return Qnil;
568}
569
570#ifdef HAVE_GETGRENT
571/* call-seq:
572 * Etc::Group.each { |group| block } -> obj
573 * Etc::Group.each -> Enumerator
574 *
575 * Iterates for each entry in the /etc/group file if a block is given.
576 *
577 * If no block is given, returns the Enumerator.
578 *
579 * The code block is passed a Group struct.
580 *
581 * Example:
582 *
583 * require 'etc'
584 *
585 * Etc::Group.each {|g|
586 * puts g.name + ": " + g.mem.join(', ')
587 * }
588 *
589 * Etc::Group.collect {|g| g.name}
590 * Etc::Group.select {|g| !g.mem.empty?}
591 *
592 */
593static VALUE
594etc_each_group(VALUE obj)
595{
596 RETURN_ENUMERATOR(obj, 0, 0);
597 each_group();
598 return obj;
599}
600#endif
601
602/* Resets the process of reading the /etc/group file, so that the next call
603 * to ::getgrent will return the first entry again.
604 */
605static VALUE
606etc_setgrent(VALUE obj)
607{
608#ifdef HAVE_GETGRENT
609 setgrent();
610#endif
611 return Qnil;
612}
613
614/* Ends the process of scanning through the /etc/group file begun by
615 * ::getgrent, and closes the file.
616 */
617static VALUE
618etc_endgrent(VALUE obj)
619{
620#ifdef HAVE_GETGRENT
621 endgrent();
622#endif
623 return Qnil;
624}
625
626/* Returns an entry from the /etc/group file.
627 *
628 * The first time it is called it opens the file and returns the first entry;
629 * each successive call returns the next entry, or +nil+ if the end of the file
630 * has been reached.
631 *
632 * To close the file when processing is complete, call ::endgrent.
633 *
634 * Each entry is returned as a Group struct
635 */
636static VALUE
637etc_getgrent(VALUE obj)
638{
639#ifdef HAVE_GETGRENT
640 struct group *gr;
641
642 if ((gr = getgrent()) != 0) {
643 return setup_group(gr);
644 }
645#endif
646 return Qnil;
647}
648
649#define numberof(array) (sizeof(array) / sizeof(*(array)))
650
651#ifdef _WIN32
653UINT rb_w32_system_tmpdir(WCHAR *path, UINT len);
654VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
655#endif
656
657/*
658 * Returns system configuration directory.
659 *
660 * This is typically "/etc", but is modified by the prefix used when Ruby was
661 * compiled. For example, if Ruby is built and installed in /usr/local,
662 * returns "/usr/local/etc" on other platforms than Windows.
663 * On Windows, this always returns the directory provided by the system.
664 */
665static VALUE
666etc_sysconfdir(VALUE obj)
667{
668#ifdef _WIN32
670#else
671 return rb_filesystem_str_new_cstr(SYSCONFDIR);
672#endif
673}
674
675/*
676 * Returns system temporary directory; typically "/tmp".
677 */
678static VALUE
679etc_systmpdir(VALUE _)
680{
681 VALUE tmpdir;
682#ifdef _WIN32
683 WCHAR path[_MAX_PATH];
684 UINT len = rb_w32_system_tmpdir(path, numberof(path));
685 if (!len) return Qnil;
687#else
688 const char default_tmp[] = "/tmp";
689 const char *tmpstr = default_tmp;
690 size_t tmplen = strlen(default_tmp);
691# if defined _CS_DARWIN_USER_TEMP_DIR
692 #ifndef MAXPATHLEN
693 #define MAXPATHLEN 1024
694 #endif
695 char path[MAXPATHLEN];
696 size_t len;
697 len = confstr(_CS_DARWIN_USER_TEMP_DIR, path, sizeof(path));
698 if (len > 0) {
699 tmpstr = path;
700 tmplen = len - 1;
701 if (len > sizeof(path)) tmpstr = 0;
702 }
703# endif
704 tmpdir = rb_filesystem_str_new(tmpstr, tmplen);
705# if defined _CS_DARWIN_USER_TEMP_DIR
706 if (!tmpstr) {
707 confstr(_CS_DARWIN_USER_TEMP_DIR, RSTRING_PTR(tmpdir), len);
708 }
709# endif
710#endif
711#ifndef RB_PASS_KEYWORDS
712 /* untaint on Ruby < 2.7 */
713 FL_UNSET(tmpdir, FL_TAINT);
714#endif
715 return tmpdir;
716}
717
718#ifdef HAVE_UNAME
719/*
720 * Returns the system information obtained by uname system call.
721 *
722 * The return value is a hash which has 5 keys at least:
723 * :sysname, :nodename, :release, :version, :machine
724 *
725 * Example:
726 *
727 * require 'etc'
728 * require 'pp'
729 *
730 * pp Etc.uname
731 * #=> {:sysname=>"Linux",
732 * # :nodename=>"boron",
733 * # :release=>"2.6.18-6-xen-686",
734 * # :version=>"#1 SMP Thu Nov 5 19:54:42 UTC 2009",
735 * # :machine=>"i686"}
736 *
737 */
738static VALUE
739etc_uname(VALUE obj)
740{
741#ifdef _WIN32
742 OSVERSIONINFOW v;
743 SYSTEM_INFO s;
744 const char *sysname, *mach;
745 VALUE result, release, version;
746 VALUE vbuf, nodename = Qnil;
747 DWORD len = 0;
748 WCHAR *buf;
749
750 v.dwOSVersionInfoSize = sizeof(v);
751 if (!GetVersionExW(&v))
752 rb_sys_fail("GetVersionEx");
753
754 result = rb_hash_new();
755 switch (v.dwPlatformId) {
756 case VER_PLATFORM_WIN32s:
757 sysname = "Win32s";
758 break;
759 case VER_PLATFORM_WIN32_NT:
760 sysname = "Windows_NT";
761 break;
762 case VER_PLATFORM_WIN32_WINDOWS:
763 default:
764 sysname = "Windows";
765 break;
766 }
767 rb_hash_aset(result, ID2SYM(rb_intern("sysname")), rb_str_new_cstr(sysname));
768 release = rb_sprintf("%lu.%lu.%lu", v.dwMajorVersion, v.dwMinorVersion, v.dwBuildNumber);
769 rb_hash_aset(result, ID2SYM(rb_intern("release")), release);
770 version = rb_sprintf("%s Version %"PRIsVALUE": %"PRIsVALUE, sysname, release,
771 rb_w32_conv_from_wchar(v.szCSDVersion, rb_utf8_encoding()));
772 rb_hash_aset(result, ID2SYM(rb_intern("version")), version);
773
774# if defined _MSC_VER && _MSC_VER < 1300
775# define GET_COMPUTER_NAME(ptr, plen) GetComputerNameW(ptr, plen)
776# else
777# define GET_COMPUTER_NAME(ptr, plen) GetComputerNameExW(ComputerNameDnsFullyQualified, ptr, plen)
778# endif
779 GET_COMPUTER_NAME(NULL, &len);
780 buf = ALLOCV_N(WCHAR, vbuf, len);
781 if (GET_COMPUTER_NAME(buf, &len)) {
783 }
784 ALLOCV_END(vbuf);
785 if (NIL_P(nodename)) nodename = rb_str_new(0, 0);
786 rb_hash_aset(result, ID2SYM(rb_intern("nodename")), nodename);
787
788# ifndef PROCESSOR_ARCHITECTURE_AMD64
789# define PROCESSOR_ARCHITECTURE_AMD64 9
790# endif
791# ifndef PROCESSOR_ARCHITECTURE_INTEL
792# define PROCESSOR_ARCHITECTURE_INTEL 0
793# endif
794 GetSystemInfo(&s);
795 switch (s.wProcessorArchitecture) {
796 case PROCESSOR_ARCHITECTURE_AMD64:
797 mach = "x64";
798 break;
799 case PROCESSOR_ARCHITECTURE_ARM:
800 mach = "ARM";
801 break;
802 case PROCESSOR_ARCHITECTURE_INTEL:
803 mach = "x86";
804 break;
805 default:
806 mach = "unknown";
807 break;
808 }
809
810 rb_hash_aset(result, ID2SYM(rb_intern("machine")), rb_str_new_cstr(mach));
811#else
812 struct utsname u;
813 int ret;
814 VALUE result;
815
816 ret = uname(&u);
817 if (ret == -1)
818 rb_sys_fail("uname");
819
820 result = rb_hash_new();
821 rb_hash_aset(result, ID2SYM(rb_intern("sysname")), rb_str_new_cstr(u.sysname));
822 rb_hash_aset(result, ID2SYM(rb_intern("nodename")), rb_str_new_cstr(u.nodename));
823 rb_hash_aset(result, ID2SYM(rb_intern("release")), rb_str_new_cstr(u.release));
824 rb_hash_aset(result, ID2SYM(rb_intern("version")), rb_str_new_cstr(u.version));
825 rb_hash_aset(result, ID2SYM(rb_intern("machine")), rb_str_new_cstr(u.machine));
826#endif
827
828 return result;
829}
830#else
831#define etc_uname rb_f_notimplement
832#endif
833
834#ifdef HAVE_SYSCONF
835/*
836 * Returns system configuration variable using sysconf().
837 *
838 * _name_ should be a constant under <code>Etc</code> which begins with <code>SC_</code>.
839 *
840 * The return value is an integer or nil.
841 * nil means indefinite limit. (sysconf() returns -1 but errno is not set.)
842 *
843 * Etc.sysconf(Etc::SC_ARG_MAX) #=> 2097152
844 * Etc.sysconf(Etc::SC_LOGIN_NAME_MAX) #=> 256
845 *
846 */
847static VALUE
848etc_sysconf(VALUE obj, VALUE arg)
849{
850 int name;
851 long ret;
852
853 name = NUM2INT(arg);
854
855 errno = 0;
856 ret = sysconf(name);
857 if (ret == -1) {
858 if (errno == 0) /* no limit */
859 return Qnil;
860 rb_sys_fail("sysconf");
861 }
862 return LONG2NUM(ret);
863}
864#else
865#define etc_sysconf rb_f_notimplement
866#endif
867
868#ifdef HAVE_CONFSTR
869/*
870 * Returns system configuration variable using confstr().
871 *
872 * _name_ should be a constant under <code>Etc</code> which begins with <code>CS_</code>.
873 *
874 * The return value is a string or nil.
875 * nil means no configuration-defined value. (confstr() returns 0 but errno is not set.)
876 *
877 * Etc.confstr(Etc::CS_PATH) #=> "/bin:/usr/bin"
878 *
879 * # GNU/Linux
880 * Etc.confstr(Etc::CS_GNU_LIBC_VERSION) #=> "glibc 2.18"
881 * Etc.confstr(Etc::CS_GNU_LIBPTHREAD_VERSION) #=> "NPTL 2.18"
882 *
883 */
884static VALUE
885etc_confstr(VALUE obj, VALUE arg)
886{
887 int name;
888 char localbuf[128], *buf = localbuf;
889 size_t bufsize = sizeof(localbuf), ret;
890 VALUE tmp;
891
892 name = NUM2INT(arg);
893
894 errno = 0;
895 ret = confstr(name, buf, bufsize);
896 if (bufsize < ret) {
897 bufsize = ret;
898 buf = ALLOCV_N(char, tmp, bufsize);
899 errno = 0;
900 ret = confstr(name, buf, bufsize);
901 }
902 if (bufsize < ret)
903 rb_bug("required buffer size for confstr() changed dynamically.");
904 if (ret == 0) {
905 if (errno == 0) /* no configuration-defined value */
906 return Qnil;
907 rb_sys_fail("confstr");
908 }
909 return rb_str_new_cstr(buf);
910}
911#else
912#define etc_confstr rb_f_notimplement
913#endif
914
915#ifdef HAVE_FPATHCONF
916/*
917 * Returns pathname configuration variable using fpathconf().
918 *
919 * _name_ should be a constant under <code>Etc</code> which begins with <code>PC_</code>.
920 *
921 * The return value is an integer or nil.
922 * nil means indefinite limit. (fpathconf() returns -1 but errno is not set.)
923 *
924 * require 'etc'
925 * IO.pipe {|r, w|
926 * p w.pathconf(Etc::PC_PIPE_BUF) #=> 4096
927 * }
928 *
929 */
930static VALUE
931io_pathconf(VALUE io, VALUE arg)
932{
933 int name;
934 long ret;
935 rb_io_t *fptr;
936
937 name = NUM2INT(arg);
938
939 GetOpenFile(io, fptr);
940
941 errno = 0;
942 ret = fpathconf(fptr->fd, name);
943 if (ret == -1) {
944 if (errno == 0) /* no limit */
945 return Qnil;
946 rb_sys_fail("fpathconf");
947 }
948 return LONG2NUM(ret);
949}
950#else
951#define io_pathconf rb_f_notimplement
952#endif
953
954#if (defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)) || defined(_WIN32)
955
956#if defined(HAVE_SCHED_GETAFFINITY) && defined(CPU_ALLOC)
957static int
958etc_nprocessors_affin(void)
959{
960 cpu_set_t *cpuset, cpuset_buff[1024 / sizeof(cpu_set_t)];
961 size_t size;
962 int ret;
963 int n;
964
965 CPU_ZERO_S(sizeof(cpuset_buff), cpuset_buff);
966
967 /*
968 * XXX:
969 * man page says CPU_ALLOC takes number of cpus. But it is not accurate
970 * explanation. sched_getaffinity() returns EINVAL if cpuset bitmap is
971 * smaller than kernel internal bitmap.
972 * That said, sched_getaffinity() can fail when a kernel have sparse bitmap
973 * even if cpuset bitmap is larger than number of cpus.
974 * The precious way is to use /sys/devices/system/cpu/online. But there are
975 * two problems,
976 * - Costly calculation
977 * It is a minor issue, but possibly kill a benefit of a parallel processing.
978 * - No guarantee to exist /sys/devices/system/cpu/online
979 * This is an issue especially when using Linux containers.
980 * So, we use hardcode number for a workaround. Current linux kernel
981 * (Linux 3.17) support 8192 cpus at maximum. Then 16384 must be enough.
982 */
983 for (n=64; n <= 16384; n *= 2) {
984 size = CPU_ALLOC_SIZE(n);
985 if (size >= sizeof(cpuset_buff)) {
986 cpuset = xcalloc(1, size);
987 if (!cpuset)
988 return -1;
989 } else {
990 cpuset = cpuset_buff;
991 }
992
993 ret = sched_getaffinity(0, size, cpuset);
994 if (ret == 0) {
995 /* On success, count number of cpus. */
996 ret = CPU_COUNT_S(size, cpuset);
997 }
998
999 if (size >= sizeof(cpuset_buff)) {
1000 xfree(cpuset);
1001 }
1002 if (ret > 0 || errno != EINVAL) {
1003 return ret;
1004 }
1005 }
1006
1007 return ret;
1008}
1009#endif
1010
1011/*
1012 * Returns the number of online processors.
1013 *
1014 * The result is intended as the number of processes to
1015 * use all available processors.
1016 *
1017 * This method is implemented using:
1018 * - sched_getaffinity(): Linux
1019 * - sysconf(_SC_NPROCESSORS_ONLN): GNU/Linux, NetBSD, FreeBSD, OpenBSD, DragonFly BSD, OpenIndiana, Mac OS X, AIX
1020 *
1021 * Example:
1022 *
1023 * require 'etc'
1024 * p Etc.nprocessors #=> 4
1025 *
1026 * The result might be smaller number than physical cpus especially when ruby
1027 * process is bound to specific cpus. This is intended for getting better
1028 * parallel processing.
1029 *
1030 * Example: (Linux)
1031 *
1032 * linux$ taskset 0x3 ./ruby -retc -e "p Etc.nprocessors" #=> 2
1033 *
1034 */
1035static VALUE
1037{
1038 long ret;
1039
1040#if !defined(_WIN32)
1041
1042#if defined(HAVE_SCHED_GETAFFINITY) && defined(CPU_ALLOC)
1043 int ncpus;
1044
1045 ncpus = etc_nprocessors_affin();
1046 if (ncpus != -1) {
1047 return INT2NUM(ncpus);
1048 }
1049 /* fallback to _SC_NPROCESSORS_ONLN */
1050#endif
1051
1052 errno = 0;
1053 ret = sysconf(_SC_NPROCESSORS_ONLN);
1054 if (ret == -1) {
1055 rb_sys_fail("sysconf(_SC_NPROCESSORS_ONLN)");
1056 }
1057#else
1058 SYSTEM_INFO si;
1059 GetSystemInfo(&si);
1060 ret = (long)si.dwNumberOfProcessors;
1061#endif
1062 return LONG2NUM(ret);
1063}
1064#else
1065#define etc_nprocessors rb_f_notimplement
1066#endif
1067
1068/*
1069 * The Etc module provides access to information typically stored in
1070 * files in the /etc directory on Unix systems.
1071 *
1072 * The information accessible consists of the information found in the
1073 * /etc/passwd and /etc/group files, plus information about the system's
1074 * temporary directory (/tmp) and configuration directory (/etc).
1075 *
1076 * The Etc module provides a more reliable way to access information about
1077 * the logged in user than environment variables such as +$USER+.
1078 *
1079 * == Example:
1080 *
1081 * require 'etc'
1082 *
1083 * login = Etc.getlogin
1084 * info = Etc.getpwnam(login)
1085 * username = info.gecos.split(/,/).first
1086 * puts "Hello #{username}, I see your login name is #{login}"
1087 *
1088 * Note that the methods provided by this module are not always secure.
1089 * It should be used for informational purposes, and not for security.
1090 *
1091 * All operations defined in this module are class methods, so that you can
1092 * include the Etc module into your class.
1093 */
1094void
1096{
1097 VALUE mEtc;
1098
1099 #ifdef HAVE_RB_EXT_RACTOR_SAFE
1100 RB_EXT_RACTOR_SAFE(true);
1101 #endif
1102 mEtc = rb_define_module("Etc");
1104 init_constants(mEtc);
1105
1106 rb_define_module_function(mEtc, "getlogin", etc_getlogin, 0);
1107
1108 rb_define_module_function(mEtc, "getpwuid", etc_getpwuid, -1);
1109 rb_define_module_function(mEtc, "getpwnam", etc_getpwnam, 1);
1110 rb_define_module_function(mEtc, "setpwent", etc_setpwent, 0);
1111 rb_define_module_function(mEtc, "endpwent", etc_endpwent, 0);
1112 rb_define_module_function(mEtc, "getpwent", etc_getpwent, 0);
1113 rb_define_module_function(mEtc, "passwd", etc_passwd, 0);
1114
1115 rb_define_module_function(mEtc, "getgrgid", etc_getgrgid, -1);
1116 rb_define_module_function(mEtc, "getgrnam", etc_getgrnam, 1);
1117 rb_define_module_function(mEtc, "group", etc_group, 0);
1118 rb_define_module_function(mEtc, "setgrent", etc_setgrent, 0);
1119 rb_define_module_function(mEtc, "endgrent", etc_endgrent, 0);
1120 rb_define_module_function(mEtc, "getgrent", etc_getgrent, 0);
1121 rb_define_module_function(mEtc, "sysconfdir", etc_sysconfdir, 0);
1122 rb_define_module_function(mEtc, "systmpdir", etc_systmpdir, 0);
1123 rb_define_module_function(mEtc, "uname", etc_uname, 0);
1124 rb_define_module_function(mEtc, "sysconf", etc_sysconf, 1);
1125 rb_define_module_function(mEtc, "confstr", etc_confstr, 1);
1126 rb_define_method(rb_cIO, "pathconf", io_pathconf, 1);
1127 rb_define_module_function(mEtc, "nprocessors", etc_nprocessors, 0);
1128
1129 sPasswd = rb_struct_define_under(mEtc, "Passwd",
1130 "name",
1131#ifdef HAVE_STRUCT_PASSWD_PW_PASSWD
1132 "passwd",
1133#endif
1134 "uid",
1135 "gid",
1136#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
1137 "gecos",
1138#endif
1139 "dir",
1140 "shell",
1141#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE
1142 "change",
1143#endif
1144#ifdef HAVE_STRUCT_PASSWD_PW_QUOTA
1145 "quota",
1146#endif
1147#ifdef HAVE_STRUCT_PASSWD_PW_AGE
1148 "age",
1149#endif
1150#ifdef HAVE_STRUCT_PASSWD_PW_CLASS
1151 "uclass",
1152#endif
1153#ifdef HAVE_STRUCT_PASSWD_PW_COMMENT
1154 "comment",
1155#endif
1156#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
1157 "expire",
1158#endif
1159 NULL);
1160#if 0
1161 /* Define-const: Passwd
1162 *
1163 * Passwd is a Struct that contains the following members:
1164 *
1165 * name::
1166 * contains the short login name of the user as a String.
1167 * passwd::
1168 * contains the encrypted password of the user as a String.
1169 * an 'x' is returned if shadow passwords are in use. An '*' is returned
1170 * if the user cannot log in using a password.
1171 * uid::
1172 * contains the integer user ID (uid) of the user.
1173 * gid::
1174 * contains the integer group ID (gid) of the user's primary group.
1175 * dir::
1176 * contains the path to the home directory of the user as a String.
1177 * shell::
1178 * contains the path to the login shell of the user as a String.
1179 *
1180 * === The following members below are optional, and must be compiled with special flags:
1181 *
1182 * gecos::
1183 * contains a longer String description of the user, such as
1184 * a full name. Some Unix systems provide structured information in the
1185 * gecos field, but this is system-dependent.
1186 * must be compiled with +HAVE_STRUCT_PASSWD_PW_GECOS+
1187 * change::
1188 * password change time(integer) must be compiled with +HAVE_STRUCT_PASSWD_PW_CHANGE+
1189 * quota::
1190 * quota value(integer) must be compiled with +HAVE_STRUCT_PASSWD_PW_QUOTA+
1191 * age::
1192 * password age(integer) must be compiled with +HAVE_STRUCT_PASSWD_PW_AGE+
1193 * class::
1194 * user access class(string) must be compiled with +HAVE_STRUCT_PASSWD_PW_CLASS+
1195 * comment::
1196 * comment(string) must be compiled with +HAVE_STRUCT_PASSWD_PW_COMMENT+
1197 * expire::
1198 * account expiration time(integer) must be compiled with +HAVE_STRUCT_PASSWD_PW_EXPIRE+
1199 */
1200 rb_define_const(mEtc, "Passwd", sPasswd);
1201#endif
1202 rb_define_const(rb_cStruct, "Passwd", sPasswd); /* deprecated name */
1205 rb_define_singleton_method(sPasswd, "each", etc_each_passwd, 0);
1206#ifdef HAVE_GETGRENT
1207 sGroup = rb_struct_define_under(mEtc, "Group", "name",
1208#ifdef HAVE_STRUCT_GROUP_GR_PASSWD
1209 "passwd",
1210#endif
1211 "gid", "mem", NULL);
1212
1213#if 0
1214 /* Define-const: Group
1215 *
1216 * Group is a Struct that is only available when compiled with +HAVE_GETGRENT+.
1217 *
1218 * The struct contains the following members:
1219 *
1220 * name::
1221 * contains the name of the group as a String.
1222 * passwd::
1223 * contains the encrypted password as a String. An 'x' is
1224 * returned if password access to the group is not available; an empty
1225 * string is returned if no password is needed to obtain membership of
1226 * the group.
1227 *
1228 * Must be compiled with +HAVE_STRUCT_GROUP_GR_PASSWD+.
1229 * gid::
1230 * contains the group's numeric ID as an integer.
1231 * mem::
1232 * is an Array of Strings containing the short login names of the
1233 * members of the group.
1234 */
1235 rb_define_const(mEtc, "Group", sGroup);
1236#endif
1237 rb_define_const(rb_cStruct, "Group", sGroup); /* deprecated name */
1240 rb_define_singleton_method(sGroup, "each", etc_each_group, 0);
1241#endif
1242}
VALUE rb_ary_push(VALUE ary, VALUE item)
Definition: array.c:1301
VALUE rb_ary_new(void)
Definition: array.c:749
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:653
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition: cxxanyargs.hpp:668
#define rb_define_module_function(klass, mid, func, arity)
Defines klass#mid and makes it a module function.
Definition: cxxanyargs.hpp:672
#define mod(x, y)
Definition: date_strftime.c:28
#define MAXPATHLEN
Definition: dln.c:69
rb_encoding * rb_utf8_encoding(void)
Definition: encoding.c:1537
rb_encoding * rb_filesystem_encoding(void)
Definition: encoding.c:1602
rb_encoding * rb_locale_encoding(void)
Definition: encoding.c:1583
VALUE rb_mEnumerable
Definition: enum.c:27
uint8_t len
Definition: escape.c:17
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
#define numberof(array)
Definition: etc.c:649
#define etc_sysconf
Definition: etc.c:865
char * getlogin()
Definition: win32.c:918
#define io_pathconf
Definition: etc.c:951
#define RUBY_ETC_VERSION
Definition: etc.c:55
char * getenv()
#define etc_confstr
Definition: etc.c:912
#define rb_deprecate_constant(mod, name)
Definition: etc.c:60
#define etc_nprocessors
Definition: etc.c:1065
void Init_etc(void)
Definition: etc.c:1095
#define etc_uname
Definition: etc.c:831
#define RSTRING_PTR(string)
Definition: fbuffer.h:19
#define endpwent()
#define FL_TAINT
Definition: fl_type.h:54
#define PRIsVALUE
Definition: function.c:10
#define GIDT2NUM
Definition: gid_t.h:27
#define NUM2GIDT
Definition: gid_t.h:31
VALUE rb_cIO
Definition: io.c:183
VALUE rb_cStruct
Definition: struct.c:34
void rb_extend_object(VALUE obj, VALUE module)
Extend the object with the module.
Definition: eval.c:1730
VALUE rb_define_module(const char *name)
Definition: class.c:871
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2296
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:935
#define FL_UNSET
Definition: fl_type.h:132
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2917
void rb_bug(const char *fmt,...)
Definition: error.c:768
VALUE rb_eRuntimeError
Definition: error.c:1055
VALUE rb_eArgError
Definition: error.c:1058
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1148
void rb_sys_fail(const char *mesg)
Definition: error.c:3041
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
Definition: hash.c:2901
VALUE rb_hash_new(void)
Definition: hash.c:1538
VALUE rb_external_str_new_with_enc(const char *ptr, long len, rb_encoding *)
Definition: string.c:1106
#define RETURN_ENUMERATOR(obj, argc, argv)
Definition: enumerator.h:74
#define RB_EXT_RACTOR_SAFE(f)
Definition: load.h:39
#define rb_str_new2
Definition: string.h:276
#define rb_str_new(str, len)
Definition: string.h:213
VALUE rb_filesystem_str_new(const char *, long)
Definition: string.c:1181
VALUE rb_filesystem_str_new_cstr(const char *)
Definition: string.c:1187
#define rb_locale_str_new_cstr(str)
Definition: string.h:256
#define rb_str_new_cstr(str)
Definition: string.h:219
VALUE rb_struct_define_under(VALUE, const char *,...)
Definition: struct.c:479
VALUE rb_struct_new(VALUE,...)
Definition: struct.c:766
#define ID2SYM
Definition: symbol.h:44
ID rb_intern(const char *)
Definition: symbol.c:785
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:3150
#define GetOpenFile
Definition: io.h:125
#define NUM2INT
Definition: int.h:44
#define INT2NUM
Definition: int.h:43
voidpf void uLong size
Definition: ioapi.h:138
typedef long(ZCALLBACK *tell_file_func) OF((voidpf opaque
voidpf void * buf
Definition: ioapi.h:138
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1341
#define LONG2NUM
Definition: long.h:50
#define ALLOCV_N
Definition: memory.h:139
#define ALLOCV_END
Definition: memory.h:140
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
const int id
Definition: nkf.c:209
const char * name
Definition: nkf.c:208
#define NULL
Definition: regenc.h:69
#define StringValueCStr(v)
Definition: rstring.h:52
int argc
Definition: ruby.c:240
char ** argv
Definition: ruby.c:241
#define Qnil
#define NIL_P
VALUE rb_sprintf(const char *,...)
Definition: sprintf.c:1203
#define _(args)
Definition: stdarg.h:31
size_t strlen(const char *)
Definition: io.h:61
int fd
Definition: io.h:65
#define UIDT2NUM
Definition: uid_t.h:27
#define NUM2UIDT
Definition: uid_t.h:31
unsigned long VALUE
Definition: value.h:38
VALUE rb_w32_special_folder(int type)
Definition: win32.c:506
#define CSIDL_COMMON_APPDATA
Definition: win32.c:427
VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc)
Definition: win32.c:2266
UINT rb_w32_system_tmpdir(WCHAR *path, UINT len)
Definition: win32.c:522
rb_uid_t getuid(void)
Definition: win32.c:2821
rb_gid_t getgid(void)
Definition: win32.c:2835
IUnknown DWORD
Definition: win32ole.c:33
#define xfree
Definition: xmalloc.h:49
#define xcalloc
Definition: xmalloc.h:46