1 /**********************************************************************
6 created at: Tue Aug 10 14:30:50 JST 1993
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
9 Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
10 Copyright (C) 2000 Information-technology Promotion Agency, Japan
12 **********************************************************************/
14 #include "ruby/internal/config.h"
16 #include "ruby/fiber/scheduler.h"
42 # define EXIT_SUCCESS 0
46 # define EXIT_FAILURE 1
49 #ifdef HAVE_SYS_WAIT_H
50 # include <sys/wait.h>
53 #ifdef HAVE_SYS_RESOURCE_H
54 # include <sys/resource.h>
61 #ifdef HAVE_SYS_PARAM_H
62 # include <sys/param.h>
66 # define MAXPATHLEN 1024
71 #ifdef HAVE_SYS_TIME_H
72 # include <sys/time.h>
75 #ifdef HAVE_SYS_TIMES_H
76 # include <sys/times.h>
86 int initgroups(const char *, rb_gid_t
);
95 # include <mach/mach_time.h>
100 #include "internal.h"
101 #include "internal/bits.h"
102 #include "internal/dir.h"
103 #include "internal/error.h"
104 #include "internal/eval.h"
105 #include "internal/hash.h"
106 #include "internal/io.h"
107 #include "internal/numeric.h"
108 #include "internal/object.h"
109 #include "internal/process.h"
110 #include "internal/thread.h"
111 #include "internal/variable.h"
112 #include "internal/warnings.h"
116 #include "ruby/thread.h"
117 #include "ruby/util.h"
120 #include "ruby/ractor.h"
122 /* define system APIs */
125 #define open rb_w32_uopen
128 #if defined(HAVE_TIMES) || defined(_WIN32)
129 /*********************************************************************
131 * Document-class: Process::Tms
133 * Placeholder for rusage
135 static VALUE rb_cProcessTms
;
139 #define WIFEXITED(w) (((w) & 0xff) == 0)
142 #define WIFSIGNALED(w) (((w) & 0x7f) > 0 && (((w) & 0x7f) < 0x7f))
145 #define WIFSTOPPED(w) (((w) & 0xff) == 0x7f)
148 #define WEXITSTATUS(w) (((w) >> 8) & 0xff)
151 #define WTERMSIG(w) ((w) & 0x7f)
154 #define WSTOPSIG WEXITSTATUS
157 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__)
158 #define HAVE_44BSD_SETUID 1
159 #define HAVE_44BSD_SETGID 1
167 #ifdef BROKEN_SETREUID
168 #define setreuid ruby_setreuid
169 int setreuid(rb_uid_t ruid
, rb_uid_t euid
);
171 #ifdef BROKEN_SETREGID
172 #define setregid ruby_setregid
173 int setregid(rb_gid_t rgid
, rb_gid_t egid
);
176 #if defined(HAVE_44BSD_SETUID) || defined(__APPLE__)
177 #if !defined(USE_SETREUID) && !defined(BROKEN_SETREUID)
178 #define OBSOLETE_SETREUID 1
180 #if !defined(USE_SETREGID) && !defined(BROKEN_SETREGID)
181 #define OBSOLETE_SETREGID 1
185 static void check_uid_switch(void);
186 static void check_gid_switch(void);
187 static int exec_async_signal_safe(const struct rb_execarg
*, char *, size_t);
189 VALUE
rb_envtbl(void);
190 VALUE
rb_env_to_hash(void);
193 #define p_uid_from_name p_uid_from_name
194 #define p_gid_from_name p_gid_from_name
197 #if defined(HAVE_UNISTD_H)
198 # if defined(HAVE_GETLOGIN_R)
199 # define USE_GETLOGIN_R 1
200 # define GETLOGIN_R_SIZE_DEFAULT 0x100
201 # define GETLOGIN_R_SIZE_LIMIT 0x1000
202 # if defined(_SC_LOGIN_NAME_MAX)
203 # define GETLOGIN_R_SIZE_INIT sysconf(_SC_LOGIN_NAME_MAX)
205 # define GETLOGIN_R_SIZE_INIT GETLOGIN_R_SIZE_DEFAULT
207 # elif defined(HAVE_GETLOGIN)
208 # define USE_GETLOGIN 1
212 #if defined(HAVE_PWD_H)
213 # if defined(HAVE_GETPWUID_R)
214 # define USE_GETPWUID_R 1
215 # elif defined(HAVE_GETPWUID)
216 # define USE_GETPWUID 1
218 # if defined(HAVE_GETPWNAM_R)
219 # define USE_GETPWNAM_R 1
220 # elif defined(HAVE_GETPWNAM)
221 # define USE_GETPWNAM 1
223 # if defined(HAVE_GETPWNAM_R) || defined(HAVE_GETPWUID_R)
224 # define GETPW_R_SIZE_DEFAULT 0x1000
225 # define GETPW_R_SIZE_LIMIT 0x10000
226 # if defined(_SC_GETPW_R_SIZE_MAX)
227 # define GETPW_R_SIZE_INIT sysconf(_SC_GETPW_R_SIZE_MAX)
229 # define GETPW_R_SIZE_INIT GETPW_R_SIZE_DEFAULT
232 # ifdef USE_GETPWNAM_R
233 # define PREPARE_GETPWNAM \
235 # define FINISH_GETPWNAM \
236 (getpw_buf ? (void)rb_str_resize(getpw_buf, 0) : (void)0)
237 # define OBJ2UID1(id) obj2uid((id), &getpw_buf)
238 # define OBJ2UID(id) obj2uid0(id)
239 static rb_uid_t
obj2uid(VALUE id
, VALUE
*getpw_buf
);
240 static inline rb_uid_t
250 # define PREPARE_GETPWNAM /* do nothing */
251 # define FINISH_GETPWNAM /* do nothing */
252 # define OBJ2UID1(id) obj2uid((id))
253 # define OBJ2UID(id) obj2uid((id))
254 static rb_uid_t
obj2uid(VALUE id
);
257 # define PREPARE_GETPWNAM /* do nothing */
258 # define FINISH_GETPWNAM /* do nothing */
259 # define OBJ2UID1(id) NUM2UIDT(id)
260 # define OBJ2UID(id) NUM2UIDT(id)
261 # ifdef p_uid_from_name
262 # undef p_uid_from_name
263 # define p_uid_from_name rb_f_notimplement
267 #if defined(HAVE_GRP_H)
268 # if defined(HAVE_GETGRNAM_R) && defined(_SC_GETGR_R_SIZE_MAX)
269 # define USE_GETGRNAM_R
270 # define GETGR_R_SIZE_INIT sysconf(_SC_GETGR_R_SIZE_MAX)
271 # define GETGR_R_SIZE_DEFAULT 0x1000
272 # define GETGR_R_SIZE_LIMIT 0x10000
274 # ifdef USE_GETGRNAM_R
275 # define PREPARE_GETGRNAM \
277 # define FINISH_GETGRNAM \
278 (getgr_buf ? (void)rb_str_resize(getgr_buf, 0) : (void)0)
279 # define OBJ2GID1(id) obj2gid((id), &getgr_buf)
280 # define OBJ2GID(id) obj2gid0(id)
281 static rb_gid_t
obj2gid(VALUE id
, VALUE
*getgr_buf
);
282 static inline rb_gid_t
291 static rb_gid_t
obj2gid(VALUE id
, VALUE
*getgr_buf
);
293 # define PREPARE_GETGRNAM /* do nothing */
294 # define FINISH_GETGRNAM /* do nothing */
295 # define OBJ2GID1(id) obj2gid((id))
296 # define OBJ2GID(id) obj2gid((id))
297 static rb_gid_t
obj2gid(VALUE id
);
300 # define PREPARE_GETGRNAM /* do nothing */
301 # define FINISH_GETGRNAM /* do nothing */
302 # define OBJ2GID1(id) NUM2GIDT(id)
303 # define OBJ2GID(id) NUM2GIDT(id)
304 # ifdef p_gid_from_name
305 # undef p_gid_from_name
306 # define p_gid_from_name rb_f_notimplement
310 #if SIZEOF_CLOCK_T == SIZEOF_INT
311 typedef unsigned int unsigned_clock_t
;
312 #elif SIZEOF_CLOCK_T == SIZEOF_LONG
313 typedef unsigned long unsigned_clock_t
;
314 #elif defined(HAVE_LONG_LONG) && SIZEOF_CLOCK_T == SIZEOF_LONG_LONG
315 typedef unsigned LONG_LONG unsigned_clock_t
;
318 typedef void (*sig_t
) (int);
321 #define id_exception idException
322 static ID id_in
, id_out
, id_err
, id_pid
, id_uid
, id_gid
;
323 static ID id_close
, id_child
;
328 static ID id_new_pgroup
;
330 static ID id_unsetenv_others
, id_chdir
, id_umask
, id_close_others
;
331 static ID id_nanosecond
, id_microsecond
, id_millisecond
, id_second
;
332 static ID id_float_microsecond
, id_float_millisecond
, id_float_second
;
333 static ID id_GETTIMEOFDAY_BASED_CLOCK_REALTIME
, id_TIME_BASED_CLOCK_REALTIME
;
334 #ifdef CLOCK_REALTIME
335 static ID id_CLOCK_REALTIME
;
336 # define RUBY_CLOCK_REALTIME ID2SYM(id_CLOCK_REALTIME)
338 #ifdef CLOCK_MONOTONIC
339 static ID id_CLOCK_MONOTONIC
;
340 # define RUBY_CLOCK_MONOTONIC ID2SYM(id_CLOCK_MONOTONIC)
342 #ifdef CLOCK_PROCESS_CPUTIME_ID
343 static ID id_CLOCK_PROCESS_CPUTIME_ID
;
344 # define RUBY_CLOCK_PROCESS_CPUTIME_ID ID2SYM(id_CLOCK_PROCESS_CPUTIME_ID)
346 #ifdef CLOCK_THREAD_CPUTIME_ID
347 static ID id_CLOCK_THREAD_CPUTIME_ID
;
348 # define RUBY_CLOCK_THREAD_CPUTIME_ID ID2SYM(id_CLOCK_THREAD_CPUTIME_ID)
351 static ID id_TIMES_BASED_CLOCK_MONOTONIC
;
352 static ID id_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
;
355 static ID id_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
;
357 static ID id_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
;
359 static ID id_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
;
360 # define RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC ID2SYM(id_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC)
364 static rb_pid_t cached_pid
;
366 /* execv and execl are async-signal-safe since SUSv4 (POSIX.1-2008, XPG7) */
367 #if defined(__sun) && !defined(_XPG7) /* Solaris 10, 9, ... */
368 #define execv(path, argv) (rb_async_bug_errno("unreachable: async-signal-unsafe execv() is called", 0))
369 #define execl(path, arg0, arg1, arg2, term) do { extern char **environ; execle((path), (arg0), (arg1), (arg2), (term), (environ)); } while (0)
370 #define ALWAYS_NEED_ENVP 1
372 #define ALWAYS_NEED_ENVP 0
376 assert_close_on_exec(int fd
)
378 #if VM_CHECK_MODE > 0
379 #if defined(HAVE_FCNTL) && defined(F_GETFD) && defined(FD_CLOEXEC)
380 int flags
= fcntl(fd
, F_GETFD
);
382 static const char m
[] = "reserved FD closed unexpectedly?\n";
383 (void)!write(2, m
, sizeof(m
) - 1);
386 if (flags
& FD_CLOEXEC
) return;
387 rb_bug("reserved FD did not have close-on-exec set");
389 rb_bug("reserved FD without close-on-exec support");
390 #endif /* FD_CLOEXEC */
391 #endif /* VM_CHECK_MODE */
395 close_unless_reserved(int fd
)
397 if (rb_reserved_fd_p(fd
)) { /* async-signal-safe */
398 assert_close_on_exec(fd
);
401 return close(fd
); /* async-signal-safe */
404 /*#define DEBUG_REDIRECT*/
405 #if defined(DEBUG_REDIRECT)
408 ttyprintf(const char *fmt
, ...)
414 tty
= fopen("con", "w");
416 tty
= fopen("/dev/tty", "w");
422 vfprintf(tty
, fmt
, ap
);
429 redirect_dup(int oldfd
)
433 ttyprintf("dup(%d) => %d\n", oldfd
, ret
);
438 redirect_dup2(int oldfd
, int newfd
)
441 ret
= dup2(oldfd
, newfd
);
442 ttyprintf("dup2(%d, %d) => %d\n", oldfd
, newfd
, ret
);
447 redirect_cloexec_dup(int oldfd
)
450 ret
= rb_cloexec_dup(oldfd
);
451 ttyprintf("cloexec_dup(%d) => %d\n", oldfd
, ret
);
456 redirect_cloexec_dup2(int oldfd
, int newfd
)
459 ret
= rb_cloexec_dup2(oldfd
, newfd
);
460 ttyprintf("cloexec_dup2(%d, %d) => %d\n", oldfd
, newfd
, ret
);
465 redirect_close(int fd
)
468 ret
= close_unless_reserved(fd
);
469 ttyprintf("close(%d) => %d\n", fd
, ret
);
474 parent_redirect_open(const char *pathname
, int flags
, mode_t perm
)
477 ret
= rb_cloexec_open(pathname
, flags
, perm
);
478 ttyprintf("parent_open(\"%s\", 0x%x, 0%o) => %d\n", pathname
, flags
, perm
, ret
);
483 parent_redirect_close(int fd
)
486 ret
= close_unless_reserved(fd
);
487 ttyprintf("parent_close(%d) => %d\n", fd
, ret
);
492 #define redirect_dup(oldfd) dup(oldfd)
493 #define redirect_dup2(oldfd, newfd) dup2((oldfd), (newfd))
494 #define redirect_cloexec_dup(oldfd) rb_cloexec_dup(oldfd)
495 #define redirect_cloexec_dup2(oldfd, newfd) rb_cloexec_dup2((oldfd), (newfd))
496 #define redirect_close(fd) close_unless_reserved(fd)
497 #define parent_redirect_open(pathname, flags, perm) rb_cloexec_open((pathname), (flags), (perm))
498 #define parent_redirect_close(fd) close_unless_reserved(fd)
504 if (UNLIKELY(!cached_pid
)) { /* 0 is not a valid pid */
505 cached_pid
= getpid();
507 /* pid should be likely POSFIXABLE() */
508 return PIDT2NUM(cached_pid
);
511 #if defined HAVE_WORKING_FORK || defined HAVE_DAEMON
513 clear_pid_cache(void)
521 * Process.pid -> integer
523 * Returns the process ID of the current process:
525 * Process.pid # => 15668
530 proc_get_pid(VALUE _
)
538 return PIDT2NUM(getppid());
543 * Process.ppid -> integer
545 * Returns the process ID of the parent of the current process:
547 * puts "Pid is #{Process.pid}."
548 * fork { puts "Parent pid is #{Process.ppid}." }
553 * Parent pid is 271290.
555 * May not return a trustworthy value on certain platforms.
559 proc_get_ppid(VALUE _
)
565 /*********************************************************************
567 * Document-class: Process::Status
569 * A Process::Status contains information about a system process.
571 * Thread-local variable <tt>$?</tt> is initially +nil+.
572 * Some methods assign to it a Process::Status object
573 * that represents a system process (either running or terminated):
575 * `ruby -e "exit 99"`
576 * stat = $? # => #<Process::Status: pid 1262862 exit 99>
577 * stat.class # => Process::Status
578 * stat.to_i # => 25344
579 * stat.stopped? # => false
580 * stat.exited? # => true
581 * stat.exitstatus # => 99
585 static VALUE rb_cProcessStatus
;
587 struct rb_process_status
{
593 static const rb_data_type_t rb_process_status_type
= {
594 .wrap_struct_name
= "Process::Status",
597 .dfree
= RUBY_DEFAULT_FREE
,
600 .flags
= RUBY_TYPED_FREE_IMMEDIATELY
| RUBY_TYPED_WB_PROTECTED
| RUBY_TYPED_EMBEDDABLE
,
604 rb_process_status_allocate(VALUE klass
)
606 struct rb_process_status
*data
;
607 return TypedData_Make_Struct(klass
, struct rb_process_status
, &rb_process_status_type
, data
);
611 rb_last_status_get(void)
613 return GET_THREAD()->last_status
;
618 * Process.last_status -> Process::Status or nil
620 * Returns a Process::Status object representing the most recently exited
621 * child process in the current thread, or +nil+ if none:
623 * Process.spawn('ruby', '-e', 'exit 13')
625 * Process.last_status # => #<Process::Status: pid 14396 exit 13>
627 * Process.spawn('ruby', '-e', 'exit 14')
629 * Process.last_status # => #<Process::Status: pid 4692 exit 14>
631 * Process.spawn('ruby', '-e', 'exit 15')
632 * # 'exit 15' has not been reaped by #wait.
633 * Process.last_status # => #<Process::Status: pid 4692 exit 14>
635 * Process.last_status # => #<Process::Status: pid 1380 exit 15>
639 proc_s_last_status(VALUE mod
)
641 return rb_last_status_get();
645 rb_process_status_new(rb_pid_t pid
, int status
, int error
)
647 VALUE last_status
= rb_process_status_allocate(rb_cProcessStatus
);
648 struct rb_process_status
*data
= RTYPEDDATA_GET_DATA(last_status
);
650 data
->status
= status
;
653 rb_obj_freeze(last_status
);
658 process_status_dump(VALUE status
)
660 VALUE dump
= rb_class_new_instance(0, 0, rb_cObject
);
661 struct rb_process_status
*data
;
662 TypedData_Get_Struct(status
, struct rb_process_status
, &rb_process_status_type
, data
);
664 rb_ivar_set(dump
, id_status
, INT2NUM(data
->status
));
665 rb_ivar_set(dump
, id_pid
, PIDT2NUM(data
->pid
));
671 process_status_load(VALUE real_obj
, VALUE load_obj
)
673 struct rb_process_status
*data
= rb_check_typeddata(real_obj
, &rb_process_status_type
);
674 VALUE status
= rb_attr_get(load_obj
, id_status
);
675 VALUE pid
= rb_attr_get(load_obj
, id_pid
);
676 data
->pid
= NIL_P(pid
) ? 0 : NUM2PIDT(pid
);
677 data
->status
= NIL_P(status
) ? 0 : NUM2INT(status
);
682 rb_last_status_set(int status
, rb_pid_t pid
)
684 GET_THREAD()->last_status
= rb_process_status_new(pid
, status
, 0);
688 last_status_clear(rb_thread_t
*th
)
690 th
->last_status
= Qnil
;
694 rb_last_status_clear(void)
696 last_status_clear(GET_THREAD());
700 pst_pid(VALUE status
)
702 struct rb_process_status
*data
;
703 TypedData_Get_Struct(status
, struct rb_process_status
, &rb_process_status_type
, data
);
708 pst_status(VALUE status
)
710 struct rb_process_status
*data
;
711 TypedData_Get_Struct(status
, struct rb_process_status
, &rb_process_status_type
, data
);
719 * Returns the system-dependent integer status of +self+:
728 int status
= pst_status(self
);
729 return RB_INT2NUM(status
);
732 #define PST2INT(st) pst_status(st)
738 * Returns the process ID of the process:
741 * $?.pid # => 1247002
746 pst_pid_m(VALUE self
)
748 rb_pid_t pid
= pst_pid(self
);
749 return PIDT2NUM(pid
);
752 static VALUE
pst_message_status(VALUE str
, int status
);
755 pst_message(VALUE str
, rb_pid_t pid
, int status
)
757 rb_str_catf(str
, "pid %ld", (long)pid
);
758 pst_message_status(str
, status
);
762 pst_message_status(VALUE str
, int status
)
764 if (WIFSTOPPED(status
)) {
765 int stopsig
= WSTOPSIG(status
);
766 const char *signame
= ruby_signal_name(stopsig
);
768 rb_str_catf(str
, " stopped SIG%s (signal %d)", signame
, stopsig
);
771 rb_str_catf(str
, " stopped signal %d", stopsig
);
774 if (WIFSIGNALED(status
)) {
775 int termsig
= WTERMSIG(status
);
776 const char *signame
= ruby_signal_name(termsig
);
778 rb_str_catf(str
, " SIG%s (signal %d)", signame
, termsig
);
781 rb_str_catf(str
, " signal %d", termsig
);
784 if (WIFEXITED(status
)) {
785 rb_str_catf(str
, " exit %d", WEXITSTATUS(status
));
788 if (WCOREDUMP(status
)) {
789 rb_str_cat2(str
, " (core dumped)");
800 * Returns a string representation of +self+:
803 * $?.to_s # => "pid 1262141 exit 1"
816 status
= PST2INT(st
);
818 str
= rb_str_buf_new(0);
819 pst_message(str
, pid
, status
);
828 * Returns a string representation of +self+:
831 * $?.inspect # => "#<Process::Status: pid 1303494 exit 1>"
836 pst_inspect(VALUE st
)
844 return rb_sprintf("#<%s: uninitialized>", rb_class2name(CLASS_OF(st
)));
846 status
= PST2INT(st
);
848 str
= rb_sprintf("#<%s: ", rb_class2name(CLASS_OF(st
)));
849 pst_message(str
, pid
, status
);
850 rb_str_cat2(str
, ">");
857 * stat == other -> true or false
859 * Returns whether the value of #to_i == +other+:
862 * stat = $? # => #<Process::Status: pid 1170366 exit 1>
863 * sprintf('%x', stat.to_i) # => "100"
864 * stat == 0x100 # => true
869 pst_equal(VALUE st1
, VALUE st2
)
871 if (st1
== st2
) return Qtrue
;
872 return rb_equal(pst_to_i(st1
), st2
);
878 * stat & mask -> integer
880 * This method is deprecated as #to_i value is system-specific; use
881 * predicate methods like #exited? or #stopped?, or getters like #exitstatus
884 * Returns the logical AND of the value of #to_i with +mask+:
887 * stat = $? # => #<Process::Status: pid 1155508 exit 1>
888 * sprintf('%x', stat.to_i) # => "100"
891 * ArgumentError is raised if +mask+ is negative.
895 pst_bitand(VALUE st1
, VALUE st2
)
897 int status
= PST2INT(st1
);
898 int mask
= NUM2INT(st2
);
901 rb_raise(rb_eArgError
, "negative mask value: %d", mask
);
903 #define WARN_SUGGEST(suggest) \
904 rb_warn_deprecated_to_remove_at(3.5, "Process::Status#&", suggest)
908 WARN_SUGGEST("Process::Status#coredump?");
911 WARN_SUGGEST("Process::Status#signaled? or Process::Status#termsig");
914 WARN_SUGGEST("Process::Status#exited?, Process::Status#stopped? or Process::Status#coredump?");
917 WARN_SUGGEST("Process::Status#exitstatus or Process::Status#stopsig");
920 WARN_SUGGEST("other Process::Status predicates");
926 return INT2NUM(status
);
932 * stat >> places -> integer
934 * This method is deprecated as #to_i value is system-specific; use
935 * predicate methods like #exited? or #stopped?, or getters like #exitstatus
938 * Returns the value of #to_i, shifted +places+ to the right:
941 * stat = $? # => #<Process::Status: pid 1155508 exit 1>
946 * ArgumentError is raised if +places+ is negative.
950 pst_rshift(VALUE st1
, VALUE st2
)
952 int status
= PST2INT(st1
);
953 int places
= NUM2INT(st2
);
956 rb_raise(rb_eArgError
, "negative shift value: %d", places
);
958 #define WARN_SUGGEST(suggest) \
959 rb_warn_deprecated_to_remove_at(3.5, "Process::Status#>>", suggest)
963 WARN_SUGGEST("Process::Status#coredump?");
966 WARN_SUGGEST("Process::Status#exitstatus or Process::Status#stopsig");
969 WARN_SUGGEST("other Process::Status attributes");
975 return INT2NUM(status
);
981 * stopped? -> true or false
983 * Returns +true+ if this process is stopped,
984 * and if the corresponding #wait call had the Process::WUNTRACED flag set,
989 pst_wifstopped(VALUE st
)
991 int status
= PST2INT(st
);
993 return RBOOL(WIFSTOPPED(status
));
999 * stopsig -> integer or nil
1001 * Returns the number of the signal that caused the process to stop,
1002 * or +nil+ if the process is not stopped.
1006 pst_wstopsig(VALUE st
)
1008 int status
= PST2INT(st
);
1010 if (WIFSTOPPED(status
))
1011 return INT2NUM(WSTOPSIG(status
));
1018 * signaled? -> true or false
1020 * Returns +true+ if the process terminated because of an uncaught signal,
1021 * +false+ otherwise.
1025 pst_wifsignaled(VALUE st
)
1027 int status
= PST2INT(st
);
1029 return RBOOL(WIFSIGNALED(status
));
1035 * termsig -> integer or nil
1037 * Returns the number of the signal that caused the process to terminate
1038 * or +nil+ if the process was not terminated by an uncaught signal.
1042 pst_wtermsig(VALUE st
)
1044 int status
= PST2INT(st
);
1046 if (WIFSIGNALED(status
))
1047 return INT2NUM(WTERMSIG(status
));
1054 * exited? -> true or false
1056 * Returns +true+ if the process exited normally
1057 * (for example using an <code>exit()</code> call or finishing the
1058 * program), +false+ if not.
1062 pst_wifexited(VALUE st
)
1064 int status
= PST2INT(st
);
1066 return RBOOL(WIFEXITED(status
));
1072 * exitstatus -> integer or nil
1074 * Returns the least significant eight bits of the return code
1075 * of the process if it has exited;
1079 * $?.exitstatus # => 99
1084 pst_wexitstatus(VALUE st
)
1086 int status
= PST2INT(st
);
1088 if (WIFEXITED(status
))
1089 return INT2NUM(WEXITSTATUS(status
));
1096 * success? -> true, false, or nil
1100 * - +true+ if the process has completed successfully and exited.
1101 * - +false+ if the process has completed unsuccessfully and exited.
1102 * - +nil+ if the process has not exited.
1107 pst_success_p(VALUE st
)
1109 int status
= PST2INT(st
);
1111 if (!WIFEXITED(status
))
1113 return RBOOL(WEXITSTATUS(status
) == EXIT_SUCCESS
);
1119 * coredump? -> true or false
1121 * Returns +true+ if the process generated a coredump
1122 * when it terminated, +false+ if not.
1124 * Not available on all platforms.
1128 pst_wcoredump(VALUE st
)
1131 int status
= PST2INT(st
);
1133 return RBOOL(WCOREDUMP(status
));
1140 do_waitpid(rb_pid_t pid
, int *st
, int flags
)
1142 #if defined HAVE_WAITPID
1143 return waitpid(pid
, st
, flags
);
1144 #elif defined HAVE_WAIT4
1145 return wait4(pid
, st
, flags
, NULL
);
1147 # error waitpid or wait4 is required.
1151 struct waitpid_state
{
1152 struct ccan_list_node wnode
;
1153 rb_execution_context_t
*ec
;
1154 rb_nativethread_cond_t
*cond
;
1163 waitpid_state_init(struct waitpid_state
*w
, rb_pid_t pid
, int options
)
1167 w
->options
= options
;
1173 waitpid_blocking_no_SIGCHLD(void *x
)
1175 struct waitpid_state
*w
= x
;
1177 w
->ret
= do_waitpid(w
->pid
, &w
->status
, w
->options
);
1183 waitpid_no_SIGCHLD(struct waitpid_state
*w
)
1185 if (w
->options
& WNOHANG
) {
1186 w
->ret
= do_waitpid(w
->pid
, &w
->status
, w
->options
);
1190 rb_thread_call_without_gvl(waitpid_blocking_no_SIGCHLD
, w
, RUBY_UBF_PROCESS
, 0);
1191 } while (w
->ret
< 0 && errno
== EINTR
&& (RUBY_VM_CHECK_INTS(w
->ec
),1));
1198 rb_process_status_wait(rb_pid_t pid
, int flags
)
1200 // We only enter the scheduler if we are "blocking":
1201 if (!(flags
& WNOHANG
)) {
1202 VALUE scheduler
= rb_fiber_scheduler_current();
1203 VALUE result
= rb_fiber_scheduler_process_wait(scheduler
, pid
, flags
);
1204 if (!UNDEF_P(result
)) return result
;
1207 struct waitpid_state waitpid_state
;
1209 waitpid_state_init(&waitpid_state
, pid
, flags
);
1210 waitpid_state
.ec
= GET_EC();
1212 waitpid_no_SIGCHLD(&waitpid_state
);
1214 if (waitpid_state
.ret
== 0) return Qnil
;
1216 return rb_process_status_new(waitpid_state
.ret
, waitpid_state
.status
, waitpid_state
.errnum
);
1221 * Process::Status.wait(pid = -1, flags = 0) -> Process::Status
1223 * Like Process.wait, but returns a Process::Status object
1224 * (instead of an integer pid or nil);
1225 * see Process.wait for the values of +pid+ and +flags+.
1227 * If there are child processes,
1228 * waits for a child process to exit and returns a Process::Status object
1229 * containing information on that process;
1230 * sets thread-local variable <tt>$?</tt>:
1232 * Process.spawn('cat /nop') # => 1155880
1233 * Process::Status.wait # => #<Process::Status: pid 1155880 exit 1>
1234 * $? # => #<Process::Status: pid 1155508 exit 1>
1236 * If there is no child process,
1237 * returns an "empty" Process::Status object
1238 * that does not represent an actual process;
1239 * does not set thread-local variable <tt>$?</tt>:
1241 * Process::Status.wait # => #<Process::Status: pid -1 exit 0>
1242 * $? # => #<Process::Status: pid 1155508 exit 1> # Unchanged.
1244 * May invoke the scheduler hook Fiber::Scheduler#process_wait.
1246 * Not available on all platforms.
1250 rb_process_status_waitv(int argc
, VALUE
*argv
, VALUE _
)
1252 rb_check_arity(argc
, 0, 2);
1258 pid
= NUM2PIDT(argv
[0]);
1262 flags
= RB_NUM2INT(argv
[1]);
1265 return rb_process_status_wait(pid
, flags
);
1269 rb_waitpid(rb_pid_t pid
, int *st
, int flags
)
1271 VALUE status
= rb_process_status_wait(pid
, flags
);
1272 if (NIL_P(status
)) return 0;
1274 struct rb_process_status
*data
= rb_check_typeddata(status
, &rb_process_status_type
);
1277 if (st
) *st
= data
->status
;
1280 errno
= data
->error
;
1283 GET_THREAD()->last_status
= status
;
1290 proc_wait(int argc
, VALUE
*argv
)
1296 if (rb_check_arity(argc
, 0, 2) == 0) {
1301 pid
= NUM2PIDT(argv
[0]);
1302 if (argc
== 2 && !NIL_P(vflags
= argv
[1])) {
1303 flags
= NUM2UINT(vflags
);
1307 if ((pid
= rb_waitpid(pid
, &status
, flags
)) < 0)
1311 rb_last_status_clear();
1315 return PIDT2NUM(pid
);
1318 /* [MG]:FIXME: I wasn't sure how this should be done, since ::wait()
1319 has historically been documented as if it didn't take any arguments
1320 despite the fact that it's just an alias for ::waitpid(). The way I
1321 have it below is more truthful, but a little confusing.
1323 I also took the liberty of putting in the pid values, as they're
1324 pretty useful, and it looked as if the original 'ri' output was
1325 supposed to contain them after "[...]depending on the value of
1328 The 'ansi' and 'bs' formats of the ri output don't display the
1329 definition list for some reason, but the plain text one does.
1334 * Process.wait(pid = -1, flags = 0) -> integer
1336 * Waits for a suitable child process to exit, returns its process ID,
1337 * and sets <tt>$?</tt> to a Process::Status object
1338 * containing information on that process.
1339 * Which child it waits for depends on the value of the given +pid+:
1341 * - Positive integer: Waits for the child process whose process ID is +pid+:
1343 * pid0 = Process.spawn('ruby', '-e', 'exit 13') # => 230866
1344 * pid1 = Process.spawn('ruby', '-e', 'exit 14') # => 230891
1345 * Process.wait(pid0) # => 230866
1346 * $? # => #<Process::Status: pid 230866 exit 13>
1347 * Process.wait(pid1) # => 230891
1348 * $? # => #<Process::Status: pid 230891 exit 14>
1349 * Process.wait(pid0) # Raises Errno::ECHILD
1351 * - <tt>0</tt>: Waits for any child process whose group ID
1352 * is the same as that of the current process:
1354 * parent_pgpid = Process.getpgid(Process.pid)
1355 * puts "Parent process group ID is #{parent_pgpid}."
1356 * child0_pid = fork do
1357 * puts "Child 0 pid is #{Process.pid}"
1358 * child0_pgid = Process.getpgid(Process.pid)
1359 * puts "Child 0 process group ID is #{child0_pgid} (same as parent's)."
1361 * child1_pid = fork do
1362 * puts "Child 1 pid is #{Process.pid}"
1363 * Process.setpgid(0, Process.pid)
1364 * child1_pgid = Process.getpgid(Process.pid)
1365 * puts "Child 1 process group ID is #{child1_pgid} (different from parent's)."
1367 * retrieved_pid = Process.wait(0)
1368 * puts "Process.wait(0) returned pid #{retrieved_pid}, which is child 0 pid."
1371 * rescue Errno::ECHILD => x
1372 * puts "Raised #{x.class}, because child 1 process group ID differs from parent process group ID."
1377 * Parent process group ID is 225764.
1378 * Child 0 pid is 225788
1379 * Child 0 process group ID is 225764 (same as parent's).
1380 * Child 1 pid is 225789
1381 * Child 1 process group ID is 225789 (different from parent's).
1382 * Process.wait(0) returned pid 225788, which is child 0 pid.
1383 * Raised Errno::ECHILD, because child 1 process group ID differs from parent process group ID.
1385 * - <tt>-1</tt> (default): Waits for any child process:
1387 * parent_pgpid = Process.getpgid(Process.pid)
1388 * puts "Parent process group ID is #{parent_pgpid}."
1389 * child0_pid = fork do
1390 * puts "Child 0 pid is #{Process.pid}"
1391 * child0_pgid = Process.getpgid(Process.pid)
1392 * puts "Child 0 process group ID is #{child0_pgid} (same as parent's)."
1394 * child1_pid = fork do
1395 * puts "Child 1 pid is #{Process.pid}"
1396 * Process.setpgid(0, Process.pid)
1397 * child1_pgid = Process.getpgid(Process.pid)
1398 * puts "Child 1 process group ID is #{child1_pgid} (different from parent's)."
1399 * sleep 3 # To force child 1 to exit later than child 0 exit.
1401 * child_pids = [child0_pid, child1_pid]
1402 * retrieved_pid = Process.wait(-1)
1403 * puts child_pids.include?(retrieved_pid)
1404 * retrieved_pid = Process.wait(-1)
1405 * puts child_pids.include?(retrieved_pid)
1409 * Parent process group ID is 228736.
1410 * Child 0 pid is 228758
1411 * Child 0 process group ID is 228736 (same as parent's).
1412 * Child 1 pid is 228759
1413 * Child 1 process group ID is 228759 (different from parent's).
1417 * - Less than <tt>-1</tt>: Waits for any child whose process group ID is <tt>-pid</tt>:
1419 * parent_pgpid = Process.getpgid(Process.pid)
1420 * puts "Parent process group ID is #{parent_pgpid}."
1421 * child0_pid = fork do
1422 * puts "Child 0 pid is #{Process.pid}"
1423 * child0_pgid = Process.getpgid(Process.pid)
1424 * puts "Child 0 process group ID is #{child0_pgid} (same as parent's)."
1426 * child1_pid = fork do
1427 * puts "Child 1 pid is #{Process.pid}"
1428 * Process.setpgid(0, Process.pid)
1429 * child1_pgid = Process.getpgid(Process.pid)
1430 * puts "Child 1 process group ID is #{child1_pgid} (different from parent's)."
1433 * retrieved_pid = Process.wait(-child1_pid)
1434 * puts "Process.wait(-child1_pid) returned pid #{retrieved_pid}, which is child 1 pid."
1436 * Process.wait(-child1_pid)
1437 * rescue Errno::ECHILD => x
1438 * puts "Raised #{x.class}, because there's no longer a child with process group id #{child1_pid}."
1443 * Parent process group ID is 230083.
1444 * Child 0 pid is 230108
1445 * Child 0 process group ID is 230083 (same as parent's).
1446 * Child 1 pid is 230109
1447 * Child 1 process group ID is 230109 (different from parent's).
1448 * Process.wait(-child1_pid) returned pid 230109, which is child 1 pid.
1449 * Raised Errno::ECHILD, because there's no longer a child with process group id 230109.
1451 * Argument +flags+ should be given as one of the following constants,
1452 * or as the logical OR of both:
1454 * - Process::WNOHANG: Does not block if no child process is available.
1455 * - Process::WUNTRACED: May return a stopped child process, even if not yet reported.
1457 * Not all flags are available on all platforms.
1459 * Raises Errno::ECHILD if there is no suitable child process.
1461 * Not available on all platforms.
1463 * Process.waitpid is an alias for Process.wait.
1466 proc_m_wait(int c
, VALUE
*v
, VALUE _
)
1468 return proc_wait(c
, v
);
1473 * Process.wait2(pid = -1, flags = 0) -> [pid, status]
1475 * Like Process.waitpid, but returns an array
1476 * containing the child process +pid+ and Process::Status +status+:
1478 * pid = Process.spawn('ruby', '-e', 'exit 13') # => 309581
1479 * Process.wait2(pid)
1480 * # => [309581, #<Process::Status: pid 309581 exit 13>]
1482 * Process.waitpid2 is an alias for Process.wait2.
1486 proc_wait2(int argc
, VALUE
*argv
, VALUE _
)
1488 VALUE pid
= proc_wait(argc
, argv
);
1489 if (NIL_P(pid
)) return Qnil
;
1490 return rb_assoc_new(pid
, rb_last_status_get());
1496 * Process.waitall -> array
1498 * Waits for all children, returns an array of 2-element arrays;
1499 * each subarray contains the integer pid and Process::Status status
1500 * for one of the reaped child processes:
1502 * pid0 = Process.spawn('ruby', '-e', 'exit 13') # => 325470
1503 * pid1 = Process.spawn('ruby', '-e', 'exit 14') # => 325495
1505 * # => [[325470, #<Process::Status: pid 325470 exit 13>], [325495, #<Process::Status: pid 325495 exit 14>]]
1510 proc_waitall(VALUE _
)
1516 result
= rb_ary_new();
1517 rb_last_status_clear();
1520 pid
= rb_waitpid(-1, &status
, 0);
1525 rb_syserr_fail(e
, 0);
1527 rb_ary_push(result
, rb_assoc_new(PIDT2NUM(pid
), rb_last_status_get()));
1532 static VALUE rb_cWaiter
;
1535 detach_process_pid(VALUE thread
)
1537 return rb_thread_local_aref(thread
, id_pid
);
1541 detach_process_watcher(void *arg
)
1543 rb_pid_t cpid
, pid
= (rb_pid_t
)(VALUE
)arg
;
1546 while ((cpid
= rb_waitpid(pid
, &status
, 0)) == 0) {
1547 /* wait while alive */
1549 return rb_last_status_get();
1553 rb_detach_process(rb_pid_t pid
)
1555 VALUE watcher
= rb_thread_create(detach_process_watcher
, (void*)(VALUE
)pid
);
1556 rb_thread_local_aset(watcher
, id_pid
, PIDT2NUM(pid
));
1557 RBASIC_SET_CLASS(watcher
, rb_cWaiter
);
1564 * Process.detach(pid) -> thread
1566 * Avoids the potential for a child process to become a
1567 * {zombie process}[https://en.wikipedia.org/wiki/Zombie_process].
1568 * Process.detach prevents this by setting up a separate Ruby thread
1569 * whose sole job is to reap the status of the process _pid_ when it terminates.
1571 * This method is needed only when the parent process will never wait
1572 * for the child process.
1574 * This example does not reap the second child process;
1575 * that process appears as a zombie in the process status (+ps+) output:
1577 * pid = Process.spawn('ruby', '-e', 'exit 13') # => 312691
1580 * system("ps -ho pid,state -p #{pid}")
1586 * This example also does not reap the second child process,
1587 * but it does detach the process so that it does not become a zombie:
1589 * pid = Process.spawn('ruby', '-e', 'exit 13') # => 313213
1590 * thread = Process.detach(pid)
1592 * # => #<Process::Waiter:0x00007f038f48b838 run>
1593 * system("ps -ho pid,state -p #{pid}") # Finds no zombies.
1595 * The waiting thread can return the pid of the detached child process:
1597 * thread.join.pid # => 313262
1602 proc_detach(VALUE obj
, VALUE pid
)
1604 return rb_detach_process(NUM2PIDT(pid
));
1607 /* This function should be async-signal-safe. Actually it is. */
1609 before_exec_async_signal_safe(void)
1614 before_exec_non_async_signal_safe(void)
1617 * On Mac OS X 10.5.x (Leopard) or earlier, exec() may return ENOTSUP
1618 * if the process have multiple threads. Therefore we have to kill
1619 * internal threads temporary. [ruby-core:10583]
1620 * This is also true on Haiku. It returns Errno::EPERM against exec()
1621 * in multiple threads.
1623 * Nowadays, we always stop the timer thread completely to allow redirects.
1625 rb_thread_stop_timer_thread();
1628 #define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
1630 int rb_w32_set_nonblock2(int fd
, int nonblock
);
1634 set_blocking(int fd
)
1637 return rb_w32_set_nonblock2(fd
, 0);
1638 #elif defined(F_GETFL) && defined(F_SETFL)
1639 int fl
= fcntl(fd
, F_GETFL
); /* async-signal-safe */
1641 /* EBADF ought to be possible */
1642 if (fl
== -1) return fl
;
1643 if (fl
& O_NONBLOCK
) {
1645 return fcntl(fd
, F_SETFL
, fl
);
1652 stdfd_clear_nonblock(void)
1654 /* many programs cannot deal with non-blocking stdin/stdout/stderr */
1656 for (fd
= 0; fd
< 3; fd
++) {
1657 (void)set_blocking(fd
); /* can't do much about errors anyhow */
1664 before_exec_non_async_signal_safe();
1665 before_exec_async_signal_safe();
1671 rb_thread_reset_timer_thread();
1672 rb_thread_start_timer_thread();
1675 #if defined HAVE_WORKING_FORK || defined HAVE_DAEMON
1677 before_fork_ruby(void)
1683 after_fork_ruby(rb_pid_t pid
)
1697 #if defined(HAVE_WORKING_FORK)
1699 COMPILER_WARNING_PUSH
1700 #if __has_warning("-Wdeprecated-declarations") || RBIMPL_COMPILER_IS(GCC)
1701 COMPILER_WARNING_IGNORED(-Wdeprecated
-declarations
)
1703 static inline rb_pid_t
1708 COMPILER_WARNING_POP
1710 /* try_with_sh and exec_with_sh should be async-signal-safe. Actually it is.*/
1711 #define try_with_sh(err, prog, argv, envp) ((err == ENOEXEC) ? exec_with_sh((prog), (argv), (envp)) : (void)0)
1713 exec_with_sh(const char *prog
, char **argv
, char **envp
)
1715 *argv
= (char *)prog
;
1716 *--argv
= (char *)"sh";
1718 execve("/bin/sh", argv
, envp
); /* async-signal-safe */
1720 execv("/bin/sh", argv
); /* async-signal-safe (since SUSv4) */
1724 #define try_with_sh(err, prog, argv, envp) (void)0
1727 /* This function should be async-signal-safe. Actually it is. */
1729 proc_exec_cmd(const char *prog
, VALUE argv_str
, VALUE envp_str
)
1737 argv
= ARGVSTR2ARGV(argv_str
);
1744 rb_w32_uaspawn(P_OVERLAY
, prog
, argv
);
1747 envp
= envp_str
? RB_IMEMO_TMPBUF_PTR(envp_str
) : NULL
;
1749 execve(prog
, argv
, envp
); /* async-signal-safe */
1751 execv(prog
, argv
); /* async-signal-safe (since SUSv4) */
1753 try_with_sh(err
, prog
, argv
, envp
); /* try_with_sh() is async-signal-safe. */
1758 /* This function should be async-signal-safe. Actually it is. */
1760 proc_exec_sh(const char *str
, VALUE envp_str
)
1765 while (*s
== ' ' || *s
== '\t' || *s
== '\n')
1773 rb_w32_uspawn(P_OVERLAY
, (char *)str
, 0);
1774 #elif defined(__CYGWIN32__)
1776 char fbuf
[MAXPATHLEN
];
1777 char *shell
= dln_find_exe_r("sh", 0, fbuf
, sizeof(fbuf
));
1780 execl(shell
, "sh", "-c", str
, (char *) NULL
);
1782 status
= system(str
);
1788 execle("/bin/sh", "sh", "-c", str
, (char *)NULL
, RB_IMEMO_TMPBUF_PTR(envp_str
)); /* async-signal-safe */
1790 execl("/bin/sh", "sh", "-c", str
, (char *)NULL
); /* async-signal-safe (since SUSv4) */
1796 rb_proc_exec(const char *str
)
1800 ret
= proc_exec_sh(str
, Qfalse
);
1807 mark_exec_arg(void *ptr
)
1809 struct rb_execarg
*eargp
= ptr
;
1810 if (eargp
->use_shell
)
1811 rb_gc_mark(eargp
->invoke
.sh
.shell_script
);
1813 rb_gc_mark(eargp
->invoke
.cmd
.command_name
);
1814 rb_gc_mark(eargp
->invoke
.cmd
.command_abspath
);
1815 rb_gc_mark(eargp
->invoke
.cmd
.argv_str
);
1816 rb_gc_mark(eargp
->invoke
.cmd
.argv_buf
);
1818 rb_gc_mark(eargp
->redirect_fds
);
1819 rb_gc_mark(eargp
->envp_str
);
1820 rb_gc_mark(eargp
->envp_buf
);
1821 rb_gc_mark(eargp
->dup2_tmpbuf
);
1822 rb_gc_mark(eargp
->rlimit_limits
);
1823 rb_gc_mark(eargp
->fd_dup2
);
1824 rb_gc_mark(eargp
->fd_close
);
1825 rb_gc_mark(eargp
->fd_open
);
1826 rb_gc_mark(eargp
->fd_dup2_child
);
1827 rb_gc_mark(eargp
->env_modification
);
1828 rb_gc_mark(eargp
->path_env
);
1829 rb_gc_mark(eargp
->chdir_dir
);
1833 memsize_exec_arg(const void *ptr
)
1835 return sizeof(struct rb_execarg
);
1838 static const rb_data_type_t exec_arg_data_type
= {
1840 {mark_exec_arg
, RUBY_TYPED_DEFAULT_FREE
, memsize_exec_arg
},
1841 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
| RUBY_TYPED_EMBEDDABLE
1845 # define DEFAULT_PROCESS_ENCODING rb_utf8_encoding()
1847 #ifdef DEFAULT_PROCESS_ENCODING
1848 # define EXPORT_STR(str) rb_str_export_to_enc((str), DEFAULT_PROCESS_ENCODING)
1849 # define EXPORT_DUP(str) export_dup(str)
1851 export_dup(VALUE str
)
1853 VALUE newstr
= EXPORT_STR(str
);
1854 if (newstr
== str
) newstr
= rb_str_dup(str
);
1858 # define EXPORT_STR(str) (str)
1859 # define EXPORT_DUP(str) rb_str_dup(str)
1862 #if !defined(HAVE_WORKING_FORK) && defined(HAVE_SPAWNV)
1863 # define USE_SPAWNV 1
1865 # define USE_SPAWNV 0
1868 # define P_NOWAIT _P_NOWAIT
1873 #define proc_spawn_cmd_internal(argv, prog) rb_w32_uaspawn(P_NOWAIT, (prog), (argv))
1876 proc_spawn_cmd_internal(char **argv
, char *prog
)
1878 char fbuf
[MAXPATHLEN
];
1883 prog
= dln_find_exe_r(prog
, 0, fbuf
, sizeof(fbuf
));
1888 status
= spawnv(P_NOWAIT
, prog
, (const char **)argv
);
1889 if (status
== -1 && errno
== ENOEXEC
) {
1890 *argv
= (char *)prog
;
1891 *--argv
= (char *)"sh";
1892 status
= spawnv(P_NOWAIT
, "/bin/sh", (const char **)argv
);
1894 if (status
== -1) errno
= ENOEXEC
;
1901 proc_spawn_cmd(char **argv
, VALUE prog
, struct rb_execarg
*eargp
)
1908 if (eargp
->new_pgroup_given
&& eargp
->new_pgroup_flag
) {
1909 flags
= CREATE_NEW_PROCESS_GROUP
;
1911 pid
= rb_w32_uaspawn_flags(P_NOWAIT
, prog
? RSTRING_PTR(prog
) : 0, argv
, flags
);
1913 pid
= proc_spawn_cmd_internal(argv
, prog
? RSTRING_PTR(prog
) : 0);
1920 #define proc_spawn_sh(str) rb_w32_uspawn(P_NOWAIT, (str), 0)
1923 proc_spawn_sh(char *str
)
1925 char fbuf
[MAXPATHLEN
];
1928 char *shell
= dln_find_exe_r("sh", 0, fbuf
, sizeof(fbuf
));
1930 status
= spawnl(P_NOWAIT
, (shell
? shell
: "/bin/sh"), "sh", "-c", str
, (char*)NULL
);
1940 RBASIC_CLEAR_CLASS(obj
);
1945 check_exec_redirect_fd(VALUE v
, int iskey
)
1952 else if (SYMBOL_P(v
)) {
1953 ID id
= rb_check_id(&v
);
1956 else if (id
== id_out
)
1958 else if (id
== id_err
)
1963 else if (!NIL_P(tmp
= rb_io_check_io(v
))) {
1965 GetOpenFile(tmp
, fptr
);
1966 if (fptr
->tied_io_for_writing
)
1967 rb_raise(rb_eArgError
, "duplex IO redirection");
1974 rb_raise(rb_eArgError
, "negative file descriptor");
1977 else if (fd
>= 3 && iskey
) {
1978 rb_raise(rb_eArgError
, "wrong file descriptor (%d)", fd
);
1984 rb_raise(rb_eArgError
, "wrong exec redirect");
1985 UNREACHABLE_RETURN(Qundef
);
1989 check_exec_redirect1(VALUE ary
, VALUE key
, VALUE param
)
1991 if (ary
== Qfalse
) {
1992 ary
= hide_obj(rb_ary_new());
1994 if (!RB_TYPE_P(key
, T_ARRAY
)) {
1995 VALUE fd
= check_exec_redirect_fd(key
, !NIL_P(param
));
1996 rb_ary_push(ary
, hide_obj(rb_assoc_new(fd
, param
)));
2000 for (i
= 0 ; i
< RARRAY_LEN(key
); i
++) {
2001 VALUE v
= RARRAY_AREF(key
, i
);
2002 VALUE fd
= check_exec_redirect_fd(v
, !NIL_P(param
));
2003 rb_ary_push(ary
, hide_obj(rb_assoc_new(fd
, param
)));
2010 check_exec_redirect(VALUE key
, VALUE val
, struct rb_execarg
*eargp
)
2013 VALUE path
, flags
, perm
;
2017 switch (TYPE(val
)) {
2019 id
= rb_check_id(&val
);
2020 if (id
== id_close
) {
2022 eargp
->fd_close
= check_exec_redirect1(eargp
->fd_close
, key
, param
);
2024 else if (id
== id_in
) {
2026 eargp
->fd_dup2
= check_exec_redirect1(eargp
->fd_dup2
, key
, param
);
2028 else if (id
== id_out
) {
2030 eargp
->fd_dup2
= check_exec_redirect1(eargp
->fd_dup2
, key
, param
);
2032 else if (id
== id_err
) {
2034 eargp
->fd_dup2
= check_exec_redirect1(eargp
->fd_dup2
, key
, param
);
2037 rb_raise(rb_eArgError
, "wrong exec redirect symbol: %"PRIsVALUE
,
2044 val
= check_exec_redirect_fd(val
, 0);
2048 eargp
->fd_dup2
= check_exec_redirect1(eargp
->fd_dup2
, key
, param
);
2052 path
= rb_ary_entry(val
, 0);
2053 if (RARRAY_LEN(val
) == 2 && SYMBOL_P(path
) &&
2054 path
== ID2SYM(id_child
)) {
2055 param
= check_exec_redirect_fd(rb_ary_entry(val
, 1), 0);
2056 eargp
->fd_dup2_child
= check_exec_redirect1(eargp
->fd_dup2_child
, key
, param
);
2059 FilePathValue(path
);
2060 flags
= rb_ary_entry(val
, 1);
2062 flags
= INT2NUM(O_RDONLY
);
2063 else if (RB_TYPE_P(flags
, T_STRING
))
2064 flags
= INT2NUM(rb_io_modestr_oflags(StringValueCStr(flags
)));
2066 flags
= rb_to_int(flags
);
2067 perm
= rb_ary_entry(val
, 2);
2068 perm
= NIL_P(perm
) ? INT2FIX(0644) : rb_to_int(perm
);
2069 param
= hide_obj(rb_ary_new3(4, hide_obj(EXPORT_DUP(path
)),
2070 flags
, perm
, Qnil
));
2071 eargp
->fd_open
= check_exec_redirect1(eargp
->fd_open
, key
, param
);
2077 FilePathValue(path
);
2078 if (RB_TYPE_P(key
, T_FILE
))
2079 key
= check_exec_redirect_fd(key
, 1);
2080 if (FIXNUM_P(key
) && (FIX2INT(key
) == 1 || FIX2INT(key
) == 2))
2081 flags
= INT2NUM(O_WRONLY
|O_CREAT
|O_TRUNC
);
2082 else if (RB_TYPE_P(key
, T_ARRAY
)) {
2084 for (i
= 0; i
< RARRAY_LEN(key
); i
++) {
2085 VALUE v
= RARRAY_AREF(key
, i
);
2086 VALUE fd
= check_exec_redirect_fd(v
, 1);
2087 if (FIX2INT(fd
) != 1 && FIX2INT(fd
) != 2) break;
2089 if (i
== RARRAY_LEN(key
))
2090 flags
= INT2NUM(O_WRONLY
|O_CREAT
|O_TRUNC
);
2092 flags
= INT2NUM(O_RDONLY
);
2095 flags
= INT2NUM(O_RDONLY
);
2096 perm
= INT2FIX(0644);
2097 param
= hide_obj(rb_ary_new3(4, hide_obj(EXPORT_DUP(path
)),
2098 flags
, perm
, Qnil
));
2099 eargp
->fd_open
= check_exec_redirect1(eargp
->fd_open
, key
, param
);
2104 val
= rb_io_check_io(tmp
);
2105 if (!NIL_P(val
)) goto io
;
2106 rb_raise(rb_eArgError
, "wrong exec redirect action");
2111 #if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
2112 static int rlimit_type_by_sym(VALUE key
);
2115 rb_execarg_addopt_rlimit(struct rb_execarg
*eargp
, int rtype
, VALUE val
)
2117 VALUE ary
= eargp
->rlimit_limits
;
2118 VALUE tmp
, softlim
, hardlim
;
2119 if (eargp
->rlimit_limits
== Qfalse
)
2120 ary
= eargp
->rlimit_limits
= hide_obj(rb_ary_new());
2122 ary
= eargp
->rlimit_limits
;
2123 tmp
= rb_check_array_type(val
);
2125 if (RARRAY_LEN(tmp
) == 1)
2126 softlim
= hardlim
= rb_to_int(rb_ary_entry(tmp
, 0));
2127 else if (RARRAY_LEN(tmp
) == 2) {
2128 softlim
= rb_to_int(rb_ary_entry(tmp
, 0));
2129 hardlim
= rb_to_int(rb_ary_entry(tmp
, 1));
2132 rb_raise(rb_eArgError
, "wrong exec rlimit option");
2136 softlim
= hardlim
= rb_to_int(val
);
2138 tmp
= hide_obj(rb_ary_new3(3, INT2NUM(rtype
), softlim
, hardlim
));
2139 rb_ary_push(ary
, tmp
);
2143 #define TO_BOOL(val, name) (NIL_P(val) ? 0 : rb_bool_expected((val), name, TRUE))
2145 rb_execarg_addopt(VALUE execarg_obj
, VALUE key
, VALUE val
)
2147 struct rb_execarg
*eargp
= rb_execarg_get(execarg_obj
);
2151 switch (TYPE(key
)) {
2153 #if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
2155 int rtype
= rlimit_type_by_sym(key
);
2157 rb_execarg_addopt_rlimit(eargp
, rtype
, val
);
2158 RB_GC_GUARD(execarg_obj
);
2163 if (!(id
= rb_check_id(&key
))) return ST_STOP
;
2165 if (id
== id_pgroup
) {
2167 if (eargp
->pgroup_given
) {
2168 rb_raise(rb_eArgError
, "pgroup option specified twice");
2171 pgroup
= -1; /* asis(-1) means "don't call setpgid()". */
2172 else if (val
== Qtrue
)
2173 pgroup
= 0; /* new process group. */
2175 pgroup
= NUM2PIDT(val
);
2177 rb_raise(rb_eArgError
, "negative process group ID : %ld", (long)pgroup
);
2180 eargp
->pgroup_given
= 1;
2181 eargp
->pgroup_pgid
= pgroup
;
2186 if (id
== id_new_pgroup
) {
2187 if (eargp
->new_pgroup_given
) {
2188 rb_raise(rb_eArgError
, "new_pgroup option specified twice");
2190 eargp
->new_pgroup_given
= 1;
2191 eargp
->new_pgroup_flag
= TO_BOOL(val
, "new_pgroup");
2195 if (id
== id_unsetenv_others
) {
2196 if (eargp
->unsetenv_others_given
) {
2197 rb_raise(rb_eArgError
, "unsetenv_others option specified twice");
2199 eargp
->unsetenv_others_given
= 1;
2200 eargp
->unsetenv_others_do
= TO_BOOL(val
, "unsetenv_others");
2202 else if (id
== id_chdir
) {
2203 if (eargp
->chdir_given
) {
2204 rb_raise(rb_eArgError
, "chdir option specified twice");
2207 val
= rb_str_encode_ospath(val
);
2208 eargp
->chdir_given
= 1;
2209 eargp
->chdir_dir
= hide_obj(EXPORT_DUP(val
));
2211 else if (id
== id_umask
) {
2212 mode_t cmask
= NUM2MODET(val
);
2213 if (eargp
->umask_given
) {
2214 rb_raise(rb_eArgError
, "umask option specified twice");
2216 eargp
->umask_given
= 1;
2217 eargp
->umask_mask
= cmask
;
2219 else if (id
== id_close_others
) {
2220 if (eargp
->close_others_given
) {
2221 rb_raise(rb_eArgError
, "close_others option specified twice");
2223 eargp
->close_others_given
= 1;
2224 eargp
->close_others_do
= TO_BOOL(val
, "close_others");
2226 else if (id
== id_in
) {
2230 else if (id
== id_out
) {
2234 else if (id
== id_err
) {
2238 else if (id
== id_uid
) {
2240 if (eargp
->uid_given
) {
2241 rb_raise(rb_eArgError
, "uid option specified twice");
2245 eargp
->uid
= OBJ2UID(val
);
2246 eargp
->uid_given
= 1;
2249 rb_raise(rb_eNotImpError
,
2250 "uid option is unimplemented on this machine");
2253 else if (id
== id_gid
) {
2255 if (eargp
->gid_given
) {
2256 rb_raise(rb_eArgError
, "gid option specified twice");
2260 eargp
->gid
= OBJ2GID(val
);
2261 eargp
->gid_given
= 1;
2264 rb_raise(rb_eNotImpError
,
2265 "gid option is unimplemented on this machine");
2268 else if (id
== id_exception
) {
2269 if (eargp
->exception_given
) {
2270 rb_raise(rb_eArgError
, "exception option specified twice");
2272 eargp
->exception_given
= 1;
2273 eargp
->exception
= TO_BOOL(val
, "exception");
2284 check_exec_redirect(key
, val
, eargp
);
2291 RB_GC_GUARD(execarg_obj
);
2296 check_exec_options_i(st_data_t st_key
, st_data_t st_val
, st_data_t arg
)
2298 VALUE key
= (VALUE
)st_key
;
2299 VALUE val
= (VALUE
)st_val
;
2300 VALUE execarg_obj
= (VALUE
)arg
;
2301 if (rb_execarg_addopt(execarg_obj
, key
, val
) != ST_CONTINUE
) {
2303 rb_raise(rb_eArgError
, "wrong exec option symbol: % "PRIsVALUE
,
2305 rb_raise(rb_eArgError
, "wrong exec option");
2311 check_exec_options_i_extract(st_data_t st_key
, st_data_t st_val
, st_data_t arg
)
2313 VALUE key
= (VALUE
)st_key
;
2314 VALUE val
= (VALUE
)st_val
;
2315 VALUE
*args
= (VALUE
*)arg
;
2316 VALUE execarg_obj
= args
[0];
2317 if (rb_execarg_addopt(execarg_obj
, key
, val
) != ST_CONTINUE
) {
2318 VALUE nonopts
= args
[1];
2319 if (NIL_P(nonopts
)) args
[1] = nonopts
= rb_hash_new();
2320 rb_hash_aset(nonopts
, key
, val
);
2326 check_exec_fds_1(struct rb_execarg
*eargp
, VALUE h
, int maxhint
, VALUE ary
)
2330 if (ary
!= Qfalse
) {
2331 for (i
= 0; i
< RARRAY_LEN(ary
); i
++) {
2332 VALUE elt
= RARRAY_AREF(ary
, i
);
2333 int fd
= FIX2INT(RARRAY_AREF(elt
, 0));
2334 if (RTEST(rb_hash_lookup(h
, INT2FIX(fd
)))) {
2335 rb_raise(rb_eArgError
, "fd %d specified twice", fd
);
2337 if (ary
== eargp
->fd_dup2
)
2338 rb_hash_aset(h
, INT2FIX(fd
), Qtrue
);
2339 else if (ary
== eargp
->fd_dup2_child
)
2340 rb_hash_aset(h
, INT2FIX(fd
), RARRAY_AREF(elt
, 1));
2341 else /* ary == eargp->fd_close */
2342 rb_hash_aset(h
, INT2FIX(fd
), INT2FIX(-1));
2345 if (ary
== eargp
->fd_dup2
|| ary
== eargp
->fd_dup2_child
) {
2346 fd
= FIX2INT(RARRAY_AREF(elt
, 1));
2356 check_exec_fds(struct rb_execarg
*eargp
)
2358 VALUE h
= rb_hash_new();
2363 maxhint
= check_exec_fds_1(eargp
, h
, maxhint
, eargp
->fd_dup2
);
2364 maxhint
= check_exec_fds_1(eargp
, h
, maxhint
, eargp
->fd_close
);
2365 maxhint
= check_exec_fds_1(eargp
, h
, maxhint
, eargp
->fd_dup2_child
);
2367 if (eargp
->fd_dup2_child
) {
2368 ary
= eargp
->fd_dup2_child
;
2369 for (i
= 0; i
< RARRAY_LEN(ary
); i
++) {
2370 VALUE elt
= RARRAY_AREF(ary
, i
);
2371 int newfd
= FIX2INT(RARRAY_AREF(elt
, 0));
2372 int oldfd
= FIX2INT(RARRAY_AREF(elt
, 1));
2374 VALUE val
= rb_hash_lookup(h
, INT2FIX(lastfd
));
2376 while (FIXNUM_P(val
) && 0 <= FIX2INT(val
)) {
2377 lastfd
= FIX2INT(val
);
2378 val
= rb_hash_lookup(h
, val
);
2379 if (RARRAY_LEN(ary
) < depth
)
2380 rb_raise(rb_eArgError
, "cyclic child fd redirection from %d", oldfd
);
2384 rb_raise(rb_eArgError
, "child fd %d is not redirected", oldfd
);
2385 if (oldfd
!= lastfd
) {
2387 rb_ary_store(elt
, 1, INT2FIX(lastfd
));
2388 rb_hash_aset(h
, INT2FIX(newfd
), INT2FIX(lastfd
));
2389 val
= INT2FIX(oldfd
);
2390 while (FIXNUM_P(val2
= rb_hash_lookup(h
, val
))) {
2391 rb_hash_aset(h
, val
, INT2FIX(lastfd
));
2398 eargp
->close_others_maxhint
= maxhint
;
2403 rb_check_exec_options(VALUE opthash
, VALUE execarg_obj
)
2405 if (RHASH_EMPTY_P(opthash
))
2407 rb_hash_stlike_foreach(opthash
, check_exec_options_i
, (st_data_t
)execarg_obj
);
2411 rb_execarg_extract_options(VALUE execarg_obj
, VALUE opthash
)
2414 if (RHASH_EMPTY_P(opthash
))
2416 args
[0] = execarg_obj
;
2418 rb_hash_stlike_foreach(opthash
, check_exec_options_i_extract
, (st_data_t
)args
);
2422 #ifdef ENV_IGNORECASE
2423 #define ENVMATCH(s1, s2) (STRCASECMP((s1), (s2)) == 0)
2425 #define ENVMATCH(n1, n2) (strcmp((n1), (n2)) == 0)
2429 check_exec_env_i(st_data_t st_key
, st_data_t st_val
, st_data_t arg
)
2431 VALUE key
= (VALUE
)st_key
;
2432 VALUE val
= (VALUE
)st_val
;
2433 VALUE env
= ((VALUE
*)arg
)[0];
2434 VALUE
*path
= &((VALUE
*)arg
)[1];
2437 k
= StringValueCStr(key
);
2439 rb_raise(rb_eArgError
, "environment name contains a equal : %"PRIsVALUE
, key
);
2442 StringValueCStr(val
);
2444 key
= EXPORT_STR(key
);
2445 if (!NIL_P(val
)) val
= EXPORT_STR(val
);
2447 if (ENVMATCH(k
, PATH_ENV
)) {
2450 rb_ary_push(env
, hide_obj(rb_assoc_new(key
, val
)));
2456 rb_check_exec_env(VALUE hash
, VALUE
*path
)
2460 env
[0] = hide_obj(rb_ary_new());
2462 rb_hash_stlike_foreach(hash
, check_exec_env_i
, (st_data_t
)env
);
2469 rb_check_argv(int argc
, VALUE
*argv
)
2474 rb_check_arity(argc
, 1, UNLIMITED_ARGUMENTS
);
2477 tmp
= rb_check_array_type(argv
[0]);
2479 if (RARRAY_LEN(tmp
) != 2) {
2480 rb_raise(rb_eArgError
, "wrong first argument");
2482 prog
= RARRAY_AREF(tmp
, 0);
2483 argv
[0] = RARRAY_AREF(tmp
, 1);
2485 StringValueCStr(prog
);
2486 prog
= rb_str_new_frozen(prog
);
2488 for (i
= 0; i
< argc
; i
++) {
2489 StringValue(argv
[i
]);
2490 argv
[i
] = rb_str_new_frozen(argv
[i
]);
2491 StringValueCStr(argv
[i
]);
2497 check_hash(VALUE obj
)
2499 if (RB_SPECIAL_CONST_P(obj
)) return Qnil
;
2500 switch (RB_BUILTIN_TYPE(obj
)) {
2507 return rb_check_hash_type(obj
);
2511 rb_exec_getargs(int *argc_p
, VALUE
**argv_p
, int accept_shell
, VALUE
*env_ret
, VALUE
*opthash_ret
)
2516 hash
= check_hash((*argv_p
)[*argc_p
-1]);
2518 *opthash_ret
= hash
;
2524 hash
= check_hash((*argv_p
)[0]);
2531 prog
= rb_check_argv(*argc_p
, *argv_p
);
2533 prog
= (*argv_p
)[0];
2534 if (accept_shell
&& *argc_p
== 1) {
2543 struct string_part
{
2549 compare_posix_sh(const void *key
, const void *el
)
2551 const struct string_part
*word
= key
;
2552 int ret
= strncmp(word
->ptr
, el
, word
->len
);
2553 if (!ret
&& ((const char *)el
)[word
->len
]) ret
= -1;
2559 rb_exec_fillarg(VALUE prog
, int argc
, VALUE
*argv
, VALUE env
, VALUE opthash
, VALUE execarg_obj
)
2561 struct rb_execarg
*eargp
= rb_execarg_get(execarg_obj
);
2562 char fbuf
[MAXPATHLEN
];
2564 MEMZERO(eargp
, struct rb_execarg
, 1);
2566 if (!NIL_P(opthash
)) {
2567 rb_check_exec_options(opthash
, execarg_obj
);
2570 env
= rb_check_exec_env(env
, &eargp
->path_env
);
2571 eargp
->env_modification
= env
;
2574 prog
= EXPORT_STR(prog
);
2575 eargp
->use_shell
= argc
== 0;
2576 if (eargp
->use_shell
)
2577 eargp
->invoke
.sh
.shell_script
= prog
;
2579 eargp
->invoke
.cmd
.command_name
= prog
;
2582 if (eargp
->use_shell
) {
2583 static const char posix_sh_cmds
[][9] = {
2585 ".", /* special built-in */
2586 ":", /* special built-in */
2587 "break", /* special built-in */
2588 "case", /* reserved */
2589 "continue", /* special built-in */
2590 "do", /* reserved */
2591 "done", /* reserved */
2592 "elif", /* reserved */
2593 "else", /* reserved */
2594 "esac", /* reserved */
2595 "eval", /* special built-in */
2596 "exec", /* special built-in */
2597 "exit", /* special built-in */
2598 "export", /* special built-in */
2599 "fi", /* reserved */
2600 "for", /* reserved */
2601 "if", /* reserved */
2602 "in", /* reserved */
2603 "readonly", /* special built-in */
2604 "return", /* special built-in */
2605 "set", /* special built-in */
2606 "shift", /* special built-in */
2607 "then", /* reserved */
2608 "times", /* special built-in */
2609 "trap", /* special built-in */
2610 "unset", /* special built-in */
2611 "until", /* reserved */
2612 "while", /* reserved */
2615 struct string_part first
= {0, 0};
2620 * * Pathname Expansion
2621 * ? Pathname Expansion
2622 * {} Grouping Commands
2623 * [] Pathname Expansion
2625 * () Grouping Commands
2627 * & AND Lists, Asynchronous Lists
2628 * | OR Lists, Pipelines
2629 * \ Escape Character
2630 * $ Parameter Expansion
2631 * ; Sequential Lists
2633 * ` Command Substitution
2638 * = Assignment preceding command name
2639 * % (used in Parameter Expansion)
2641 for (p
= RSTRING_PTR(prog
); *p
; p
++) {
2642 if (*p
== ' ' || *p
== '\t') {
2643 if (first
.ptr
&& !first
.len
) first
.len
= p
- first
.ptr
;
2646 if (!first
.ptr
) first
.ptr
= p
;
2648 if (!has_meta
&& strchr("*?{}[]<>()~&|\\$;'`\"\n#", *p
))
2654 else if (*p
== '/') {
2655 first
.len
= 0x100; /* longer than any posix_sh_cmds */
2661 if (!has_meta
&& first
.ptr
) {
2662 if (!first
.len
) first
.len
= p
- first
.ptr
;
2663 if (first
.len
> 0 && first
.len
<= sizeof(posix_sh_cmds
[0]) &&
2664 bsearch(&first
, posix_sh_cmds
, numberof(posix_sh_cmds
), sizeof(posix_sh_cmds
[0]), compare_posix_sh
))
2668 /* avoid shell since no shell meta character found. */
2669 eargp
->use_shell
= 0;
2671 if (!eargp
->use_shell
) {
2673 argv_buf
= hide_obj(rb_str_buf_new(0));
2674 p
= RSTRING_PTR(prog
);
2676 while (*p
== ' ' || *p
== '\t')
2680 while (*p
&& *p
!= ' ' && *p
!= '\t')
2682 rb_str_buf_cat(argv_buf
, w
, p
-w
);
2683 rb_str_buf_cat(argv_buf
, "", 1); /* append '\0' */
2686 eargp
->invoke
.cmd
.argv_buf
= argv_buf
;
2687 eargp
->invoke
.cmd
.command_name
=
2688 hide_obj(rb_str_subseq(argv_buf
, 0, strlen(RSTRING_PTR(argv_buf
))));
2689 rb_enc_copy(eargp
->invoke
.cmd
.command_name
, prog
);
2694 if (!eargp
->use_shell
) {
2695 const char *abspath
;
2696 const char *path_env
= 0;
2697 if (RTEST(eargp
->path_env
)) path_env
= RSTRING_PTR(eargp
->path_env
);
2698 abspath
= dln_find_exe_r(RSTRING_PTR(eargp
->invoke
.cmd
.command_name
),
2699 path_env
, fbuf
, sizeof(fbuf
));
2701 eargp
->invoke
.cmd
.command_abspath
= rb_str_new_cstr(abspath
);
2703 eargp
->invoke
.cmd
.command_abspath
= Qnil
;
2706 if (!eargp
->use_shell
&& !eargp
->invoke
.cmd
.argv_buf
) {
2709 argv_buf
= rb_str_buf_new(0);
2711 for (i
= 0; i
< argc
; i
++) {
2712 VALUE arg
= argv
[i
];
2713 const char *s
= StringValueCStr(arg
);
2714 #ifdef DEFAULT_PROCESS_ENCODING
2715 arg
= EXPORT_STR(arg
);
2716 s
= RSTRING_PTR(arg
);
2718 rb_str_buf_cat(argv_buf
, s
, RSTRING_LEN(arg
) + 1); /* include '\0' */
2720 eargp
->invoke
.cmd
.argv_buf
= argv_buf
;
2723 if (!eargp
->use_shell
) {
2724 const char *p
, *ep
, *null
=NULL
;
2726 argv_str
= hide_obj(rb_str_buf_new(sizeof(char*) * (argc
+ 2)));
2727 rb_str_buf_cat(argv_str
, (char *)&null
, sizeof(null
)); /* place holder for /bin/sh of try_with_sh. */
2728 p
= RSTRING_PTR(eargp
->invoke
.cmd
.argv_buf
);
2729 ep
= p
+ RSTRING_LEN(eargp
->invoke
.cmd
.argv_buf
);
2731 rb_str_buf_cat(argv_str
, (char *)&p
, sizeof(p
));
2734 rb_str_buf_cat(argv_str
, (char *)&null
, sizeof(null
)); /* terminator for execve. */
2735 eargp
->invoke
.cmd
.argv_str
=
2736 rb_imemo_tmpbuf_auto_free_pointer_new_from_an_RString(argv_str
);
2738 RB_GC_GUARD(execarg_obj
);
2742 rb_execarg_get(VALUE execarg_obj
)
2744 struct rb_execarg
*eargp
;
2745 TypedData_Get_Struct(execarg_obj
, struct rb_execarg
, &exec_arg_data_type
, eargp
);
2750 rb_execarg_init(int argc
, const VALUE
*orig_argv
, int accept_shell
, VALUE execarg_obj
)
2752 struct rb_execarg
*eargp
= rb_execarg_get(execarg_obj
);
2754 VALUE env
= Qnil
, opthash
= Qnil
;
2756 VALUE
*argv
= ALLOCV_N(VALUE
, argv_buf
, argc
);
2757 MEMCPY(argv
, orig_argv
, VALUE
, argc
);
2758 prog
= rb_exec_getargs(&argc
, &argv
, accept_shell
, &env
, &opthash
);
2759 rb_exec_fillarg(prog
, argc
, argv
, env
, opthash
, execarg_obj
);
2760 ALLOCV_END(argv_buf
);
2761 ret
= eargp
->use_shell
? eargp
->invoke
.sh
.shell_script
: eargp
->invoke
.cmd
.command_name
;
2762 RB_GC_GUARD(execarg_obj
);
2767 rb_execarg_new(int argc
, const VALUE
*argv
, int accept_shell
, int allow_exc_opt
)
2770 struct rb_execarg
*eargp
;
2771 execarg_obj
= TypedData_Make_Struct(0, struct rb_execarg
, &exec_arg_data_type
, eargp
);
2772 rb_execarg_init(argc
, argv
, accept_shell
, execarg_obj
);
2773 if (!allow_exc_opt
&& eargp
->exception_given
) {
2774 rb_raise(rb_eArgError
, "exception option is not allowed");
2780 rb_execarg_setenv(VALUE execarg_obj
, VALUE env
)
2782 struct rb_execarg
*eargp
= rb_execarg_get(execarg_obj
);
2783 env
= !NIL_P(env
) ? rb_check_exec_env(env
, &eargp
->path_env
) : Qfalse
;
2784 eargp
->env_modification
= env
;
2785 RB_GC_GUARD(execarg_obj
);
2789 fill_envp_buf_i(st_data_t st_key
, st_data_t st_val
, st_data_t arg
)
2791 VALUE key
= (VALUE
)st_key
;
2792 VALUE val
= (VALUE
)st_val
;
2793 VALUE envp_buf
= (VALUE
)arg
;
2795 rb_str_buf_cat2(envp_buf
, StringValueCStr(key
));
2796 rb_str_buf_cat2(envp_buf
, "=");
2797 rb_str_buf_cat2(envp_buf
, StringValueCStr(val
));
2798 rb_str_buf_cat(envp_buf
, "", 1); /* append '\0' */
2804 static long run_exec_dup2_tmpbuf_size(long n
);
2806 struct open_struct
{
2815 open_func(void *ptr
)
2817 struct open_struct
*data
= ptr
;
2818 const char *fname
= RSTRING_PTR(data
->fname
);
2819 data
->ret
= parent_redirect_open(fname
, data
->oflags
, data
->perm
);
2825 rb_execarg_allocate_dup2_tmpbuf(struct rb_execarg
*eargp
, long len
)
2827 VALUE tmpbuf
= rb_imemo_tmpbuf_auto_free_pointer();
2828 rb_imemo_tmpbuf_set_ptr(tmpbuf
, ruby_xmalloc(run_exec_dup2_tmpbuf_size(len
)));
2829 eargp
->dup2_tmpbuf
= tmpbuf
;
2833 rb_execarg_parent_start1(VALUE execarg_obj
)
2835 struct rb_execarg
*eargp
= rb_execarg_get(execarg_obj
);
2836 int unsetenv_others
;
2840 ary
= eargp
->fd_open
;
2841 if (ary
!= Qfalse
) {
2843 for (i
= 0; i
< RARRAY_LEN(ary
); i
++) {
2844 VALUE elt
= RARRAY_AREF(ary
, i
);
2845 int fd
= FIX2INT(RARRAY_AREF(elt
, 0));
2846 VALUE param
= RARRAY_AREF(elt
, 1);
2847 VALUE vpath
= RARRAY_AREF(param
, 0);
2848 int flags
= NUM2INT(RARRAY_AREF(param
, 1));
2849 mode_t perm
= NUM2MODET(RARRAY_AREF(param
, 2));
2850 VALUE fd2v
= RARRAY_AREF(param
, 3);
2853 struct open_struct open_data
;
2855 open_data
.fname
= vpath
;
2856 open_data
.oflags
= flags
;
2857 open_data
.perm
= perm
;
2859 open_data
.err
= EINTR
;
2860 rb_thread_call_without_gvl2(open_func
, (void *)&open_data
, RUBY_UBF_IO
, 0);
2861 if (open_data
.ret
== -1) {
2862 if (open_data
.err
== EINTR
) {
2863 rb_thread_check_ints();
2866 rb_syserr_fail_str(open_data
.err
, vpath
);
2868 fd2
= open_data
.ret
;
2869 rb_update_max_fd(fd2
);
2870 RARRAY_ASET(param
, 3, INT2FIX(fd2
));
2871 rb_thread_check_ints();
2874 fd2
= NUM2INT(fd2v
);
2876 rb_execarg_addopt(execarg_obj
, INT2FIX(fd
), INT2FIX(fd2
));
2880 eargp
->redirect_fds
= check_exec_fds(eargp
);
2882 ary
= eargp
->fd_dup2
;
2883 if (ary
!= Qfalse
) {
2884 rb_execarg_allocate_dup2_tmpbuf(eargp
, RARRAY_LEN(ary
));
2887 unsetenv_others
= eargp
->unsetenv_others_given
&& eargp
->unsetenv_others_do
;
2888 envopts
= eargp
->env_modification
;
2889 if (ALWAYS_NEED_ENVP
|| unsetenv_others
|| envopts
!= Qfalse
) {
2890 VALUE envtbl
, envp_str
, envp_buf
;
2892 if (unsetenv_others
) {
2893 envtbl
= rb_hash_new();
2896 envtbl
= rb_env_to_hash();
2899 if (envopts
!= Qfalse
) {
2900 st_table
*stenv
= RHASH_TBL_RAW(envtbl
);
2902 for (i
= 0; i
< RARRAY_LEN(envopts
); i
++) {
2903 VALUE pair
= RARRAY_AREF(envopts
, i
);
2904 VALUE key
= RARRAY_AREF(pair
, 0);
2905 VALUE val
= RARRAY_AREF(pair
, 1);
2907 st_data_t stkey
= (st_data_t
)key
;
2908 st_delete(stenv
, &stkey
, NULL
);
2911 st_insert(stenv
, (st_data_t
)key
, (st_data_t
)val
);
2912 RB_OBJ_WRITTEN(envtbl
, Qundef
, key
);
2913 RB_OBJ_WRITTEN(envtbl
, Qundef
, val
);
2917 envp_buf
= rb_str_buf_new(0);
2919 rb_hash_stlike_foreach(envtbl
, fill_envp_buf_i
, (st_data_t
)envp_buf
);
2920 envp_str
= rb_str_buf_new(sizeof(char*) * (RHASH_SIZE(envtbl
) + 1));
2922 p
= RSTRING_PTR(envp_buf
);
2923 ep
= p
+ RSTRING_LEN(envp_buf
);
2925 rb_str_buf_cat(envp_str
, (char *)&p
, sizeof(p
));
2929 rb_str_buf_cat(envp_str
, (char *)&p
, sizeof(p
));
2931 rb_imemo_tmpbuf_auto_free_pointer_new_from_an_RString(envp_str
);
2932 eargp
->envp_buf
= envp_buf
;
2935 char **tmp_envp = (char **)RSTRING_PTR(envp_str);
2937 printf("%s\n", *tmp_envp);
2943 RB_GC_GUARD(execarg_obj
);
2948 rb_execarg_parent_start(VALUE execarg_obj
)
2951 rb_protect(rb_execarg_parent_start1
, execarg_obj
, &state
);
2953 rb_execarg_parent_end(execarg_obj
);
2959 execarg_parent_end(VALUE execarg_obj
)
2961 struct rb_execarg
*eargp
= rb_execarg_get(execarg_obj
);
2965 ary
= eargp
->fd_open
;
2966 if (ary
!= Qfalse
) {
2968 for (i
= 0; i
< RARRAY_LEN(ary
); i
++) {
2969 VALUE elt
= RARRAY_AREF(ary
, i
);
2970 VALUE param
= RARRAY_AREF(elt
, 1);
2973 fd2v
= RARRAY_AREF(param
, 3);
2975 fd2
= FIX2INT(fd2v
);
2976 parent_redirect_close(fd2
);
2977 RARRAY_ASET(param
, 3, Qnil
);
2983 RB_GC_GUARD(execarg_obj
);
2988 rb_execarg_parent_end(VALUE execarg_obj
)
2990 execarg_parent_end(execarg_obj
);
2991 RB_GC_GUARD(execarg_obj
);
2995 rb_exec_fail(struct rb_execarg
*eargp
, int err
, const char *errmsg
)
2997 if (!errmsg
|| !*errmsg
) return;
2998 if (strcmp(errmsg
, "chdir") == 0) {
2999 rb_sys_fail_str(eargp
->chdir_dir
);
3001 rb_sys_fail(errmsg
);
3006 rb_execarg_fail(VALUE execarg_obj
, int err
, const char *errmsg
)
3008 if (!errmsg
|| !*errmsg
) return;
3009 rb_exec_fail(rb_execarg_get(execarg_obj
), err
, errmsg
);
3010 RB_GC_GUARD(execarg_obj
);
3015 rb_f_exec(int argc
, const VALUE
*argv
)
3017 VALUE execarg_obj
, fail_str
;
3018 struct rb_execarg
*eargp
;
3019 #define CHILD_ERRMSG_BUFLEN 80
3020 char errmsg
[CHILD_ERRMSG_BUFLEN
] = { '\0' };
3023 execarg_obj
= rb_execarg_new(argc
, argv
, TRUE
, FALSE
);
3024 eargp
= rb_execarg_get(execarg_obj
);
3025 before_exec(); /* stop timer thread before redirects */
3027 rb_protect(rb_execarg_parent_start1
, execarg_obj
, &state
);
3029 execarg_parent_end(execarg_obj
);
3030 after_exec(); /* restart timer thread */
3034 fail_str
= eargp
->use_shell
? eargp
->invoke
.sh
.shell_script
: eargp
->invoke
.cmd
.command_name
;
3036 err
= exec_async_signal_safe(eargp
, errmsg
, sizeof(errmsg
));
3037 after_exec(); /* restart timer thread */
3039 rb_exec_fail(eargp
, err
, errmsg
);
3040 RB_GC_GUARD(execarg_obj
);
3041 rb_syserr_fail_str(err
, fail_str
);
3042 UNREACHABLE_RETURN(Qnil
);
3045 NORETURN(static VALUE
f_exec(int c
, const VALUE
*a
, VALUE _
));
3049 * exec([env, ] command_line, options = {})
3050 * exec([env, ] exe_path, *args, options = {})
3052 * Replaces the current process by doing one of the following:
3054 * - Passing string +command_line+ to the shell.
3055 * - Invoking the executable at +exe_path+.
3057 * This method has potential security vulnerabilities if called with untrusted input;
3058 * see {Command Injection}[rdoc-ref:command_injection.rdoc].
3060 * The new process is created using the
3061 * {exec system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/execve.html];
3062 * it may inherit some of its environment from the calling program
3063 * (possibly including open file descriptors).
3065 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
3066 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
3068 * Argument +options+ is a hash of options for the new process;
3069 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
3071 * The first required argument is one of the following:
3073 * - +command_line+ if it is a string,
3074 * and if it begins with a shell reserved word or special built-in,
3075 * or if it contains one or more meta characters.
3076 * - +exe_path+ otherwise.
3078 * <b>Argument +command_line+</b>
3080 * \String argument +command_line+ is a command line to be passed to a shell;
3081 * it must begin with a shell reserved word, begin with a special built-in,
3082 * or contain meta characters:
3084 * exec('if true; then echo "Foo"; fi') # Shell reserved word.
3085 * exec('exit') # Built-in.
3086 * exec('date > date.tmp') # Contains meta character.
3088 * The command line may also contain arguments and options for the command:
3090 * exec('echo "Foo"')
3096 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
3098 * Raises an exception if the new process could not execute.
3100 * <b>Argument +exe_path+</b>
3102 * Argument +exe_path+ is one of the following:
3104 * - The string path to an executable to be called.
3105 * - A 2-element array containing the path to an executable
3106 * and the string to be used as the name of the executing process.
3110 * exec('/usr/bin/date')
3114 * Sat Aug 26 09:38:00 AM CDT 2023
3116 * Ruby invokes the executable directly.
3117 * This form does not use the shell;
3118 * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
3120 * exec('doesnt_exist') # Raises Errno::ENOENT
3122 * If one or more +args+ is given, each is an argument or option
3123 * to be passed to the executable:
3125 * exec('echo', 'C*')
3126 * exec('echo', 'hello', 'world')
3133 * Raises an exception if the new process could not execute.
3137 f_exec(int c
, const VALUE
*a
, VALUE _
)
3140 UNREACHABLE_RETURN(Qnil
);
3143 #define ERRMSG(str) \
3144 ((errmsg && 0 < errmsg_buflen) ? \
3145 (void)strlcpy(errmsg, (str), errmsg_buflen) : (void)0)
3147 #define ERRMSG_FMT(...) \
3148 ((errmsg && 0 < errmsg_buflen) ? \
3149 (void)snprintf(errmsg, errmsg_buflen, __VA_ARGS__) : (void)0)
3151 static int fd_get_cloexec(int fd
, char *errmsg
, size_t errmsg_buflen
);
3152 static int fd_set_cloexec(int fd
, char *errmsg
, size_t errmsg_buflen
);
3153 static int fd_clear_cloexec(int fd
, char *errmsg
, size_t errmsg_buflen
);
3156 save_redirect_fd(int fd
, struct rb_execarg
*sargp
, char *errmsg
, size_t errmsg_buflen
)
3159 VALUE newary
, redirection
;
3160 int save_fd
= redirect_cloexec_dup(fd
), cloexec
;
3161 if (save_fd
== -1) {
3167 rb_update_max_fd(save_fd
);
3168 newary
= sargp
->fd_dup2
;
3169 if (newary
== Qfalse
) {
3170 newary
= hide_obj(rb_ary_new());
3171 sargp
->fd_dup2
= newary
;
3173 cloexec
= fd_get_cloexec(fd
, errmsg
, errmsg_buflen
);
3174 redirection
= hide_obj(rb_assoc_new(INT2FIX(fd
), INT2FIX(save_fd
)));
3175 if (cloexec
) rb_ary_push(redirection
, Qtrue
);
3176 rb_ary_push(newary
, redirection
);
3178 newary
= sargp
->fd_close
;
3179 if (newary
== Qfalse
) {
3180 newary
= hide_obj(rb_ary_new());
3181 sargp
->fd_close
= newary
;
3183 rb_ary_push(newary
, hide_obj(rb_assoc_new(INT2FIX(save_fd
), Qnil
)));
3190 intcmp(const void *a
, const void *b
)
3192 return *(int*)a
- *(int*)b
;
3196 intrcmp(const void *a
, const void *b
)
3198 return *(int*)b
- *(int*)a
;
3201 struct run_exec_dup2_fd_pair
{
3210 run_exec_dup2_tmpbuf_size(long n
)
3212 return sizeof(struct run_exec_dup2_fd_pair
) * n
;
3215 /* This function should be async-signal-safe. Actually it is. */
3217 fd_get_cloexec(int fd
, char *errmsg
, size_t errmsg_buflen
)
3221 ret
= fcntl(fd
, F_GETFD
); /* async-signal-safe */
3223 ERRMSG("fcntl(F_GETFD)");
3226 if (ret
& FD_CLOEXEC
) return 1;
3231 /* This function should be async-signal-safe. Actually it is. */
3233 fd_set_cloexec(int fd
, char *errmsg
, size_t errmsg_buflen
)
3237 ret
= fcntl(fd
, F_GETFD
); /* async-signal-safe */
3239 ERRMSG("fcntl(F_GETFD)");
3242 if (!(ret
& FD_CLOEXEC
)) {
3244 ret
= fcntl(fd
, F_SETFD
, ret
); /* async-signal-safe */
3246 ERRMSG("fcntl(F_SETFD)");
3254 /* This function should be async-signal-safe. Actually it is. */
3256 fd_clear_cloexec(int fd
, char *errmsg
, size_t errmsg_buflen
)
3260 ret
= fcntl(fd
, F_GETFD
); /* async-signal-safe */
3262 ERRMSG("fcntl(F_GETFD)");
3265 if (ret
& FD_CLOEXEC
) {
3267 ret
= fcntl(fd
, F_SETFD
, ret
); /* async-signal-safe */
3269 ERRMSG("fcntl(F_SETFD)");
3277 /* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3279 run_exec_dup2(VALUE ary
, VALUE tmpbuf
, struct rb_execarg
*sargp
, char *errmsg
, size_t errmsg_buflen
)
3284 struct rb_imemo_tmpbuf_struct
*buf
= (void *)tmpbuf
;
3285 struct run_exec_dup2_fd_pair
*pairs
= (void *)buf
->ptr
;
3287 n
= RARRAY_LEN(ary
);
3289 /* initialize oldfd and newfd: O(n) */
3290 for (i
= 0; i
< n
; i
++) {
3291 VALUE elt
= RARRAY_AREF(ary
, i
);
3292 pairs
[i
].oldfd
= FIX2INT(RARRAY_AREF(elt
, 1));
3293 pairs
[i
].newfd
= FIX2INT(RARRAY_AREF(elt
, 0)); /* unique */
3294 pairs
[i
].cloexec
= RARRAY_LEN(elt
) > 2 && RTEST(RARRAY_AREF(elt
, 2));
3295 pairs
[i
].older_index
= -1;
3298 /* sort the table by oldfd: O(n log n) */
3300 qsort(pairs
, n
, sizeof(struct run_exec_dup2_fd_pair
), intcmp
); /* hopefully async-signal-safe */
3302 qsort(pairs
, n
, sizeof(struct run_exec_dup2_fd_pair
), intrcmp
);
3304 /* initialize older_index and num_newer: O(n log n) */
3305 for (i
= 0; i
< n
; i
++) {
3306 int newfd
= pairs
[i
].newfd
;
3307 struct run_exec_dup2_fd_pair key
, *found
;
3309 found
= bsearch(&key
, pairs
, n
, sizeof(struct run_exec_dup2_fd_pair
), intcmp
); /* hopefully async-signal-safe */
3310 pairs
[i
].num_newer
= 0;
3312 while (pairs
< found
&& (found
-1)->oldfd
== newfd
)
3314 while (found
< pairs
+n
&& found
->oldfd
== newfd
) {
3315 pairs
[i
].num_newer
++;
3316 found
->older_index
= i
;
3322 /* non-cyclic redirection: O(n) */
3323 for (i
= 0; i
< n
; i
++) {
3325 while (j
!= -1 && pairs
[j
].oldfd
!= -1 && pairs
[j
].num_newer
== 0) {
3326 if (save_redirect_fd(pairs
[j
].newfd
, sargp
, errmsg
, errmsg_buflen
) < 0) /* async-signal-safe */
3328 ret
= redirect_dup2(pairs
[j
].oldfd
, pairs
[j
].newfd
); /* async-signal-safe */
3333 if (pairs
[j
].cloexec
&&
3334 fd_set_cloexec(pairs
[j
].newfd
, errmsg
, errmsg_buflen
)) {
3337 rb_update_max_fd(pairs
[j
].newfd
); /* async-signal-safe but don't need to call it in a child process. */
3338 pairs
[j
].oldfd
= -1;
3339 j
= pairs
[j
].older_index
;
3341 pairs
[j
].num_newer
--;
3345 /* cyclic redirection: O(n) */
3346 for (i
= 0; i
< n
; i
++) {
3348 if (pairs
[i
].oldfd
== -1)
3350 if (pairs
[i
].oldfd
== pairs
[i
].newfd
) { /* self cycle */
3351 if (fd_clear_cloexec(pairs
[i
].oldfd
, errmsg
, errmsg_buflen
) == -1) /* async-signal-safe */
3353 pairs
[i
].oldfd
= -1;
3356 if (extra_fd
== -1) {
3357 extra_fd
= redirect_dup(pairs
[i
].oldfd
); /* async-signal-safe */
3358 if (extra_fd
== -1) {
3362 // without this, kqueue timer_th.event_fd fails with a reserved FD did not have close-on-exec
3363 // in #assert_close_on_exec because the FD_CLOEXEC is not dup'd by default
3364 if (fd_get_cloexec(pairs
[i
].oldfd
, errmsg
, errmsg_buflen
)) {
3365 if (fd_set_cloexec(extra_fd
, errmsg
, errmsg_buflen
)) {
3369 rb_update_max_fd(extra_fd
);
3372 ret
= redirect_dup2(pairs
[i
].oldfd
, extra_fd
); /* async-signal-safe */
3377 rb_update_max_fd(extra_fd
);
3379 pairs
[i
].oldfd
= extra_fd
;
3380 j
= pairs
[i
].older_index
;
3381 pairs
[i
].older_index
= -1;
3383 ret
= redirect_dup2(pairs
[j
].oldfd
, pairs
[j
].newfd
); /* async-signal-safe */
3388 rb_update_max_fd(ret
);
3389 pairs
[j
].oldfd
= -1;
3390 j
= pairs
[j
].older_index
;
3393 if (extra_fd
!= -1) {
3394 ret
= redirect_close(extra_fd
); /* async-signal-safe */
3407 /* This function should be async-signal-safe. Actually it is. */
3409 run_exec_close(VALUE ary
, char *errmsg
, size_t errmsg_buflen
)
3414 for (i
= 0; i
< RARRAY_LEN(ary
); i
++) {
3415 VALUE elt
= RARRAY_AREF(ary
, i
);
3416 int fd
= FIX2INT(RARRAY_AREF(elt
, 0));
3417 ret
= redirect_close(fd
); /* async-signal-safe */
3426 /* This function should be async-signal-safe when sargp is NULL. Actually it is. */
3428 run_exec_dup2_child(VALUE ary
, struct rb_execarg
*sargp
, char *errmsg
, size_t errmsg_buflen
)
3433 for (i
= 0; i
< RARRAY_LEN(ary
); i
++) {
3434 VALUE elt
= RARRAY_AREF(ary
, i
);
3435 int newfd
= FIX2INT(RARRAY_AREF(elt
, 0));
3436 int oldfd
= FIX2INT(RARRAY_AREF(elt
, 1));
3438 if (save_redirect_fd(newfd
, sargp
, errmsg
, errmsg_buflen
) < 0) /* async-signal-safe */
3440 ret
= redirect_dup2(oldfd
, newfd
); /* async-signal-safe */
3445 rb_update_max_fd(newfd
);
3451 /* This function should be async-signal-safe when sargp is NULL. Actually it is. */
3453 run_exec_pgroup(const struct rb_execarg
*eargp
, struct rb_execarg
*sargp
, char *errmsg
, size_t errmsg_buflen
)
3456 * If FD_CLOEXEC is available, rb_fork_async_signal_safe waits the child's execve.
3457 * So setpgid is done in the child when rb_fork_async_signal_safe is returned in
3459 * No race condition, even without setpgid from the parent.
3460 * (Is there an environment which has setpgid but no FD_CLOEXEC?)
3465 pgroup
= eargp
->pgroup_pgid
;
3470 /* maybe meaningless with no fork environment... */
3471 sargp
->pgroup_given
= 1;
3472 sargp
->pgroup_pgid
= getpgrp();
3476 pgroup
= getpid(); /* async-signal-safe */
3478 ret
= setpgid(getpid(), pgroup
); /* async-signal-safe */
3479 if (ret
== -1) ERRMSG("setpgid");
3484 #if defined(HAVE_SETRLIMIT) && defined(RLIM2NUM)
3485 /* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3487 run_exec_rlimit(VALUE ary
, struct rb_execarg
*sargp
, char *errmsg
, size_t errmsg_buflen
)
3490 for (i
= 0; i
< RARRAY_LEN(ary
); i
++) {
3491 VALUE elt
= RARRAY_AREF(ary
, i
);
3492 int rtype
= NUM2INT(RARRAY_AREF(elt
, 0));
3496 if (getrlimit(rtype
, &rlim
) == -1) {
3497 ERRMSG("getrlimit");
3500 tmp
= hide_obj(rb_ary_new3(3, RARRAY_AREF(elt
, 0),
3501 RLIM2NUM(rlim
.rlim_cur
),
3502 RLIM2NUM(rlim
.rlim_max
)));
3503 if (sargp
->rlimit_limits
== Qfalse
)
3504 newary
= sargp
->rlimit_limits
= hide_obj(rb_ary_new());
3506 newary
= sargp
->rlimit_limits
;
3507 rb_ary_push(newary
, tmp
);
3509 rlim
.rlim_cur
= NUM2RLIM(RARRAY_AREF(elt
, 1));
3510 rlim
.rlim_max
= NUM2RLIM(RARRAY_AREF(elt
, 2));
3511 if (setrlimit(rtype
, &rlim
) == -1) { /* hopefully async-signal-safe */
3512 ERRMSG("setrlimit");
3520 #if !defined(HAVE_WORKING_FORK)
3522 save_env_i(RB_BLOCK_CALL_FUNC_ARGLIST(i
, ary
))
3524 rb_ary_push(ary
, hide_obj(rb_ary_dup(argv
[0])));
3529 save_env(struct rb_execarg
*sargp
)
3533 if (sargp
->env_modification
== Qfalse
) {
3534 VALUE env
= rb_envtbl();
3536 VALUE ary
= hide_obj(rb_ary_new());
3537 rb_block_call(env
, idEach
, 0, 0, save_env_i
,
3539 sargp
->env_modification
= ary
;
3541 sargp
->unsetenv_others_given
= 1;
3542 sargp
->unsetenv_others_do
= 1;
3549 #define chdir(p) rb_w32_uchdir(p)
3552 /* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3554 rb_execarg_run_options(const struct rb_execarg
*eargp
, struct rb_execarg
*sargp
, char *errmsg
, size_t errmsg_buflen
)
3559 /* assume that sargp is always NULL on fork-able environments */
3560 MEMZERO(sargp
, struct rb_execarg
, 1);
3561 sargp
->redirect_fds
= Qnil
;
3565 if (eargp
->pgroup_given
) {
3566 if (run_exec_pgroup(eargp
, sargp
, errmsg
, errmsg_buflen
) == -1) /* async-signal-safe */
3571 #if defined(HAVE_SETRLIMIT) && defined(RLIM2NUM)
3572 obj
= eargp
->rlimit_limits
;
3573 if (obj
!= Qfalse
) {
3574 if (run_exec_rlimit(obj
, sargp
, errmsg
, errmsg_buflen
) == -1) /* hopefully async-signal-safe */
3579 #if !defined(HAVE_WORKING_FORK)
3580 if (eargp
->unsetenv_others_given
&& eargp
->unsetenv_others_do
) {
3585 obj
= eargp
->env_modification
;
3586 if (obj
!= Qfalse
) {
3589 for (i
= 0; i
< RARRAY_LEN(obj
); i
++) {
3590 VALUE pair
= RARRAY_AREF(obj
, i
);
3591 VALUE key
= RARRAY_AREF(pair
, 0);
3592 VALUE val
= RARRAY_AREF(pair
, 1);
3594 ruby_setenv(StringValueCStr(key
), 0);
3596 ruby_setenv(StringValueCStr(key
), StringValueCStr(val
));
3601 if (eargp
->umask_given
) {
3602 mode_t mask
= eargp
->umask_mask
;
3603 mode_t oldmask
= umask(mask
); /* never fail */ /* async-signal-safe */
3605 sargp
->umask_given
= 1;
3606 sargp
->umask_mask
= oldmask
;
3610 obj
= eargp
->fd_dup2
;
3611 if (obj
!= Qfalse
) {
3612 if (run_exec_dup2(obj
, eargp
->dup2_tmpbuf
, sargp
, errmsg
, errmsg_buflen
) == -1) /* hopefully async-signal-safe */
3616 obj
= eargp
->fd_close
;
3617 if (obj
!= Qfalse
) {
3619 rb_warn("cannot close fd before spawn");
3621 if (run_exec_close(obj
, errmsg
, errmsg_buflen
) == -1) /* async-signal-safe */
3626 #ifdef HAVE_WORKING_FORK
3627 if (eargp
->close_others_do
) {
3628 rb_close_before_exec(3, eargp
->close_others_maxhint
, eargp
->redirect_fds
); /* async-signal-safe */
3632 obj
= eargp
->fd_dup2_child
;
3633 if (obj
!= Qfalse
) {
3634 if (run_exec_dup2_child(obj
, sargp
, errmsg
, errmsg_buflen
) == -1) /* async-signal-safe */
3638 if (eargp
->chdir_given
) {
3640 sargp
->chdir_given
= 1;
3641 sargp
->chdir_dir
= hide_obj(rb_dir_getwd_ospath());
3643 if (chdir(RSTRING_PTR(eargp
->chdir_dir
)) == -1) { /* async-signal-safe */
3650 if (eargp
->gid_given
) {
3651 if (setgid(eargp
->gid
) < 0) {
3658 if (eargp
->uid_given
) {
3659 if (setuid(eargp
->uid
) < 0) {
3667 VALUE ary
= sargp
->fd_dup2
;
3668 if (ary
!= Qfalse
) {
3669 rb_execarg_allocate_dup2_tmpbuf(sargp
, RARRAY_LEN(ary
));
3673 int preserve
= errno
;
3674 stdfd_clear_nonblock();
3681 /* This function should be async-signal-safe. Hopefully it is. */
3683 rb_exec_async_signal_safe(const struct rb_execarg
*eargp
, char *errmsg
, size_t errmsg_buflen
)
3685 errno
= exec_async_signal_safe(eargp
, errmsg
, errmsg_buflen
);
3690 exec_async_signal_safe(const struct rb_execarg
*eargp
, char *errmsg
, size_t errmsg_buflen
)
3692 #if !defined(HAVE_WORKING_FORK)
3693 struct rb_execarg sarg
, *const sargp
= &sarg
;
3695 struct rb_execarg
*const sargp
= NULL
;
3699 if (rb_execarg_run_options(eargp
, sargp
, errmsg
, errmsg_buflen
) < 0) { /* hopefully async-signal-safe */
3703 if (eargp
->use_shell
) {
3704 err
= proc_exec_sh(RSTRING_PTR(eargp
->invoke
.sh
.shell_script
), eargp
->envp_str
); /* async-signal-safe */
3707 char *abspath
= NULL
;
3708 if (!NIL_P(eargp
->invoke
.cmd
.command_abspath
))
3709 abspath
= RSTRING_PTR(eargp
->invoke
.cmd
.command_abspath
);
3710 err
= proc_exec_cmd(abspath
, eargp
->invoke
.cmd
.argv_str
, eargp
->envp_str
); /* async-signal-safe */
3712 #if !defined(HAVE_WORKING_FORK)
3713 rb_execarg_run_options(sargp
, NULL
, errmsg
, errmsg_buflen
);
3719 #ifdef HAVE_WORKING_FORK
3720 /* This function should be async-signal-safe. Hopefully it is. */
3722 rb_exec_atfork(void* arg
, char *errmsg
, size_t errmsg_buflen
)
3724 return rb_exec_async_signal_safe(arg
, errmsg
, errmsg_buflen
); /* hopefully async-signal-safe */
3728 proc_syswait(VALUE pid
)
3730 rb_syswait((rb_pid_t
)pid
);
3735 move_fds_to_avoid_crash(int *fdp
, int n
, VALUE fds
)
3739 for (i
= 0; i
< n
; i
++) {
3741 while (RTEST(rb_hash_lookup(fds
, INT2FIX(fdp
[i
])))) {
3744 while (RTEST(rb_hash_lookup(fds
, INT2FIX(min
))))
3746 ret
= rb_cloexec_fcntl_dupfd(fdp
[i
], min
);
3749 rb_update_max_fd(ret
);
3758 pipe_nocrash(int filedes
[2], VALUE fds
)
3761 ret
= rb_pipe(filedes
);
3766 if (move_fds_to_avoid_crash(filedes
, 2, fds
) == -1) {
3781 rb_thread_sleep_that_takes_VALUE_as_sole_argument(VALUE n
)
3783 rb_thread_sleep(NUM2INT(n
));
3788 handle_fork_error(int err
, struct rb_process_status
*status
, int *ep
, volatile int *try_gc_p
)
3794 if ((*try_gc_p
)-- > 0 && !rb_during_gc()) {
3800 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
3803 if (!status
&& !ep
) {
3808 rb_protect(rb_thread_sleep_that_takes_VALUE_as_sole_argument
, INT2FIX(1), &state
);
3809 if (status
) status
->status
= state
;
3810 if (!state
) return 0;
3819 if (state
&& !status
) rb_jump_tag(state
);
3823 #define prefork() ( \
3824 rb_io_flush(rb_stdout), \
3825 rb_io_flush(rb_stderr) \
3829 * Forks child process, and returns the process ID in the parent
3832 * If +status+ is given, protects from any exceptions and sets the
3833 * jump status to it, and returns -1. If failed to fork new process
3834 * but no exceptions occurred, sets 0 to it. Otherwise, if forked
3835 * successfully, the value of +status+ is undetermined.
3837 * In the child process, just returns 0 if +chfunc+ is +NULL+.
3838 * Otherwise +chfunc+ will be called with +charg+, and then the child
3839 * process exits with +EXIT_SUCCESS+ when it returned zero.
3841 * In the case of the function is called and returns non-zero value,
3842 * the child process exits with non-+EXIT_SUCCESS+ value (normally
3843 * 127). And, on the platforms where +FD_CLOEXEC+ is available,
3844 * +errno+ is propagated to the parent process, and this function
3845 * returns -1 in the parent process. On the other platforms, just
3848 * If fds is not Qnil, internal pipe for the errno propagation is
3849 * arranged to avoid conflicts of the hash keys in +fds+.
3851 * +chfunc+ must not raise any exceptions.
3855 write_retry(int fd
, const void *buf
, size_t len
)
3860 w
= write(fd
, buf
, len
);
3861 } while (w
< 0 && errno
== EINTR
);
3867 read_retry(int fd
, void *buf
, size_t len
)
3871 if (set_blocking(fd
) != 0) {
3873 rb_async_bug_errno("set_blocking failed reading child error", errno
);
3878 r
= read(fd
, buf
, len
);
3879 } while (r
< 0 && errno
== EINTR
);
3885 send_child_error(int fd
, char *errmsg
, size_t errmsg_buflen
)
3890 if (write_retry(fd
, &err
, sizeof(err
)) < 0) err
= errno
;
3891 if (errmsg
&& 0 < errmsg_buflen
) {
3892 errmsg
[errmsg_buflen
-1] = '\0';
3893 errmsg_buflen
= strlen(errmsg
);
3894 if (errmsg_buflen
> 0 && write_retry(fd
, errmsg
, errmsg_buflen
) < 0)
3900 recv_child_error(int fd
, int *errp
, char *errmsg
, size_t errmsg_buflen
)
3904 if ((size
= read_retry(fd
, &err
, sizeof(err
))) < 0) {
3908 if (size
== sizeof(err
) &&
3909 errmsg
&& 0 < errmsg_buflen
) {
3910 ssize_t ret
= read_retry(fd
, errmsg
, errmsg_buflen
-1);
3919 #ifdef HAVE_WORKING_VFORK
3920 #if !defined(HAVE_GETRESUID) && defined(HAVE_GETUIDX)
3923 getresuid(rb_uid_t
*ruid
, rb_uid_t
*euid
, rb_uid_t
*suid
)
3929 ret
= getuidx(ID_SAVED
);
3930 if (ret
== (rb_uid_t
)-1)
3935 #define HAVE_GETRESUID
3938 #if !defined(HAVE_GETRESGID) && defined(HAVE_GETGIDX)
3941 getresgid(rb_gid_t
*rgid
, rb_gid_t
*egid
, rb_gid_t
*sgid
)
3947 ret
= getgidx(ID_SAVED
);
3948 if (ret
== (rb_gid_t
)-1)
3953 #define HAVE_GETRESGID
3960 * has_privilege() is used to choose vfork() or fork().
3962 * If the process has privilege, the parent process or
3963 * the child process can change UID/GID.
3964 * If vfork() is used to create the child process and
3965 * the parent or child process change effective UID/GID,
3966 * different privileged processes shares memory.
3967 * It is a bad situation.
3968 * So, fork() should be used.
3971 rb_uid_t ruid
, euid
;
3972 rb_gid_t rgid
, egid
;
3974 #if defined HAVE_ISSETUGID
3979 #ifdef HAVE_GETRESUID
3983 ret
= getresuid(&ruid
, &euid
, &suid
);
3985 rb_sys_fail("getresuid(2)");
3994 if (euid
== 0 || euid
!= ruid
)
3997 #ifdef HAVE_GETRESGID
4001 ret
= getresgid(&rgid
, &egid
, &sgid
);
4003 rb_sys_fail("getresgid(2)");
4019 struct child_handler_disabler_state
4025 disable_child_handler_before_fork(struct child_handler_disabler_state
*old
)
4027 #ifdef HAVE_PTHREAD_SIGMASK
4031 ret
= sigfillset(&all
);
4033 rb_sys_fail("sigfillset");
4035 ret
= pthread_sigmask(SIG_SETMASK
, &all
, &old
->sigmask
); /* not async-signal-safe */
4037 rb_syserr_fail(ret
, "pthread_sigmask");
4040 # pragma GCC warning "pthread_sigmask on fork is not available. potentially dangerous"
4045 disable_child_handler_fork_parent(struct child_handler_disabler_state
*old
)
4047 #ifdef HAVE_PTHREAD_SIGMASK
4050 ret
= pthread_sigmask(SIG_SETMASK
, &old
->sigmask
, NULL
); /* not async-signal-safe */
4052 rb_syserr_fail(ret
, "pthread_sigmask");
4055 # pragma GCC warning "pthread_sigmask on fork is not available. potentially dangerous"
4059 /* This function should be async-signal-safe. Actually it is. */
4061 disable_child_handler_fork_child(struct child_handler_disabler_state
*old
, char *errmsg
, size_t errmsg_buflen
)
4066 for (sig
= 1; sig
< NSIG
; sig
++) {
4067 sig_t handler
= signal(sig
, SIG_DFL
);
4069 if (handler
== SIG_ERR
&& errno
== EINVAL
) {
4070 continue; /* Ignore invalid signal number */
4072 if (handler
== SIG_ERR
) {
4073 ERRMSG("signal to obtain old action");
4077 if (sig
== SIGPIPE
) {
4081 /* it will be reset to SIG_DFL at execve time, instead */
4082 if (handler
== SIG_IGN
) {
4083 signal(sig
, SIG_IGN
);
4087 /* non-Ruby child process, ensure cmake can see SIGCHLD */
4088 sigemptyset(&old
->sigmask
);
4089 ret
= sigprocmask(SIG_SETMASK
, &old
->sigmask
, NULL
); /* async-signal-safe */
4091 ERRMSG("sigprocmask");
4098 retry_fork_async_signal_safe(struct rb_process_status
*status
, int *ep
,
4099 int (*chfunc
)(void*, char *, size_t), void *charg
,
4100 char *errmsg
, size_t errmsg_buflen
,
4101 struct waitpid_state
*w
)
4104 volatile int try_gc
= 1;
4105 struct child_handler_disabler_state old
;
4110 disable_child_handler_before_fork(&old
);
4111 #ifdef HAVE_WORKING_VFORK
4112 if (!has_privilege())
4119 if (pid
== 0) {/* fork succeed, child process */
4122 ret
= disable_child_handler_fork_child(&old
, errmsg
, errmsg_buflen
); /* async-signal-safe */
4124 ret
= chfunc(charg
, errmsg
, errmsg_buflen
);
4125 if (!ret
) _exit(EXIT_SUCCESS
);
4127 send_child_error(ep
[1], errmsg
, errmsg_buflen
);
4128 #if EXIT_SUCCESS == 127
4129 _exit(EXIT_FAILURE
);
4135 disable_child_handler_fork_parent(&old
);
4136 if (0 < pid
) /* fork succeed, parent process */
4139 if (handle_fork_error(err
, status
, ep
, &try_gc
))
4145 fork_check_err(struct rb_process_status
*status
, int (*chfunc
)(void*, char *, size_t), void *charg
,
4146 VALUE fds
, char *errmsg
, size_t errmsg_buflen
,
4147 struct rb_execarg
*eargp
)
4154 struct waitpid_state
*w
= eargp
&& eargp
->waitpid_state
? eargp
->waitpid_state
: 0;
4156 if (status
) status
->status
= 0;
4158 if (pipe_nocrash(ep
, fds
)) return -1;
4160 pid
= retry_fork_async_signal_safe(status
, ep
, chfunc
, charg
, errmsg
, errmsg_buflen
, w
);
4162 if (status
) status
->pid
= pid
;
4165 if (status
) status
->error
= errno
;
4172 error_occurred
= recv_child_error(ep
[0], &err
, errmsg
, errmsg_buflen
);
4174 if (error_occurred
) {
4177 status
->error
= err
;
4179 VM_ASSERT((w
== 0) && "only used by extensions");
4180 rb_protect(proc_syswait
, (VALUE
)pid
, &state
);
4182 status
->status
= state
;
4196 * The "async_signal_safe" name is a lie, but it is used by pty.c and
4197 * maybe other exts. fork() is not async-signal-safe due to pthread_atfork
4198 * and future POSIX revisions will remove it from a list of signal-safe
4199 * functions. rb_waitpid is not async-signal-safe since RJIT, either.
4200 * For our purposes, we do not need async-signal-safety, here
4203 rb_fork_async_signal_safe(int *status
,
4204 int (*chfunc
)(void*, char *, size_t), void *charg
,
4205 VALUE fds
, char *errmsg
, size_t errmsg_buflen
)
4207 struct rb_process_status process_status
;
4209 rb_pid_t result
= fork_check_err(&process_status
, chfunc
, charg
, fds
, errmsg
, errmsg_buflen
, 0);
4212 *status
= process_status
.status
;
4219 rb_fork_ruby2(struct rb_process_status
*status
)
4222 int try_gc
= 1, err
;
4223 struct child_handler_disabler_state old
;
4225 if (status
) status
->status
= 0;
4231 disable_child_handler_before_fork(&old
);
4237 status
->error
= err
;
4240 disable_child_handler_fork_parent(&old
); /* yes, bad name */
4241 after_fork_ruby(pid
);
4243 if (pid
>= 0) { /* fork succeed */
4248 if (handle_fork_error(err
, status
, NULL
, &try_gc
)) {
4255 rb_fork_ruby(int *status
)
4257 struct rb_process_status process_status
= {0};
4259 rb_pid_t pid
= rb_fork_ruby2(&process_status
);
4261 if (status
) *status
= process_status
.status
;
4269 rb_pid_t pid
= rb_fork_ruby(NULL
);
4272 rb_sys_fail("fork(2)");
4279 rb_call_proc__fork(void)
4282 CONST_ID(id__fork
, "_fork");
4283 if (rb_method_basic_definition_p(CLASS_OF(rb_mProcess
), id__fork
)) {
4284 return proc_fork_pid();
4287 VALUE pid
= rb_funcall(rb_mProcess
, id__fork
, 0);
4288 return NUM2PIDT(pid
);
4293 #if defined(HAVE_WORKING_FORK) && !defined(CANNOT_FORK_WITH_PTHREAD)
4296 * Process._fork -> integer
4298 * An internal API for fork. Do not call this method directly.
4299 * Currently, this is called via Kernel#fork, Process.fork, and
4300 * IO.popen with <tt>"-"</tt>.
4302 * This method is not for casual code but for application monitoring
4303 * libraries. You can add custom code before and after fork events
4304 * by overriding this method.
4306 * Note: Process.daemon may be implemented using fork(2) BUT does not go
4307 * through this method.
4308 * Thus, depending on your reason to hook into this method, you
4309 * may also want to hook into that one.
4310 * See {this issue}[https://bugs.ruby-lang.org/issues/18911] for a
4311 * more detailed discussion of this.
4314 rb_proc__fork(VALUE _obj
)
4316 rb_pid_t pid
= proc_fork_pid();
4317 return PIDT2NUM(pid
);
4322 * Process.fork { ... } -> integer or nil
4323 * Process.fork -> integer or nil
4325 * Creates a child process.
4327 * With a block given, runs the block in the child process;
4328 * on block exit, the child terminates with a status of zero:
4330 * puts "Before the fork: #{Process.pid}"
4332 * puts "In the child process: #{Process.pid}"
4334 * puts "After the fork: #{Process.pid}"
4338 * Before the fork: 420496
4339 * After the fork: 420496
4340 * In the child process: 420520
4342 * With no block given, the +fork+ call returns twice:
4344 * - Once in the parent process, returning the pid of the child process.
4345 * - Once in the child process, returning +nil+.
4349 * puts "This is the first line before the fork (pid #{Process.pid})"
4351 * puts "This is the second line after the fork (pid #{Process.pid})"
4355 * This is the first line before the fork (pid 420199)
4357 * This is the second line after the fork (pid 420199)
4359 * This is the second line after the fork (pid 420223)
4361 * In either case, the child process may exit using
4362 * Kernel.exit! to avoid the call to Kernel#at_exit.
4364 * To avoid zombie processes, the parent process should call either:
4366 * - Process.wait, to collect the termination statuses of its children.
4367 * - Process.detach, to register disinterest in their status.
4369 * The thread calling +fork+ is the only thread in the created child process;
4370 * +fork+ doesn't copy other threads.
4372 * Note that method +fork+ is available on some platforms,
4373 * but not on others:
4375 * Process.respond_to?(:fork) # => true # Would be false on some.
4377 * If not, you may use ::spawn instead of +fork+.
4381 rb_f_fork(VALUE obj
)
4385 pid
= rb_call_proc__fork();
4388 if (rb_block_given_p()) {
4390 rb_protect(rb_yield
, Qundef
, &status
);
4396 return PIDT2NUM(pid
);
4399 #define rb_proc__fork rb_f_notimplement
4400 #define rb_f_fork rb_f_notimplement
4404 exit_status_code(VALUE status
)
4410 istatus
= EXIT_SUCCESS
;
4413 istatus
= EXIT_FAILURE
;
4416 istatus
= NUM2INT(status
);
4417 #if EXIT_SUCCESS != 0
4419 istatus
= EXIT_SUCCESS
;
4426 NORETURN(static VALUE
rb_f_exit_bang(int argc
, VALUE
*argv
, VALUE obj
));
4429 * exit!(status = false)
4430 * Process.exit!(status = false)
4432 * Exits the process immediately; no exit handlers are called.
4433 * Returns exit status +status+ to the underlying operating system.
4435 * Process.exit!(true)
4437 * Values +true+ and +false+ for argument +status+
4438 * indicate, respectively, success and failure;
4439 * The meanings of integer values are system-dependent.
4444 rb_f_exit_bang(int argc
, VALUE
*argv
, VALUE obj
)
4448 if (rb_check_arity(argc
, 0, 1) == 1) {
4449 istatus
= exit_status_code(argv
[0]);
4452 istatus
= EXIT_FAILURE
;
4456 UNREACHABLE_RETURN(Qnil
);
4462 if (GET_EC()->tag
) {
4465 args
[0] = INT2NUM(status
);
4466 args
[1] = rb_str_new2("exit");
4467 rb_exc_raise(rb_class_new_instance(2, args
, rb_eSystemExit
));
4473 rb_f_exit(int argc
, const VALUE
*argv
)
4477 if (rb_check_arity(argc
, 0, 1) == 1) {
4478 istatus
= exit_status_code(argv
[0]);
4481 istatus
= EXIT_SUCCESS
;
4485 UNREACHABLE_RETURN(Qnil
);
4488 NORETURN(static VALUE
f_exit(int c
, const VALUE
*a
, VALUE _
));
4491 * exit(status = true)
4492 * Process.exit(status = true)
4494 * Initiates termination of the Ruby script by raising SystemExit;
4495 * the exception may be caught.
4496 * Returns exit status +status+ to the underlying operating system.
4498 * Values +true+ and +false+ for argument +status+
4499 * indicate, respectively, success and failure;
4500 * The meanings of integer values are system-dependent.
4506 * puts 'Never get here.'
4508 * puts 'Rescued a SystemExit exception.'
4510 * puts 'After begin block.'
4514 * Rescued a SystemExit exception.
4515 * After begin block.
4517 * Just prior to final termination,
4518 * Ruby executes any at-exit procedures (see Kernel::at_exit)
4519 * and any object finalizers (see ObjectSpace::define_finalizer).
4523 * at_exit { puts 'In at_exit function.' }
4524 * ObjectSpace.define_finalizer('string', proc { puts 'In finalizer.' })
4529 * In at_exit function.
4535 f_exit(int c
, const VALUE
*a
, VALUE _
)
4538 UNREACHABLE_RETURN(Qnil
);
4542 rb_f_abort(int argc
, const VALUE
*argv
)
4544 rb_check_arity(argc
, 0, 1);
4546 rb_execution_context_t
*ec
= GET_EC();
4547 VALUE errinfo
= rb_ec_get_errinfo(ec
);
4548 if (!NIL_P(errinfo
)) {
4549 rb_ec_error_print(ec
, errinfo
);
4551 rb_exit(EXIT_FAILURE
);
4556 args
[1] = args
[0] = argv
[0];
4557 StringValue(args
[0]);
4558 rb_io_puts(1, args
, rb_ractor_stderr());
4559 args
[0] = INT2NUM(EXIT_FAILURE
);
4560 rb_exc_raise(rb_class_new_instance(2, args
, rb_eSystemExit
));
4563 UNREACHABLE_RETURN(Qnil
);
4566 NORETURN(static VALUE
f_abort(int c
, const VALUE
*a
, VALUE _
));
4571 * Process.abort(msg = nil)
4573 * Terminates execution immediately, effectively by calling
4574 * <tt>Kernel.exit(false)</tt>.
4576 * If string argument +msg+ is given,
4577 * it is written to STDERR prior to termination;
4578 * otherwise, if an exception was raised,
4579 * prints its message and backtrace.
4583 f_abort(int c
, const VALUE
*a
, VALUE _
)
4586 UNREACHABLE_RETURN(Qnil
);
4590 rb_syswait(rb_pid_t pid
)
4594 rb_waitpid(pid
, &status
, 0);
4597 #if !defined HAVE_WORKING_FORK && !defined HAVE_SPAWNV && !defined __EMSCRIPTEN__
4599 rb_execarg_commandline(const struct rb_execarg
*eargp
, VALUE
*prog
)
4602 if (eargp
&& !eargp
->use_shell
) {
4603 VALUE str
= eargp
->invoke
.cmd
.argv_str
;
4604 VALUE buf
= eargp
->invoke
.cmd
.argv_buf
;
4605 char *p
, **argv
= ARGVSTR2ARGV(str
);
4606 long i
, argc
= ARGVSTR2ARGC(str
);
4607 const char *start
= RSTRING_PTR(buf
);
4608 cmd
= rb_str_new(start
, RSTRING_LEN(buf
));
4609 p
= RSTRING_PTR(cmd
);
4610 for (i
= 1; i
< argc
; ++i
) {
4611 p
[argv
[i
] - start
- 1] = ' ';
4616 return StringValueCStr(*prog
);
4621 rb_spawn_process(struct rb_execarg
*eargp
, char *errmsg
, size_t errmsg_buflen
)
4624 #if !defined HAVE_WORKING_FORK || USE_SPAWNV
4626 struct rb_execarg sarg
;
4627 # if !defined HAVE_SPAWNV
4632 #if defined HAVE_WORKING_FORK && !USE_SPAWNV
4633 pid
= fork_check_err(eargp
->status
, rb_exec_atfork
, eargp
, eargp
->redirect_fds
, errmsg
, errmsg_buflen
, eargp
);
4635 prog
= eargp
->use_shell
? eargp
->invoke
.sh
.shell_script
: eargp
->invoke
.cmd
.command_name
;
4637 if (rb_execarg_run_options(eargp
, &sarg
, errmsg
, errmsg_buflen
) < 0) {
4641 if (prog
&& !eargp
->use_shell
) {
4642 char **argv
= ARGVSTR2ARGV(eargp
->invoke
.cmd
.argv_str
);
4643 argv
[0] = RSTRING_PTR(prog
);
4645 # if defined HAVE_SPAWNV
4646 if (eargp
->use_shell
) {
4647 pid
= proc_spawn_sh(RSTRING_PTR(prog
));
4650 char **argv
= ARGVSTR2ARGV(eargp
->invoke
.cmd
.argv_str
);
4651 pid
= proc_spawn_cmd(argv
, prog
, eargp
);
4655 rb_last_status_set(0x7f << 8, pid
);
4658 status
= system(rb_execarg_commandline(eargp
, &prog
));
4659 pid
= 1; /* dummy */
4660 rb_last_status_set((status
& 0xff) << 8, pid
);
4663 if (eargp
->waitpid_state
) {
4664 eargp
->waitpid_state
->pid
= pid
;
4667 rb_execarg_run_options(&sarg
, NULL
, errmsg
, errmsg_buflen
);
4682 do_spawn_process(VALUE arg
)
4684 struct spawn_args
*argp
= (struct spawn_args
*)arg
;
4686 rb_execarg_parent_start1(argp
->execarg
);
4688 return (VALUE
)rb_spawn_process(rb_execarg_get(argp
->execarg
),
4689 argp
->errmsg
.ptr
, argp
->errmsg
.buflen
);
4692 NOINLINE(static rb_pid_t
4693 rb_execarg_spawn(VALUE execarg_obj
, char *errmsg
, size_t errmsg_buflen
));
4696 rb_execarg_spawn(VALUE execarg_obj
, char *errmsg
, size_t errmsg_buflen
)
4698 struct spawn_args args
;
4700 args
.execarg
= execarg_obj
;
4701 args
.errmsg
.ptr
= errmsg
;
4702 args
.errmsg
.buflen
= errmsg_buflen
;
4704 rb_pid_t r
= (rb_pid_t
)rb_ensure(do_spawn_process
, (VALUE
)&args
,
4705 execarg_parent_end
, execarg_obj
);
4710 rb_spawn_internal(int argc
, const VALUE
*argv
, char *errmsg
, size_t errmsg_buflen
)
4714 execarg_obj
= rb_execarg_new(argc
, argv
, TRUE
, FALSE
);
4715 return rb_execarg_spawn(execarg_obj
, errmsg
, errmsg_buflen
);
4719 rb_spawn_err(int argc
, const VALUE
*argv
, char *errmsg
, size_t errmsg_buflen
)
4721 return rb_spawn_internal(argc
, argv
, errmsg
, errmsg_buflen
);
4725 rb_spawn(int argc
, const VALUE
*argv
)
4727 return rb_spawn_internal(argc
, argv
, NULL
, 0);
4732 * system([env, ] command_line, options = {}, exception: false) -> true, false, or nil
4733 * system([env, ] exe_path, *args, options = {}, exception: false) -> true, false, or nil
4735 * Creates a new child process by doing one of the following
4738 * - Passing string +command_line+ to the shell.
4739 * - Invoking the executable at +exe_path+.
4741 * This method has potential security vulnerabilities if called with untrusted input;
4742 * see {Command Injection}[rdoc-ref:command_injection.rdoc].
4746 * - +true+ if the command exits with status zero.
4747 * - +false+ if the exit status is a non-zero integer.
4748 * - +nil+ if the command could not execute.
4750 * Raises an exception (instead of returning +false+ or +nil+)
4751 * if keyword argument +exception+ is set to +true+.
4753 * Assigns the command's error status to <tt>$?</tt>.
4755 * The new process is created using the
4756 * {system system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/system.html];
4757 * it may inherit some of its environment from the calling program
4758 * (possibly including open file descriptors).
4760 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
4761 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
4763 * Argument +options+ is a hash of options for the new process;
4764 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
4766 * The first required argument is one of the following:
4768 * - +command_line+ if it is a string,
4769 * and if it begins with a shell reserved word or special built-in,
4770 * or if it contains one or more meta characters.
4771 * - +exe_path+ otherwise.
4773 * <b>Argument +command_line+</b>
4775 * \String argument +command_line+ is a command line to be passed to a shell;
4776 * it must begin with a shell reserved word, begin with a special built-in,
4777 * or contain meta characters:
4779 * system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
4780 * system('exit') # => true # Built-in.
4781 * system('date > /tmp/date.tmp') # => true # Contains meta character.
4782 * system('date > /nop/date.tmp') # => false
4783 * system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
4785 * Assigns the command's error status to <tt>$?</tt>:
4787 * system('exit') # => true # Built-in.
4788 * $? # => #<Process::Status: pid 640610 exit 0>
4789 * system('date > /nop/date.tmp') # => false
4790 * $? # => #<Process::Status: pid 640742 exit 2>
4792 * The command line may also contain arguments and options for the command:
4794 * system('echo "Foo"') # => true
4800 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
4802 * Raises an exception if the new process could not execute.
4804 * <b>Argument +exe_path+</b>
4806 * Argument +exe_path+ is one of the following:
4808 * - The string path to an executable to be called.
4809 * - A 2-element array containing the path to an executable
4810 * and the string to be used as the name of the executing process.
4814 * system('/usr/bin/date') # => true # Path to date on Unix-style system.
4815 * system('foo') # => nil # Command failed.
4819 * Mon Aug 28 11:43:10 AM CDT 2023
4821 * Assigns the command's error status to <tt>$?</tt>:
4823 * system('/usr/bin/date') # => true
4824 * $? # => #<Process::Status: pid 645605 exit 0>
4825 * system('foo') # => nil
4826 * $? # => #<Process::Status: pid 645608 exit 127>
4828 * Ruby invokes the executable directly.
4829 * This form does not use the shell;
4830 * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
4832 * system('doesnt_exist') # => nil
4834 * If one or more +args+ is given, each is an argument or option
4835 * to be passed to the executable:
4837 * system('echo', 'C*') # => true
4838 * system('echo', 'hello', 'world') # => true
4845 * Raises an exception if the new process could not execute.
4849 rb_f_system(int argc
, VALUE
*argv
, VALUE _
)
4851 rb_thread_t
*th
= GET_THREAD();
4852 VALUE execarg_obj
= rb_execarg_new(argc
, argv
, TRUE
, TRUE
);
4853 struct rb_execarg
*eargp
= rb_execarg_get(execarg_obj
);
4855 struct rb_process_status status
= {0};
4856 eargp
->status
= &status
;
4858 last_status_clear(th
);
4860 // This function can set the thread's last status.
4861 // May be different from waitpid_state.pid on exec failure.
4862 rb_pid_t pid
= rb_execarg_spawn(execarg_obj
, 0, 0);
4865 VALUE status
= rb_process_status_wait(pid
, 0);
4866 struct rb_process_status
*data
= rb_check_typeddata(status
, &rb_process_status_type
);
4867 // Set the last status:
4868 rb_obj_freeze(status
);
4869 th
->last_status
= status
;
4871 if (data
->status
== EXIT_SUCCESS
) {
4875 if (data
->error
!= 0) {
4876 if (eargp
->exception
) {
4877 VALUE command
= eargp
->invoke
.sh
.shell_script
;
4878 RB_GC_GUARD(execarg_obj
);
4879 rb_syserr_fail_str(data
->error
, command
);
4885 else if (eargp
->exception
) {
4886 VALUE command
= eargp
->invoke
.sh
.shell_script
;
4887 VALUE str
= rb_str_new_cstr("Command failed with");
4888 rb_str_cat_cstr(pst_message_status(str
, data
->status
), ": ");
4889 rb_str_append(str
, command
);
4890 RB_GC_GUARD(execarg_obj
);
4891 rb_exc_raise(rb_exc_new_str(rb_eRuntimeError
, str
));
4897 RB_GC_GUARD(status
);
4900 if (eargp
->exception
) {
4901 VALUE command
= eargp
->invoke
.sh
.shell_script
;
4902 RB_GC_GUARD(execarg_obj
);
4903 rb_syserr_fail_str(errno
, command
);
4912 * spawn([env, ] command_line, options = {}) -> pid
4913 * spawn([env, ] exe_path, *args, options = {}) -> pid
4915 * Creates a new child process by doing one of the following
4918 * - Passing string +command_line+ to the shell.
4919 * - Invoking the executable at +exe_path+.
4921 * This method has potential security vulnerabilities if called with untrusted input;
4922 * see {Command Injection}[rdoc-ref:command_injection.rdoc].
4924 * Returns the process ID (pid) of the new process,
4925 * without waiting for it to complete.
4927 * To avoid zombie processes, the parent process should call either:
4929 * - Process.wait, to collect the termination statuses of its children.
4930 * - Process.detach, to register disinterest in their status.
4932 * The new process is created using the
4933 * {exec system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/execve.html];
4934 * it may inherit some of its environment from the calling program
4935 * (possibly including open file descriptors).
4937 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
4938 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
4940 * Argument +options+ is a hash of options for the new process;
4941 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
4943 * The first required argument is one of the following:
4945 * - +command_line+ if it is a string,
4946 * and if it begins with a shell reserved word or special built-in,
4947 * or if it contains one or more meta characters.
4948 * - +exe_path+ otherwise.
4950 * <b>Argument +command_line+</b>
4952 * \String argument +command_line+ is a command line to be passed to a shell;
4953 * it must begin with a shell reserved word, begin with a special built-in,
4954 * or contain meta characters:
4956 * spawn('if true; then echo "Foo"; fi') # => 798847 # Shell reserved word.
4957 * Process.wait # => 798847
4958 * spawn('exit') # => 798848 # Built-in.
4959 * Process.wait # => 798848
4960 * spawn('date > /tmp/date.tmp') # => 798879 # Contains meta character.
4961 * Process.wait # => 798849
4962 * spawn('date > /nop/date.tmp') # => 798882 # Issues error message.
4963 * Process.wait # => 798882
4965 * The command line may also contain arguments and options for the command:
4967 * spawn('echo "Foo"') # => 799031
4968 * Process.wait # => 799031
4974 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
4976 * Raises an exception if the new process could not execute.
4978 * <b>Argument +exe_path+</b>
4980 * Argument +exe_path+ is one of the following:
4982 * - The string path to an executable to be called.
4983 * - A 2-element array containing the path to an executable to be called,
4984 * and the string to be used as the name of the executing process.
4986 * spawn('/usr/bin/date') # Path to date on Unix-style system.
4991 * Mon Aug 28 11:43:10 AM CDT 2023
4993 * Ruby invokes the executable directly.
4994 * This form does not use the shell;
4995 * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
4997 * If one or more +args+ is given, each is an argument or option
4998 * to be passed to the executable:
5000 * spawn('echo', 'C*') # => 799392
5001 * Process.wait # => 799392
5002 * spawn('echo', 'hello', 'world') # => 799393
5003 * Process.wait # => 799393
5010 * Raises an exception if the new process could not execute.
5014 rb_f_spawn(int argc
, VALUE
*argv
, VALUE _
)
5017 char errmsg
[CHILD_ERRMSG_BUFLEN
] = { '\0' };
5018 VALUE execarg_obj
, fail_str
;
5019 struct rb_execarg
*eargp
;
5021 execarg_obj
= rb_execarg_new(argc
, argv
, TRUE
, FALSE
);
5022 eargp
= rb_execarg_get(execarg_obj
);
5023 fail_str
= eargp
->use_shell
? eargp
->invoke
.sh
.shell_script
: eargp
->invoke
.cmd
.command_name
;
5025 pid
= rb_execarg_spawn(execarg_obj
, errmsg
, sizeof(errmsg
));
5029 rb_exec_fail(eargp
, err
, errmsg
);
5030 RB_GC_GUARD(execarg_obj
);
5031 rb_syserr_fail_str(err
, fail_str
);
5033 #if defined(HAVE_WORKING_FORK) || defined(HAVE_SPAWNV)
5034 return PIDT2NUM(pid
);
5042 * sleep(secs = nil) -> slept_secs
5044 * Suspends execution of the current thread for the number of seconds
5045 * specified by numeric argument +secs+, or forever if +secs+ is +nil+;
5046 * returns the integer number of seconds suspended (rounded).
5048 * Time.new # => 2008-03-08 19:56:19 +0900
5050 * Time.new # => 2008-03-08 19:56:20 +0900
5052 * Time.new # => 2008-03-08 19:56:22 +0900
5057 rb_f_sleep(int argc
, VALUE
*argv
, VALUE _
)
5059 time_t beg
= time(0);
5060 VALUE scheduler
= rb_fiber_scheduler_current();
5062 if (scheduler
!= Qnil
) {
5063 rb_fiber_scheduler_kernel_sleepv(scheduler
, argc
, argv
);
5066 if (argc
== 0 || (argc
== 1 && NIL_P(argv
[0]))) {
5067 rb_thread_sleep_forever();
5070 rb_check_arity(argc
, 0, 1);
5071 rb_thread_wait_for(rb_time_interval(argv
[0]));
5075 time_t end
= time(0) - beg
;
5077 return TIMET2NUM(end
);
5081 #if (defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)) || defined(HAVE_GETPGID)
5084 * Process.getpgrp -> integer
5086 * Returns the process group ID for the current process:
5088 * Process.getpgid(0) # => 25527
5089 * Process.getpgrp # => 25527
5094 proc_getpgrp(VALUE _
)
5098 #if defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)
5100 if (pgrp
< 0) rb_sys_fail(0);
5101 return PIDT2NUM(pgrp
);
5102 #else /* defined(HAVE_GETPGID) */
5104 if (pgrp
< 0) rb_sys_fail(0);
5105 return PIDT2NUM(pgrp
);
5109 #define proc_getpgrp rb_f_notimplement
5113 #if defined(HAVE_SETPGID) || (defined(HAVE_SETPGRP) && defined(SETPGRP_VOID))
5116 * Process.setpgrp -> 0
5118 * Equivalent to <tt>setpgid(0, 0)</tt>.
5120 * Not available on all platforms.
5124 proc_setpgrp(VALUE _
)
5126 /* check for posix setpgid() first; this matches the posix */
5127 /* getpgrp() above. It appears that configure will set SETPGRP_VOID */
5128 /* even though setpgrp(0,0) would be preferred. The posix call avoids */
5129 /* this confusion. */
5131 if (setpgid(0,0) < 0) rb_sys_fail(0);
5132 #elif defined(HAVE_SETPGRP) && defined(SETPGRP_VOID)
5133 if (setpgrp() < 0) rb_sys_fail(0);
5138 #define proc_setpgrp rb_f_notimplement
5142 #if defined(HAVE_GETPGID)
5145 * Process.getpgid(pid) -> integer
5147 * Returns the process group ID for the given process ID +pid+:
5149 * Process.getpgid(Process.ppid) # => 25527
5151 * Not available on all platforms.
5155 proc_getpgid(VALUE obj
, VALUE pid
)
5159 i
= getpgid(NUM2PIDT(pid
));
5160 if (i
< 0) rb_sys_fail(0);
5164 #define proc_getpgid rb_f_notimplement
5171 * Process.setpgid(pid, pgid) -> 0
5173 * Sets the process group ID for the process given by process ID +pid+
5176 * Not available on all platforms.
5180 proc_setpgid(VALUE obj
, VALUE pid
, VALUE pgrp
)
5182 rb_pid_t ipid
, ipgrp
;
5184 ipid
= NUM2PIDT(pid
);
5185 ipgrp
= NUM2PIDT(pgrp
);
5187 if (setpgid(ipid
, ipgrp
) < 0) rb_sys_fail(0);
5191 #define proc_setpgid rb_f_notimplement
5198 * Process.getsid(pid = nil) -> integer
5200 * Returns the session ID of the given process ID +pid+,
5201 * or of the current process if not given:
5203 * Process.getsid # => 27422
5204 * Process.getsid(0) # => 27422
5205 * Process.getsid(Process.pid()) # => 27422
5207 * Not available on all platforms.
5210 proc_getsid(int argc
, VALUE
*argv
, VALUE _
)
5215 if (rb_check_arity(argc
, 0, 1) == 1 && !NIL_P(argv
[0]))
5216 pid
= NUM2PIDT(argv
[0]);
5219 if (sid
< 0) rb_sys_fail(0);
5220 return PIDT2NUM(sid
);
5223 #define proc_getsid rb_f_notimplement
5227 #if defined(HAVE_SETSID) || (defined(HAVE_SETPGRP) && defined(TIOCNOTTY))
5228 #if !defined(HAVE_SETSID)
5229 static rb_pid_t
ruby_setsid(void);
5230 #define setsid() ruby_setsid()
5234 * Process.setsid -> integer
5236 * Establishes the current process as a new session and process group leader,
5237 * with no controlling tty;
5238 * returns the session ID:
5240 * Process.setsid # => 27422
5242 * Not available on all platforms.
5246 proc_setsid(VALUE _
)
5251 if (pid
< 0) rb_sys_fail(0);
5252 return PIDT2NUM(pid
);
5255 #if !defined(HAVE_SETSID)
5256 #define HAVE_SETSID 1
5264 #if defined(SETPGRP_VOID)
5266 /* If `pid_t setpgrp(void)' is equivalent to setsid(),
5267 `ret' will be the same value as `pid', and following open() will fail.
5268 In Linux, `int setpgrp(void)' is equivalent to setpgid(0, 0). */
5270 ret
= setpgrp(0, pid
);
5272 if (ret
== -1) return -1;
5274 if ((fd
= rb_cloexec_open("/dev/tty", O_RDWR
, 0)) >= 0) {
5275 rb_update_max_fd(fd
);
5276 ioctl(fd
, TIOCNOTTY
, NULL
);
5283 #define proc_setsid rb_f_notimplement
5287 #ifdef HAVE_GETPRIORITY
5290 * Process.getpriority(kind, id) -> integer
5292 * Returns the scheduling priority for specified process, process group,
5295 * Argument +kind+ is one of:
5297 * - Process::PRIO_PROCESS: return priority for process.
5298 * - Process::PRIO_PGRP: return priority for process group.
5299 * - Process::PRIO_USER: return priority for user.
5301 * Argument +id+ is the ID for the process, process group, or user;
5302 * zero specified the current ID for +kind+.
5306 * Process.getpriority(Process::PRIO_USER, 0) # => 19
5307 * Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
5309 * Not available on all platforms.
5313 proc_getpriority(VALUE obj
, VALUE which
, VALUE who
)
5315 int prio
, iwhich
, iwho
;
5317 iwhich
= NUM2INT(which
);
5318 iwho
= NUM2INT(who
);
5321 prio
= getpriority(iwhich
, iwho
);
5322 if (errno
) rb_sys_fail(0);
5323 return INT2FIX(prio
);
5326 #define proc_getpriority rb_f_notimplement
5330 #ifdef HAVE_GETPRIORITY
5333 * Process.setpriority(kind, integer, priority) -> 0
5335 * See Process.getpriority.
5339 * Process.setpriority(Process::PRIO_USER, 0, 19) # => 0
5340 * Process.setpriority(Process::PRIO_PROCESS, 0, 19) # => 0
5341 * Process.getpriority(Process::PRIO_USER, 0) # => 19
5342 * Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
5344 * Not available on all platforms.
5348 proc_setpriority(VALUE obj
, VALUE which
, VALUE who
, VALUE prio
)
5350 int iwhich
, iwho
, iprio
;
5352 iwhich
= NUM2INT(which
);
5353 iwho
= NUM2INT(who
);
5354 iprio
= NUM2INT(prio
);
5356 if (setpriority(iwhich
, iwho
, iprio
) < 0)
5361 #define proc_setpriority rb_f_notimplement
5364 #if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
5366 rlimit_resource_name2int(const char *name
, long len
, int casetype
)
5370 #define RESCHECK(r) \
5372 if (len == rb_strlen_lit(#r) && STRCASECMP(name, #r) == 0) { \
5373 resource = RLIMIT_##r; \
5378 switch (TOUPPER(*name
)) {
5407 #ifdef RLIMIT_MEMLOCK
5410 #ifdef RLIMIT_MSGQUEUE
5416 #ifdef RLIMIT_NOFILE
5434 #ifdef RLIMIT_RTPRIO
5437 #ifdef RLIMIT_RTTIME
5446 #ifdef RLIMIT_SBSIZE
5449 #ifdef RLIMIT_SIGPENDING
5450 RESCHECK(SIGPENDING
);
5459 for (p
= name
; *p
; p
++)
5465 for (p
= name
; *p
; p
++)
5471 rb_bug("unexpected casetype");
5478 rlimit_type_by_hname(const char *name
, long len
)
5480 return rlimit_resource_name2int(name
, len
, 0);
5484 rlimit_type_by_lname(const char *name
, long len
)
5486 return rlimit_resource_name2int(name
, len
, 1);
5490 rlimit_type_by_sym(VALUE key
)
5492 VALUE name
= rb_sym2str(key
);
5493 const char *rname
= RSTRING_PTR(name
);
5494 long len
= RSTRING_LEN(name
);
5496 static const char prefix
[] = "rlimit_";
5497 enum {prefix_len
= sizeof(prefix
)-1};
5499 if (len
> prefix_len
&& strncmp(prefix
, rname
, prefix_len
) == 0) {
5500 rtype
= rlimit_type_by_lname(rname
+ prefix_len
, len
- prefix_len
);
5508 rlimit_resource_type(VALUE rtype
)
5515 switch (TYPE(rtype
)) {
5517 v
= rb_sym2str(rtype
);
5518 name
= RSTRING_PTR(v
);
5519 len
= RSTRING_LEN(v
);
5523 v
= rb_check_string_type(rtype
);
5527 name
= StringValueCStr(rtype
);
5528 len
= RSTRING_LEN(rtype
);
5535 return NUM2INT(rtype
);
5538 r
= rlimit_type_by_hname(name
, len
);
5542 rb_raise(rb_eArgError
, "invalid resource name: % "PRIsVALUE
, rtype
);
5544 UNREACHABLE_RETURN(-1);
5548 rlimit_resource_value(VALUE rval
)
5553 switch (TYPE(rval
)) {
5555 v
= rb_sym2str(rval
);
5556 name
= RSTRING_PTR(v
);
5560 v
= rb_check_string_type(rval
);
5564 name
= StringValueCStr(rval
);
5571 return NUM2RLIM(rval
);
5574 #ifdef RLIM_INFINITY
5575 if (strcmp(name
, "INFINITY") == 0) return RLIM_INFINITY
;
5577 #ifdef RLIM_SAVED_MAX
5578 if (strcmp(name
, "SAVED_MAX") == 0) return RLIM_SAVED_MAX
;
5580 #ifdef RLIM_SAVED_CUR
5581 if (strcmp(name
, "SAVED_CUR") == 0) return RLIM_SAVED_CUR
;
5583 rb_raise(rb_eArgError
, "invalid resource value: %"PRIsVALUE
, rval
);
5585 UNREACHABLE_RETURN((rlim_t
)-1);
5589 #if defined(HAVE_GETRLIMIT) && defined(RLIM2NUM)
5592 * Process.getrlimit(resource) -> [cur_limit, max_limit]
5594 * Returns a 2-element array of the current (soft) limit
5595 * and maximum (hard) limit for the given +resource+.
5597 * Argument +resource+ specifies the resource whose limits are to be returned;
5598 * see Process.setrlimit.
5600 * Each of the returned values +cur_limit+ and +max_limit+ is an integer;
5601 * see Process.setrlimit.
5605 * Process.getrlimit(:CORE) # => [0, 18446744073709551615]
5607 * See Process.setrlimit.
5609 * Not available on all platforms.
5613 proc_getrlimit(VALUE obj
, VALUE resource
)
5617 if (getrlimit(rlimit_resource_type(resource
), &rlim
) < 0) {
5618 rb_sys_fail("getrlimit");
5620 return rb_assoc_new(RLIM2NUM(rlim
.rlim_cur
), RLIM2NUM(rlim
.rlim_max
));
5623 #define proc_getrlimit rb_f_notimplement
5626 #if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
5629 * Process.setrlimit(resource, cur_limit, max_limit = cur_limit) -> nil
5631 * Sets limits for the current process for the given +resource+
5632 * to +cur_limit+ (soft limit) and +max_limit+ (hard limit);
5635 * Argument +resource+ specifies the resource whose limits are to be set;
5636 * the argument may be given as a symbol, as a string, or as a constant
5637 * beginning with <tt>Process::RLIMIT_</tt>
5638 * (e.g., +:CORE+, <tt>'CORE'</tt>, or <tt>Process::RLIMIT_CORE</tt>.
5640 * The resources available and supported are system-dependent,
5641 * and may include (here expressed as symbols):
5643 * - +:AS+: Total available memory (bytes) (SUSv3, NetBSD, FreeBSD, OpenBSD except 4.4BSD-Lite).
5644 * - +:CORE+: Core size (bytes) (SUSv3).
5645 * - +:CPU+: CPU time (seconds) (SUSv3).
5646 * - +:DATA+: Data segment (bytes) (SUSv3).
5647 * - +:FSIZE+: File size (bytes) (SUSv3).
5648 * - +:MEMLOCK+: Total size for mlock(2) (bytes) (4.4BSD, GNU/Linux).
5649 * - +:MSGQUEUE+: Allocation for POSIX message queues (bytes) (GNU/Linux).
5650 * - +:NICE+: Ceiling on process's nice(2) value (number) (GNU/Linux).
5651 * - +:NOFILE+: File descriptors (number) (SUSv3).
5652 * - +:NPROC+: Number of processes for the user (number) (4.4BSD, GNU/Linux).
5653 * - +:NPTS+: Number of pseudo terminals (number) (FreeBSD).
5654 * - +:RSS+: Resident memory size (bytes) (4.2BSD, GNU/Linux).
5655 * - +:RTPRIO+: Ceiling on the process's real-time priority (number) (GNU/Linux).
5656 * - +:RTTIME+: CPU time for real-time process (us) (GNU/Linux).
5657 * - +:SBSIZE+: All socket buffers (bytes) (NetBSD, FreeBSD).
5658 * - +:SIGPENDING+: Number of queued signals allowed (signals) (GNU/Linux).
5659 * - +:STACK+: Stack size (bytes) (SUSv3).
5661 * Arguments +cur_limit+ and +max_limit+ may be:
5663 * - Integers (+max_limit+ should not be smaller than +cur_limit+).
5664 * - Symbol +:SAVED_MAX+, string <tt>'SAVED_MAX'</tt>,
5665 * or constant <tt>Process::RLIM_SAVED_MAX</tt>: saved maximum limit.
5666 * - Symbol +:SAVED_CUR+, string <tt>'SAVED_CUR'</tt>,
5667 * or constant <tt>Process::RLIM_SAVED_CUR</tt>: saved current limit.
5668 * - Symbol +:INFINITY+, string <tt>'INFINITY'</tt>,
5669 * or constant <tt>Process::RLIM_INFINITY</tt>: no limit on resource.
5671 * This example raises the soft limit of core size to
5672 * the hard limit to try to make core dump possible:
5674 * Process.setrlimit(:CORE, Process.getrlimit(:CORE)[1])
5676 * Not available on all platforms.
5680 proc_setrlimit(int argc
, VALUE
*argv
, VALUE obj
)
5682 VALUE resource
, rlim_cur
, rlim_max
;
5685 rb_check_arity(argc
, 2, 3);
5688 if (argc
< 3 || NIL_P(rlim_max
= argv
[2]))
5689 rlim_max
= rlim_cur
;
5691 rlim
.rlim_cur
= rlimit_resource_value(rlim_cur
);
5692 rlim
.rlim_max
= rlimit_resource_value(rlim_max
);
5694 if (setrlimit(rlimit_resource_type(resource
), &rlim
) < 0) {
5695 rb_sys_fail("setrlimit");
5700 #define proc_setrlimit rb_f_notimplement
5703 static int under_uid_switch
= 0;
5705 check_uid_switch(void)
5707 if (under_uid_switch
) {
5708 rb_raise(rb_eRuntimeError
, "can't handle UID while evaluating block given to Process::UID.switch method");
5712 static int under_gid_switch
= 0;
5714 check_gid_switch(void)
5716 if (under_gid_switch
) {
5717 rb_raise(rb_eRuntimeError
, "can't handle GID while evaluating block given to Process::UID.switch method");
5722 #if defined(HAVE_PWD_H)
5724 * Best-effort attempt to obtain the name of the login user, if any,
5725 * associated with the process. Processes not descended from login(1) (or
5726 * similar) may not have a logged-in user; returns Qnil in that case.
5731 #if ( !defined(USE_GETLOGIN_R) && !defined(USE_GETLOGIN) )
5734 char MAYBE_UNUSED(*login
) = NULL
;
5736 # ifdef USE_GETLOGIN_R
5738 #if defined(__FreeBSD__)
5739 typedef int getlogin_r_size_t
;
5741 typedef size_t getlogin_r_size_t
;
5744 long loginsize
= GETLOGIN_R_SIZE_INIT
; /* maybe -1 */
5747 loginsize
= GETLOGIN_R_SIZE_DEFAULT
;
5749 VALUE maybe_result
= rb_str_buf_new(loginsize
);
5751 login
= RSTRING_PTR(maybe_result
);
5752 loginsize
= rb_str_capacity(maybe_result
);
5753 rb_str_set_len(maybe_result
, loginsize
);
5757 while ((gle
= getlogin_r(login
, (getlogin_r_size_t
)loginsize
)) != 0) {
5759 if (gle
== ENOTTY
|| gle
== ENXIO
|| gle
== ENOENT
) {
5760 rb_str_resize(maybe_result
, 0);
5764 if (gle
!= ERANGE
|| loginsize
>= GETLOGIN_R_SIZE_LIMIT
) {
5765 rb_str_resize(maybe_result
, 0);
5766 rb_syserr_fail(gle
, "getlogin_r");
5769 rb_str_modify_expand(maybe_result
, loginsize
);
5770 login
= RSTRING_PTR(maybe_result
);
5771 loginsize
= rb_str_capacity(maybe_result
);
5774 if (login
== NULL
) {
5775 rb_str_resize(maybe_result
, 0);
5779 return maybe_result
;
5786 if (errno
== ENOTTY
|| errno
== ENXIO
|| errno
== ENOENT
) {
5789 rb_syserr_fail(errno
, "getlogin");
5792 return login
? rb_str_new_cstr(login
) : Qnil
;
5799 rb_getpwdirnam_for_login(VALUE login_name
)
5801 #if ( !defined(USE_GETPWNAM_R) && !defined(USE_GETPWNAM) )
5805 if (NIL_P(login_name
)) {
5806 /* nothing to do; no name with which to query the password database */
5810 char *login
= RSTRING_PTR(login_name
);
5812 struct passwd
*pwptr
;
5814 # ifdef USE_GETPWNAM_R
5816 struct passwd pwdnm
;
5818 long bufsizenm
= GETPW_R_SIZE_INIT
; /* maybe -1 */
5821 bufsizenm
= GETPW_R_SIZE_DEFAULT
;
5823 VALUE getpwnm_tmp
= rb_str_tmp_new(bufsizenm
);
5825 bufnm
= RSTRING_PTR(getpwnm_tmp
);
5826 bufsizenm
= rb_str_capacity(getpwnm_tmp
);
5827 rb_str_set_len(getpwnm_tmp
, bufsizenm
);
5831 while ((enm
= getpwnam_r(login
, &pwdnm
, bufnm
, bufsizenm
, &pwptr
)) != 0) {
5833 if (enm
== ENOENT
|| enm
== ESRCH
|| enm
== EBADF
|| enm
== EPERM
) {
5834 /* not found; non-errors */
5835 rb_str_resize(getpwnm_tmp
, 0);
5839 if (enm
!= ERANGE
|| bufsizenm
>= GETPW_R_SIZE_LIMIT
) {
5840 rb_str_resize(getpwnm_tmp
, 0);
5841 rb_syserr_fail(enm
, "getpwnam_r");
5844 rb_str_modify_expand(getpwnm_tmp
, bufsizenm
);
5845 bufnm
= RSTRING_PTR(getpwnm_tmp
);
5846 bufsizenm
= rb_str_capacity(getpwnm_tmp
);
5849 if (pwptr
== NULL
) {
5850 /* no record in the password database for the login name */
5851 rb_str_resize(getpwnm_tmp
, 0);
5856 VALUE result
= rb_str_new_cstr(pwptr
->pw_dir
);
5857 rb_str_resize(getpwnm_tmp
, 0);
5863 pwptr
= getpwnam(login
);
5866 return rb_str_new_cstr(pwptr
->pw_dir
);
5869 /* avoid treating as errors errno values that indicate "not found" */
5870 && ( errno
!= ENOENT
&& errno
!= ESRCH
&& errno
!= EBADF
&& errno
!= EPERM
)) {
5871 rb_syserr_fail(errno
, "getpwnam");
5874 return Qnil
; /* not found */
5881 * Look up the user's dflt home dir in the password db, by uid.
5884 rb_getpwdiruid(void)
5886 # if !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID)
5887 /* Should never happen... </famous-last-words> */
5890 uid_t ruid
= getuid();
5892 struct passwd
*pwptr
;
5894 # ifdef USE_GETPWUID_R
5896 struct passwd pwdid
;
5898 long bufsizeid
= GETPW_R_SIZE_INIT
; /* maybe -1 */
5901 bufsizeid
= GETPW_R_SIZE_DEFAULT
;
5903 VALUE getpwid_tmp
= rb_str_tmp_new(bufsizeid
);
5905 bufid
= RSTRING_PTR(getpwid_tmp
);
5906 bufsizeid
= rb_str_capacity(getpwid_tmp
);
5907 rb_str_set_len(getpwid_tmp
, bufsizeid
);
5911 while ((eid
= getpwuid_r(ruid
, &pwdid
, bufid
, bufsizeid
, &pwptr
)) != 0) {
5913 if (eid
== ENOENT
|| eid
== ESRCH
|| eid
== EBADF
|| eid
== EPERM
) {
5914 /* not found; non-errors */
5915 rb_str_resize(getpwid_tmp
, 0);
5919 if (eid
!= ERANGE
|| bufsizeid
>= GETPW_R_SIZE_LIMIT
) {
5920 rb_str_resize(getpwid_tmp
, 0);
5921 rb_syserr_fail(eid
, "getpwuid_r");
5924 rb_str_modify_expand(getpwid_tmp
, bufsizeid
);
5925 bufid
= RSTRING_PTR(getpwid_tmp
);
5926 bufsizeid
= rb_str_capacity(getpwid_tmp
);
5929 if (pwptr
== NULL
) {
5930 /* no record in the password database for the uid */
5931 rb_str_resize(getpwid_tmp
, 0);
5936 VALUE result
= rb_str_new_cstr(pwptr
->pw_dir
);
5937 rb_str_resize(getpwid_tmp
, 0);
5940 # elif defined(USE_GETPWUID)
5943 pwptr
= getpwuid(ruid
);
5946 return rb_str_new_cstr(pwptr
->pw_dir
);
5949 /* avoid treating as errors errno values that indicate "not found" */
5950 && ( errno
== ENOENT
|| errno
== ESRCH
|| errno
== EBADF
|| errno
== EPERM
)) {
5951 rb_syserr_fail(errno
, "getpwuid");
5954 return Qnil
; /* not found */
5957 #endif /* !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID) */
5959 #endif /* HAVE_PWD_H */
5962 /*********************************************************************
5963 * Document-class: Process::Sys
5965 * The Process::Sys module contains UID and GID
5966 * functions which provide direct bindings to the system calls of the
5967 * same names instead of the more-portable versions of the same
5968 * functionality found in the Process,
5969 * Process::UID, and Process::GID modules.
5972 #if defined(HAVE_PWD_H)
5975 # ifdef USE_GETPWNAM_R
5983 if (FIXNUM_P(id
) || NIL_P(tmp
= rb_check_string_type(id
))) {
5987 const char *usrname
= StringValueCStr(id
);
5988 struct passwd
*pwptr
;
5989 #ifdef USE_GETPWNAM_R
5990 struct passwd pwbuf
;
5995 getpw_buf_len
= GETPW_R_SIZE_INIT
;
5996 if (getpw_buf_len
< 0) getpw_buf_len
= GETPW_R_SIZE_DEFAULT
;
5997 *getpw_tmp
= rb_str_tmp_new(getpw_buf_len
);
5999 getpw_buf
= RSTRING_PTR(*getpw_tmp
);
6000 getpw_buf_len
= rb_str_capacity(*getpw_tmp
);
6001 rb_str_set_len(*getpw_tmp
, getpw_buf_len
);
6003 while ((e
= getpwnam_r(usrname
, &pwbuf
, getpw_buf
, getpw_buf_len
, &pwptr
)) != 0) {
6004 if (e
!= ERANGE
|| getpw_buf_len
>= GETPW_R_SIZE_LIMIT
) {
6005 rb_str_resize(*getpw_tmp
, 0);
6006 rb_syserr_fail(e
, "getpwnam_r");
6008 rb_str_modify_expand(*getpw_tmp
, getpw_buf_len
);
6009 getpw_buf
= RSTRING_PTR(*getpw_tmp
);
6010 getpw_buf_len
= rb_str_capacity(*getpw_tmp
);
6013 pwptr
= getpwnam(usrname
);
6016 #ifndef USE_GETPWNAM_R
6019 rb_raise(rb_eArgError
, "can't find user for %"PRIsVALUE
, id
);
6021 uid
= pwptr
->pw_uid
;
6022 #ifndef USE_GETPWNAM_R
6029 # ifdef p_uid_from_name
6032 * Process::UID.from_name(name) -> uid
6034 * Get the user ID by the _name_.
6035 * If the user is not found, +ArgumentError+ will be raised.
6037 * Process::UID.from_name("root") #=> 0
6038 * Process::UID.from_name("nosuchuser") #=> can't find user for nosuchuser (ArgumentError)
6042 p_uid_from_name(VALUE self
, VALUE id
)
6044 return UIDT2NUM(OBJ2UID(id
));
6049 #if defined(HAVE_GRP_H)
6052 # ifdef USE_GETGRNAM_R
6060 if (FIXNUM_P(id
) || NIL_P(tmp
= rb_check_string_type(id
))) {
6064 const char *grpname
= StringValueCStr(id
);
6065 struct group
*grptr
;
6066 #ifdef USE_GETGRNAM_R
6072 getgr_buf_len
= GETGR_R_SIZE_INIT
;
6073 if (getgr_buf_len
< 0) getgr_buf_len
= GETGR_R_SIZE_DEFAULT
;
6074 *getgr_tmp
= rb_str_tmp_new(getgr_buf_len
);
6076 getgr_buf
= RSTRING_PTR(*getgr_tmp
);
6077 getgr_buf_len
= rb_str_capacity(*getgr_tmp
);
6078 rb_str_set_len(*getgr_tmp
, getgr_buf_len
);
6080 while ((e
= getgrnam_r(grpname
, &grbuf
, getgr_buf
, getgr_buf_len
, &grptr
)) != 0) {
6081 if (e
!= ERANGE
|| getgr_buf_len
>= GETGR_R_SIZE_LIMIT
) {
6082 rb_str_resize(*getgr_tmp
, 0);
6083 rb_syserr_fail(e
, "getgrnam_r");
6085 rb_str_modify_expand(*getgr_tmp
, getgr_buf_len
);
6086 getgr_buf
= RSTRING_PTR(*getgr_tmp
);
6087 getgr_buf_len
= rb_str_capacity(*getgr_tmp
);
6089 #elif defined(HAVE_GETGRNAM)
6090 grptr
= getgrnam(grpname
);
6095 #if !defined(USE_GETGRNAM_R) && defined(HAVE_ENDGRENT)
6098 rb_raise(rb_eArgError
, "can't find group for %"PRIsVALUE
, id
);
6100 gid
= grptr
->gr_gid
;
6101 #if !defined(USE_GETGRNAM_R) && defined(HAVE_ENDGRENT)
6108 # ifdef p_gid_from_name
6111 * Process::GID.from_name(name) -> gid
6113 * Get the group ID by the _name_.
6114 * If the group is not found, +ArgumentError+ will be raised.
6116 * Process::GID.from_name("wheel") #=> 0
6117 * Process::GID.from_name("nosuchgroup") #=> can't find group for nosuchgroup (ArgumentError)
6121 p_gid_from_name(VALUE self
, VALUE id
)
6123 return GIDT2NUM(OBJ2GID(id
));
6128 #if defined HAVE_SETUID
6131 * Process::Sys.setuid(user) -> nil
6133 * Set the user ID of the current process to _user_. Not
6134 * available on all platforms.
6139 p_sys_setuid(VALUE obj
, VALUE id
)
6142 if (setuid(OBJ2UID(id
)) != 0) rb_sys_fail(0);
6146 #define p_sys_setuid rb_f_notimplement
6150 #if defined HAVE_SETRUID
6153 * Process::Sys.setruid(user) -> nil
6155 * Set the real user ID of the calling process to _user_.
6156 * Not available on all platforms.
6161 p_sys_setruid(VALUE obj
, VALUE id
)
6164 if (setruid(OBJ2UID(id
)) != 0) rb_sys_fail(0);
6168 #define p_sys_setruid rb_f_notimplement
6172 #if defined HAVE_SETEUID
6175 * Process::Sys.seteuid(user) -> nil
6177 * Set the effective user ID of the calling process to
6178 * _user_. Not available on all platforms.
6183 p_sys_seteuid(VALUE obj
, VALUE id
)
6186 if (seteuid(OBJ2UID(id
)) != 0) rb_sys_fail(0);
6190 #define p_sys_seteuid rb_f_notimplement
6194 #if defined HAVE_SETREUID
6197 * Process::Sys.setreuid(rid, eid) -> nil
6199 * Sets the (user) real and/or effective user IDs of the current
6200 * process to _rid_ and _eid_, respectively. A value of
6201 * <code>-1</code> for either means to leave that ID unchanged. Not
6202 * available on all platforms.
6207 p_sys_setreuid(VALUE obj
, VALUE rid
, VALUE eid
)
6209 rb_uid_t ruid
, euid
;
6212 ruid
= OBJ2UID1(rid
);
6213 euid
= OBJ2UID1(eid
);
6215 if (setreuid(ruid
, euid
) != 0) rb_sys_fail(0);
6219 #define p_sys_setreuid rb_f_notimplement
6223 #if defined HAVE_SETRESUID
6226 * Process::Sys.setresuid(rid, eid, sid) -> nil
6228 * Sets the (user) real, effective, and saved user IDs of the
6229 * current process to _rid_, _eid_, and _sid_ respectively. A
6230 * value of <code>-1</code> for any value means to
6231 * leave that ID unchanged. Not available on all platforms.
6236 p_sys_setresuid(VALUE obj
, VALUE rid
, VALUE eid
, VALUE sid
)
6238 rb_uid_t ruid
, euid
, suid
;
6241 ruid
= OBJ2UID1(rid
);
6242 euid
= OBJ2UID1(eid
);
6243 suid
= OBJ2UID1(sid
);
6245 if (setresuid(ruid
, euid
, suid
) != 0) rb_sys_fail(0);
6249 #define p_sys_setresuid rb_f_notimplement
6255 * Process.uid -> integer
6256 * Process::UID.rid -> integer
6257 * Process::Sys.getuid -> integer
6259 * Returns the (real) user ID of the current process.
6261 * Process.uid # => 1000
6266 proc_getuid(VALUE obj
)
6268 rb_uid_t uid
= getuid();
6269 return UIDT2NUM(uid
);
6273 #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRUID) || defined(HAVE_SETUID)
6276 * Process.uid = new_uid -> new_uid
6278 * Sets the (user) user ID for the current process to +new_uid+:
6280 * Process.uid = 1000 # => 1000
6282 * Not available on all platforms.
6286 proc_setuid(VALUE obj
, VALUE id
)
6293 #if defined(HAVE_SETRESUID)
6294 if (setresuid(uid
, -1, -1) < 0) rb_sys_fail(0);
6295 #elif defined HAVE_SETREUID
6296 if (setreuid(uid
, -1) < 0) rb_sys_fail(0);
6297 #elif defined HAVE_SETRUID
6298 if (setruid(uid
) < 0) rb_sys_fail(0);
6299 #elif defined HAVE_SETUID
6301 if (geteuid() == uid
) {
6302 if (setuid(uid
) < 0) rb_sys_fail(0);
6312 #define proc_setuid rb_f_notimplement
6316 /********************************************************************
6318 * Document-class: Process::UID
6320 * The Process::UID module contains a collection of
6321 * module functions which can be used to portably get, set, and
6322 * switch the current process's real, effective, and saved user IDs.
6326 static rb_uid_t SAVED_USER_ID
= -1;
6328 #ifdef BROKEN_SETREUID
6330 setreuid(rb_uid_t ruid
, rb_uid_t euid
)
6332 if (ruid
!= (rb_uid_t
)-1 && ruid
!= getuid()) {
6333 if (euid
== (rb_uid_t
)-1) euid
= geteuid();
6334 if (setuid(ruid
) < 0) return -1;
6336 if (euid
!= (rb_uid_t
)-1 && euid
!= geteuid()) {
6337 if (seteuid(euid
) < 0) return -1;
6345 * Process::UID.change_privilege(user) -> integer
6347 * Change the current process's real and effective user ID to that
6348 * specified by _user_. Returns the new user ID. Not
6349 * available on all platforms.
6351 * [Process.uid, Process.euid] #=> [0, 0]
6352 * Process::UID.change_privilege(31) #=> 31
6353 * [Process.uid, Process.euid] #=> [31, 31]
6357 p_uid_change_privilege(VALUE obj
, VALUE id
)
6365 if (geteuid() == 0) { /* root-user */
6366 #if defined(HAVE_SETRESUID)
6367 if (setresuid(uid
, uid
, uid
) < 0) rb_sys_fail(0);
6368 SAVED_USER_ID
= uid
;
6369 #elif defined(HAVE_SETUID)
6370 if (setuid(uid
) < 0) rb_sys_fail(0);
6371 SAVED_USER_ID
= uid
;
6372 #elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
6373 if (getuid() == uid
) {
6374 if (SAVED_USER_ID
== uid
) {
6375 if (setreuid(-1, uid
) < 0) rb_sys_fail(0);
6378 if (uid
== 0) { /* (r,e,s) == (root, root, x) */
6379 if (setreuid(-1, SAVED_USER_ID
) < 0) rb_sys_fail(0);
6380 if (setreuid(SAVED_USER_ID
, 0) < 0) rb_sys_fail(0);
6381 SAVED_USER_ID
= 0; /* (r,e,s) == (x, root, root) */
6382 if (setreuid(uid
, uid
) < 0) rb_sys_fail(0);
6383 SAVED_USER_ID
= uid
;
6386 if (setreuid(0, -1) < 0) rb_sys_fail(0);
6388 if (setreuid(uid
, uid
) < 0) rb_sys_fail(0);
6389 SAVED_USER_ID
= uid
;
6394 if (setreuid(uid
, uid
) < 0) rb_sys_fail(0);
6395 SAVED_USER_ID
= uid
;
6397 #elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
6398 if (getuid() == uid
) {
6399 if (SAVED_USER_ID
== uid
) {
6400 if (seteuid(uid
) < 0) rb_sys_fail(0);
6404 if (setruid(SAVED_USER_ID
) < 0) rb_sys_fail(0);
6406 if (setruid(0) < 0) rb_sys_fail(0);
6409 if (setruid(0) < 0) rb_sys_fail(0);
6411 if (seteuid(uid
) < 0) rb_sys_fail(0);
6412 if (setruid(uid
) < 0) rb_sys_fail(0);
6413 SAVED_USER_ID
= uid
;
6418 if (seteuid(uid
) < 0) rb_sys_fail(0);
6419 if (setruid(uid
) < 0) rb_sys_fail(0);
6420 SAVED_USER_ID
= uid
;
6427 else { /* unprivileged user */
6428 #if defined(HAVE_SETRESUID)
6429 if (setresuid((getuid() == uid
)? (rb_uid_t
)-1: uid
,
6430 (geteuid() == uid
)? (rb_uid_t
)-1: uid
,
6431 (SAVED_USER_ID
== uid
)? (rb_uid_t
)-1: uid
) < 0) rb_sys_fail(0);
6432 SAVED_USER_ID
= uid
;
6433 #elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
6434 if (SAVED_USER_ID
== uid
) {
6435 if (setreuid((getuid() == uid
)? (rb_uid_t
)-1: uid
,
6436 (geteuid() == uid
)? (rb_uid_t
)-1: uid
) < 0)
6439 else if (getuid() != uid
) {
6440 if (setreuid(uid
, (geteuid() == uid
)? (rb_uid_t
)-1: uid
) < 0)
6442 SAVED_USER_ID
= uid
;
6444 else if (/* getuid() == uid && */ geteuid() != uid
) {
6445 if (setreuid(geteuid(), uid
) < 0) rb_sys_fail(0);
6446 SAVED_USER_ID
= uid
;
6447 if (setreuid(uid
, -1) < 0) rb_sys_fail(0);
6449 else { /* getuid() == uid && geteuid() == uid */
6450 if (setreuid(-1, SAVED_USER_ID
) < 0) rb_sys_fail(0);
6451 if (setreuid(SAVED_USER_ID
, uid
) < 0) rb_sys_fail(0);
6452 SAVED_USER_ID
= uid
;
6453 if (setreuid(uid
, -1) < 0) rb_sys_fail(0);
6455 #elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
6456 if (SAVED_USER_ID
== uid
) {
6457 if (geteuid() != uid
&& seteuid(uid
) < 0) rb_sys_fail(0);
6458 if (getuid() != uid
&& setruid(uid
) < 0) rb_sys_fail(0);
6460 else if (/* SAVED_USER_ID != uid && */ geteuid() == uid
) {
6461 if (getuid() != uid
) {
6462 if (setruid(uid
) < 0) rb_sys_fail(0);
6463 SAVED_USER_ID
= uid
;
6466 if (setruid(SAVED_USER_ID
) < 0) rb_sys_fail(0);
6467 SAVED_USER_ID
= uid
;
6468 if (setruid(uid
) < 0) rb_sys_fail(0);
6471 else if (/* geteuid() != uid && */ getuid() == uid
) {
6472 if (seteuid(uid
) < 0) rb_sys_fail(0);
6473 if (setruid(SAVED_USER_ID
) < 0) rb_sys_fail(0);
6474 SAVED_USER_ID
= uid
;
6475 if (setruid(uid
) < 0) rb_sys_fail(0);
6478 rb_syserr_fail(EPERM
, 0);
6480 #elif defined HAVE_44BSD_SETUID
6481 if (getuid() == uid
) {
6482 /* (r,e,s)==(uid,?,?) ==> (uid,uid,uid) */
6483 if (setuid(uid
) < 0) rb_sys_fail(0);
6484 SAVED_USER_ID
= uid
;
6487 rb_syserr_fail(EPERM
, 0);
6489 #elif defined HAVE_SETEUID
6490 if (getuid() == uid
&& SAVED_USER_ID
== uid
) {
6491 if (seteuid(uid
) < 0) rb_sys_fail(0);
6494 rb_syserr_fail(EPERM
, 0);
6496 #elif defined HAVE_SETUID
6497 if (getuid() == uid
&& SAVED_USER_ID
== uid
) {
6498 if (setuid(uid
) < 0) rb_sys_fail(0);
6501 rb_syserr_fail(EPERM
, 0);
6512 #if defined HAVE_SETGID
6515 * Process::Sys.setgid(group) -> nil
6517 * Set the group ID of the current process to _group_. Not
6518 * available on all platforms.
6523 p_sys_setgid(VALUE obj
, VALUE id
)
6526 if (setgid(OBJ2GID(id
)) != 0) rb_sys_fail(0);
6530 #define p_sys_setgid rb_f_notimplement
6534 #if defined HAVE_SETRGID
6537 * Process::Sys.setrgid(group) -> nil
6539 * Set the real group ID of the calling process to _group_.
6540 * Not available on all platforms.
6545 p_sys_setrgid(VALUE obj
, VALUE id
)
6548 if (setrgid(OBJ2GID(id
)) != 0) rb_sys_fail(0);
6552 #define p_sys_setrgid rb_f_notimplement
6556 #if defined HAVE_SETEGID
6559 * Process::Sys.setegid(group) -> nil
6561 * Set the effective group ID of the calling process to
6562 * _group_. Not available on all platforms.
6567 p_sys_setegid(VALUE obj
, VALUE id
)
6570 if (setegid(OBJ2GID(id
)) != 0) rb_sys_fail(0);
6574 #define p_sys_setegid rb_f_notimplement
6578 #if defined HAVE_SETREGID
6581 * Process::Sys.setregid(rid, eid) -> nil
6583 * Sets the (group) real and/or effective group IDs of the current
6584 * process to <em>rid</em> and <em>eid</em>, respectively. A value of
6585 * <code>-1</code> for either means to leave that ID unchanged. Not
6586 * available on all platforms.
6591 p_sys_setregid(VALUE obj
, VALUE rid
, VALUE eid
)
6593 rb_gid_t rgid
, egid
;
6595 rgid
= OBJ2GID(rid
);
6596 egid
= OBJ2GID(eid
);
6597 if (setregid(rgid
, egid
) != 0) rb_sys_fail(0);
6601 #define p_sys_setregid rb_f_notimplement
6604 #if defined HAVE_SETRESGID
6607 * Process::Sys.setresgid(rid, eid, sid) -> nil
6609 * Sets the (group) real, effective, and saved user IDs of the
6610 * current process to <em>rid</em>, <em>eid</em>, and <em>sid</em>
6611 * respectively. A value of <code>-1</code> for any value means to
6612 * leave that ID unchanged. Not available on all platforms.
6617 p_sys_setresgid(VALUE obj
, VALUE rid
, VALUE eid
, VALUE sid
)
6619 rb_gid_t rgid
, egid
, sgid
;
6621 rgid
= OBJ2GID(rid
);
6622 egid
= OBJ2GID(eid
);
6623 sgid
= OBJ2GID(sid
);
6624 if (setresgid(rgid
, egid
, sgid
) != 0) rb_sys_fail(0);
6628 #define p_sys_setresgid rb_f_notimplement
6632 #if defined HAVE_ISSETUGID
6635 * Process::Sys.issetugid -> true or false
6637 * Returns +true+ if the process was created as a result
6638 * of an execve(2) system call which had either of the setuid or
6639 * setgid bits set (and extra privileges were given as a result) or
6640 * if it has changed any of its real, effective or saved user or
6641 * group IDs since it began execution.
6646 p_sys_issetugid(VALUE obj
)
6648 return RBOOL(issetugid());
6651 #define p_sys_issetugid rb_f_notimplement
6657 * Process.gid -> integer
6658 * Process::GID.rid -> integer
6659 * Process::Sys.getgid -> integer
6661 * Returns the (real) group ID for the current process:
6663 * Process.gid # => 1000
6668 proc_getgid(VALUE obj
)
6670 rb_gid_t gid
= getgid();
6671 return GIDT2NUM(gid
);
6675 #if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETRGID) || defined(HAVE_SETGID)
6678 * Process.gid = new_gid -> new_gid
6680 * Sets the group ID for the current process to +new_gid+:
6682 * Process.gid = 1000 # => 1000
6687 proc_setgid(VALUE obj
, VALUE id
)
6694 #if defined(HAVE_SETRESGID)
6695 if (setresgid(gid
, -1, -1) < 0) rb_sys_fail(0);
6696 #elif defined HAVE_SETREGID
6697 if (setregid(gid
, -1) < 0) rb_sys_fail(0);
6698 #elif defined HAVE_SETRGID
6699 if (setrgid(gid
) < 0) rb_sys_fail(0);
6700 #elif defined HAVE_SETGID
6702 if (getegid() == gid
) {
6703 if (setgid(gid
) < 0) rb_sys_fail(0);
6710 return GIDT2NUM(gid
);
6713 #define proc_setgid rb_f_notimplement
6717 #if defined(_SC_NGROUPS_MAX) || defined(NGROUPS_MAX)
6719 * Maximum supplementary groups are platform dependent.
6720 * FWIW, 65536 is enough big for our supported OSs.
6722 * OS Name max groups
6723 * -----------------------------------------------
6724 * Linux Kernel >= 2.6.3 65536
6725 * Linux Kernel < 2.6.3 32
6727 * IBM AIX 5.3 ... 6.1 128
6728 * IBM AIX 7.1 128 (can be configured to be up to 2048)
6729 * OpenBSD, NetBSD 16
6731 * FreeBSD >=8.0 1023
6732 * Darwin (Mac OS X) 16
6733 * Sun Solaris 7,8,9,10 16
6734 * Sun Solaris 11 / OpenSolaris 1024
6737 static int _maxgroups
= -1;
6739 get_sc_ngroups_max(void)
6741 #ifdef _SC_NGROUPS_MAX
6742 return (int)sysconf(_SC_NGROUPS_MAX
);
6743 #elif defined(NGROUPS_MAX)
6744 return (int)NGROUPS_MAX
;
6752 if (_maxgroups
< 0) {
6753 _maxgroups
= get_sc_ngroups_max();
6755 _maxgroups
= RB_MAX_GROUPS
;
6764 #ifdef HAVE_GETGROUPS
6767 * Process.groups -> array
6769 * Returns an array of the group IDs
6770 * in the supplemental group access list for the current process:
6772 * Process.groups # => [4, 24, 27, 30, 46, 122, 135, 136, 1000]
6774 * These properties of the returned array are system-dependent:
6776 * - Whether (and how) the array is sorted.
6777 * - Whether the array includes effective group IDs.
6778 * - Whether the array includes duplicate group IDs.
6779 * - Whether the array size exceeds the value of Process.maxgroups.
6781 * Use this call to get a sorted and unique array:
6783 * Process.groups.uniq.sort
6788 proc_getgroups(VALUE obj
)
6794 ngroups
= getgroups(0, NULL
);
6798 groups
= ALLOCV_N(rb_gid_t
, tmp
, ngroups
);
6800 ngroups
= getgroups(ngroups
, groups
);
6805 for (i
= 0; i
< ngroups
; i
++)
6806 rb_ary_push(ary
, GIDT2NUM(groups
[i
]));
6813 #define proc_getgroups rb_f_notimplement
6817 #ifdef HAVE_SETGROUPS
6820 * Process.groups = new_groups -> new_groups
6822 * Sets the supplemental group access list to the given
6823 * array of group IDs.
6825 * Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
6826 * Process.groups = [27, 6, 10, 11] # => [27, 6, 10, 11]
6827 * Process.groups # => [27, 6, 10, 11]
6832 proc_setgroups(VALUE obj
, VALUE ary
)
6839 Check_Type(ary
, T_ARRAY
);
6841 ngroups
= RARRAY_LENINT(ary
);
6842 if (ngroups
> maxgroups())
6843 rb_raise(rb_eArgError
, "too many groups, %d max", maxgroups());
6845 groups
= ALLOCV_N(rb_gid_t
, tmp
, ngroups
);
6847 for (i
= 0; i
< ngroups
; i
++) {
6848 VALUE g
= RARRAY_AREF(ary
, i
);
6850 groups
[i
] = OBJ2GID1(g
);
6854 if (setgroups(ngroups
, groups
) == -1) /* ngroups <= maxgroups */
6859 return proc_getgroups(obj
);
6862 #define proc_setgroups rb_f_notimplement
6866 #ifdef HAVE_INITGROUPS
6869 * Process.initgroups(username, gid) -> array
6871 * Sets the supplemental group access list;
6872 * the new list includes:
6874 * - The group IDs of those groups to which the user given by +username+ belongs.
6875 * - The group ID +gid+.
6879 * Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
6880 * Process.initgroups('me', 30) # => [30, 6, 10, 11]
6881 * Process.groups # => [30, 6, 10, 11]
6883 * Not available on all platforms.
6887 proc_initgroups(VALUE obj
, VALUE uname
, VALUE base_grp
)
6889 if (initgroups(StringValueCStr(uname
), OBJ2GID(base_grp
)) != 0) {
6892 return proc_getgroups(obj
);
6895 #define proc_initgroups rb_f_notimplement
6898 #if defined(_SC_NGROUPS_MAX) || defined(NGROUPS_MAX)
6901 * Process.maxgroups -> integer
6903 * Returns the maximum number of group IDs allowed
6904 * in the supplemental group access list:
6906 * Process.maxgroups # => 32
6911 proc_getmaxgroups(VALUE obj
)
6913 return INT2FIX(maxgroups());
6916 #define proc_getmaxgroups rb_f_notimplement
6919 #ifdef HAVE_SETGROUPS
6922 * Process.maxgroups = new_max -> new_max
6924 * Sets the maximum number of group IDs allowed
6925 * in the supplemental group access list.
6929 proc_setmaxgroups(VALUE obj
, VALUE val
)
6931 int ngroups
= FIX2INT(val
);
6932 int ngroups_max
= get_sc_ngroups_max();
6935 rb_raise(rb_eArgError
, "maxgroups %d should be positive", ngroups
);
6937 if (ngroups
> RB_MAX_GROUPS
)
6938 ngroups
= RB_MAX_GROUPS
;
6940 if (ngroups_max
> 0 && ngroups
> ngroups_max
)
6941 ngroups
= ngroups_max
;
6943 _maxgroups
= ngroups
;
6945 return INT2FIX(_maxgroups
);
6948 #define proc_setmaxgroups rb_f_notimplement
6951 #if defined(HAVE_DAEMON) || (defined(HAVE_WORKING_FORK) && defined(HAVE_SETSID))
6952 static int rb_daemon(int nochdir
, int noclose
);
6956 * Process.daemon(nochdir = nil, noclose = nil) -> 0
6958 * Detaches the current process from its controlling terminal
6959 * and runs it in the background as system daemon;
6964 * - Changes the current working directory to the root directory.
6965 * - Redirects $stdin, $stdout, and $stderr to the null device.
6967 * If optional argument +nochdir+ is +true+,
6968 * does not change the current working directory.
6970 * If optional argument +noclose+ is +true+,
6971 * does not redirect $stdin, $stdout, or $stderr.
6975 proc_daemon(int argc
, VALUE
*argv
, VALUE _
)
6977 int n
, nochdir
= FALSE
, noclose
= FALSE
;
6979 switch (rb_check_arity(argc
, 0, 2)) {
6980 case 2: noclose
= TO_BOOL(argv
[1], "noclose");
6981 case 1: nochdir
= TO_BOOL(argv
[0], "nochdir");
6985 n
= rb_daemon(nochdir
, noclose
);
6986 if (n
< 0) rb_sys_fail("daemon");
6990 extern const char ruby_null_device
[];
6993 rb_daemon(int nochdir
, int noclose
)
6998 err
= daemon(nochdir
, noclose
);
7003 switch (rb_fork_ruby(NULL
)) {
7006 default: _exit(EXIT_SUCCESS
);
7009 /* ignore EPERM which means already being process-leader */
7010 if (setsid() < 0) (void)0;
7015 if (!noclose
&& (n
= rb_cloexec_open(ruby_null_device
, O_RDWR
, 0)) != -1) {
7016 rb_update_max_fd(n
);
7027 #define proc_daemon rb_f_notimplement
7030 /********************************************************************
7032 * Document-class: Process::GID
7034 * The Process::GID module contains a collection of
7035 * module functions which can be used to portably get, set, and
7036 * switch the current process's real, effective, and saved group IDs.
7040 static rb_gid_t SAVED_GROUP_ID
= -1;
7042 #ifdef BROKEN_SETREGID
7044 setregid(rb_gid_t rgid
, rb_gid_t egid
)
7046 if (rgid
!= (rb_gid_t
)-1 && rgid
!= getgid()) {
7047 if (egid
== (rb_gid_t
)-1) egid
= getegid();
7048 if (setgid(rgid
) < 0) return -1;
7050 if (egid
!= (rb_gid_t
)-1 && egid
!= getegid()) {
7051 if (setegid(egid
) < 0) return -1;
7059 * Process::GID.change_privilege(group) -> integer
7061 * Change the current process's real and effective group ID to that
7062 * specified by _group_. Returns the new group ID. Not
7063 * available on all platforms.
7065 * [Process.gid, Process.egid] #=> [0, 0]
7066 * Process::GID.change_privilege(33) #=> 33
7067 * [Process.gid, Process.egid] #=> [33, 33]
7071 p_gid_change_privilege(VALUE obj
, VALUE id
)
7079 if (geteuid() == 0) { /* root-user */
7080 #if defined(HAVE_SETRESGID)
7081 if (setresgid(gid
, gid
, gid
) < 0) rb_sys_fail(0);
7082 SAVED_GROUP_ID
= gid
;
7083 #elif defined HAVE_SETGID
7084 if (setgid(gid
) < 0) rb_sys_fail(0);
7085 SAVED_GROUP_ID
= gid
;
7086 #elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7087 if (getgid() == gid
) {
7088 if (SAVED_GROUP_ID
== gid
) {
7089 if (setregid(-1, gid
) < 0) rb_sys_fail(0);
7092 if (gid
== 0) { /* (r,e,s) == (root, y, x) */
7093 if (setregid(-1, SAVED_GROUP_ID
) < 0) rb_sys_fail(0);
7094 if (setregid(SAVED_GROUP_ID
, 0) < 0) rb_sys_fail(0);
7095 SAVED_GROUP_ID
= 0; /* (r,e,s) == (x, root, root) */
7096 if (setregid(gid
, gid
) < 0) rb_sys_fail(0);
7097 SAVED_GROUP_ID
= gid
;
7099 else { /* (r,e,s) == (z, y, x) */
7100 if (setregid(0, 0) < 0) rb_sys_fail(0);
7102 if (setregid(gid
, gid
) < 0) rb_sys_fail(0);
7103 SAVED_GROUP_ID
= gid
;
7108 if (setregid(gid
, gid
) < 0) rb_sys_fail(0);
7109 SAVED_GROUP_ID
= gid
;
7111 #elif defined(HAVE_SETRGID) && defined (HAVE_SETEGID)
7112 if (getgid() == gid
) {
7113 if (SAVED_GROUP_ID
== gid
) {
7114 if (setegid(gid
) < 0) rb_sys_fail(0);
7118 if (setegid(gid
) < 0) rb_sys_fail(0);
7119 if (setrgid(SAVED_GROUP_ID
) < 0) rb_sys_fail(0);
7121 if (setrgid(0) < 0) rb_sys_fail(0);
7124 if (setrgid(0) < 0) rb_sys_fail(0);
7126 if (setegid(gid
) < 0) rb_sys_fail(0);
7127 if (setrgid(gid
) < 0) rb_sys_fail(0);
7128 SAVED_GROUP_ID
= gid
;
7133 if (setegid(gid
) < 0) rb_sys_fail(0);
7134 if (setrgid(gid
) < 0) rb_sys_fail(0);
7135 SAVED_GROUP_ID
= gid
;
7141 else { /* unprivileged user */
7142 #if defined(HAVE_SETRESGID)
7143 if (setresgid((getgid() == gid
)? (rb_gid_t
)-1: gid
,
7144 (getegid() == gid
)? (rb_gid_t
)-1: gid
,
7145 (SAVED_GROUP_ID
== gid
)? (rb_gid_t
)-1: gid
) < 0) rb_sys_fail(0);
7146 SAVED_GROUP_ID
= gid
;
7147 #elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7148 if (SAVED_GROUP_ID
== gid
) {
7149 if (setregid((getgid() == gid
)? (rb_uid_t
)-1: gid
,
7150 (getegid() == gid
)? (rb_uid_t
)-1: gid
) < 0)
7153 else if (getgid() != gid
) {
7154 if (setregid(gid
, (getegid() == gid
)? (rb_uid_t
)-1: gid
) < 0)
7156 SAVED_GROUP_ID
= gid
;
7158 else if (/* getgid() == gid && */ getegid() != gid
) {
7159 if (setregid(getegid(), gid
) < 0) rb_sys_fail(0);
7160 SAVED_GROUP_ID
= gid
;
7161 if (setregid(gid
, -1) < 0) rb_sys_fail(0);
7163 else { /* getgid() == gid && getegid() == gid */
7164 if (setregid(-1, SAVED_GROUP_ID
) < 0) rb_sys_fail(0);
7165 if (setregid(SAVED_GROUP_ID
, gid
) < 0) rb_sys_fail(0);
7166 SAVED_GROUP_ID
= gid
;
7167 if (setregid(gid
, -1) < 0) rb_sys_fail(0);
7169 #elif defined(HAVE_SETRGID) && defined(HAVE_SETEGID)
7170 if (SAVED_GROUP_ID
== gid
) {
7171 if (getegid() != gid
&& setegid(gid
) < 0) rb_sys_fail(0);
7172 if (getgid() != gid
&& setrgid(gid
) < 0) rb_sys_fail(0);
7174 else if (/* SAVED_GROUP_ID != gid && */ getegid() == gid
) {
7175 if (getgid() != gid
) {
7176 if (setrgid(gid
) < 0) rb_sys_fail(0);
7177 SAVED_GROUP_ID
= gid
;
7180 if (setrgid(SAVED_GROUP_ID
) < 0) rb_sys_fail(0);
7181 SAVED_GROUP_ID
= gid
;
7182 if (setrgid(gid
) < 0) rb_sys_fail(0);
7185 else if (/* getegid() != gid && */ getgid() == gid
) {
7186 if (setegid(gid
) < 0) rb_sys_fail(0);
7187 if (setrgid(SAVED_GROUP_ID
) < 0) rb_sys_fail(0);
7188 SAVED_GROUP_ID
= gid
;
7189 if (setrgid(gid
) < 0) rb_sys_fail(0);
7192 rb_syserr_fail(EPERM
, 0);
7194 #elif defined HAVE_44BSD_SETGID
7195 if (getgid() == gid
) {
7196 /* (r,e,s)==(gid,?,?) ==> (gid,gid,gid) */
7197 if (setgid(gid
) < 0) rb_sys_fail(0);
7198 SAVED_GROUP_ID
= gid
;
7201 rb_syserr_fail(EPERM
, 0);
7203 #elif defined HAVE_SETEGID
7204 if (getgid() == gid
&& SAVED_GROUP_ID
== gid
) {
7205 if (setegid(gid
) < 0) rb_sys_fail(0);
7208 rb_syserr_fail(EPERM
, 0);
7210 #elif defined HAVE_SETGID
7211 if (getgid() == gid
&& SAVED_GROUP_ID
== gid
) {
7212 if (setgid(gid
) < 0) rb_sys_fail(0);
7215 rb_syserr_fail(EPERM
, 0);
7228 * Process.euid -> integer
7229 * Process::UID.eid -> integer
7230 * Process::Sys.geteuid -> integer
7232 * Returns the effective user ID for the current process.
7234 * Process.euid # => 501
7239 proc_geteuid(VALUE obj
)
7241 rb_uid_t euid
= geteuid();
7242 return UIDT2NUM(euid
);
7245 #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID) || defined(_POSIX_SAVED_IDS)
7247 proc_seteuid(rb_uid_t uid
)
7249 #if defined(HAVE_SETRESUID)
7250 if (setresuid(-1, uid
, -1) < 0) rb_sys_fail(0);
7251 #elif defined HAVE_SETREUID
7252 if (setreuid(-1, uid
) < 0) rb_sys_fail(0);
7253 #elif defined HAVE_SETEUID
7254 if (seteuid(uid
) < 0) rb_sys_fail(0);
7255 #elif defined HAVE_SETUID
7256 if (uid
== getuid()) {
7257 if (setuid(uid
) < 0) rb_sys_fail(0);
7268 #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID)
7271 * Process.euid = new_euid -> new_euid
7273 * Sets the effective user ID for the current process.
7275 * Not available on all platforms.
7279 proc_seteuid_m(VALUE mod
, VALUE euid
)
7282 proc_seteuid(OBJ2UID(euid
));
7286 #define proc_seteuid_m rb_f_notimplement
7290 rb_seteuid_core(rb_uid_t euid
)
7292 #if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7298 #if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7302 #if defined(HAVE_SETRESUID)
7304 if (setresuid(-1,euid
,euid
) < 0) rb_sys_fail(0);
7305 SAVED_USER_ID
= euid
;
7308 if (setresuid(-1,euid
,-1) < 0) rb_sys_fail(0);
7310 #elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7311 if (setreuid(-1, euid
) < 0) rb_sys_fail(0);
7313 if (setreuid(euid
,uid
) < 0) rb_sys_fail(0);
7314 if (setreuid(uid
,euid
) < 0) rb_sys_fail(0);
7315 SAVED_USER_ID
= euid
;
7317 #elif defined HAVE_SETEUID
7318 if (seteuid(euid
) < 0) rb_sys_fail(0);
7319 #elif defined HAVE_SETUID
7320 if (geteuid() == 0) rb_sys_fail(0);
7321 if (setuid(euid
) < 0) rb_sys_fail(0);
7331 * Process::UID.grant_privilege(user) -> integer
7332 * Process::UID.eid= user -> integer
7334 * Set the effective user ID, and if possible, the saved user ID of
7335 * the process to the given _user_. Returns the new
7336 * effective user ID. Not available on all platforms.
7338 * [Process.uid, Process.euid] #=> [0, 0]
7339 * Process::UID.grant_privilege(31) #=> 31
7340 * [Process.uid, Process.euid] #=> [0, 31]
7344 p_uid_grant_privilege(VALUE obj
, VALUE id
)
7346 rb_seteuid_core(OBJ2UID(id
));
7353 * Process.egid -> integer
7354 * Process::GID.eid -> integer
7355 * Process::Sys.geteid -> integer
7357 * Returns the effective group ID for the current process:
7359 * Process.egid # => 500
7361 * Not available on all platforms.
7365 proc_getegid(VALUE obj
)
7367 rb_gid_t egid
= getegid();
7369 return GIDT2NUM(egid
);
7372 #if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID) || defined(_POSIX_SAVED_IDS)
7375 * Process.egid = new_egid -> new_egid
7377 * Sets the effective group ID for the current process.
7379 * Not available on all platforms.
7383 proc_setegid(VALUE obj
, VALUE egid
)
7385 #if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7391 #if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7392 gid
= OBJ2GID(egid
);
7395 #if defined(HAVE_SETRESGID)
7396 if (setresgid(-1, gid
, -1) < 0) rb_sys_fail(0);
7397 #elif defined HAVE_SETREGID
7398 if (setregid(-1, gid
) < 0) rb_sys_fail(0);
7399 #elif defined HAVE_SETEGID
7400 if (setegid(gid
) < 0) rb_sys_fail(0);
7401 #elif defined HAVE_SETGID
7402 if (gid
== getgid()) {
7403 if (setgid(gid
) < 0) rb_sys_fail(0);
7415 #if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7416 #define proc_setegid_m proc_setegid
7418 #define proc_setegid_m rb_f_notimplement
7422 rb_setegid_core(rb_gid_t egid
)
7424 #if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7430 #if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7434 #if defined(HAVE_SETRESGID)
7436 if (setresgid(-1,egid
,egid
) < 0) rb_sys_fail(0);
7437 SAVED_GROUP_ID
= egid
;
7440 if (setresgid(-1,egid
,-1) < 0) rb_sys_fail(0);
7442 #elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7443 if (setregid(-1, egid
) < 0) rb_sys_fail(0);
7445 if (setregid(egid
,gid
) < 0) rb_sys_fail(0);
7446 if (setregid(gid
,egid
) < 0) rb_sys_fail(0);
7447 SAVED_GROUP_ID
= egid
;
7449 #elif defined HAVE_SETEGID
7450 if (setegid(egid
) < 0) rb_sys_fail(0);
7451 #elif defined HAVE_SETGID
7452 if (geteuid() == 0 /* root user */) rb_sys_fail(0);
7453 if (setgid(egid
) < 0) rb_sys_fail(0);
7463 * Process::GID.grant_privilege(group) -> integer
7464 * Process::GID.eid = group -> integer
7466 * Set the effective group ID, and if possible, the saved group ID of
7467 * the process to the given _group_. Returns the new
7468 * effective group ID. Not available on all platforms.
7470 * [Process.gid, Process.egid] #=> [0, 0]
7471 * Process::GID.grant_privilege(31) #=> 33
7472 * [Process.gid, Process.egid] #=> [0, 33]
7476 p_gid_grant_privilege(VALUE obj
, VALUE id
)
7478 rb_setegid_core(OBJ2GID(id
));
7485 * Process::UID.re_exchangeable? -> true or false
7487 * Returns +true+ if the real and effective user IDs of a
7488 * process may be exchanged on the current platform.
7493 p_uid_exchangeable(VALUE _
)
7495 #if defined(HAVE_SETRESUID)
7497 #elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7507 * Process::UID.re_exchange -> integer
7509 * Exchange real and effective user IDs and return the new effective
7510 * user ID. Not available on all platforms.
7512 * [Process.uid, Process.euid] #=> [0, 31]
7513 * Process::UID.re_exchange #=> 0
7514 * [Process.uid, Process.euid] #=> [31, 0]
7518 p_uid_exchange(VALUE obj
)
7521 #if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7528 #if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7532 #if defined(HAVE_SETRESUID)
7533 if (setresuid(euid
, uid
, uid
) < 0) rb_sys_fail(0);
7534 SAVED_USER_ID
= uid
;
7535 #elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7536 if (setreuid(euid
,uid
) < 0) rb_sys_fail(0);
7537 SAVED_USER_ID
= uid
;
7541 return UIDT2NUM(uid
);
7547 * Process::GID.re_exchangeable? -> true or false
7549 * Returns +true+ if the real and effective group IDs of a
7550 * process may be exchanged on the current platform.
7555 p_gid_exchangeable(VALUE _
)
7557 #if defined(HAVE_SETRESGID)
7559 #elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7569 * Process::GID.re_exchange -> integer
7571 * Exchange real and effective group IDs and return the new effective
7572 * group ID. Not available on all platforms.
7574 * [Process.gid, Process.egid] #=> [0, 33]
7575 * Process::GID.re_exchange #=> 0
7576 * [Process.gid, Process.egid] #=> [33, 0]
7580 p_gid_exchange(VALUE obj
)
7583 #if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7590 #if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7594 #if defined(HAVE_SETRESGID)
7595 if (setresgid(egid
, gid
, gid
) < 0) rb_sys_fail(0);
7596 SAVED_GROUP_ID
= gid
;
7597 #elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7598 if (setregid(egid
,gid
) < 0) rb_sys_fail(0);
7599 SAVED_GROUP_ID
= gid
;
7603 return GIDT2NUM(gid
);
7606 /* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
7610 * Process::UID.sid_available? -> true or false
7612 * Returns +true+ if the current platform has saved user
7618 p_uid_have_saved_id(VALUE _
)
7620 #if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
7628 #if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
7630 p_uid_sw_ensure(VALUE i
)
7632 rb_uid_t id
= (rb_uid_t
/* narrowing */)i
;
7633 under_uid_switch
= 0;
7634 id
= rb_seteuid_core(id
);
7635 return UIDT2NUM(id
);
7641 * Process::UID.switch -> integer
7642 * Process::UID.switch {|| block} -> object
7644 * Switch the effective and real user IDs of the current process. If
7645 * a <em>block</em> is given, the user IDs will be switched back
7646 * after the block is executed. Returns the new effective user ID if
7647 * called without a block, and the return value of the block if one
7653 p_uid_switch(VALUE obj
)
7664 if (rb_block_given_p()) {
7665 under_uid_switch
= 1;
7666 return rb_ensure(rb_yield
, Qnil
, p_uid_sw_ensure
, SAVED_USER_ID
);
7669 return UIDT2NUM(euid
);
7672 else if (euid
!= SAVED_USER_ID
) {
7673 proc_seteuid(SAVED_USER_ID
);
7674 if (rb_block_given_p()) {
7675 under_uid_switch
= 1;
7676 return rb_ensure(rb_yield
, Qnil
, p_uid_sw_ensure
, euid
);
7679 return UIDT2NUM(uid
);
7683 rb_syserr_fail(EPERM
, 0);
7686 UNREACHABLE_RETURN(Qnil
);
7690 p_uid_sw_ensure(VALUE obj
)
7692 under_uid_switch
= 0;
7693 return p_uid_exchange(obj
);
7697 p_uid_switch(VALUE obj
)
7707 rb_syserr_fail(EPERM
, 0);
7709 p_uid_exchange(obj
);
7710 if (rb_block_given_p()) {
7711 under_uid_switch
= 1;
7712 return rb_ensure(rb_yield
, Qnil
, p_uid_sw_ensure
, obj
);
7715 return UIDT2NUM(euid
);
7721 /* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
7725 * Process::GID.sid_available? -> true or false
7727 * Returns +true+ if the current platform has saved group
7733 p_gid_have_saved_id(VALUE _
)
7735 #if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
7742 #if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
7744 p_gid_sw_ensure(VALUE i
)
7746 rb_gid_t id
= (rb_gid_t
/* narrowing */)i
;
7747 under_gid_switch
= 0;
7748 id
= rb_setegid_core(id
);
7749 return GIDT2NUM(id
);
7755 * Process::GID.switch -> integer
7756 * Process::GID.switch {|| block} -> object
7758 * Switch the effective and real group IDs of the current process. If
7759 * a <em>block</em> is given, the group IDs will be switched back
7760 * after the block is executed. Returns the new effective group ID if
7761 * called without a block, and the return value of the block if one
7767 p_gid_switch(VALUE obj
)
7777 proc_setegid(obj
, GIDT2NUM(gid
));
7778 if (rb_block_given_p()) {
7779 under_gid_switch
= 1;
7780 return rb_ensure(rb_yield
, Qnil
, p_gid_sw_ensure
, SAVED_GROUP_ID
);
7783 return GIDT2NUM(egid
);
7786 else if (egid
!= SAVED_GROUP_ID
) {
7787 proc_setegid(obj
, GIDT2NUM(SAVED_GROUP_ID
));
7788 if (rb_block_given_p()) {
7789 under_gid_switch
= 1;
7790 return rb_ensure(rb_yield
, Qnil
, p_gid_sw_ensure
, egid
);
7793 return GIDT2NUM(gid
);
7797 rb_syserr_fail(EPERM
, 0);
7800 UNREACHABLE_RETURN(Qnil
);
7804 p_gid_sw_ensure(VALUE obj
)
7806 under_gid_switch
= 0;
7807 return p_gid_exchange(obj
);
7811 p_gid_switch(VALUE obj
)
7821 rb_syserr_fail(EPERM
, 0);
7823 p_gid_exchange(obj
);
7824 if (rb_block_given_p()) {
7825 under_gid_switch
= 1;
7826 return rb_ensure(rb_yield
, Qnil
, p_gid_sw_ensure
, obj
);
7829 return GIDT2NUM(egid
);
7835 #if defined(HAVE_TIMES)
7839 #ifdef HAVE__SC_CLK_TCK
7840 return sysconf(_SC_CLK_TCK
);
7841 #elif defined CLK_TCK
7852 * Process.times -> process_tms
7854 * Returns a Process::Tms structure that contains user and system CPU times
7855 * for the current process, and for its children processes:
7858 * # => #<struct Process::Tms utime=55.122118, stime=35.533068, cutime=0.0, cstime=0.002846>
7860 * The precision is platform-defined.
7864 rb_proc_times(VALUE obj
)
7866 VALUE utime
, stime
, cutime
, cstime
, ret
;
7867 #if defined(RUSAGE_SELF) && defined(RUSAGE_CHILDREN)
7868 struct rusage usage_s
, usage_c
;
7870 if (getrusage(RUSAGE_SELF
, &usage_s
) != 0 || getrusage(RUSAGE_CHILDREN
, &usage_c
) != 0)
7871 rb_sys_fail("getrusage");
7872 utime
= DBL2NUM((double)usage_s
.ru_utime
.tv_sec
+ (double)usage_s
.ru_utime
.tv_usec
/1e6
);
7873 stime
= DBL2NUM((double)usage_s
.ru_stime
.tv_sec
+ (double)usage_s
.ru_stime
.tv_usec
/1e6
);
7874 cutime
= DBL2NUM((double)usage_c
.ru_utime
.tv_sec
+ (double)usage_c
.ru_utime
.tv_usec
/1e6
);
7875 cstime
= DBL2NUM((double)usage_c
.ru_stime
.tv_sec
+ (double)usage_c
.ru_stime
.tv_usec
/1e6
);
7877 const double hertz
= (double)get_clk_tck();
7881 utime
= DBL2NUM(buf
.tms_utime
/ hertz
);
7882 stime
= DBL2NUM(buf
.tms_stime
/ hertz
);
7883 cutime
= DBL2NUM(buf
.tms_cutime
/ hertz
);
7884 cstime
= DBL2NUM(buf
.tms_cstime
/ hertz
);
7886 ret
= rb_struct_new(rb_cProcessTms
, utime
, stime
, cutime
, cstime
);
7889 RB_GC_GUARD(cutime
);
7890 RB_GC_GUARD(cstime
);
7894 #define rb_proc_times rb_f_notimplement
7897 #ifdef HAVE_LONG_LONG
7898 typedef LONG_LONG timetick_int_t
;
7899 #define TIMETICK_INT_MIN LLONG_MIN
7900 #define TIMETICK_INT_MAX LLONG_MAX
7901 #define TIMETICK_INT2NUM(v) LL2NUM(v)
7902 #define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_LONG_P(a, b)
7904 typedef long timetick_int_t
;
7905 #define TIMETICK_INT_MIN LONG_MIN
7906 #define TIMETICK_INT_MAX LONG_MAX
7907 #define TIMETICK_INT2NUM(v) LONG2NUM(v)
7908 #define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_P(a, b)
7911 CONSTFUNC(static timetick_int_t
gcd_timetick_int(timetick_int_t
, timetick_int_t
));
7912 static timetick_int_t
7913 gcd_timetick_int(timetick_int_t a
, timetick_int_t b
)
7933 reduce_fraction(timetick_int_t
*np
, timetick_int_t
*dp
)
7935 timetick_int_t gcd
= gcd_timetick_int(*np
, *dp
);
7943 reduce_factors(timetick_int_t
*numerators
, int num_numerators
,
7944 timetick_int_t
*denominators
, int num_denominators
)
7947 for (i
= 0; i
< num_numerators
; i
++) {
7948 if (numerators
[i
] == 1)
7950 for (j
= 0; j
< num_denominators
; j
++) {
7951 if (denominators
[j
] == 1)
7953 reduce_fraction(&numerators
[i
], &denominators
[j
]);
7959 timetick_int_t giga_count
;
7960 int32_t count
; /* 0 .. 999999999 */
7964 timetick2dblnum(struct timetick
*ttp
,
7965 timetick_int_t
*numerators
, int num_numerators
,
7966 timetick_int_t
*denominators
, int num_denominators
)
7971 reduce_factors(numerators
, num_numerators
,
7972 denominators
, num_denominators
);
7974 d
= ttp
->giga_count
* 1e9
+ ttp
->count
;
7976 for (i
= 0; i
< num_numerators
; i
++)
7978 for (i
= 0; i
< num_denominators
; i
++)
7979 d
/= denominators
[i
];
7985 timetick2dblnum_reciprocal(struct timetick
*ttp
,
7986 timetick_int_t
*numerators
, int num_numerators
,
7987 timetick_int_t
*denominators
, int num_denominators
)
7992 reduce_factors(numerators
, num_numerators
,
7993 denominators
, num_denominators
);
7996 for (i
= 0; i
< num_denominators
; i
++)
7997 d
*= denominators
[i
];
7998 for (i
= 0; i
< num_numerators
; i
++)
8000 d
/= ttp
->giga_count
* 1e9
+ ttp
->count
;
8005 #define NDIV(x,y) (-(-((x)+1)/(y))-1)
8006 #define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d))
8009 timetick2integer(struct timetick
*ttp
,
8010 timetick_int_t
*numerators
, int num_numerators
,
8011 timetick_int_t
*denominators
, int num_denominators
)
8016 reduce_factors(numerators
, num_numerators
,
8017 denominators
, num_denominators
);
8019 if (!MUL_OVERFLOW_SIGNED_INTEGER_P(1000000000, ttp
->giga_count
,
8020 TIMETICK_INT_MIN
, TIMETICK_INT_MAX
-ttp
->count
)) {
8021 timetick_int_t t
= ttp
->giga_count
* 1000000000 + ttp
->count
;
8022 for (i
= 0; i
< num_numerators
; i
++) {
8023 timetick_int_t factor
= numerators
[i
];
8024 if (MUL_OVERFLOW_TIMETICK_P(factor
, t
))
8028 for (i
= 0; i
< num_denominators
; i
++) {
8029 t
= DIV(t
, denominators
[i
]);
8031 return TIMETICK_INT2NUM(t
);
8035 v
= TIMETICK_INT2NUM(ttp
->giga_count
);
8036 v
= rb_funcall(v
, '*', 1, LONG2FIX(1000000000));
8037 v
= rb_funcall(v
, '+', 1, LONG2FIX(ttp
->count
));
8038 for (i
= 0; i
< num_numerators
; i
++) {
8039 timetick_int_t factor
= numerators
[i
];
8042 v
= rb_funcall(v
, '*', 1, TIMETICK_INT2NUM(factor
));
8044 for (i
= 0; i
< num_denominators
; i
++) {
8045 v
= rb_funcall(v
, '/', 1, TIMETICK_INT2NUM(denominators
[i
])); /* Ruby's '/' is div. */
8051 make_clock_result(struct timetick
*ttp
,
8052 timetick_int_t
*numerators
, int num_numerators
,
8053 timetick_int_t
*denominators
, int num_denominators
,
8056 if (unit
== ID2SYM(id_nanosecond
)) {
8057 numerators
[num_numerators
++] = 1000000000;
8058 return timetick2integer(ttp
, numerators
, num_numerators
, denominators
, num_denominators
);
8060 else if (unit
== ID2SYM(id_microsecond
)) {
8061 numerators
[num_numerators
++] = 1000000;
8062 return timetick2integer(ttp
, numerators
, num_numerators
, denominators
, num_denominators
);
8064 else if (unit
== ID2SYM(id_millisecond
)) {
8065 numerators
[num_numerators
++] = 1000;
8066 return timetick2integer(ttp
, numerators
, num_numerators
, denominators
, num_denominators
);
8068 else if (unit
== ID2SYM(id_second
)) {
8069 return timetick2integer(ttp
, numerators
, num_numerators
, denominators
, num_denominators
);
8071 else if (unit
== ID2SYM(id_float_microsecond
)) {
8072 numerators
[num_numerators
++] = 1000000;
8073 return timetick2dblnum(ttp
, numerators
, num_numerators
, denominators
, num_denominators
);
8075 else if (unit
== ID2SYM(id_float_millisecond
)) {
8076 numerators
[num_numerators
++] = 1000;
8077 return timetick2dblnum(ttp
, numerators
, num_numerators
, denominators
, num_denominators
);
8079 else if (NIL_P(unit
) || unit
== ID2SYM(id_float_second
)) {
8080 return timetick2dblnum(ttp
, numerators
, num_numerators
, denominators
, num_denominators
);
8083 rb_raise(rb_eArgError
, "unexpected unit: %"PRIsVALUE
, unit
);
8087 static const mach_timebase_info_data_t
*
8088 get_mach_timebase_info(void)
8090 static mach_timebase_info_data_t sTimebaseInfo
;
8092 if ( sTimebaseInfo
.denom
== 0 ) {
8093 (void) mach_timebase_info(&sTimebaseInfo
);
8096 return &sTimebaseInfo
;
8100 ruby_real_ms_time(void)
8102 const mach_timebase_info_data_t
*info
= get_mach_timebase_info();
8103 uint64_t t
= mach_absolute_time();
8104 return (double)t
* info
->numer
/ info
->denom
/ 1e6
;
8108 #if defined(NUM2CLOCKID)
8109 # define NUMERIC_CLOCKID 1
8111 # define NUMERIC_CLOCKID 0
8112 # define NUM2CLOCKID(x) 0
8115 #define clock_failed(name, err, arg) do { \
8116 int clock_error = (err); \
8117 rb_syserr_fail_str(clock_error, rb_sprintf("clock_" name "(%+"PRIsVALUE")", (arg))); \
8122 * Process.clock_gettime(clock_id, unit = :float_second) -> number
8124 * Returns a clock time as determined by POSIX function
8125 * {clock_gettime()}[https://man7.org/linux/man-pages/man3/clock_gettime.3.html]:
8127 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID) # => 198.650379677
8129 * Argument +clock_id+ should be a symbol or a constant that specifies
8130 * the clock whose time is to be returned;
8133 * Optional argument +unit+ should be a symbol that specifies
8134 * the unit to be used in the returned clock time;
8137 * <b>Argument +clock_id+</b>
8139 * Argument +clock_id+ specifies the clock whose time is to be returned;
8140 * it may be a constant such as <tt>Process::CLOCK_REALTIME</tt>,
8141 * or a symbol shorthand such as +:CLOCK_REALTIME+.
8143 * The supported clocks depend on the underlying operating system;
8144 * this method supports the following clocks on the indicated platforms
8145 * (raises Errno::EINVAL if called with an unsupported clock):
8147 * - +:CLOCK_BOOTTIME+: Linux 2.6.39.
8148 * - +:CLOCK_BOOTTIME_ALARM+: Linux 3.0.
8149 * - +:CLOCK_MONOTONIC+: SUSv3 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4, macOS 10.12, Windows-2000.
8150 * - +:CLOCK_MONOTONIC_COARSE+: Linux 2.6.32.
8151 * - +:CLOCK_MONOTONIC_FAST+: FreeBSD 8.1.
8152 * - +:CLOCK_MONOTONIC_PRECISE+: FreeBSD 8.1.
8153 * - +:CLOCK_MONOTONIC_RAW+: Linux 2.6.28, macOS 10.12.
8154 * - +:CLOCK_MONOTONIC_RAW_APPROX+: macOS 10.12.
8155 * - +:CLOCK_PROCESS_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 9.3, OpenBSD 5.4, macOS 10.12.
8156 * - +:CLOCK_PROF+: FreeBSD 3.0, OpenBSD 2.1.
8157 * - +:CLOCK_REALTIME+: SUSv2 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 2.1, macOS 10.12, Windows-8/Server-2012.
8158 * Time.now is recommended over +:CLOCK_REALTIME:.
8159 * - +:CLOCK_REALTIME_ALARM+: Linux 3.0.
8160 * - +:CLOCK_REALTIME_COARSE+: Linux 2.6.32.
8161 * - +:CLOCK_REALTIME_FAST+: FreeBSD 8.1.
8162 * - +:CLOCK_REALTIME_PRECISE+: FreeBSD 8.1.
8163 * - +:CLOCK_SECOND+: FreeBSD 8.1.
8164 * - +:CLOCK_TAI+: Linux 3.10.
8165 * - +:CLOCK_THREAD_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 7.1, OpenBSD 5.4, macOS 10.12.
8166 * - +:CLOCK_UPTIME+: FreeBSD 7.0, OpenBSD 5.5.
8167 * - +:CLOCK_UPTIME_FAST+: FreeBSD 8.1.
8168 * - +:CLOCK_UPTIME_PRECISE+: FreeBSD 8.1.
8169 * - +:CLOCK_UPTIME_RAW+: macOS 10.12.
8170 * - +:CLOCK_UPTIME_RAW_APPROX+: macOS 10.12.
8171 * - +:CLOCK_VIRTUAL+: FreeBSD 3.0, OpenBSD 2.1.
8173 * Note that SUS stands for Single Unix Specification.
8174 * SUS contains POSIX and clock_gettime is defined in the POSIX part.
8175 * SUS defines +:CLOCK_REALTIME+ as mandatory but
8176 * +:CLOCK_MONOTONIC+, +:CLOCK_PROCESS_CPUTIME_ID+,
8177 * and +:CLOCK_THREAD_CPUTIME_ID+ are optional.
8179 * Certain emulations are used when the given +clock_id+
8180 * is not supported directly:
8182 * - Emulations for +:CLOCK_REALTIME+:
8184 * - +:GETTIMEOFDAY_BASED_CLOCK_REALTIME+:
8185 * Use gettimeofday() defined by SUS (deprecated in SUSv4).
8186 * The resolution is 1 microsecond.
8187 * - +:TIME_BASED_CLOCK_REALTIME+:
8188 * Use time() defined by ISO C.
8189 * The resolution is 1 second.
8191 * - Emulations for +:CLOCK_MONOTONIC+:
8193 * - +:MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC+:
8194 * Use mach_absolute_time(), available on Darwin.
8195 * The resolution is CPU dependent.
8196 * - +:TIMES_BASED_CLOCK_MONOTONIC+:
8197 * Use the result value of times() defined by POSIX, thus:
8199 * Upon successful completion, times() shall return the elapsed real time,
8200 * in clock ticks, since an arbitrary point in the past
8201 * (for example, system start-up time).
8203 * For example, GNU/Linux returns a value based on jiffies and it is monotonic.
8204 * However, 4.4BSD uses gettimeofday() and it is not monotonic.
8205 * (FreeBSD uses +:CLOCK_MONOTONIC+ instead, though.)
8207 * The resolution is the clock tick.
8208 * "getconf CLK_TCK" command shows the clock ticks per second.
8209 * (The clock ticks-per-second is defined by HZ macro in older systems.)
8210 * If it is 100 and clock_t is 32 bits integer type,
8211 * the resolution is 10 millisecond and cannot represent over 497 days.
8213 * - Emulations for +:CLOCK_PROCESS_CPUTIME_ID+:
8215 * - +:GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8216 * Use getrusage() defined by SUS.
8217 * getrusage() is used with RUSAGE_SELF to obtain the time only for
8218 * the calling process (excluding the time for child processes).
8219 * The result is addition of user time (ru_utime) and system time (ru_stime).
8220 * The resolution is 1 microsecond.
8221 * - +:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8222 * Use times() defined by POSIX.
8223 * The result is addition of user time (tms_utime) and system time (tms_stime).
8224 * tms_cutime and tms_cstime are ignored to exclude the time for child processes.
8225 * The resolution is the clock tick.
8226 * "getconf CLK_TCK" command shows the clock ticks per second.
8227 * (The clock ticks per second is defined by HZ macro in older systems.)
8228 * If it is 100, the resolution is 10 millisecond.
8229 * - +:CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8230 * Use clock() defined by ISO C.
8231 * The resolution is <tt>1/CLOCKS_PER_SEC</tt>.
8232 * +CLOCKS_PER_SEC+ is the C-level macro defined by time.h.
8233 * SUS defines +CLOCKS_PER_SEC+ as 1000000;
8234 * other systems may define it differently.
8235 * If +CLOCKS_PER_SEC+ is 1000000 (as in SUS),
8236 * the resolution is 1 microsecond.
8237 * If +CLOCKS_PER_SEC+ is 1000000 and clock_t is a 32-bit integer type,
8238 * it cannot represent over 72 minutes.
8240 * <b>Argument +unit+</b>
8242 * Optional argument +unit+ (default +:float_second+)
8243 * specifies the unit for the returned value.
8245 * - +:float_microsecond+: Number of microseconds as a float.
8246 * - +:float_millisecond+: Number of milliseconds as a float.
8247 * - +:float_second+: Number of seconds as a float.
8248 * - +:microsecond+: Number of microseconds as an integer.
8249 * - +:millisecond+: Number of milliseconds as an integer.
8250 * - +:nanosecond+: Number of nanoseconds as an integer.
8251 * - +::second+: Number of seconds as an integer.
8255 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond)
8256 * # => 203605054.825
8257 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond)
8258 * # => 203643.696848
8259 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_second)
8260 * # => 203.762181929
8261 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :microsecond)
8263 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :millisecond)
8265 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond)
8267 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :second)
8270 * The underlying function, clock_gettime(), returns a number of nanoseconds.
8271 * Float object (IEEE 754 double) is not enough to represent
8272 * the return value for +:CLOCK_REALTIME+.
8273 * If the exact nanoseconds value is required, use +:nanosecond+ as the +unit+.
8275 * The origin (time zero) of the returned value is system-dependent,
8276 * and may be, for example, system start up time,
8277 * process start up time, the Epoch, etc.
8279 * The origin in +:CLOCK_REALTIME+ is defined as the Epoch:
8280 * <tt>1970-01-01 00:00:00 UTC</tt>;
8281 * some systems count leap seconds and others don't,
8282 * so the result may vary across systems.
8285 rb_clock_gettime(int argc
, VALUE
*argv
, VALUE _
)
8290 timetick_int_t numerators
[2];
8291 timetick_int_t denominators
[2];
8292 int num_numerators
= 0;
8293 int num_denominators
= 0;
8295 VALUE unit
= (rb_check_arity(argc
, 1, 2) == 2) ? argv
[1] : Qnil
;
8296 VALUE clk_id
= argv
[0];
8297 #ifdef HAVE_CLOCK_GETTIME
8301 if (SYMBOL_P(clk_id
)) {
8302 #ifdef CLOCK_REALTIME
8303 if (clk_id
== RUBY_CLOCK_REALTIME
) {
8309 #ifdef CLOCK_MONOTONIC
8310 if (clk_id
== RUBY_CLOCK_MONOTONIC
) {
8311 c
= CLOCK_MONOTONIC
;
8316 #ifdef CLOCK_PROCESS_CPUTIME_ID
8317 if (clk_id
== RUBY_CLOCK_PROCESS_CPUTIME_ID
) {
8318 c
= CLOCK_PROCESS_CPUTIME_ID
;
8323 #ifdef CLOCK_THREAD_CPUTIME_ID
8324 if (clk_id
== RUBY_CLOCK_THREAD_CPUTIME_ID
) {
8325 c
= CLOCK_THREAD_CPUTIME_ID
;
8331 * Non-clock_gettime clocks are provided by symbol clk_id.
8333 #ifdef HAVE_GETTIMEOFDAY
8335 * GETTIMEOFDAY_BASED_CLOCK_REALTIME is used for
8336 * CLOCK_REALTIME if clock_gettime is not available.
8338 #define RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME ID2SYM(id_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
8339 if (clk_id
== RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
) {
8341 ret
= gettimeofday(&tv
, 0);
8343 rb_sys_fail("gettimeofday");
8344 tt
.giga_count
= tv
.tv_sec
;
8345 tt
.count
= (int32_t)tv
.tv_usec
* 1000;
8346 denominators
[num_denominators
++] = 1000000000;
8351 #define RUBY_TIME_BASED_CLOCK_REALTIME ID2SYM(id_TIME_BASED_CLOCK_REALTIME)
8352 if (clk_id
== RUBY_TIME_BASED_CLOCK_REALTIME
) {
8355 if (t
== (time_t)-1)
8356 rb_sys_fail("time");
8359 denominators
[num_denominators
++] = 1000000000;
8364 #define RUBY_TIMES_BASED_CLOCK_MONOTONIC \
8365 ID2SYM(id_TIMES_BASED_CLOCK_MONOTONIC)
8366 if (clk_id
== RUBY_TIMES_BASED_CLOCK_MONOTONIC
) {
8369 unsigned_clock_t uc
;
8371 if (c
== (clock_t)-1)
8372 rb_sys_fail("times");
8373 uc
= (unsigned_clock_t
)c
;
8374 tt
.count
= (int32_t)(uc
% 1000000000);
8375 tt
.giga_count
= (uc
/ 1000000000);
8376 denominators
[num_denominators
++] = get_clk_tck();
8382 #define RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID \
8383 ID2SYM(id_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
8384 if (clk_id
== RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
) {
8385 struct rusage usage
;
8387 ret
= getrusage(RUSAGE_SELF
, &usage
);
8389 rb_sys_fail("getrusage");
8390 tt
.giga_count
= usage
.ru_utime
.tv_sec
+ usage
.ru_stime
.tv_sec
;
8391 usec
= (int32_t)(usage
.ru_utime
.tv_usec
+ usage
.ru_stime
.tv_usec
);
8392 if (1000000 <= usec
) {
8396 tt
.count
= usec
* 1000;
8397 denominators
[num_denominators
++] = 1000000000;
8403 #define RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID \
8404 ID2SYM(id_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID)
8405 if (clk_id
== RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
) {
8407 unsigned_clock_t utime
, stime
;
8408 if (times(&buf
) == (clock_t)-1)
8409 rb_sys_fail("times");
8410 utime
= (unsigned_clock_t
)buf
.tms_utime
;
8411 stime
= (unsigned_clock_t
)buf
.tms_stime
;
8412 tt
.count
= (int32_t)((utime
% 1000000000) + (stime
% 1000000000));
8413 tt
.giga_count
= (utime
/ 1000000000) + (stime
/ 1000000000);
8414 if (1000000000 <= tt
.count
) {
8415 tt
.count
-= 1000000000;
8418 denominators
[num_denominators
++] = get_clk_tck();
8423 #define RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID \
8424 ID2SYM(id_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID)
8425 if (clk_id
== RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
) {
8427 unsigned_clock_t uc
;
8430 if (c
== (clock_t)-1)
8431 rb_sys_fail("clock");
8432 uc
= (unsigned_clock_t
)c
;
8433 tt
.count
= (int32_t)(uc
% 1000000000);
8434 tt
.giga_count
= uc
/ 1000000000;
8435 denominators
[num_denominators
++] = CLOCKS_PER_SEC
;
8440 if (clk_id
== RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
) {
8441 const mach_timebase_info_data_t
*info
= get_mach_timebase_info();
8442 uint64_t t
= mach_absolute_time();
8443 tt
.count
= (int32_t)(t
% 1000000000);
8444 tt
.giga_count
= t
/ 1000000000;
8445 numerators
[num_numerators
++] = info
->numer
;
8446 denominators
[num_denominators
++] = info
->denom
;
8447 denominators
[num_denominators
++] = 1000000000;
8452 else if (NUMERIC_CLOCKID
) {
8453 #if defined(HAVE_CLOCK_GETTIME)
8455 c
= NUM2CLOCKID(clk_id
);
8457 ret
= clock_gettime(c
, &ts
);
8459 clock_failed("gettime", errno
, clk_id
);
8460 tt
.count
= (int32_t)ts
.tv_nsec
;
8461 tt
.giga_count
= ts
.tv_sec
;
8462 denominators
[num_denominators
++] = 1000000000;
8467 rb_unexpected_type(clk_id
, T_SYMBOL
);
8469 clock_failed("gettime", EINVAL
, clk_id
);
8472 return make_clock_result(&tt
, numerators
, num_numerators
, denominators
, num_denominators
, unit
);
8477 * Process.clock_getres(clock_id, unit = :float_second) -> number
8479 * Returns a clock resolution as determined by POSIX function
8480 * {clock_getres()}[https://man7.org/linux/man-pages/man3/clock_getres.3.html]:
8482 * Process.clock_getres(:CLOCK_REALTIME) # => 1.0e-09
8484 * See Process.clock_gettime for the values of +clock_id+ and +unit+.
8488 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond) # => 0.001
8489 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond) # => 1.0e-06
8490 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 1.0e-09
8491 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :microsecond) # => 0
8492 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :millisecond) # => 0
8493 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond) # => 1
8494 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :second) # => 0
8496 * In addition to the values for +unit+ supported in Process.clock_gettime,
8497 * this method supports +:hertz+, the integer number of clock ticks per second
8498 * (which is the reciprocal of +:float_second+):
8500 * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz) # => 100.0
8501 * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 0.01
8504 * Note that the returned resolution may be inaccurate on some platforms
8505 * due to underlying bugs.
8506 * Inaccurate resolutions have been reported for various clocks including
8507 * +:CLOCK_MONOTONIC+ and +:CLOCK_MONOTONIC_RAW+
8508 * on Linux, macOS, BSD or AIX platforms, when using ARM processors,
8509 * or when using virtualization.
8512 rb_clock_getres(int argc
, VALUE
*argv
, VALUE _
)
8517 timetick_int_t numerators
[2];
8518 timetick_int_t denominators
[2];
8519 int num_numerators
= 0;
8520 int num_denominators
= 0;
8521 #ifdef HAVE_CLOCK_GETRES
8525 VALUE unit
= (rb_check_arity(argc
, 1, 2) == 2) ? argv
[1] : Qnil
;
8526 VALUE clk_id
= argv
[0];
8528 if (SYMBOL_P(clk_id
)) {
8529 #ifdef CLOCK_REALTIME
8530 if (clk_id
== RUBY_CLOCK_REALTIME
) {
8536 #ifdef CLOCK_MONOTONIC
8537 if (clk_id
== RUBY_CLOCK_MONOTONIC
) {
8538 c
= CLOCK_MONOTONIC
;
8543 #ifdef CLOCK_PROCESS_CPUTIME_ID
8544 if (clk_id
== RUBY_CLOCK_PROCESS_CPUTIME_ID
) {
8545 c
= CLOCK_PROCESS_CPUTIME_ID
;
8550 #ifdef CLOCK_THREAD_CPUTIME_ID
8551 if (clk_id
== RUBY_CLOCK_THREAD_CPUTIME_ID
) {
8552 c
= CLOCK_THREAD_CPUTIME_ID
;
8557 #ifdef RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
8558 if (clk_id
== RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
) {
8561 denominators
[num_denominators
++] = 1000000000;
8566 #ifdef RUBY_TIME_BASED_CLOCK_REALTIME
8567 if (clk_id
== RUBY_TIME_BASED_CLOCK_REALTIME
) {
8570 denominators
[num_denominators
++] = 1000000000;
8575 #ifdef RUBY_TIMES_BASED_CLOCK_MONOTONIC
8576 if (clk_id
== RUBY_TIMES_BASED_CLOCK_MONOTONIC
) {
8579 denominators
[num_denominators
++] = get_clk_tck();
8584 #ifdef RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
8585 if (clk_id
== RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
) {
8588 denominators
[num_denominators
++] = 1000000000;
8593 #ifdef RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
8594 if (clk_id
== RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
) {
8597 denominators
[num_denominators
++] = get_clk_tck();
8602 #ifdef RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
8603 if (clk_id
== RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
) {
8606 denominators
[num_denominators
++] = CLOCKS_PER_SEC
;
8611 #ifdef RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
8612 if (clk_id
== RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
) {
8613 const mach_timebase_info_data_t
*info
= get_mach_timebase_info();
8616 numerators
[num_numerators
++] = info
->numer
;
8617 denominators
[num_denominators
++] = info
->denom
;
8618 denominators
[num_denominators
++] = 1000000000;
8623 else if (NUMERIC_CLOCKID
) {
8624 #if defined(HAVE_CLOCK_GETRES)
8626 c
= NUM2CLOCKID(clk_id
);
8628 ret
= clock_getres(c
, &ts
);
8630 clock_failed("getres", errno
, clk_id
);
8631 tt
.count
= (int32_t)ts
.tv_nsec
;
8632 tt
.giga_count
= ts
.tv_sec
;
8633 denominators
[num_denominators
++] = 1000000000;
8638 rb_unexpected_type(clk_id
, T_SYMBOL
);
8640 clock_failed("getres", EINVAL
, clk_id
);
8643 if (unit
== ID2SYM(id_hertz
)) {
8644 return timetick2dblnum_reciprocal(&tt
, numerators
, num_numerators
, denominators
, num_denominators
);
8647 return make_clock_result(&tt
, numerators
, num_numerators
, denominators
, num_denominators
, unit
);
8652 get_CHILD_STATUS(ID _x
, VALUE
*_y
)
8654 return rb_last_status_get();
8658 get_PROCESS_ID(ID _x
, VALUE
*_y
)
8665 * Process.kill(signal, *ids) -> count
8667 * Sends a signal to each process specified by +ids+
8668 * (which must specify at least one ID);
8669 * returns the count of signals sent.
8671 * For each given +id+, if +id+ is:
8673 * - Positive, sends the signal to the process whose process ID is +id+.
8674 * - Zero, send the signal to all processes in the current process group.
8675 * - Negative, sends the signal to a system-dependent collection of processes.
8677 * Argument +signal+ specifies the signal to be sent;
8678 * the argument may be:
8680 * - An integer signal number: e.g., +-29+, +0+, +29+.
8681 * - A signal name (string), with or without leading <tt>'SIG'</tt>,
8682 * and with or without a further prefixed minus sign (<tt>'-'</tt>):
8685 * - <tt>'SIGPOLL'</tt>.
8686 * - <tt>'POLL'</tt>,
8687 * - <tt>'-SIGPOLL'</tt>.
8688 * - <tt>'-POLL'</tt>.
8690 * - A signal symbol, with or without leading <tt>'SIG'</tt>,
8691 * and with or without a further prefixed minus sign (<tt>'-'</tt>):
8696 * - <tt>:'-SIGPOLL'</tt>.
8697 * - <tt>:'-POLL'</tt>.
8701 * - A non-negative integer, or a signal name or symbol
8702 * without prefixed <tt>'-'</tt>,
8703 * each process with process ID +id+ is signalled.
8704 * - A negative integer, or a signal name or symbol
8705 * with prefixed <tt>'-'</tt>,
8706 * each process group with group ID +id+ is signalled.
8708 * Use method Signal.list to see which signals are supported
8709 * by Ruby on the underlying platform;
8710 * the method returns a hash of the string names
8711 * and non-negative integer values of the supported signals.
8712 * The size and content of the returned hash varies widely
8715 * Additionally, signal +0+ is useful to determine if the process exists.
8720 * Signal.trap('HUP') { puts 'Ouch!'; exit }
8721 * # ... do some work ...
8724 * Process.kill('HUP', pid)
8733 * - Raises Errno::EINVAL or RangeError if +signal+ is an integer
8735 * - Raises ArgumentError if +signal+ is a string or symbol
8737 * - Raises Errno::ESRCH or RangeError if one of +ids+ is invalid.
8738 * - Raises Errno::EPERM if needed permissions are not in force.
8740 * In the last two cases, signals may have been sent to some processes.
8744 proc_rb_f_kill(int c
, const VALUE
*v
, VALUE _
)
8746 return rb_f_kill(c
, v
);
8750 static VALUE rb_mProcUID
;
8751 static VALUE rb_mProcGID
;
8752 static VALUE rb_mProcID_Syscall
;
8756 * Process.warmup -> true
8758 * Notify the Ruby virtual machine that the boot sequence is finished,
8759 * and that now is a good time to optimize the application. This is useful
8760 * for long running applications.
8762 * This method is expected to be called at the end of the application boot.
8763 * If the application is deployed using a pre-forking model, +Process.warmup+
8764 * should be called in the original process before the first fork.
8766 * The actual optimizations performed are entirely implementation specific
8767 * and may change in the future without notice.
8769 * On CRuby, +Process.warmup+:
8771 * * Performs a major GC.
8772 * * Compacts the heap.
8773 * * Promotes all surviving objects to the old generation.
8774 * * Precomputes the coderange of all strings.
8775 * * Frees all empty heap pages and increments the allocatable pages counter
8776 * by the number of pages freed.
8777 * * Invoke +malloc_trim+ if available to free empty malloc pages.
8781 proc_warmup(VALUE _
)
8784 rb_gc_prepare_heap();
8790 * Document-module: Process
8792 * \Module +Process+ represents a process in the underlying operating system.
8793 * Its methods support management of the current process and its child processes.
8795 * == \Process Creation
8797 * Each of the following methods executes a given command in a new process or subshell,
8798 * or multiple commands in new processes and/or subshells.
8799 * The choice of process or subshell depends on the form of the command;
8800 * see {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
8802 * - Process.spawn, Kernel#spawn: Executes the command;
8803 * returns the new pid without waiting for completion.
8804 * - Process.exec: Replaces the current process by executing the command.
8808 * - \Method Kernel#system executes a given command-line (string) in a subshell;
8809 * returns +true+, +false+, or +nil+.
8810 * - \Method Kernel#` executes a given command-line (string) in a subshell;
8811 * returns its $stdout string.
8812 * - \Module Open3 supports creating child processes
8813 * with access to their $stdin, $stdout, and $stderr streams.
8815 * === Execution Environment
8817 * Optional leading argument +env+ is a hash of name/value pairs,
8818 * where each name is a string and each value is a string or +nil+;
8819 * each name/value pair is added to ENV in the new process.
8821 * Process.spawn( 'ruby -e "p ENV[\"Foo\"]"')
8822 * Process.spawn({'Foo' => '0'}, 'ruby -e "p ENV[\"Foo\"]"')
8828 * The effect is usually similar to that of calling ENV#update with argument +env+,
8829 * where each named environment variable is created or updated
8830 * (if the value is non-+nil+),
8831 * or deleted (if the value is +nil+).
8833 * However, some modifications to the calling process may remain
8834 * if the new process fails.
8835 * For example, hard resource limits are not restored.
8837 * === Argument +command_line+ or +exe_path+
8839 * The required string argument is one of the following:
8841 * - +command_line+ if it begins with a shell reserved word or special built-in,
8842 * or if it contains one or more meta characters.
8843 * - +exe_path+ otherwise.
8845 * ==== Argument +command_line+
8847 * \String argument +command_line+ is a command line to be passed to a shell;
8848 * it must begin with a shell reserved word, begin with a special built-in,
8849 * or contain meta characters:
8851 * system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
8852 * system('exit') # => true # Built-in.
8853 * system('date > /tmp/date.tmp') # => true # Contains meta character.
8854 * system('date > /nop/date.tmp') # => false
8855 * system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
8857 * The command line may also contain arguments and options for the command:
8859 * system('echo "Foo"') # => true
8865 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
8867 * ==== Argument +exe_path+
8869 * Argument +exe_path+ is one of the following:
8871 * - The string path to an executable file to be called:
8875 * system('/usr/bin/date') # => true # Path to date on Unix-style system.
8876 * system('foo') # => nil # Command execlution failed.
8880 * Thu Aug 31 10:06:48 AM CDT 2023
8882 * A path or command name containing spaces without arguments cannot
8883 * be distinguished from +command_line+ above, so you must quote or
8884 * escape the entire command name using a shell in platform
8885 * dependent manner, or use the array form below.
8887 * If +exe_path+ does not contain any path separator, an executable
8888 * file is searched from directories specified with the +PATH+
8889 * environment variable. What the word "executable" means here is
8890 * depending on platforms.
8892 * Even if the file considered "executable", its content may not be
8893 * in proper executable format. In that case, Ruby tries to run it
8894 * by using <tt>/bin/sh</tt> on a Unix-like system, like system(3)
8897 * File.write('shell_command', 'echo $SHELL', perm: 0o755)
8898 * system('./shell_command') # prints "/bin/sh" or something.
8900 * - A 2-element array containing the path to an executable
8901 * and the string to be used as the name of the executing process:
8905 * pid = spawn(['sleep', 'Hello!'], '1') # 2-element array.
8906 * p `ps -p #{pid} -o command=`
8912 * === Arguments +args+
8914 * If +command_line+ does not contain shell meta characters except for
8915 * spaces and tabs, or +exe_path+ is given, Ruby invokes the
8916 * executable directly. This form does not use the shell:
8918 * spawn("doesnt_exist") # Raises Errno::ENOENT
8919 * spawn("doesnt_exist", "\n") # Raises Errno::ENOENT
8921 * spawn("doesnt_exist\n") # => false
8922 * # sh: 1: doesnot_exist: not found
8924 * The error message is from a shell and would vary depending on your
8927 * If one or more +args+ is given after +exe_path+, each is an
8928 * argument or option to be passed to the executable:
8932 * system('echo', '<', 'C*', '|', '$SHELL', '>') # => true
8938 * However, there are exceptions on Windows. See {Execution Shell on
8939 * Windows}[rdoc-ref:Process@Execution+Shell+on+Windows].
8941 * If you want to invoke a path containing spaces with no arguments
8942 * without shell, you will need to use a 2-element array +exe_path+.
8946 * path = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
8947 * spawn(path) # Raises Errno::ENOENT; No such file or directory - /Applications/Google
8950 * === Execution Options
8952 * Optional trailing argument +options+ is a hash of execution options.
8954 * ==== Working Directory (+:chdir+)
8956 * By default, the working directory for the new process is the same as
8957 * that of the current process:
8960 * Process.spawn('ruby -e "puts Dir.pwd"')
8966 * Use option +:chdir+ to set the working directory for the new process:
8968 * Process.spawn('ruby -e "puts Dir.pwd"', {chdir: '/tmp'})
8974 * The working directory of the current process is not changed:
8976 * Dir.pwd # => "/var"
8978 * ==== \File Redirection (\File Descriptor)
8980 * Use execution options for file redirection in the new process.
8982 * The key for such an option may be an integer file descriptor (fd),
8983 * specifying a source,
8984 * or an array of fds, specifying multiple sources.
8986 * An integer source fd may be specified as:
8988 * - _n_: Specifies file descriptor _n_.
8990 * There are these shorthand symbols for fds:
8992 * - +:in+: Specifies file descriptor 0 (STDIN).
8993 * - +:out+: Specifies file descriptor 1 (STDOUT).
8994 * - +:err+: Specifies file descriptor 2 (STDERR).
8996 * The value given with a source is one of:
8999 * Redirects to fd _n_ in the parent process.
9001 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>,
9002 * where +mode+ is <tt>'r'</tt> for source +:in+,
9003 * or <tt>'w'</tt> for source +:out+ or +:err+.
9004 * - <tt>[filepath]</tt>:
9005 * Redirects from the file at +filepath+ via <tt>open(filepath, 'r', 0644)</tt>.
9006 * - <tt>[filepath, mode]</tt>:
9007 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>.
9008 * - <tt>[filepath, mode, perm]</tt>:
9009 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, perm)</tt>.
9010 * - <tt>[:child, fd]</tt>:
9011 * Redirects to the redirected +fd+.
9012 * - +:close+: Closes the file descriptor in child process.
9014 * See {Access Modes}[rdoc-ref:File@Access+Modes]
9015 * and {File Permissions}[rdoc-ref:File@File+Permissions].
9017 * ==== Environment Variables (+:unsetenv_others+)
9019 * By default, the new process inherits environment variables
9020 * from the parent process;
9021 * use execution option key +:unsetenv_others+ with value +true+
9022 * to clear environment variables in the new process.
9024 * Any changes specified by execution option +env+ are made after the new process
9025 * inherits or clears its environment variables;
9026 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
9028 * ==== \File-Creation Access (+:umask+)
9030 * Use execution option +:umask+ to set the file-creation access
9031 * for the new process;
9032 * see {Access Modes}[rdoc-ref:File@Access+Modes]:
9034 * command = 'ruby -e "puts sprintf(\"0%o\", File.umask)"'
9035 * options = {:umask => 0644}
9036 * Process.spawn(command, options)
9042 * ==== \Process Groups (+:pgroup+ and +:new_pgroup+)
9044 * By default, the new process belongs to the same
9045 * {process group}[https://en.wikipedia.org/wiki/Process_group]
9046 * as the parent process.
9048 * To specify a different process group.
9049 * use execution option +:pgroup+ with one of the following values:
9051 * - +true+: Create a new process group for the new process.
9052 * - _pgid_: Create the new process in the process group
9053 * whose id is _pgid_.
9055 * On Windows only, use execution option +:new_pgroup+ with value +true+
9056 * to create a new process group for the new process.
9058 * ==== Resource Limits
9060 * Use execution options to set resource limits.
9062 * The keys for these options are symbols of the form
9063 * <tt>:rlimit_<i>resource_name</i></tt>,
9064 * where _resource_name_ is the downcased form of one of the string
9065 * resource names described at method Process.setrlimit.
9066 * For example, key +:rlimit_cpu+ corresponds to resource limit <tt>'CPU'</tt>.
9068 * The value for such as key is one of:
9070 * - An integer, specifying both the current and maximum limits.
9071 * - A 2-element array of integers, specifying the current and maximum limits.
9073 * ==== \File Descriptor Inheritance
9075 * By default, the new process inherits file descriptors from the parent process.
9077 * Use execution option <tt>:close_others => true</tt> to modify that inheritance
9078 * by closing non-standard fds (3 and greater) that are not otherwise redirected.
9080 * === Execution Shell
9082 * On a Unix-like system, the shell invoked is <tt>/bin/sh</tt>;
9083 * the entire string +command_line+ is passed as an argument
9084 * to {shell option -c}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/sh.html].
9086 * The shell performs normal shell expansion on the command line:
9090 * system('echo $SHELL: C*') # => true
9094 * /bin/bash: CONTRIBUTING.md COPYING COPYING.ja
9096 * ==== Execution Shell on Windows
9098 * On Windows, the shell invoked is determined by environment variable
9099 * +RUBYSHELL+, if defined, or +COMSPEC+ otherwise; the entire string
9100 * +command_line+ is passed as an argument to <tt>-c</tt> option for
9101 * +RUBYSHELL+, as well as <tt>/bin/sh</tt>, and {/c
9102 * option}[https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/cmd]
9103 * for +COMSPEC+. The shell is invoked automatically in the following
9106 * - The command is a built-in of +cmd.exe+, such as +echo+.
9107 * - The executable file is a batch file; its name ends with +.bat+ or
9110 * Note that the command will still be invoked as +command_line+ form
9111 * even when called in +exe_path+ form, because +cmd.exe+ does not
9112 * accept a script name like <tt>/bin/sh</tt> does but only works with
9113 * <tt>/c</tt> option.
9115 * The standard shell +cmd.exe+ performs environment variable
9116 * expansion but does not have globbing functionality:
9120 * system("echo %COMSPEC%: C*")' # => true
9124 * C:\WINDOWS\system32\cmd.exe: C*
9128 * === Current-Process Getters
9130 * - ::argv0: Returns the process name as a frozen string.
9131 * - ::egid: Returns the effective group ID.
9132 * - ::euid: Returns the effective user ID.
9133 * - ::getpgrp: Return the process group ID.
9134 * - ::getrlimit: Returns the resource limit.
9135 * - ::gid: Returns the (real) group ID.
9136 * - ::pid: Returns the process ID.
9137 * - ::ppid: Returns the process ID of the parent process.
9138 * - ::uid: Returns the (real) user ID.
9140 * === Current-Process Setters
9142 * - ::egid=: Sets the effective group ID.
9143 * - ::euid=: Sets the effective user ID.
9144 * - ::gid=: Sets the (real) group ID.
9145 * - ::setproctitle: Sets the process title.
9146 * - ::setpgrp: Sets the process group ID of the process to zero.
9147 * - ::setrlimit: Sets a resource limit.
9148 * - ::setsid: Establishes the process as a new session and process group leader,
9149 * with no controlling tty.
9150 * - ::uid=: Sets the user ID.
9152 * === Current-Process Execution
9154 * - ::abort: Immediately terminates the process.
9155 * - ::daemon: Detaches the process from its controlling terminal
9156 * and continues running it in the background as system daemon.
9157 * - ::exec: Replaces the process by running a given external command.
9158 * - ::exit: Initiates process termination by raising exception SystemExit
9159 * (which may be caught).
9160 * - ::exit!: Immediately exits the process.
9161 * - ::warmup: Notifies the Ruby virtual machine that the boot sequence
9162 * for the application is completed,
9163 * and that the VM may begin optimizing the application.
9165 * === Child Processes
9167 * - ::detach: Guards against a child process becoming a zombie.
9168 * - ::fork: Creates a child process.
9169 * - ::kill: Sends a given signal to processes.
9170 * - ::spawn: Creates a child process.
9171 * - ::wait, ::waitpid: Waits for a child process to exit; returns its process ID.
9172 * - ::wait2, ::waitpid2: Waits for a child process to exit; returns its process ID and status.
9173 * - ::waitall: Waits for all child processes to exit;
9174 * returns their process IDs and statuses.
9176 * === \Process Groups
9178 * - ::getpgid: Returns the process group ID for a process.
9179 * - ::getpriority: Returns the scheduling priority
9180 * for a process, process group, or user.
9181 * - ::getsid: Returns the session ID for a process.
9182 * - ::groups: Returns an array of the group IDs
9183 * in the supplemental group access list for this process.
9184 * - ::groups=: Sets the supplemental group access list
9185 * to the given array of group IDs.
9186 * - ::initgroups: Initializes the supplemental group access list.
9187 * - ::last_status: Returns the status of the last executed child process
9188 * in the current thread.
9189 * - ::maxgroups: Returns the maximum number of group IDs allowed
9190 * in the supplemental group access list.
9191 * - ::maxgroups=: Sets the maximum number of group IDs allowed
9192 * in the supplemental group access list.
9193 * - ::setpgid: Sets the process group ID of a process.
9194 * - ::setpriority: Sets the scheduling priority
9195 * for a process, process group, or user.
9199 * - ::clock_getres: Returns the resolution of a system clock.
9200 * - ::clock_gettime: Returns the time from a system clock.
9201 * - ::times: Returns a Process::Tms object containing times
9202 * for the current process and its child processes.
9207 InitVM_process(void)
9209 rb_define_virtual_variable("$?", get_CHILD_STATUS
, 0);
9210 rb_define_virtual_variable("$$", get_PROCESS_ID
, 0);
9212 rb_gvar_ractor_local("$$");
9213 rb_gvar_ractor_local("$?");
9215 rb_define_global_function("exec", f_exec
, -1);
9216 rb_define_global_function("fork", rb_f_fork
, 0);
9217 rb_define_global_function("exit!", rb_f_exit_bang
, -1);
9218 rb_define_global_function("system", rb_f_system
, -1);
9219 rb_define_global_function("spawn", rb_f_spawn
, -1);
9220 rb_define_global_function("sleep", rb_f_sleep
, -1);
9221 rb_define_global_function("exit", f_exit
, -1);
9222 rb_define_global_function("abort", f_abort
, -1);
9224 rb_mProcess
= rb_define_module("Process");
9227 /* see Process.wait */
9228 rb_define_const(rb_mProcess
, "WNOHANG", INT2FIX(WNOHANG
));
9230 /* see Process.wait */
9231 rb_define_const(rb_mProcess
, "WNOHANG", INT2FIX(0));
9234 /* see Process.wait */
9235 rb_define_const(rb_mProcess
, "WUNTRACED", INT2FIX(WUNTRACED
));
9237 /* see Process.wait */
9238 rb_define_const(rb_mProcess
, "WUNTRACED", INT2FIX(0));
9241 rb_define_singleton_method(rb_mProcess
, "exec", f_exec
, -1);
9242 rb_define_singleton_method(rb_mProcess
, "fork", rb_f_fork
, 0);
9243 rb_define_singleton_method(rb_mProcess
, "spawn", rb_f_spawn
, -1);
9244 rb_define_singleton_method(rb_mProcess
, "exit!", rb_f_exit_bang
, -1);
9245 rb_define_singleton_method(rb_mProcess
, "exit", f_exit
, -1);
9246 rb_define_singleton_method(rb_mProcess
, "abort", f_abort
, -1);
9247 rb_define_singleton_method(rb_mProcess
, "last_status", proc_s_last_status
, 0);
9248 rb_define_singleton_method(rb_mProcess
, "_fork", rb_proc__fork
, 0);
9250 rb_define_module_function(rb_mProcess
, "kill", proc_rb_f_kill
, -1);
9251 rb_define_module_function(rb_mProcess
, "wait", proc_m_wait
, -1);
9252 rb_define_module_function(rb_mProcess
, "wait2", proc_wait2
, -1);
9253 rb_define_module_function(rb_mProcess
, "waitpid", proc_m_wait
, -1);
9254 rb_define_module_function(rb_mProcess
, "waitpid2", proc_wait2
, -1);
9255 rb_define_module_function(rb_mProcess
, "waitall", proc_waitall
, 0);
9256 rb_define_module_function(rb_mProcess
, "detach", proc_detach
, 1);
9259 rb_cWaiter
= rb_define_class_under(rb_mProcess
, "Waiter", rb_cThread
);
9260 rb_undef_alloc_func(rb_cWaiter
);
9261 rb_undef_method(CLASS_OF(rb_cWaiter
), "new");
9262 rb_define_method(rb_cWaiter
, "pid", detach_process_pid
, 0);
9264 rb_cProcessStatus
= rb_define_class_under(rb_mProcess
, "Status", rb_cObject
);
9265 rb_define_alloc_func(rb_cProcessStatus
, rb_process_status_allocate
);
9266 rb_undef_method(CLASS_OF(rb_cProcessStatus
), "new");
9267 rb_marshal_define_compat(rb_cProcessStatus
, rb_cObject
,
9268 process_status_dump
, process_status_load
);
9270 rb_define_singleton_method(rb_cProcessStatus
, "wait", rb_process_status_waitv
, -1);
9272 rb_define_method(rb_cProcessStatus
, "==", pst_equal
, 1);
9273 rb_define_method(rb_cProcessStatus
, "&", pst_bitand
, 1);
9274 rb_define_method(rb_cProcessStatus
, ">>", pst_rshift
, 1);
9275 rb_define_method(rb_cProcessStatus
, "to_i", pst_to_i
, 0);
9276 rb_define_method(rb_cProcessStatus
, "to_s", pst_to_s
, 0);
9277 rb_define_method(rb_cProcessStatus
, "inspect", pst_inspect
, 0);
9279 rb_define_method(rb_cProcessStatus
, "pid", pst_pid_m
, 0);
9281 rb_define_method(rb_cProcessStatus
, "stopped?", pst_wifstopped
, 0);
9282 rb_define_method(rb_cProcessStatus
, "stopsig", pst_wstopsig
, 0);
9283 rb_define_method(rb_cProcessStatus
, "signaled?", pst_wifsignaled
, 0);
9284 rb_define_method(rb_cProcessStatus
, "termsig", pst_wtermsig
, 0);
9285 rb_define_method(rb_cProcessStatus
, "exited?", pst_wifexited
, 0);
9286 rb_define_method(rb_cProcessStatus
, "exitstatus", pst_wexitstatus
, 0);
9287 rb_define_method(rb_cProcessStatus
, "success?", pst_success_p
, 0);
9288 rb_define_method(rb_cProcessStatus
, "coredump?", pst_wcoredump
, 0);
9290 rb_define_module_function(rb_mProcess
, "pid", proc_get_pid
, 0);
9291 rb_define_module_function(rb_mProcess
, "ppid", proc_get_ppid
, 0);
9293 rb_define_module_function(rb_mProcess
, "getpgrp", proc_getpgrp
, 0);
9294 rb_define_module_function(rb_mProcess
, "setpgrp", proc_setpgrp
, 0);
9295 rb_define_module_function(rb_mProcess
, "getpgid", proc_getpgid
, 1);
9296 rb_define_module_function(rb_mProcess
, "setpgid", proc_setpgid
, 2);
9298 rb_define_module_function(rb_mProcess
, "getsid", proc_getsid
, -1);
9299 rb_define_module_function(rb_mProcess
, "setsid", proc_setsid
, 0);
9301 rb_define_module_function(rb_mProcess
, "getpriority", proc_getpriority
, 2);
9302 rb_define_module_function(rb_mProcess
, "setpriority", proc_setpriority
, 3);
9304 rb_define_module_function(rb_mProcess
, "warmup", proc_warmup
, 0);
9306 #ifdef HAVE_GETPRIORITY
9307 /* see Process.setpriority */
9308 rb_define_const(rb_mProcess
, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS
));
9309 /* see Process.setpriority */
9310 rb_define_const(rb_mProcess
, "PRIO_PGRP", INT2FIX(PRIO_PGRP
));
9311 /* see Process.setpriority */
9312 rb_define_const(rb_mProcess
, "PRIO_USER", INT2FIX(PRIO_USER
));
9315 rb_define_module_function(rb_mProcess
, "getrlimit", proc_getrlimit
, 1);
9316 rb_define_module_function(rb_mProcess
, "setrlimit", proc_setrlimit
, -1);
9317 #if defined(RLIM2NUM) && defined(RLIM_INFINITY)
9319 VALUE inf
= RLIM2NUM(RLIM_INFINITY
);
9320 #ifdef RLIM_SAVED_MAX
9322 VALUE v
= RLIM_INFINITY
== RLIM_SAVED_MAX
? inf
: RLIM2NUM(RLIM_SAVED_MAX
);
9323 /* see Process.setrlimit */
9324 rb_define_const(rb_mProcess
, "RLIM_SAVED_MAX", v
);
9327 /* see Process.setrlimit */
9328 rb_define_const(rb_mProcess
, "RLIM_INFINITY", inf
);
9329 #ifdef RLIM_SAVED_CUR
9331 VALUE v
= RLIM_INFINITY
== RLIM_SAVED_CUR
? inf
: RLIM2NUM(RLIM_SAVED_CUR
);
9332 /* see Process.setrlimit */
9333 rb_define_const(rb_mProcess
, "RLIM_SAVED_CUR", v
);
9338 /* Maximum size of the process's virtual memory (address space) in bytes.
9340 * see the system getrlimit(2) manual for details.
9342 rb_define_const(rb_mProcess
, "RLIMIT_AS", INT2FIX(RLIMIT_AS
));
9345 /* Maximum size of the core file.
9347 * see the system getrlimit(2) manual for details.
9349 rb_define_const(rb_mProcess
, "RLIMIT_CORE", INT2FIX(RLIMIT_CORE
));
9352 /* CPU time limit in seconds.
9354 * see the system getrlimit(2) manual for details.
9356 rb_define_const(rb_mProcess
, "RLIMIT_CPU", INT2FIX(RLIMIT_CPU
));
9359 /* Maximum size of the process's data segment.
9361 * see the system getrlimit(2) manual for details.
9363 rb_define_const(rb_mProcess
, "RLIMIT_DATA", INT2FIX(RLIMIT_DATA
));
9366 /* Maximum size of files that the process may create.
9368 * see the system getrlimit(2) manual for details.
9370 rb_define_const(rb_mProcess
, "RLIMIT_FSIZE", INT2FIX(RLIMIT_FSIZE
));
9372 #ifdef RLIMIT_MEMLOCK
9373 /* Maximum number of bytes of memory that may be locked into RAM.
9375 * see the system getrlimit(2) manual for details.
9377 rb_define_const(rb_mProcess
, "RLIMIT_MEMLOCK", INT2FIX(RLIMIT_MEMLOCK
));
9379 #ifdef RLIMIT_MSGQUEUE
9380 /* Specifies the limit on the number of bytes that can be allocated
9381 * for POSIX message queues for the real user ID of the calling process.
9383 * see the system getrlimit(2) manual for details.
9385 rb_define_const(rb_mProcess
, "RLIMIT_MSGQUEUE", INT2FIX(RLIMIT_MSGQUEUE
));
9388 /* Specifies a ceiling to which the process's nice value can be raised.
9390 * see the system getrlimit(2) manual for details.
9392 rb_define_const(rb_mProcess
, "RLIMIT_NICE", INT2FIX(RLIMIT_NICE
));
9394 #ifdef RLIMIT_NOFILE
9395 /* Specifies a value one greater than the maximum file descriptor
9396 * number that can be opened by this process.
9398 * see the system getrlimit(2) manual for details.
9400 rb_define_const(rb_mProcess
, "RLIMIT_NOFILE", INT2FIX(RLIMIT_NOFILE
));
9403 /* The maximum number of processes that can be created for the
9404 * real user ID of the calling process.
9406 * see the system getrlimit(2) manual for details.
9408 rb_define_const(rb_mProcess
, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC
));
9411 /* The maximum number of pseudo-terminals that can be created for the
9412 * real user ID of the calling process.
9414 * see the system getrlimit(2) manual for details.
9416 rb_define_const(rb_mProcess
, "RLIMIT_NPTS", INT2FIX(RLIMIT_NPTS
));
9419 /* Specifies the limit (in pages) of the process's resident set.
9421 * see the system getrlimit(2) manual for details.
9423 rb_define_const(rb_mProcess
, "RLIMIT_RSS", INT2FIX(RLIMIT_RSS
));
9425 #ifdef RLIMIT_RTPRIO
9426 /* Specifies a ceiling on the real-time priority that may be set for this process.
9428 * see the system getrlimit(2) manual for details.
9430 rb_define_const(rb_mProcess
, "RLIMIT_RTPRIO", INT2FIX(RLIMIT_RTPRIO
));
9432 #ifdef RLIMIT_RTTIME
9433 /* Specifies limit on CPU time this process scheduled under a real-time
9434 * scheduling policy can consume.
9436 * see the system getrlimit(2) manual for details.
9438 rb_define_const(rb_mProcess
, "RLIMIT_RTTIME", INT2FIX(RLIMIT_RTTIME
));
9440 #ifdef RLIMIT_SBSIZE
9441 /* Maximum size of the socket buffer.
9443 rb_define_const(rb_mProcess
, "RLIMIT_SBSIZE", INT2FIX(RLIMIT_SBSIZE
));
9445 #ifdef RLIMIT_SIGPENDING
9446 /* Specifies a limit on the number of signals that may be queued for
9447 * the real user ID of the calling process.
9449 * see the system getrlimit(2) manual for details.
9451 rb_define_const(rb_mProcess
, "RLIMIT_SIGPENDING", INT2FIX(RLIMIT_SIGPENDING
));
9454 /* Maximum size of the stack, in bytes.
9456 * see the system getrlimit(2) manual for details.
9458 rb_define_const(rb_mProcess
, "RLIMIT_STACK", INT2FIX(RLIMIT_STACK
));
9462 rb_define_module_function(rb_mProcess
, "uid", proc_getuid
, 0);
9463 rb_define_module_function(rb_mProcess
, "uid=", proc_setuid
, 1);
9464 rb_define_module_function(rb_mProcess
, "gid", proc_getgid
, 0);
9465 rb_define_module_function(rb_mProcess
, "gid=", proc_setgid
, 1);
9466 rb_define_module_function(rb_mProcess
, "euid", proc_geteuid
, 0);
9467 rb_define_module_function(rb_mProcess
, "euid=", proc_seteuid_m
, 1);
9468 rb_define_module_function(rb_mProcess
, "egid", proc_getegid
, 0);
9469 rb_define_module_function(rb_mProcess
, "egid=", proc_setegid_m
, 1);
9470 rb_define_module_function(rb_mProcess
, "initgroups", proc_initgroups
, 2);
9471 rb_define_module_function(rb_mProcess
, "groups", proc_getgroups
, 0);
9472 rb_define_module_function(rb_mProcess
, "groups=", proc_setgroups
, 1);
9473 rb_define_module_function(rb_mProcess
, "maxgroups", proc_getmaxgroups
, 0);
9474 rb_define_module_function(rb_mProcess
, "maxgroups=", proc_setmaxgroups
, 1);
9476 rb_define_module_function(rb_mProcess
, "daemon", proc_daemon
, -1);
9478 rb_define_module_function(rb_mProcess
, "times", rb_proc_times
, 0);
9480 #if defined(RUBY_CLOCK_REALTIME)
9481 #elif defined(RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
9482 # define RUBY_CLOCK_REALTIME RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
9483 #elif defined(RUBY_TIME_BASED_CLOCK_REALTIME)
9484 # define RUBY_CLOCK_REALTIME RUBY_TIME_BASED_CLOCK_REALTIME
9486 #if defined(CLOCK_REALTIME) && defined(CLOCKID2NUM)
9487 /* see Process.clock_gettime */
9488 rb_define_const(rb_mProcess
, "CLOCK_REALTIME", CLOCKID2NUM(CLOCK_REALTIME
));
9489 #elif defined(RUBY_CLOCK_REALTIME)
9490 rb_define_const(rb_mProcess
, "CLOCK_REALTIME", RUBY_CLOCK_REALTIME
);
9493 #if defined(RUBY_CLOCK_MONOTONIC)
9494 #elif defined(RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC)
9495 # define RUBY_CLOCK_MONOTONIC RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
9497 #if defined(CLOCK_MONOTONIC) && defined(CLOCKID2NUM)
9498 /* see Process.clock_gettime */
9499 rb_define_const(rb_mProcess
, "CLOCK_MONOTONIC", CLOCKID2NUM(CLOCK_MONOTONIC
));
9500 #elif defined(RUBY_CLOCK_MONOTONIC)
9501 rb_define_const(rb_mProcess
, "CLOCK_MONOTONIC", RUBY_CLOCK_MONOTONIC
);
9504 #if defined(RUBY_CLOCK_PROCESS_CPUTIME_ID)
9505 #elif defined(RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
9506 # define RUBY_CLOCK_PROCESS_CPUTIME_ID RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
9508 #if defined(CLOCK_PROCESS_CPUTIME_ID) && defined(CLOCKID2NUM)
9509 /* see Process.clock_gettime */
9510 rb_define_const(rb_mProcess
, "CLOCK_PROCESS_CPUTIME_ID", CLOCKID2NUM(CLOCK_PROCESS_CPUTIME_ID
));
9511 #elif defined(RUBY_CLOCK_PROCESS_CPUTIME_ID)
9512 rb_define_const(rb_mProcess
, "CLOCK_PROCESS_CPUTIME_ID", RUBY_CLOCK_PROCESS_CPUTIME_ID
);
9515 #if defined(CLOCK_THREAD_CPUTIME_ID) && defined(CLOCKID2NUM)
9516 /* see Process.clock_gettime */
9517 rb_define_const(rb_mProcess
, "CLOCK_THREAD_CPUTIME_ID", CLOCKID2NUM(CLOCK_THREAD_CPUTIME_ID
));
9518 #elif defined(RUBY_CLOCK_THREAD_CPUTIME_ID)
9519 rb_define_const(rb_mProcess
, "CLOCK_THREAD_CPUTIME_ID", RUBY_CLOCK_THREAD_CPUTIME_ID
);
9523 #ifdef CLOCK_VIRTUAL
9524 /* see Process.clock_gettime */
9525 rb_define_const(rb_mProcess
, "CLOCK_VIRTUAL", CLOCKID2NUM(CLOCK_VIRTUAL
));
9528 /* see Process.clock_gettime */
9529 rb_define_const(rb_mProcess
, "CLOCK_PROF", CLOCKID2NUM(CLOCK_PROF
));
9531 #ifdef CLOCK_REALTIME_FAST
9532 /* see Process.clock_gettime */
9533 rb_define_const(rb_mProcess
, "CLOCK_REALTIME_FAST", CLOCKID2NUM(CLOCK_REALTIME_FAST
));
9535 #ifdef CLOCK_REALTIME_PRECISE
9536 /* see Process.clock_gettime */
9537 rb_define_const(rb_mProcess
, "CLOCK_REALTIME_PRECISE", CLOCKID2NUM(CLOCK_REALTIME_PRECISE
));
9539 #ifdef CLOCK_REALTIME_COARSE
9540 /* see Process.clock_gettime */
9541 rb_define_const(rb_mProcess
, "CLOCK_REALTIME_COARSE", CLOCKID2NUM(CLOCK_REALTIME_COARSE
));
9543 #ifdef CLOCK_REALTIME_ALARM
9544 /* see Process.clock_gettime */
9545 rb_define_const(rb_mProcess
, "CLOCK_REALTIME_ALARM", CLOCKID2NUM(CLOCK_REALTIME_ALARM
));
9547 #ifdef CLOCK_MONOTONIC_FAST
9548 /* see Process.clock_gettime */
9549 rb_define_const(rb_mProcess
, "CLOCK_MONOTONIC_FAST", CLOCKID2NUM(CLOCK_MONOTONIC_FAST
));
9551 #ifdef CLOCK_MONOTONIC_PRECISE
9552 /* see Process.clock_gettime */
9553 rb_define_const(rb_mProcess
, "CLOCK_MONOTONIC_PRECISE", CLOCKID2NUM(CLOCK_MONOTONIC_PRECISE
));
9555 #ifdef CLOCK_MONOTONIC_RAW
9556 /* see Process.clock_gettime */
9557 rb_define_const(rb_mProcess
, "CLOCK_MONOTONIC_RAW", CLOCKID2NUM(CLOCK_MONOTONIC_RAW
));
9559 #ifdef CLOCK_MONOTONIC_RAW_APPROX
9560 /* see Process.clock_gettime */
9561 rb_define_const(rb_mProcess
, "CLOCK_MONOTONIC_RAW_APPROX", CLOCKID2NUM(CLOCK_MONOTONIC_RAW_APPROX
));
9563 #ifdef CLOCK_MONOTONIC_COARSE
9564 /* see Process.clock_gettime */
9565 rb_define_const(rb_mProcess
, "CLOCK_MONOTONIC_COARSE", CLOCKID2NUM(CLOCK_MONOTONIC_COARSE
));
9567 #ifdef CLOCK_BOOTTIME
9568 /* see Process.clock_gettime */
9569 rb_define_const(rb_mProcess
, "CLOCK_BOOTTIME", CLOCKID2NUM(CLOCK_BOOTTIME
));
9571 #ifdef CLOCK_BOOTTIME_ALARM
9572 /* see Process.clock_gettime */
9573 rb_define_const(rb_mProcess
, "CLOCK_BOOTTIME_ALARM", CLOCKID2NUM(CLOCK_BOOTTIME_ALARM
));
9576 /* see Process.clock_gettime */
9577 rb_define_const(rb_mProcess
, "CLOCK_UPTIME", CLOCKID2NUM(CLOCK_UPTIME
));
9579 #ifdef CLOCK_UPTIME_FAST
9580 /* see Process.clock_gettime */
9581 rb_define_const(rb_mProcess
, "CLOCK_UPTIME_FAST", CLOCKID2NUM(CLOCK_UPTIME_FAST
));
9583 #ifdef CLOCK_UPTIME_PRECISE
9584 /* see Process.clock_gettime */
9585 rb_define_const(rb_mProcess
, "CLOCK_UPTIME_PRECISE", CLOCKID2NUM(CLOCK_UPTIME_PRECISE
));
9587 #ifdef CLOCK_UPTIME_RAW
9588 /* see Process.clock_gettime */
9589 rb_define_const(rb_mProcess
, "CLOCK_UPTIME_RAW", CLOCKID2NUM(CLOCK_UPTIME_RAW
));
9591 #ifdef CLOCK_UPTIME_RAW_APPROX
9592 /* see Process.clock_gettime */
9593 rb_define_const(rb_mProcess
, "CLOCK_UPTIME_RAW_APPROX", CLOCKID2NUM(CLOCK_UPTIME_RAW_APPROX
));
9596 /* see Process.clock_gettime */
9597 rb_define_const(rb_mProcess
, "CLOCK_SECOND", CLOCKID2NUM(CLOCK_SECOND
));
9600 /* see Process.clock_gettime */
9601 rb_define_const(rb_mProcess
, "CLOCK_TAI", CLOCKID2NUM(CLOCK_TAI
));
9604 rb_define_module_function(rb_mProcess
, "clock_gettime", rb_clock_gettime
, -1);
9605 rb_define_module_function(rb_mProcess
, "clock_getres", rb_clock_getres
, -1);
9607 #if defined(HAVE_TIMES) || defined(_WIN32)
9608 rb_cProcessTms
= rb_struct_define_under(rb_mProcess
, "Tms", "utime", "stime", "cutime", "cstime", NULL
);
9609 #if 0 /* for RDoc */
9610 /* user time used in this process */
9611 rb_define_attr(rb_cProcessTms
, "utime", TRUE
, TRUE
);
9612 /* system time used in this process */
9613 rb_define_attr(rb_cProcessTms
, "stime", TRUE
, TRUE
);
9614 /* user time used in the child processes */
9615 rb_define_attr(rb_cProcessTms
, "cutime", TRUE
, TRUE
);
9616 /* system time used in the child processes */
9617 rb_define_attr(rb_cProcessTms
, "cstime", TRUE
, TRUE
);
9621 SAVED_USER_ID
= geteuid();
9622 SAVED_GROUP_ID
= getegid();
9624 rb_mProcUID
= rb_define_module_under(rb_mProcess
, "UID");
9625 rb_mProcGID
= rb_define_module_under(rb_mProcess
, "GID");
9627 rb_define_module_function(rb_mProcUID
, "rid", proc_getuid
, 0);
9628 rb_define_module_function(rb_mProcGID
, "rid", proc_getgid
, 0);
9629 rb_define_module_function(rb_mProcUID
, "eid", proc_geteuid
, 0);
9630 rb_define_module_function(rb_mProcGID
, "eid", proc_getegid
, 0);
9631 rb_define_module_function(rb_mProcUID
, "change_privilege", p_uid_change_privilege
, 1);
9632 rb_define_module_function(rb_mProcGID
, "change_privilege", p_gid_change_privilege
, 1);
9633 rb_define_module_function(rb_mProcUID
, "grant_privilege", p_uid_grant_privilege
, 1);
9634 rb_define_module_function(rb_mProcGID
, "grant_privilege", p_gid_grant_privilege
, 1);
9635 rb_define_alias(rb_singleton_class(rb_mProcUID
), "eid=", "grant_privilege");
9636 rb_define_alias(rb_singleton_class(rb_mProcGID
), "eid=", "grant_privilege");
9637 rb_define_module_function(rb_mProcUID
, "re_exchange", p_uid_exchange
, 0);
9638 rb_define_module_function(rb_mProcGID
, "re_exchange", p_gid_exchange
, 0);
9639 rb_define_module_function(rb_mProcUID
, "re_exchangeable?", p_uid_exchangeable
, 0);
9640 rb_define_module_function(rb_mProcGID
, "re_exchangeable?", p_gid_exchangeable
, 0);
9641 rb_define_module_function(rb_mProcUID
, "sid_available?", p_uid_have_saved_id
, 0);
9642 rb_define_module_function(rb_mProcGID
, "sid_available?", p_gid_have_saved_id
, 0);
9643 rb_define_module_function(rb_mProcUID
, "switch", p_uid_switch
, 0);
9644 rb_define_module_function(rb_mProcGID
, "switch", p_gid_switch
, 0);
9645 #ifdef p_uid_from_name
9646 rb_define_module_function(rb_mProcUID
, "from_name", p_uid_from_name
, 1);
9648 #ifdef p_gid_from_name
9649 rb_define_module_function(rb_mProcGID
, "from_name", p_gid_from_name
, 1);
9652 rb_mProcID_Syscall
= rb_define_module_under(rb_mProcess
, "Sys");
9654 rb_define_module_function(rb_mProcID_Syscall
, "getuid", proc_getuid
, 0);
9655 rb_define_module_function(rb_mProcID_Syscall
, "geteuid", proc_geteuid
, 0);
9656 rb_define_module_function(rb_mProcID_Syscall
, "getgid", proc_getgid
, 0);
9657 rb_define_module_function(rb_mProcID_Syscall
, "getegid", proc_getegid
, 0);
9659 rb_define_module_function(rb_mProcID_Syscall
, "setuid", p_sys_setuid
, 1);
9660 rb_define_module_function(rb_mProcID_Syscall
, "setgid", p_sys_setgid
, 1);
9662 rb_define_module_function(rb_mProcID_Syscall
, "setruid", p_sys_setruid
, 1);
9663 rb_define_module_function(rb_mProcID_Syscall
, "setrgid", p_sys_setrgid
, 1);
9665 rb_define_module_function(rb_mProcID_Syscall
, "seteuid", p_sys_seteuid
, 1);
9666 rb_define_module_function(rb_mProcID_Syscall
, "setegid", p_sys_setegid
, 1);
9668 rb_define_module_function(rb_mProcID_Syscall
, "setreuid", p_sys_setreuid
, 2);
9669 rb_define_module_function(rb_mProcID_Syscall
, "setregid", p_sys_setregid
, 2);
9671 rb_define_module_function(rb_mProcID_Syscall
, "setresuid", p_sys_setresuid
, 3);
9672 rb_define_module_function(rb_mProcID_Syscall
, "setresgid", p_sys_setresgid
, 3);
9673 rb_define_module_function(rb_mProcID_Syscall
, "issetugid", p_sys_issetugid
, 0);
9679 #define define_id(name) id_##name = rb_intern_const(#name)
9692 define_id(new_pgroup
);
9694 define_id(unsetenv_others
);
9697 define_id(close_others
);
9698 define_id(nanosecond
);
9699 define_id(microsecond
);
9700 define_id(millisecond
);
9702 define_id(float_microsecond
);
9703 define_id(float_millisecond
);
9704 define_id(float_second
);
9705 define_id(GETTIMEOFDAY_BASED_CLOCK_REALTIME
);
9706 define_id(TIME_BASED_CLOCK_REALTIME
);
9707 #ifdef CLOCK_REALTIME
9708 define_id(CLOCK_REALTIME
);
9710 #ifdef CLOCK_MONOTONIC
9711 define_id(CLOCK_MONOTONIC
);
9713 #ifdef CLOCK_PROCESS_CPUTIME_ID
9714 define_id(CLOCK_PROCESS_CPUTIME_ID
);
9716 #ifdef CLOCK_THREAD_CPUTIME_ID
9717 define_id(CLOCK_THREAD_CPUTIME_ID
);
9720 define_id(TIMES_BASED_CLOCK_MONOTONIC
);
9721 define_id(TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
);
9724 define_id(GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
);
9726 define_id(CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
);
9728 define_id(MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
);