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