Ruby  2.4.2p198(2017-09-14revision59899)
etc.c
Go to the documentation of this file.
1 /************************************************
2 
3  etc.c -
4 
5  $Author: nobu $
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 
37 static VALUE sPasswd;
38 #ifdef HAVE_GETGRENT
39 static 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
51 char *getenv();
52 #endif
53 char *getlogin();
54 
55 #include "constdefs.h"
56 
57 /* call-seq:
58  * getlogin -> String
59  *
60  * Returns the short user name of the currently logged in user.
61  * Unfortunately, it is often rather easy to fool ::getlogin.
62  *
63  * Avoid ::getlogin for security-related purposes.
64  *
65  * If ::getlogin fails, try ::getpwuid.
66  *
67  * See the unix manpage for <code>getpwuid(3)</code> for more detail.
68  *
69  * e.g.
70  * Etc.getlogin -> 'guest'
71  */
72 static VALUE
74 {
75  char *login;
76 
77 #ifdef HAVE_GETLOGIN
78  login = getlogin();
79  if (!login) login = getenv("USER");
80 #else
81  login = getenv("USER");
82 #endif
83 
84  if (login) {
85 #ifdef _WIN32
86  rb_encoding *extenc = rb_utf8_encoding();
87 #else
88  rb_encoding *extenc = rb_locale_encoding();
89 #endif
90  return rb_external_str_new_with_enc(login, strlen(login), extenc);
91  }
92 
93  return Qnil;
94 }
95 
96 #if defined(HAVE_GETPWENT) || defined(HAVE_GETGRENT)
97 static VALUE
98 safe_setup_str(const char *str)
99 {
100  if (str == 0) str = "";
101  return rb_tainted_str_new2(str);
102 }
103 
104 static VALUE
105 safe_setup_locale_str(const char *str)
106 {
107  if (str == 0) str = "";
108  return rb_locale_str_new_cstr(str);
109 }
110 
111 static VALUE
112 safe_setup_filesystem_str(const char *str)
113 {
114  if (str == 0) str = "";
115  return rb_filesystem_str_new_cstr(str);
116 }
117 #endif
118 
119 #ifdef HAVE_GETPWENT
120 static VALUE
121 setup_passwd(struct passwd *pwd)
122 {
123  if (pwd == 0) rb_sys_fail("/etc/passwd");
124  return rb_struct_new(sPasswd,
125  safe_setup_locale_str(pwd->pw_name),
126 #ifdef HAVE_STRUCT_PASSWD_PW_PASSWD
127  safe_setup_str(pwd->pw_passwd),
128 #endif
129  UIDT2NUM(pwd->pw_uid),
130  GIDT2NUM(pwd->pw_gid),
131 #ifdef HAVE_STRUCT_PASSWD_PW_GECOS
132  safe_setup_locale_str(pwd->pw_gecos),
133 #endif
134  safe_setup_filesystem_str(pwd->pw_dir),
135  safe_setup_filesystem_str(pwd->pw_shell),
136 #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE
137  INT2NUM(pwd->pw_change),
138 #endif
139 #ifdef HAVE_STRUCT_PASSWD_PW_QUOTA
140  INT2NUM(pwd->pw_quota),
141 #endif
142 #ifdef HAVE_STRUCT_PASSWD_PW_AGE
143  PW_AGE2VAL(pwd->pw_age),
144 #endif
145 #ifdef HAVE_STRUCT_PASSWD_PW_CLASS
146  safe_setup_locale_str(pwd->pw_class),
147 #endif
148 #ifdef HAVE_STRUCT_PASSWD_PW_COMMENT
149  safe_setup_locale_str(pwd->pw_comment),
150 #endif
151 #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
152  INT2NUM(pwd->pw_expire),
153 #endif
154  0 /*dummy*/
155  );
156 }
157 #endif
158 
159 /* call-seq:
160  * getpwuid(uid) -> Passwd
161  *
162  * Returns the /etc/passwd information for the user with the given integer +uid+.
163  *
164  * The information is returned as a Passwd struct.
165  *
166  * If +uid+ is omitted, the value from <code>Passwd[:uid]</code> is returned
167  * instead.
168  *
169  * See the unix manpage for <code>getpwuid(3)</code> for more detail.
170  *
171  * === Example:
172  *
173  * Etc.getpwuid(0)
174  * #=> #<struct Etc::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
175  */
176 static VALUE
178 {
179 #if defined(HAVE_GETPWENT)
180  VALUE id;
181  rb_uid_t uid;
182  struct passwd *pwd;
183 
184  if (rb_scan_args(argc, argv, "01", &id) == 1) {
185  uid = NUM2UIDT(id);
186  }
187  else {
188  uid = getuid();
189  }
190  pwd = getpwuid(uid);
191  if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %d", (int)uid);
192  return setup_passwd(pwd);
193 #else
194  return Qnil;
195 #endif
196 }
197 
198 /* call-seq:
199  * getpwnam(name) -> Passwd
200  *
201  * Returns the /etc/passwd information for the user with specified login
202  * +name+.
203  *
204  * The information is returned as a Passwd struct.
205  *
206  * See the unix manpage for <code>getpwnam(3)</code> for more detail.
207  *
208  * === Example:
209  *
210  * Etc.getpwnam('root')
211  * #=> #<struct Etc::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
212  */
213 static VALUE
215 {
216 #ifdef HAVE_GETPWENT
217  struct passwd *pwd;
218 
219  SafeStringValue(nam);
220  pwd = getpwnam(RSTRING_PTR(nam));
221  if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, nam);
222  return setup_passwd(pwd);
223 #else
224  return Qnil;
225 #endif
226 }
227 
228 #ifdef HAVE_GETPWENT
229 static int passwd_blocking = 0;
230 static VALUE
231 passwd_ensure(void)
232 {
233  endpwent();
234  passwd_blocking = (int)Qfalse;
235  return Qnil;
236 }
237 
238 static VALUE
239 passwd_iterate(void)
240 {
241  struct passwd *pw;
242 
243  setpwent();
244  while ((pw = getpwent()) != 0) {
245  rb_yield(setup_passwd(pw));
246  }
247  return Qnil;
248 }
249 
250 static void
251 each_passwd(void)
252 {
253  if (passwd_blocking) {
254  rb_raise(rb_eRuntimeError, "parallel passwd iteration");
255  }
256  passwd_blocking = (int)Qtrue;
257  rb_ensure(passwd_iterate, 0, passwd_ensure, 0);
258 }
259 #endif
260 
261 /* call-seq:
262  * Etc.passwd { |struct| block } -> Passwd
263  * Etc.passwd -> Passwd
264  *
265  * Provides a convenient Ruby iterator which executes a block for each entry
266  * in the /etc/passwd file.
267  *
268  * The code block is passed an Passwd struct.
269  *
270  * See ::getpwent above for details.
271  *
272  * Example:
273  *
274  * require 'etc'
275  *
276  * Etc.passwd {|u|
277  * puts u.name + " = " + u.gecos
278  * }
279  *
280  */
281 static VALUE
283 {
284 #ifdef HAVE_GETPWENT
285  struct passwd *pw;
286 
287  if (rb_block_given_p()) {
288  each_passwd();
289  }
290  else if ((pw = getpwent()) != 0) {
291  return setup_passwd(pw);
292  }
293 #endif
294  return Qnil;
295 }
296 
297 /* call-seq:
298  * Etc::Passwd.each { |struct| block } -> Passwd
299  * Etc::Passwd.each -> Enumerator
300  *
301  * Iterates for each entry in the /etc/passwd file if a block is given.
302  *
303  * If no block is given, returns the Enumerator.
304  *
305  * The code block is passed an Passwd struct.
306  *
307  * See ::getpwent above for details.
308  *
309  * Example:
310  *
311  * require 'etc'
312  *
313  * Etc::Passwd.each {|u|
314  * puts u.name + " = " + u.gecos
315  * }
316  *
317  * Etc::Passwd.collect {|u| u.gecos}
318  * Etc::Passwd.collect {|u| u.gecos}
319  *
320  */
321 static VALUE
323 {
324 #ifdef HAVE_GETPWENT
325  RETURN_ENUMERATOR(obj, 0, 0);
326  each_passwd();
327 #endif
328  return obj;
329 }
330 
331 /* Resets the process of reading the /etc/passwd file, so that the next call
332  * to ::getpwent will return the first entry again.
333  */
334 static VALUE
336 {
337 #ifdef HAVE_GETPWENT
338  setpwent();
339 #endif
340  return Qnil;
341 }
342 
343 /* Ends the process of scanning through the /etc/passwd file begun with
344  * ::getpwent, and closes the file.
345  */
346 static VALUE
348 {
349 #ifdef HAVE_GETPWENT
350  endpwent();
351 #endif
352  return Qnil;
353 }
354 
355 /* Returns an entry from the /etc/passwd file.
356  *
357  * The first time it is called it opens the file and returns the first entry;
358  * each successive call returns the next entry, or +nil+ if the end of the file
359  * has been reached.
360  *
361  * To close the file when processing is complete, call ::endpwent.
362  *
363  * Each entry is returned as a Passwd struct.
364  *
365  */
366 static VALUE
368 {
369 #ifdef HAVE_GETPWENT
370  struct passwd *pw;
371 
372  if ((pw = getpwent()) != 0) {
373  return setup_passwd(pw);
374  }
375 #endif
376  return Qnil;
377 }
378 
379 #ifdef HAVE_GETGRENT
380 static VALUE
381 setup_group(struct group *grp)
382 {
383  VALUE mem;
384  char **tbl;
385 
386  mem = rb_ary_new();
387  tbl = grp->gr_mem;
388  while (*tbl) {
389  rb_ary_push(mem, safe_setup_locale_str(*tbl));
390  tbl++;
391  }
392  return rb_struct_new(sGroup,
393  safe_setup_locale_str(grp->gr_name),
394 #ifdef HAVE_STRUCT_GROUP_GR_PASSWD
395  safe_setup_str(grp->gr_passwd),
396 #endif
397  GIDT2NUM(grp->gr_gid),
398  mem);
399 }
400 #endif
401 
402 /* call-seq:
403  * getgrgid(group_id) -> Group
404  *
405  * Returns information about the group with specified integer +group_id+,
406  * as found in /etc/group.
407  *
408  * The information is returned as a Group struct.
409  *
410  * See the unix manpage for <code>getgrgid(3)</code> for more detail.
411  *
412  * === Example:
413  *
414  * Etc.getgrgid(100)
415  * #=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
416  *
417  */
418 static VALUE
420 {
421 #ifdef HAVE_GETGRENT
422  VALUE id;
423  gid_t gid;
424  struct group *grp;
425 
426  if (rb_scan_args(argc, argv, "01", &id) == 1) {
427  gid = NUM2GIDT(id);
428  }
429  else {
430  gid = getgid();
431  }
432  grp = getgrgid(gid);
433  if (grp == 0) rb_raise(rb_eArgError, "can't find group for %d", (int)gid);
434  return setup_group(grp);
435 #else
436  return Qnil;
437 #endif
438 }
439 
440 /* call-seq:
441  * getgrnam(name) -> Group
442  *
443  * Returns information about the group with specified +name+, as found in
444  * /etc/group.
445  *
446  * The information is returned as a Group struct.
447  *
448  * See the unix manpage for <code>getgrnam(3)</code> for more detail.
449  *
450  * === Example:
451  *
452  * Etc.getgrnam('users')
453  * #=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
454  *
455  */
456 static VALUE
458 {
459 #ifdef HAVE_GETGRENT
460  struct group *grp;
461 
462  SafeStringValue(nam);
463  grp = getgrnam(RSTRING_PTR(nam));
464  if (grp == 0) rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, nam);
465  return setup_group(grp);
466 #else
467  return Qnil;
468 #endif
469 }
470 
471 #ifdef HAVE_GETGRENT
472 static int group_blocking = 0;
473 static VALUE
474 group_ensure(void)
475 {
476  endgrent();
477  group_blocking = (int)Qfalse;
478  return Qnil;
479 }
480 
481 
482 static VALUE
483 group_iterate(void)
484 {
485  struct group *pw;
486 
487  setgrent();
488  while ((pw = getgrent()) != 0) {
489  rb_yield(setup_group(pw));
490  }
491  return Qnil;
492 }
493 
494 static void
495 each_group(void)
496 {
497  if (group_blocking) {
498  rb_raise(rb_eRuntimeError, "parallel group iteration");
499  }
500  group_blocking = (int)Qtrue;
501  rb_ensure(group_iterate, 0, group_ensure, 0);
502 }
503 #endif
504 
505 /* Provides a convenient Ruby iterator which executes a block for each entry
506  * in the /etc/group file.
507  *
508  * The code block is passed an Group struct.
509  *
510  * See ::getgrent above for details.
511  *
512  * Example:
513  *
514  * require 'etc'
515  *
516  * Etc.group {|g|
517  * puts g.name + ": " + g.mem.join(', ')
518  * }
519  *
520  */
521 static VALUE
523 {
524 #ifdef HAVE_GETGRENT
525  struct group *grp;
526 
527  if (rb_block_given_p()) {
528  each_group();
529  }
530  else if ((grp = getgrent()) != 0) {
531  return setup_group(grp);
532  }
533 #endif
534  return Qnil;
535 }
536 
537 #ifdef HAVE_GETGRENT
538 /* call-seq:
539  * Etc::Group.each { |group| block } -> obj
540  * Etc::Group.each -> Enumerator
541  *
542  * Iterates for each entry in the /etc/group file if a block is given.
543  *
544  * If no block is given, returns the Enumerator.
545  *
546  * The code block is passed a Group struct.
547  *
548  * Example:
549  *
550  * require 'etc'
551  *
552  * Etc::Group.each {|g|
553  * puts g.name + ": " + g.mem.join(', ')
554  * }
555  *
556  * Etc::Group.collect {|g| g.name}
557  * Etc::Group.select {|g| !g.mem.empty?}
558  *
559  */
560 static VALUE
561 etc_each_group(VALUE obj)
562 {
563  RETURN_ENUMERATOR(obj, 0, 0);
564  each_group();
565  return obj;
566 }
567 #endif
568 
569 /* Resets the process of reading the /etc/group file, so that the next call
570  * to ::getgrent will return the first entry again.
571  */
572 static VALUE
574 {
575 #ifdef HAVE_GETGRENT
576  setgrent();
577 #endif
578  return Qnil;
579 }
580 
581 /* Ends the process of scanning through the /etc/group file begun by
582  * ::getgrent, and closes the file.
583  */
584 static VALUE
586 {
587 #ifdef HAVE_GETGRENT
588  endgrent();
589 #endif
590  return Qnil;
591 }
592 
593 /* Returns an entry from the /etc/group file.
594  *
595  * The first time it is called it opens the file and returns the first entry;
596  * each successive call returns the next entry, or +nil+ if the end of the file
597  * has been reached.
598  *
599  * To close the file when processing is complete, call ::endgrent.
600  *
601  * Each entry is returned as a Group struct
602  */
603 static VALUE
605 {
606 #ifdef HAVE_GETGRENT
607  struct group *gr;
608 
609  if ((gr = getgrent()) != 0) {
610  return setup_group(gr);
611  }
612 #endif
613  return Qnil;
614 }
615 
616 #define numberof(array) (sizeof(array) / sizeof(*(array)))
617 
618 #ifdef _WIN32
619 VALUE rb_w32_special_folder(int type);
620 UINT rb_w32_system_tmpdir(WCHAR *path, UINT len);
621 VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
622 #endif
623 
624 /*
625  * Returns system configuration directory.
626  *
627  * This is typically "/etc", but is modified by the prefix used when Ruby was
628  * compiled. For example, if Ruby is built and installed in /usr/local, returns
629  * "/usr/local/etc".
630  */
631 static VALUE
633 {
634 #ifdef _WIN32
636 #else
637  return rb_filesystem_str_new_cstr(SYSCONFDIR);
638 #endif
639 }
640 
641 /*
642  * Returns system temporary directory; typically "/tmp".
643  */
644 static VALUE
646 {
647  VALUE tmpdir;
648 #ifdef _WIN32
649  WCHAR path[_MAX_PATH];
650  UINT len = rb_w32_system_tmpdir(path, numberof(path));
651  if (!len) return Qnil;
653 #else
654  const char default_tmp[] = "/tmp";
655  const char *tmpstr = default_tmp;
656  size_t tmplen = strlen(default_tmp);
657 # if defined _CS_DARWIN_USER_TEMP_DIR
658  #ifndef MAXPATHLEN
659  #define MAXPATHLEN 1024
660  #endif
661  char path[MAXPATHLEN];
662  size_t len;
663  len = confstr(_CS_DARWIN_USER_TEMP_DIR, path, sizeof(path));
664  if (len > 0) {
665  tmpstr = path;
666  tmplen = len - 1;
667  if (len > sizeof(path)) tmpstr = 0;
668  }
669 # endif
670  tmpdir = rb_filesystem_str_new(tmpstr, tmplen);
671 # if defined _CS_DARWIN_USER_TEMP_DIR
672  if (!tmpstr) {
673  confstr(_CS_DARWIN_USER_TEMP_DIR, RSTRING_PTR(tmpdir), len);
674  }
675 # endif
676 #endif
677  FL_UNSET(tmpdir, FL_TAINT);
678  return tmpdir;
679 }
680 
681 #ifdef HAVE_UNAME
682 /*
683  * Returns the system information obtained by uname system call.
684  *
685  * The return value is a hash which has 5 keys at least:
686  * :sysname, :nodename, :release, :version, :machine
687  *
688  * Example:
689  *
690  * require 'etc'
691  * require 'pp'
692  *
693  * pp Etc.uname
694  * #=> {:sysname=>"Linux",
695  * # :nodename=>"boron",
696  * # :release=>"2.6.18-6-xen-686",
697  * # :version=>"#1 SMP Thu Nov 5 19:54:42 UTC 2009",
698  * # :machine=>"i686"}
699  *
700  */
701 static VALUE
702 etc_uname(VALUE obj)
703 {
704 #ifdef _WIN32
705  OSVERSIONINFOW v;
706  SYSTEM_INFO s;
707  const char *sysname, *mach;
708  VALUE result, release, version;
709  VALUE vbuf, nodename = Qnil;
710  DWORD len = 0;
711  WCHAR *buf;
712 
713  v.dwOSVersionInfoSize = sizeof(v);
714  if (!GetVersionExW(&v))
715  rb_sys_fail("GetVersionEx");
716 
717  result = rb_hash_new();
718  switch (v.dwPlatformId) {
719  case VER_PLATFORM_WIN32s:
720  sysname = "Win32s";
721  break;
722  case VER_PLATFORM_WIN32_NT:
723  sysname = "Windows_NT";
724  break;
725  case VER_PLATFORM_WIN32_WINDOWS:
726  default:
727  sysname = "Windows";
728  break;
729  }
730  rb_hash_aset(result, ID2SYM(rb_intern("sysname")), rb_str_new_cstr(sysname));
731  release = rb_sprintf("%lu.%lu.%lu", v.dwMajorVersion, v.dwMinorVersion, v.dwBuildNumber);
732  rb_hash_aset(result, ID2SYM(rb_intern("release")), release);
733  version = rb_sprintf("%s Version %"PRIsVALUE": %"PRIsVALUE, sysname, release,
734  rb_w32_conv_from_wchar(v.szCSDVersion, rb_utf8_encoding()));
735  rb_hash_aset(result, ID2SYM(rb_intern("version")), version);
736 
737 # if defined _MSC_VER && _MSC_VER < 1300
738 # define GET_COMPUTER_NAME(ptr, plen) GetComputerNameW(ptr, plen)
739 # else
740 # define GET_COMPUTER_NAME(ptr, plen) GetComputerNameExW(ComputerNameDnsFullyQualified, ptr, plen)
741 # endif
742  GET_COMPUTER_NAME(NULL, &len);
743  buf = ALLOCV_N(WCHAR, vbuf, len);
744  if (GET_COMPUTER_NAME(buf, &len)) {
745  nodename = rb_w32_conv_from_wchar(buf, rb_utf8_encoding());
746  }
747  ALLOCV_END(vbuf);
748  if (NIL_P(nodename)) nodename = rb_str_new(0, 0);
749  rb_hash_aset(result, ID2SYM(rb_intern("nodename")), nodename);
750 
751 # ifndef PROCESSOR_ARCHITECTURE_AMD64
752 # define PROCESSOR_ARCHITECTURE_AMD64 9
753 # endif
754 # ifndef PROCESSOR_ARCHITECTURE_IA64
755 # define PROCESSOR_ARCHITECTURE_IA64 6
756 # endif
757 # ifndef PROCESSOR_ARCHITECTURE_INTEL
758 # define PROCESSOR_ARCHITECTURE_INTEL 0
759 # endif
760  GetSystemInfo(&s);
761  switch (s.wProcessorArchitecture) {
762  case PROCESSOR_ARCHITECTURE_AMD64:
763  mach = "x64";
764  break;
765  case PROCESSOR_ARCHITECTURE_ARM:
766  mach = "ARM";
767  break;
768  case PROCESSOR_ARCHITECTURE_IA64:
769  mach = "IA64";
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  */
816 static VALUE
817 etc_sysconf(VALUE obj, VALUE arg)
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  */
853 static VALUE
854 etc_confstr(VALUE obj, VALUE arg)
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  */
899 static VALUE
900 io_pathconf(VALUE io, VALUE arg)
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)
926 static int
927 etc_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) {
951  size = CPU_ALLOC_SIZE(n);
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  */
1003 static 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;
1021  ret = sysconf(_SC_NPROCESSORS_ONLN);
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  */
1062 void
1064 {
1065  VALUE mEtc;
1066 
1067  mEtc = rb_define_module("Etc");
1068  init_constants(mEtc);
1069 
1070  rb_define_module_function(mEtc, "getlogin", etc_getlogin, 0);
1071 
1072  rb_define_module_function(mEtc, "getpwuid", etc_getpwuid, -1);
1073  rb_define_module_function(mEtc, "getpwnam", etc_getpwnam, 1);
1074  rb_define_module_function(mEtc, "setpwent", etc_setpwent, 0);
1075  rb_define_module_function(mEtc, "endpwent", etc_endpwent, 0);
1076  rb_define_module_function(mEtc, "getpwent", etc_getpwent, 0);
1077  rb_define_module_function(mEtc, "passwd", etc_passwd, 0);
1078 
1079  rb_define_module_function(mEtc, "getgrgid", etc_getgrgid, -1);
1080  rb_define_module_function(mEtc, "getgrnam", etc_getgrnam, 1);
1081  rb_define_module_function(mEtc, "group", etc_group, 0);
1082  rb_define_module_function(mEtc, "setgrent", etc_setgrent, 0);
1083  rb_define_module_function(mEtc, "endgrent", etc_endgrent, 0);
1084  rb_define_module_function(mEtc, "getgrent", etc_getgrent, 0);
1085  rb_define_module_function(mEtc, "sysconfdir", etc_sysconfdir, 0);
1086  rb_define_module_function(mEtc, "systmpdir", etc_systmpdir, 0);
1087  rb_define_module_function(mEtc, "uname", etc_uname, 0);
1088  rb_define_module_function(mEtc, "sysconf", etc_sysconf, 1);
1089  rb_define_module_function(mEtc, "confstr", etc_confstr, 1);
1090  rb_define_method(rb_cIO, "pathconf", io_pathconf, 1);
1091  rb_define_module_function(mEtc, "nprocessors", etc_nprocessors, 0);
1092 
1093  sPasswd = rb_struct_define_under(mEtc, "Passwd",
1094  "name",
1095 #ifdef HAVE_STRUCT_PASSWD_PW_PASSWD
1096  "passwd",
1097 #endif
1098  "uid",
1099  "gid",
1100 #ifdef HAVE_STRUCT_PASSWD_PW_GECOS
1101  "gecos",
1102 #endif
1103  "dir",
1104  "shell",
1105 #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE
1106  "change",
1107 #endif
1108 #ifdef HAVE_STRUCT_PASSWD_PW_QUOTA
1109  "quota",
1110 #endif
1111 #ifdef HAVE_STRUCT_PASSWD_PW_AGE
1112  "age",
1113 #endif
1114 #ifdef HAVE_STRUCT_PASSWD_PW_CLASS
1115  "uclass",
1116 #endif
1117 #ifdef HAVE_STRUCT_PASSWD_PW_COMMENT
1118  "comment",
1119 #endif
1120 #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
1121  "expire",
1122 #endif
1123  NULL);
1124 #if 0
1125  /* Define-const: Passwd
1126  *
1127  * Passwd is a Struct that contains the following members:
1128  *
1129  * name::
1130  * contains the short login name of the user as a String.
1131  * passwd::
1132  * contains the encrypted password of the user as a String.
1133  * an 'x' is returned if shadow passwords are in use. An '*' is returned
1134  * if the user cannot log in using a password.
1135  * uid::
1136  * contains the integer user ID (uid) of the user.
1137  * gid::
1138  * contains the integer group ID (gid) of the user's primary group.
1139  * dir::
1140  * contains the path to the home directory of the user as a String.
1141  * shell::
1142  * contains the path to the login shell of the user as a String.
1143  *
1144  * === The following members below are optional, and must be compiled with special flags:
1145  *
1146  * gecos::
1147  * contains a longer String description of the user, such as
1148  * a full name. Some Unix systems provide structured information in the
1149  * gecos field, but this is system-dependent.
1150  * must be compiled with +HAVE_STRUCT_PASSWD_PW_GECOS+
1151  * change::
1152  * password change time(integer) must be compiled with +HAVE_STRUCT_PASSWD_PW_CHANGE+
1153  * quota::
1154  * quota value(integer) must be compiled with +HAVE_STRUCT_PASSWD_PW_QUOTA+
1155  * age::
1156  * password age(integer) must be compiled with +HAVE_STRUCT_PASSWD_PW_AGE+
1157  * class::
1158  * user access class(string) must be compiled with +HAVE_STRUCT_PASSWD_PW_CLASS+
1159  * comment::
1160  * comment(string) must be compiled with +HAVE_STRUCT_PASSWD_PW_COMMENT+
1161  * expire::
1162  * account expiration time(integer) must be compiled with +HAVE_STRUCT_PASSWD_PW_EXPIRE+
1163  */
1164  rb_define_const(mEtc, "Passwd", sPasswd);
1165 #endif
1166  rb_define_const(rb_cStruct, "Passwd", sPasswd); /* deprecated name */
1169 
1170 #ifdef HAVE_GETGRENT
1171  sGroup = rb_struct_define_under(mEtc, "Group", "name",
1172 #ifdef HAVE_STRUCT_GROUP_GR_PASSWD
1173  "passwd",
1174 #endif
1175  "gid", "mem", NULL);
1176 
1177 #if 0
1178  /* Define-const: Group
1179  *
1180  * Group is a Struct that is only available when compiled with +HAVE_GETGRENT+.
1181  *
1182  * The struct contains the following members:
1183  *
1184  * name::
1185  * contains the name of the group as a String.
1186  * passwd::
1187  * contains the encrypted password as a String. An 'x' is
1188  * returned if password access to the group is not available; an empty
1189  * string is returned if no password is needed to obtain membership of
1190  * the group.
1191  *
1192  * Must be compiled with +HAVE_STRUCT_GROUP_GR_PASSWD+.
1193  * gid::
1194  * contains the group's numeric ID as an integer.
1195  * mem::
1196  * is an Array of Strings containing the short login names of the
1197  * members of the group.
1198  */
1199  rb_define_const(mEtc, "Group", sGroup);
1200 #endif
1201  rb_define_const(rb_cStruct, "Group", sGroup); /* deprecated name */
1203  rb_define_singleton_method(sGroup, "each", etc_each_group, 0);
1204 #endif
1205 }
#define NUM2UIDT(v)
Definition: ruby.h:332
static VALUE sPasswd
Definition: etc.c:37
void rb_bug(const char *fmt,...)
Definition: error.c:482
static VALUE etc_getpwnam(VALUE obj, VALUE nam)
Definition: etc.c:214
static VALUE etc_endgrent(VALUE obj)
Definition: etc.c:585
size_t strlen(const char *)
#define INT2NUM(x)
Definition: ruby.h:1538
static VALUE etc_group(VALUE obj)
Definition: etc.c:522
#define NUM2INT(x)
Definition: ruby.h:684
rb_uid_t getuid(void)
Definition: win32.c:2709
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1716
#define MAXPATHLEN
Definition: dln.c:68
#define FL_TAINT
Definition: ruby.h:1220
static VALUE etc_setpwent(VALUE obj)
Definition: etc.c:335
static VALUE etc_getpwent(VALUE obj)
Definition: etc.c:367
#define Qtrue
Definition: ruby.h:437
static VALUE etc_setgrent(VALUE obj)
Definition: etc.c:573
Definition: io.h:62
const int id
Definition: nkf.c:209
VALUE rb_external_str_new_with_enc(const char *ptr, long len, rb_encoding *)
Definition: string.c:998
static void init_constants(VALUE mod)
Definition: constdefs.h:939
VALUE rb_struct_new(VALUE,...)
Definition: struct.c:605
VALUE rb_ary_push(VALUE ary, VALUE item)
Definition: array.c:905
static VALUE etc_getgrnam(VALUE obj, VALUE nam)
Definition: etc.c:457
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2207
static VALUE etc_systmpdir(void)
Definition: etc.c:645
#define CSIDL_COMMON_APPDATA
Definition: win32.c:420
static VALUE etc_getpwuid(int argc, VALUE *argv, VALUE obj)
Definition: etc.c:177
rb_encoding * rb_utf8_encoding(void)
Definition: encoding.c:1320
#define GetOpenFile(obj, fp)
Definition: io.h:120
VALUE rb_struct_define_under(VALUE, const char *,...)
Definition: struct.c:425
char * getenv()
static VALUE etc_passwd(VALUE obj)
Definition: etc.c:282
VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc)
Definition: win32.c:2173
void Init_etc(void)
Definition: etc.c:1063
int rb_block_given_p(void)
Definition: eval.c:797
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
Definition: hash.c:1576
#define io_pathconf
Definition: etc.c:920
VALUE rb_eRuntimeError
Definition: error.c:761
IUnknown DWORD
Definition: win32ole.c:32
VALUE rb_ary_new(void)
Definition: array.c:493
#define NIL_P(v)
Definition: ruby.h:451
static VALUE etc_getgrgid(int argc, VALUE *argv, VALUE obj)
Definition: etc.c:419
int fd
Definition: io.h:64
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:2734
int argc
Definition: ruby.c:183
#define Qfalse
Definition: ruby.h:436
#define ALLOCV_N(type, v, n)
Definition: ruby.h:1657
UINT rb_w32_system_tmpdir(WCHAR *path, UINT len)
Definition: win32.c:515
#define ALLOCV_END(v)
Definition: ruby.h:1658
#define numberof(array)
Definition: etc.c:616
RUBY_EXTERN VALUE rb_cIO
Definition: ruby.h:1892
VALUE rb_locale_str_new_cstr(const char *)
Definition: string.c:1038
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
Definition: class.c:1731
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1020
int errno
VALUE rb_mEnumerable
Definition: enum.c:18
VALUE rb_sprintf(const char *format,...)
Definition: sprintf.c:1440
VALUE rb_hash_new(void)
Definition: hash.c:441
static VALUE etc_getlogin(VALUE obj)
Definition: etc.c:73
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:1919
unsigned char buf[MIME_BUF_SIZE]
Definition: nkf.c:4309
#define PRIsVALUE
Definition: ruby.h:135
#define Qnil
Definition: ruby.h:438
unsigned long VALUE
Definition: ruby.h:85
rb_encoding * rb_locale_encoding(void)
Definition: encoding.c:1370
static VALUE result
Definition: nkf.c:40
void rb_extend_object(VALUE obj, VALUE module)
Definition: eval.c:1429
#define etc_confstr
Definition: etc.c:881
#define NUM2GIDT(v)
Definition: ruby.h:338
VALUE rb_ensure(VALUE(*b_proc)(ANYARGS), VALUE data1, VALUE(*e_proc)(ANYARGS), VALUE data2)
Definition: eval.c:923
VALUE rb_str_new_cstr(const char *)
Definition: string.c:770
#define rb_tainted_str_new2
Definition: intern.h:861
void rb_sys_fail(const char *mesg)
Definition: error.c:2326
#define FL_UNSET(x, f)
Definition: ruby.h:1292
#define etc_sysconf
Definition: etc.c:834
#define LONG2NUM(x)
Definition: ruby.h:1573
char * alloca()
register unsigned int len
Definition: zonetab.h:51
#define endpwent()
#define RSTRING_PTR(str)
Definition: ruby.h:982
char * getlogin()
Definition: win32.c:867
int size
Definition: encoding.c:57
static VALUE etc_each_passwd(VALUE obj)
Definition: etc.c:322
RUBY_EXTERN VALUE rb_cStruct
Definition: ruby.h:1907
VALUE rb_w32_special_folder(int type)
Definition: win32.c:499
static VALUE etc_endpwent(VALUE obj)
Definition: etc.c:347
#define GIDT2NUM(v)
Definition: ruby.h:335
rb_encoding * rb_filesystem_encoding(void)
Definition: encoding.c:1385
#define RETURN_ENUMERATOR(obj, argc, argv)
Definition: intern.h:240
#define SafeStringValue(v)
Definition: ruby.h:574
VALUE rb_filesystem_str_new_cstr(const char *)
Definition: string.c:1050
#define etc_uname
Definition: etc.c:800
const char * name
Definition: nkf.c:208
#define ID2SYM(x)
Definition: ruby.h:383
#define UIDT2NUM(v)
Definition: ruby.h:329
rb_gid_t getgid(void)
Definition: win32.c:2723
#define etc_nprocessors
Definition: etc.c:1033
void void xfree(void *)
VALUE rb_define_module(const char *name)
Definition: class.c:768
#define rb_intern(str)
#define NULL
Definition: _sdbm.c:102
static VALUE etc_sysconfdir(VALUE obj)
Definition: etc.c:632
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1515
int version
Definition: ossl_ssl.c:55
VALUE rb_eArgError
Definition: error.c:763
static VALUE etc_getgrent(VALUE obj)
Definition: etc.c:604
char ** argv
Definition: ruby.c:184
VALUE rb_filesystem_str_new(const char *, long)
Definition: string.c:1044
VALUE rb_str_new(const char *, long)
Definition: string.c:736
#define xcalloc
Definition: defines.h:185