Ruby  2.7.2p137(2020-10-01revision5445e0435260b449decf2ac16f9d09bae3cafe72)
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 
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 #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  */
74 static VALUE
75 etc_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
90  rb_encoding *extenc = rb_locale_encoding();
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)
99 static VALUE
100 safe_setup_str(const char *str)
101 {
102  if (str == 0) str = "";
103  return rb_str_new2(str);
104 }
105 
106 static VALUE
107 safe_setup_locale_str(const char *str)
108 {
109  if (str == 0) str = "";
110  return rb_locale_str_new_cstr(str);
111 }
112 
113 static VALUE
114 safe_setup_filesystem_str(const char *str)
115 {
116  if (str == 0) str = "";
118 }
119 #endif
120 
121 #ifdef HAVE_GETPWENT
122 static VALUE
123 setup_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  */
178 static VALUE
179 etc_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  */
215 static VALUE
216 etc_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
231 static int passwd_blocking = 0;
232 static VALUE
233 passwd_ensure(VALUE _)
234 {
235  endpwent();
236  passwd_blocking = (int)Qfalse;
237  return Qnil;
238 }
239 
240 static VALUE
241 passwd_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 
252 static void
253 each_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  */
283 static VALUE
284 etc_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  */
323 static VALUE
324 etc_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  */
336 static VALUE
337 etc_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  */
348 static VALUE
349 etc_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  */
368 static VALUE
369 etc_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
382 static VALUE
383 setup_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  */
420 static VALUE
421 etc_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  */
458 static VALUE
459 etc_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
474 static int group_blocking = 0;
475 static VALUE
476 group_ensure(VALUE _)
477 {
478  endgrent();
479  group_blocking = (int)Qfalse;
480  return Qnil;
481 }
482 
483 
484 static VALUE
485 group_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 
496 static void
497 each_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  */
523 static VALUE
524 etc_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  */
562 static VALUE
563 etc_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  */
574 static VALUE
575 etc_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  */
586 static VALUE
587 etc_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  */
605 static VALUE
606 etc_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
622 UINT rb_w32_system_tmpdir(WCHAR *path, UINT len);
623 VALUE 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  */
634 static VALUE
635 etc_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  */
647 static VALUE
648 etc_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  */
707 static 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  */
816 static 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  */
853 static 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  */
899 static 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)
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;
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");
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 */
1168  rb_extend_object(sPasswd, rb_mEnumerable);
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 }
rb_struct_define_under
VALUE rb_struct_define_under(VALUE, const char *,...)
Definition: struct.c:446
xcalloc
#define xcalloc
Definition: defines.h:213
rb_str_new2
#define rb_str_new2
Definition: intern.h:903
rb_filesystem_encoding
rb_encoding * rb_filesystem_encoding(void)
Definition: encoding.c:1387
id
const int id
Definition: nkf.c:209
rb_hash_new
VALUE rb_hash_new(void)
Definition: hash.c:1523
rb_block_given_p
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:898
rb_locale_str_new_cstr
#define rb_locale_str_new_cstr(str)
Definition: rb_mjit_min_header-2.7.2.h:6120
etc_nprocessors
#define etc_nprocessors
Definition: etc.c:1033
int
__inline__ int
Definition: rb_mjit_min_header-2.7.2.h:2845
gid_t
__gid_t gid_t
Definition: rb_mjit_min_header-2.7.2.h:1321
RSTRING_PTR
#define RSTRING_PTR(str)
Definition: ruby.h:1009
rb_locale_encoding
rb_encoding * rb_locale_encoding(void)
Definition: encoding.c:1372
VALUE
unsigned long VALUE
Definition: ruby.h:102
long
#define long
Definition: rb_mjit_min_header-2.7.2.h:2889
rb_eArgError
VALUE rb_eArgError
Definition: error.c:925
encoding.h
rb_intern
#define rb_intern(str)
rb_define_module
VALUE rb_define_module(const char *name)
Definition: class.c:772
alloca
#define alloca(size)
Definition: rb_mjit_min_header-2.7.2.h:2493
endpwent
#define endpwent()
DWORD
IUnknown DWORD
Definition: win32ole.c:33
rb_define_singleton_method
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1755
NUM2GIDT
#define NUM2GIDT(v)
Definition: ruby.h:369
rb_define_method
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1551
INT2NUM
#define INT2NUM(x)
Definition: ruby.h:1609
Qfalse
#define Qfalse
Definition: ruby.h:467
rb_io_t::fd
int fd
Definition: io.h:68
NULL
#define NULL
Definition: _sdbm.c:101
rb_filesystem_str_new_cstr
VALUE rb_filesystem_str_new_cstr(const char *)
Definition: string.c:1117
PRIsVALUE
#define PRIsVALUE
Definition: ruby.h:166
rb_w32_special_folder
VALUE rb_w32_special_folder(int type)
Definition: win32.c:499
ID2SYM
#define ID2SYM(x)
Definition: ruby.h:414
strlen
size_t strlen(const char *)
etc_uname
#define etc_uname
Definition: etc.c:800
MAXPATHLEN
#define MAXPATHLEN
Definition: dln.c:69
rb_struct_new
VALUE rb_struct_new(VALUE,...)
Definition: struct.c:730
Init_etc
void Init_etc(void)
Definition: etc.c:1063
RUBY_ETC_VERSION
#define RUBY_ETC_VERSION
Definition: etc.c:55
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2671
getuid
rb_uid_t getuid(void)
Definition: win32.c:2765
LONG2NUM
#define LONG2NUM(x)
Definition: ruby.h:1644
FL_TAINT
#define FL_TAINT
Definition: ruby.h:1283
UIDT2NUM
#define UIDT2NUM(v)
Definition: ruby.h:360
_SC_NPROCESSORS_ONLN
#define _SC_NPROCESSORS_ONLN
Definition: rb_mjit_min_header-2.7.2.h:3385
ALLOCV_END
#define ALLOCV_END(v)
Definition: ruby.h:1750
io_pathconf
#define io_pathconf
Definition: etc.c:920
ALLOCV_N
#define ALLOCV_N(type, v, n)
Definition: ruby.h:1749
OnigEncodingTypeST
Definition: onigmo.h:160
FL_UNSET
#define FL_UNSET(x, f)
Definition: ruby.h:1361
etc_confstr
#define etc_confstr
Definition: etc.c:881
rb_ary_push
VALUE rb_ary_push(VALUE ary, VALUE item)
Definition: array.c:1195
ruby.h
rb_sys_fail
void rb_sys_fail(const char *mesg)
Definition: error.c:2795
rb_eRuntimeError
VALUE rb_eRuntimeError
Definition: error.c:922
RETURN_ENUMERATOR
#define RETURN_ENUMERATOR(obj, argc, argv)
Definition: intern.h:279
rb_uid_t
#define rb_uid_t
Definition: rb_mjit_min_header-2.7.2.h:105
CPU_COUNT_S
#define CPU_COUNT_S(siz, set)
Definition: rb_mjit_min_header-2.7.2.h:1362
size
int size
Definition: encoding.c:58
CSIDL_COMMON_APPDATA
#define CSIDL_COMMON_APPDATA
Definition: win32.c:420
arg
VALUE arg
Definition: rb_mjit_min_header-2.7.2.h:5597
sysconf
long sysconf(int __name)
rb_extend_object
void rb_extend_object(VALUE obj, VALUE module)
Extend the object with the module.
Definition: eval.c:1701
StringValueCStr
#define StringValueCStr(v)
Definition: ruby.h:604
rb_scan_args
#define rb_scan_args(argc, argvp, fmt,...)
Definition: rb_mjit_min_header-2.7.2.h:6368
constdefs.h
rb_define_module_function
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
Definition: class.c:1771
buf
unsigned char buf[MIME_BUF_SIZE]
Definition: nkf.c:4322
obj
const VALUE VALUE obj
Definition: rb_mjit_min_header-2.7.2.h:5738
cpu_set_t
Definition: rb_mjit_min_header-2.7.2.h:1351
rb_bug
void rb_bug(const char *fmt,...)
Definition: error.c:636
argv
char ** argv
Definition: ruby.c:223
sched_getaffinity
int sched_getaffinity(pid_t, size_t, cpu_set_t *)
CPU_ZERO_S
#define CPU_ZERO_S(siz, set)
Definition: rb_mjit_min_header-2.7.2.h:1358
rb_sprintf
VALUE rb_sprintf(const char *format,...)
Definition: sprintf.c:1197
rb_utf8_encoding
rb_encoding * rb_utf8_encoding(void)
Definition: encoding.c:1328
fpathconf
long fpathconf(int __fd, int __name)
str
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
etc_sysconf
#define etc_sysconf
Definition: etc.c:834
path
VALUE path
Definition: rb_mjit_min_header-2.7.2.h:7336
rb_hash_aset
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
Definition: hash.c:2852
NIL_P
#define NIL_P(v)
Definition: ruby.h:482
getlogin
char * getlogin()
Definition: win32.c:881
io.h
rb_external_str_new_with_enc
VALUE rb_external_str_new_with_enc(const char *ptr, long len, rb_encoding *)
Definition: string.c:1036
argc
int argc
Definition: ruby.c:222
rb_define_const
void rb_define_const(VALUE, const char *, VALUE)
Definition: variable.c:2891
getenv
char * getenv()
getgid
rb_gid_t getgid(void)
Definition: win32.c:2779
xfree
#define xfree
Definition: defines.h:216
GetOpenFile
#define GetOpenFile(obj, fp)
Definition: io.h:127
CPU_ALLOC_SIZE
#define CPU_ALLOC_SIZE(num)
Definition: rb_mjit_min_header-2.7.2.h:1355
_
#define _(args)
Definition: dln.h:28
rb_w32_system_tmpdir
UINT rb_w32_system_tmpdir(WCHAR *path, UINT len)
Definition: win32.c:515
Qtrue
#define Qtrue
Definition: ruby.h:468
errno
int errno
v
int VALUE v
Definition: rb_mjit_min_header-2.7.2.h:12300
NUM2UIDT
#define NUM2UIDT(v)
Definition: ruby.h:363
rb_filesystem_str_new
VALUE rb_filesystem_str_new(const char *, long)
Definition: string.c:1111
len
uint8_t len
Definition: escape.c:17
rb_cStruct
RUBY_EXTERN VALUE rb_cStruct
Definition: ruby.h:2045
rb_yield
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1237
rb_str_new_cstr
#define rb_str_new_cstr(str)
Definition: rb_mjit_min_header-2.7.2.h:6113
rb_ensure
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1115
rb_ary_new
VALUE rb_ary_new(void)
Definition: array.c:723
confstr
size_t confstr(int __name, char *__buf, size_t __len)
NUM2INT
#define NUM2INT(x)
Definition: ruby.h:715
Qnil
#define Qnil
Definition: ruby.h:469
rb_str_new
#define rb_str_new(str, len)
Definition: rb_mjit_min_header-2.7.2.h:6112
rb_mEnumerable
VALUE rb_mEnumerable
Definition: enum.c:20
rb_io_t
Definition: io.h:66
numberof
#define numberof(array)
Definition: etc.c:618
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:39
rb_cIO
RUBY_EXTERN VALUE rb_cIO
Definition: ruby.h:2030
rb_w32_conv_from_wchar
VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc)
Definition: win32.c:2229
GIDT2NUM
#define GIDT2NUM(v)
Definition: ruby.h:366
name
const char * name
Definition: nkf.c:208
n
const char size_t n
Definition: rb_mjit_min_header-2.7.2.h:5452