40 #define EXIT_SUCCESS 0 43 #define EXIT_FAILURE 1 46 #ifdef HAVE_SYS_WAIT_H 47 # include <sys/wait.h> 49 #ifdef HAVE_SYS_RESOURCE_H 50 # include <sys/resource.h> 52 #ifdef HAVE_SYS_PARAM_H 53 # include <sys/param.h> 56 # define MAXPATHLEN 1024 66 #ifdef HAVE_SYS_TIMES_H 67 #include <sys/times.h> 74 #if defined(HAVE_TIMES) || defined(_WIN32) 75 static VALUE rb_cProcessTms;
79 #define WIFEXITED(w) (((w) & 0xff) == 0) 82 #define WIFSIGNALED(w) (((w) & 0x7f) > 0 && (((w) & 0x7f) < 0x7f)) 85 #define WIFSTOPPED(w) (((w) & 0xff) == 0x7f) 88 #define WEXITSTATUS(w) (((w) >> 8) & 0xff) 91 #define WTERMSIG(w) ((w) & 0x7f) 94 #define WSTOPSIG WEXITSTATUS 97 #if defined(__APPLE__) && ( defined(__MACH__) || defined(__DARWIN__) ) && !defined(__MacOS_X__) 101 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) 102 #define HAVE_44BSD_SETUID 1 103 #define HAVE_44BSD_SETGID 1 111 #ifdef BROKEN_SETREUID 112 #define setreuid ruby_setreuid 113 int setreuid(rb_uid_t ruid, rb_uid_t euid);
115 #ifdef BROKEN_SETREGID 116 #define setregid ruby_setregid 117 int setregid(rb_gid_t rgid, rb_gid_t egid);
120 #if defined(HAVE_44BSD_SETUID) || defined(__MacOS_X__) 121 #if !defined(USE_SETREUID) && !defined(BROKEN_SETREUID) 122 #define OBSOLETE_SETREUID 1 124 #if !defined(USE_SETREGID) && !defined(BROKEN_SETREGID) 125 #define OBSOLETE_SETREGID 1 129 #define preserving_errno(stmts) \ 130 do {int saved_errno = errno; stmts; errno = saved_errno;} while (0) 247 #define PST2INT(st) NUM2INT(pst_to_i(st)) 274 rb_str_catf(str,
" stopped SIG%s (signal %d)", signame, stopsig);
284 rb_str_catf(str,
" SIG%s (signal %d)", signame, termsig);
294 if (WCOREDUMP(status)) {
371 if (st1 == st2)
return Qtrue;
582 if (WCOREDUMP(status))
591 #if !defined(HAVE_WAITPID) && !defined(HAVE_WAIT4) 630 struct waitpid_arg *arg = data;
633 #if defined NO_WAITPID 635 #elif defined HAVE_WAITPID 638 result = wait4(arg->pid, arg->st, arg->flags,
NULL);
649 struct waitpid_arg arg;
658 if (
errno == EINTR) {
667 if (pid == (rb_pid_t)-1) {
669 data.
pid = (rb_pid_t)-1;
672 if (data.status != -1) {
691 if (
errno == EINTR) {
873 if (
errno == EINTR) {
905 rb_pid_t cpid,
pid = (rb_pid_t)(
VALUE)arg;
978 #ifndef HAVE_STRING_H 985 static RETSIGTYPE (*saved_sigpipe_handler)(int) = 0;
988 #if defined(POSIX_SIGNAL) 989 # define signal(a,b) posix_signal((a),(b)) 993 static RETSIGTYPE sig_do_nothing(
int sig)
1013 saved_sigpipe_handler =
signal(SIGPIPE, sig_do_nothing);
1032 signal(SIGPIPE, saved_sigpipe_handler);
1039 #define before_fork() before_exec() 1040 #define after_fork() (GET_THREAD()->thrown_errinfo = 0, after_exec()) 1055 #define try_with_sh(prog, argv) ((saved_errno == ENOEXEC) ? exec_with_sh((prog), (argv)) : (void)0) 1057 exec_with_sh(
const char *prog,
char **
argv)
1059 *
argv = (
char *)prog;
1060 *--
argv = (
char *)
"sh";
1063 #define ARGV_COUNT(n) ((n)+1) 1065 #define try_with_sh(prog, argv) (void)0 1066 #define ARGV_COUNT(n) (n) 1068 #define ARGV_SIZE(n) (sizeof(char*) * ARGV_COUNT(n)) 1069 #define ALLOC_ARGV(n, v) ALLOCV_N(char*, (v), ARGV_COUNT(n)) 1070 #define ALLOC_ARGV_WITH_STR(n, v, s, l) \ 1071 (char **)(((s) = ALLOCV_N(char, (v), ARGV_SIZE(n) + (l)) + ARGV_SIZE(n)) - ARGV_SIZE(n)) 1077 #if defined(__EMX__) || defined(OS2) 1078 char **new_argv =
NULL;
1089 #if defined(__EMX__) || defined(OS2) 1091 #define COMMAND "cmd.exe" 1098 for (n = 0;
argv[n]; n++)
1100 new_argv =
ALLOC_N(
char*, n + 2);
1102 new_argv[n + 1] =
argv[n];
1104 for (
p = new_argv[1]; *
p !=
'\0';
p++)
1107 new_argv[0] = COMMAND;
1120 #if defined(__EMX__) || defined(OS2) 1153 const char *s = str;
1169 for (s=str; *s; s++) {
1171 const char *
p, *nl =
NULL;
1173 if (*
p ==
'\n') nl =
p;
1178 if (*s !=
' ' && !
ISALPHA(*s) &&
strchr(
"*?{}[]<>()~&|\\$;'`\"\n",*s)) {
1179 #if defined(__CYGWIN32__) || defined(__EMX__) 1185 execl(shell,
"sh",
"-c", str, (
char *)
NULL);
1193 execl(
"/bin/sh",
"sh",
"-c", str, (
char *)
NULL);
1200 memcpy(ss, str, s-str);
1202 if ((*a++ =
strtok(ss,
" \t")) != 0) {
1235 #define HAVE_SPAWNV 1 1238 #if !defined(HAVE_FORK) && defined(HAVE_SPAWNV) 1239 # define USE_SPAWNV 1 1241 # define USE_SPAWNV 0 1244 # define P_NOWAIT _P_NOWAIT 1249 #define proc_spawn_v(argv, prog) rb_w32_aspawn(P_NOWAIT, (prog), (argv)) 1252 proc_spawn_v(
char **
argv,
char *prog)
1267 *
argv = (
char *)prog;
1268 *--
argv = (
char *)
"sh";
1290 args[
i] = (
char*) 0;
1295 flags = CREATE_NEW_PROCESS_GROUP;
1307 #define proc_spawn(str) rb_w32_spawn(P_NOWAIT, (str), 0) 1310 proc_spawn(
char *str)
1318 for (s = str; *s; s++) {
1319 if (*s !=
' ' && !
ISALPHA(*s) &&
strchr(
"*?{}[]<>()~&|\\$;'`\"\n",*s)) {
1322 status = spawnl(
P_NOWAIT, (shell ? shell :
"/bin/sh"),
"sh",
"-c", str, (
char*)
NULL);
1330 if (*a++ =
strtok(s,
" \t")) {
1383 else if (fd >= 3 && iskey) {
1395 VALUE path, flags, perm;
1398 switch (
TYPE(val)) {
1462 flags =
INT2NUM(O_WRONLY|O_CREAT|O_TRUNC);
1494 #if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM) 1495 static int rlimit_type_by_lname(
const char *
name);
1503 #if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM) 1519 else if (val ==
Qtrue)
1542 #if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM) 1543 if (strncmp(
"rlimit_",
rb_id2name(
id), 7) == 0 &&
1544 (rtype = rlimit_type_by_lname(
rb_id2name(
id)+7)) != -1) {
1546 VALUE tmp, softlim, hardlim;
1571 if (
id ==
rb_intern(
"unsetenv_others")) {
1593 else if (
id ==
rb_intern(
"close_others")) {
1646 int index, maxhint = -1;
1693 if (oldfd != lastfd) {
1756 const char *
name = 0;
1792 *opthash_ret =
hash;
1807 prog = (*argv_p)[0];
1808 if (accept_shell && *argc_p == 1) {
1824 if (!
NIL_P(opthash)) {
1901 #define CHILD_ERRMSG_BUFLEN 80 1916 #define ERRMSG(str) do { if (errmsg && 0 < errmsg_buflen) strlcpy(errmsg, (str), errmsg_buflen); } while (0) 1919 #if defined(DEBUG_REDIRECT) 1924 ttyprintf(
const char *fmt, ...)
1930 tty = fopen(
"con",
"w");
1932 tty = fopen(
"/dev/tty",
"w");
1938 vfprintf(tty, fmt, ap);
1949 ttyprintf(
"dup(%d) => %d\n", oldfd, ret);
1953 #define redirect_dup(oldfd) dup(oldfd) 1956 #if defined(DEBUG_REDIRECT) || defined(_WIN32) 1961 ret =
dup2(oldfd, newfd);
1962 if (newfd >= 0 && newfd <= 2)
1963 SetStdHandle(newfd == 0 ? STD_INPUT_HANDLE : newfd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE, (HANDLE)
rb_w32_get_osfhandle(newfd));
1964 #if defined(DEBUG_REDIRECT) 1965 ttyprintf(
"dup2(%d, %d)\n", oldfd, newfd);
1970 #define redirect_dup2(oldfd, newfd) dup2((oldfd), (newfd)) 1973 #if defined(DEBUG_REDIRECT) 1979 ttyprintf(
"close(%d)\n", fd);
1987 ret = open(pathname, flags, perm);
1988 ttyprintf(
"open(\"%s\", 0x%x, 0%o) => %d\n", pathname, flags, perm, ret);
1993 #define redirect_close(fd) close(fd) 1994 #define redirect_open(pathname, flags, perm) open((pathname), (flags), (perm)) 2003 if (save_fd == -1) {
2011 if (
NIL_P(newary)) {
2019 if (
NIL_P(newary)) {
2054 return *(
int*)a - *(
int*)b;
2060 return *(
int*)b - *(
int*)a;
2077 pairs = (
struct fd_pair *)
malloc(
sizeof(
struct fd_pair) * n);
2078 if (pairs ==
NULL) {
2084 for (
i = 0;
i < n;
i++) {
2088 pairs[
i].older_index = -1;
2093 qsort(pairs, n,
sizeof(
struct fd_pair),
intcmp);
2095 qsort(pairs, n,
sizeof(
struct fd_pair),
intrcmp);
2098 for (
i = 0;
i < n;
i++) {
2099 int newfd = pairs[
i].newfd;
2100 struct fd_pair
key, *found;
2102 found = bsearch(&
key, pairs, n,
sizeof(
struct fd_pair),
intcmp);
2103 pairs[
i].num_newer = 0;
2105 while (pairs < found && (found-1)->oldfd == newfd)
2107 while (found < pairs+n && found->oldfd == newfd) {
2108 pairs[
i].num_newer++;
2109 found->older_index =
i;
2116 for (
i = 0;
i < n;
i++) {
2118 while (j != -1 && pairs[j].oldfd != -1 && pairs[j].num_newer == 0) {
2127 pairs[j].oldfd = -1;
2128 j = pairs[j].older_index;
2130 pairs[j].num_newer--;
2135 for (
i = 0;
i < n;
i++) {
2137 if (pairs[
i].oldfd == -1)
2139 if (pairs[
i].oldfd == pairs[
i].newfd) {
2141 int fd = pairs[
i].oldfd;
2142 ret =
fcntl(fd, F_GETFD);
2144 ERRMSG(
"fcntl(F_GETFD)");
2147 if (ret & FD_CLOEXEC) {
2149 ret =
fcntl(fd, F_SETFD, ret);
2151 ERRMSG(
"fcntl(F_SETFD)");
2156 pairs[
i].oldfd = -1;
2159 if (extra_fd == -1) {
2161 if (extra_fd == -1) {
2175 pairs[
i].oldfd = extra_fd;
2176 j = pairs[
i].older_index;
2177 pairs[
i].older_index = -1;
2185 pairs[j].oldfd = -1;
2186 j = pairs[j].older_index;
2189 if (extra_fd != -1) {
2297 run_exec_pgroup(
VALUE obj,
VALUE save,
char *errmsg,
size_t errmsg_buflen)
2315 ret = setpgid(getpid(), pgroup);
2316 if (ret == -1)
ERRMSG(
"setpgid");
2321 #if defined(HAVE_SETRLIMIT) && defined(RLIM2NUM) 2323 run_exec_rlimit(
VALUE ary,
VALUE save,
char *errmsg,
size_t errmsg_buflen)
2337 RLIM2NUM(rlim.rlim_cur),
2338 RLIM2NUM(rlim.rlim_max)));
2340 if (
NIL_P(newary)) {
2346 rlim.rlim_cur = NUM2RLIM(
RARRAY_PTR(elt)[1]);
2347 rlim.rlim_max = NUM2RLIM(
RARRAY_PTR(elt)[2]);
2378 if (run_exec_pgroup(obj, soptions, errmsg, errmsg_buflen) == -1)
2383 #if defined(HAVE_SETRLIMIT) && defined(RLIM2NUM) 2386 if (run_exec_rlimit(obj, soptions, errmsg, errmsg_buflen) == -1)
2414 if (!
NIL_P(soptions)) {
2429 mode_t oldmask = umask(mask);
2430 if (!
NIL_P(soptions))
2436 if (
run_exec_dup2(obj, soptions, errmsg, errmsg_buflen) == -1)
2442 if (!
NIL_P(soptions))
2443 rb_warn(
"cannot close fd before spawn");
2459 if (
run_exec_open(obj, soptions, errmsg, errmsg_buflen) == -1)
2483 const char *prog = e->
prog;
2501 #if !defined FD_CLOEXEC && !defined HAVE_SPAWNV 2502 char errmsg[80] = {
'\0' };
2506 fprintf(stderr,
"%s\n", errmsg);
2509 fprintf(stderr,
"%s:%d: command not found: %s\n",
2521 rb_exec_atfork(
void* arg,
char *errmsg,
size_t errmsg_buflen)
2530 #if SIZEOF_INT == SIZEOF_LONG 2531 #define proc_syswait (VALUE (*)(VALUE))rb_syswait 2534 proc_syswait(
VALUE pid)
2543 move_fds_to_avoid_crash(
int *fdp,
int n,
VALUE fds)
2547 for (
i = 0;
i < n;
i++) {
2566 pipe_nocrash(
int filedes[2],
VALUE fds)
2574 if (move_fds_to_avoid_crash(filedes, 2, fds) == -1) {
2584 struct chfunc_protect_t {
2585 int (*
chfunc)(
void*,
char *, size_t);
2592 chfunc_protect(
VALUE arg)
2594 struct chfunc_protect_t *
p = (
struct chfunc_protect_t *)arg;
2596 return (
VALUE)(*
p->chfunc)(
p->arg,
p->errmsg,
p->buflen);
2628 char *errmsg,
size_t errmsg_buflen)
2637 #define prefork() ( \ 2638 rb_io_flush(rb_stdout), \ 2639 rb_io_flush(rb_stderr) \ 2645 if (pipe_nocrash(ep, fds))
return -1;
2646 if (
fcntl(ep[1], F_SETFD, FD_CLOEXEC)) {
2652 for (;
before_fork(), (pid = fork()) < 0; prefork()) {
2656 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN 2659 if (!status && !
chfunc) {
2665 if (status) *status =
state;
2666 if (!
state)
continue;
2681 struct chfunc_protect_t arg;
2684 arg.errmsg = errmsg;
2685 arg.buflen = errmsg_buflen;
2699 if (errmsg && 0 < errmsg_buflen) {
2700 errmsg[errmsg_buflen-1] =
'\0';
2701 errmsg_buflen =
strlen(errmsg);
2702 if (errmsg_buflen > 0 &&write(ep[1], errmsg, errmsg_buflen) < 0)
2707 #if EXIT_SUCCESS == 127 2725 #define READ_FROM_CHILD(ptr, len) \ 2726 (NIL_P(io) ? read(ep[0], (ptr), (len)) : rb_io_bufread(io, (ptr), (len))) 2727 if ((
size = READ_FROM_CHILD(&
err,
sizeof(
err))) < 0) {
2731 errmsg && 0 < errmsg_buflen) {
2732 ssize_t ret = READ_FROM_CHILD(errmsg, errmsg_buflen-1);
2758 struct chfunc_wrapper_t {
2764 chfunc_wrapper(
void *arg_,
char *errmsg,
size_t errmsg_buflen)
2766 struct chfunc_wrapper_t *arg = arg_;
2767 return arg->chfunc(arg->arg);
2774 struct chfunc_wrapper_t warg;
2787 #if defined(HAVE_FORK) && !defined(CANNOT_FORK_WITH_PTHREAD) 2838 #define rb_f_fork rb_f_notimplement 2953 #if EXIT_SUCCESS != 0 3004 static int overriding;
3006 RETSIGTYPE (*hfunc)(int) = 0;
3009 RETSIGTYPE (*qfunc)(int) = 0;
3011 RETSIGTYPE (*ifunc)(int) = 0;
3017 hfunc =
signal(SIGHUP, SIG_IGN);
3020 qfunc =
signal(SIGQUIT, SIG_IGN);
3029 }
while (
i == -1 &&
errno == EINTR);
3062 #if !defined HAVE_FORK || USE_SPAWNV 3068 #if defined HAVE_FORK && !USE_SPAWNV 3078 # if defined HAVE_SPAWNV 3085 # if defined(_WIN32) 3102 char *errmsg,
size_t errmsg_buflen)
3160 #if defined(SIGCLD) && !defined(SIGCHLD) 3161 # define SIGCHLD SIGCLD 3165 RETSIGTYPE (*
chfunc)(int);
3170 #if defined(HAVE_FORK) || defined(HAVE_SPAWNV) 3442 const char *
prog = errmsg;
3448 #if defined(HAVE_FORK) || defined(HAVE_SPAWNV) 3481 else if (
argc == 1) {
3488 end = time(0) - beg;
3494 #if (defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)) || defined(HAVE_GETPGID) 3512 #if defined(HAVE_GETPGRP) && defined(GETPGRP_VOID) 3523 #define proc_getpgrp rb_f_notimplement 3527 #if defined(HAVE_SETPGID) || (defined(HAVE_SETPGRP) && defined(SETPGRP_VOID)) 3546 #elif defined(HAVE_SETPGRP) && defined(SETPGRP_VOID) 3552 #define proc_setpgrp rb_f_notimplement 3556 #if defined(HAVE_GETPGID) 3578 #define proc_getpgid rb_f_notimplement 3594 rb_pid_t ipid, ipgrp;
3604 #define proc_setpgid rb_f_notimplement 3608 #if defined(HAVE_SETSID) || (defined(HAVE_SETPGRP) && defined(TIOCNOTTY)) 3609 #if !defined(HAVE_SETSID) 3610 static rb_pid_t ruby_setsid(
void);
3611 #define setsid() ruby_setsid() 3635 #if !defined(HAVE_SETSID) 3636 #define HAVE_SETSID 1 3644 #if defined(SETPGRP_VOID) 3650 ret = setpgrp(0, pid);
3652 if (ret == -1)
return -1;
3654 if ((fd = open(
"/dev/tty", O_RDWR)) >= 0) {
3663 #define proc_setsid rb_f_notimplement 3667 #ifdef HAVE_GETPRIORITY 3688 int prio, iwhich, iwho;
3695 prio = getpriority(iwhich, iwho);
3700 #define proc_getpriority rb_f_notimplement 3704 #ifdef HAVE_GETPRIORITY 3720 int iwhich, iwho, iprio;
3727 if (setpriority(iwhich, iwho, iprio) < 0)
3732 #define proc_setpriority rb_f_notimplement 3735 #if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM) 3737 rlimit_resource_name2int(
const char *
name,
int casetype)
3741 #define RESCHECK(r) \ 3743 if (STRCASECMP(name, #r) == 0) { \ 3744 resource = RLIMIT_##r; \ 3778 #ifdef RLIMIT_MEMLOCK 3781 #ifdef RLIMIT_MSGQUEUE 3787 #ifdef RLIMIT_NOFILE 3802 #ifdef RLIMIT_RTPRIO 3805 #ifdef RLIMIT_RTTIME 3814 #ifdef RLIMIT_SBSIZE 3817 #ifdef RLIMIT_SIGPENDING 3818 RESCHECK(SIGPENDING);
3839 rb_bug(
"unexpected casetype");
3846 rlimit_type_by_hname(
const char *
name)
3848 return rlimit_resource_name2int(
name, 0);
3852 rlimit_type_by_lname(
const char *
name)
3854 return rlimit_resource_name2int(
name, 1);
3858 rlimit_resource_type(
VALUE rtype)
3864 switch (
TYPE(rtype)) {
3884 r = rlimit_type_by_hname(
name);
3892 rlimit_resource_value(
VALUE rval)
3897 switch (
TYPE(rval)) {
3914 return NUM2RLIM(rval);
3917 #ifdef RLIM_INFINITY 3918 if (strcmp(
name,
"INFINITY") == 0)
return RLIM_INFINITY;
3920 #ifdef RLIM_SAVED_MAX 3921 if (strcmp(
name,
"SAVED_MAX") == 0)
return RLIM_SAVED_MAX;
3923 #ifdef RLIM_SAVED_CUR 3924 if (strcmp(
name,
"SAVED_CUR") == 0)
return RLIM_SAVED_CUR;
3930 #if defined(HAVE_GETRLIMIT) && defined(RLIM2NUM) 3958 if (
getrlimit(rlimit_resource_type(resource), &rlim) < 0) {
3961 return rb_assoc_new(RLIM2NUM(rlim.rlim_cur), RLIM2NUM(rlim.rlim_max));
3964 #define proc_getrlimit rb_f_notimplement 3967 #if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM) 4022 VALUE resource, rlim_cur, rlim_max;
4028 if (rlim_max ==
Qnil)
4029 rlim_max = rlim_cur;
4031 rlim.rlim_cur = rlimit_resource_value(rlim_cur);
4032 rlim.rlim_max = rlimit_resource_value(rlim_max);
4034 if (
setrlimit(rlimit_resource_type(resource), &rlim) < 0) {
4040 #define proc_setrlimit rb_f_notimplement 4075 #if defined HAVE_SETUID 4093 #define p_sys_setuid rb_f_notimplement 4097 #if defined HAVE_SETRUID 4115 #define p_sys_setruid rb_f_notimplement 4119 #if defined HAVE_SETEUID 4137 #define p_sys_seteuid rb_f_notimplement 4141 #if defined HAVE_SETREUID 4161 #define p_sys_setreuid rb_f_notimplement 4165 #if defined HAVE_SETRESUID 4185 #define p_sys_setresuid rb_f_notimplement 4208 #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRUID) || defined(HAVE_SETUID) 4225 #if defined(HAVE_SETRESUID) 4227 #elif defined HAVE_SETREUID 4229 #elif defined HAVE_SETRUID 4231 #elif defined HAVE_SETUID 4244 #define proc_setuid rb_f_notimplement 4260 #ifdef BROKEN_SETREUID 4262 setreuid(rb_uid_t ruid, rb_uid_t euid)
4264 if (ruid != (rb_uid_t)-1 && ruid !=
getuid()) {
4265 if (euid == (rb_uid_t)-1) euid =
geteuid();
4266 if (
setuid(ruid) < 0)
return -1;
4268 if (euid != (rb_uid_t)-1 && euid !=
geteuid()) {
4269 if (seteuid(euid) < 0)
return -1;
4298 #if defined(HAVE_SETRESUID) 4301 #elif defined(HAVE_SETUID) 4304 #elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID) 4326 #elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID) 4352 #if defined(HAVE_SETRESUID) 4353 if (setresuid((
getuid() == uid)? (rb_uid_t)-1: uid,
4354 (
geteuid() == uid)? (rb_uid_t)-1: uid,
4357 #elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID) 4359 if (setreuid((
getuid() == uid)? (rb_uid_t)-1: uid,
4360 (
geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
4362 }
else if (
getuid() != uid) {
4363 if (setreuid(uid, (
geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
4366 }
else if (
geteuid() != uid) {
4376 #elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID) 4380 }
else if (
geteuid() == uid) {
4389 }
else if (
getuid() == uid) {
4398 #elif defined HAVE_44BSD_SETUID 4407 #elif defined HAVE_SETEUID 4414 #elif defined HAVE_SETUID 4430 #if defined HAVE_SETGID 4448 #define p_sys_setgid rb_f_notimplement 4452 #if defined HAVE_SETRGID 4470 #define p_sys_setrgid rb_f_notimplement 4474 #if defined HAVE_SETEGID 4492 #define p_sys_setegid rb_f_notimplement 4496 #if defined HAVE_SETREGID 4516 #define p_sys_setregid rb_f_notimplement 4519 #if defined HAVE_SETRESGID 4539 #define p_sys_setresgid rb_f_notimplement 4543 #if defined HAVE_ISSETUGID 4567 #define p_sys_issetugid rb_f_notimplement 4590 #if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETRGID) || defined(HAVE_SETGID) 4606 #if defined(HAVE_SETRESGID) 4608 #elif defined HAVE_SETREGID 4610 #elif defined HAVE_SETRGID 4612 #elif defined HAVE_SETGID 4625 #define proc_setgid rb_f_notimplement 4629 #if defined(HAVE_SETGROUPS) || defined(HAVE_GETGROUPS) 4650 #define RB_MAX_GROUPS (65536) 4651 static int _maxgroups = -1;
4652 static int get_sc_ngroups_max(
void)
4654 #ifdef _SC_NGROUPS_MAX 4655 return (
int)sysconf(_SC_NGROUPS_MAX);
4656 #elif defined(NGROUPS_MAX) 4657 return (
int)NGROUPS_MAX;
4662 static int maxgroups(
void)
4664 if (_maxgroups < 0) {
4665 _maxgroups = get_sc_ngroups_max();
4667 _maxgroups = RB_MAX_GROUPS;
4676 #ifdef HAVE_GETGROUPS 4695 ngroups = getgroups(0,
NULL);
4699 groups =
ALLOCA_N(rb_gid_t, ngroups);
4701 ngroups = getgroups(ngroups, groups);
4706 for (
i = 0;
i < ngroups;
i++)
4712 #define proc_getgroups rb_f_notimplement 4716 #ifdef HAVE_SETGROUPS 4735 #ifdef HAVE_GETGRNAM_R 4736 long getgr_buf_len = sysconf(_SC_GETGR_R_SIZE_MAX);
4739 if (getgr_buf_len < 0)
4740 getgr_buf_len = 4096;
4741 getgr_buf =
ALLOCA_N(
char, getgr_buf_len);
4747 if (ngroups > maxgroups())
4750 groups =
ALLOCA_N(rb_gid_t, ngroups);
4752 for (
i = 0;
i < ngroups;
i++) {
4770 #ifdef HAVE_GETGRNAM_R 4771 ret = getgrnam_r(grpname, &grp, getgr_buf, getgr_buf_len, &
p);
4775 p = getgrnam(grpname);
4781 groups[
i] =
p->gr_gid;
4786 if (setgroups(ngroups, groups) == -1)
4792 #define proc_setgroups rb_f_notimplement 4796 #ifdef HAVE_INITGROUPS 4823 #define proc_initgroups rb_f_notimplement 4826 #if defined(_SC_NGROUPS_MAX) || defined(NGROUPS_MAX) 4843 #define proc_getmaxgroups rb_f_notimplement 4846 #ifdef HAVE_SETGROUPS 4859 int ngroups_max = get_sc_ngroups_max();
4864 if (ngroups > RB_MAX_GROUPS)
4865 ngroups = RB_MAX_GROUPS;
4867 if (ngroups_max > 0 && ngroups > ngroups_max)
4868 ngroups = ngroups_max;
4870 _maxgroups = ngroups;
4875 #define proc_setmaxgroups rb_f_notimplement 4878 #if defined(HAVE_DAEMON) || (defined(HAVE_FORK) && defined(HAVE_SETSID)) 4879 static int rb_daemon(
int nochdir,
int noclose);
4898 VALUE nochdir, noclose;
4905 n = rb_daemon(
RTEST(nochdir),
RTEST(noclose));
4911 rb_daemon(
int nochdir,
int noclose)
4916 err = daemon(nochdir, noclose);
4922 #define fork_daemon() \ 4923 switch (rb_fork(0, 0, 0, Qnil)) { \ 4924 case -1: return -1; \ 4925 case 0: rb_thread_atfork(); break; \ 4926 default: _exit(EXIT_SUCCESS); \ 4931 if (setsid() < 0)
return -1;
4939 if (!noclose && (n = open(
"/dev/null", O_RDWR, 0)) != -1) {
4951 #define proc_daemon rb_f_notimplement 4966 #ifdef BROKEN_SETREGID 4968 setregid(rb_gid_t rgid, rb_gid_t egid)
4970 if (rgid != (rb_gid_t)-1 && rgid !=
getgid()) {
4971 if (egid == (rb_gid_t)-1) egid =
getegid();
4972 if (
setgid(rgid) < 0)
return -1;
4974 if (egid != (rb_gid_t)-1 && egid !=
getegid()) {
4975 if (setegid(egid) < 0)
return -1;
5004 #if defined(HAVE_SETRESGID) 5007 #elif defined HAVE_SETGID 5010 #elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID) 5032 #elif defined(HAVE_SETRGID) && defined (HAVE_SETEGID) 5059 #if defined(HAVE_SETRESGID) 5060 if (setresgid((
getgid() == gid)? (rb_gid_t)-1: gid,
5061 (
getegid() == gid)? (rb_gid_t)-1: gid,
5064 #elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID) 5066 if (setregid((
getgid() == gid)? (rb_uid_t)-1: gid,
5067 (
getegid() == gid)? (rb_uid_t)-1: gid) < 0)
5069 }
else if (
getgid() != gid) {
5070 if (setregid(gid, (
getegid() == gid)? (rb_uid_t)-1: gid) < 0)
5073 }
else if (
getegid() != gid) {
5083 #elif defined(HAVE_SETRGID) && defined(HAVE_SETEGID) 5087 }
else if (
getegid() == gid) {
5096 }
else if (
getgid() == gid) {
5105 #elif defined HAVE_44BSD_SETGID 5114 #elif defined HAVE_SETEGID 5121 #elif defined HAVE_SETGID 5154 #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID) || defined(_POSIX_SAVED_IDS) 5171 #if defined(HAVE_SETRESUID) 5173 #elif defined HAVE_SETREUID 5175 #elif defined HAVE_SETEUID 5177 #elif defined HAVE_SETUID 5191 #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID) 5192 #define proc_seteuid_m proc_seteuid 5194 #define proc_seteuid_m rb_f_notimplement 5206 #if defined(HAVE_SETRESUID) 5213 #elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID) 5220 #elif defined HAVE_SETEUID 5222 #elif defined HAVE_SETUID 5274 #if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID) || defined(_POSIX_SAVED_IDS) 5291 #if defined(HAVE_SETRESGID) 5293 #elif defined HAVE_SETREGID 5295 #elif defined HAVE_SETEGID 5297 #elif defined HAVE_SETGID 5311 #if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID) 5312 #define proc_setegid_m proc_setegid 5314 #define proc_setegid_m rb_f_notimplement 5326 #if defined(HAVE_SETRESGID) 5333 #elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID) 5340 #elif defined HAVE_SETEGID 5342 #elif defined HAVE_SETGID 5386 #if defined(HAVE_SETRESUID) 5388 #elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID) 5418 #if defined(HAVE_SETRESUID) 5419 if (setresuid(euid, uid, uid) < 0)
rb_sys_fail(0);
5421 #elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID) 5443 #if defined(HAVE_SETRESGID) 5445 #elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID) 5475 #if defined(HAVE_SETRESGID) 5476 if (setresgid(egid, gid, gid) < 0)
rb_sys_fail(0);
5478 #elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID) 5501 #if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS) 5509 #if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS) 5610 #if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS) 5617 #if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS) 5706 #if defined(HAVE_TIMES) 5722 const double hertz =
5723 #ifdef HAVE__SC_CLK_TCK 5724 (double)sysconf(_SC_CLK_TCK);
5736 volatile VALUE utime, stime, cutime, sctime;
5746 #define rb_proc_times rb_f_notimplement 5840 #ifdef HAVE_GETPRIORITY 5851 #if defined(RLIM2NUM) && defined(RLIM_INFINITY) 5853 VALUE inf = RLIM2NUM(RLIM_INFINITY);
5854 #ifdef RLIM_SAVED_MAX 5856 VALUE v = RLIM_INFINITY == RLIM_SAVED_MAX ?
inf : RLIM2NUM(RLIM_SAVED_MAX);
5863 #ifdef RLIM_SAVED_CUR 5865 VALUE v = RLIM_INFINITY == RLIM_SAVED_CUR ?
inf : RLIM2NUM(RLIM_SAVED_CUR);
5906 #ifdef RLIMIT_MEMLOCK 5913 #ifdef RLIMIT_MSGQUEUE 5928 #ifdef RLIMIT_NOFILE 5951 #ifdef RLIMIT_RTPRIO 5958 #ifdef RLIMIT_RTTIME 5966 #ifdef RLIMIT_SBSIZE 5971 #ifdef RLIMIT_SIGPENDING 6006 #if defined(HAVE_TIMES) || defined(_WIN32) VALUE rb_thread_blocking_region(rb_blocking_function_t *func, void *data1, rb_unblock_function_t *ubf, void *data2)
static VALUE p_uid_have_saved_id(void)
static int run_exec_close(VALUE ary, char *errmsg, size_t errmsg_buflen)
struct timeval rb_time_interval(VALUE num)
void rb_syswait(rb_pid_t pid)
void rb_thread_schedule(void)
void rb_thread_atfork_before_exec(void)
static VALUE rb_cProcessStatus
#define redirect_close(fd)
static VALUE proc_getgid(VALUE obj)
void rb_thread_atfork(void)
VALUE rb_ary_new4(long n, const VALUE *elts)
static VALUE rb_check_argv(int argc, VALUE *argv)
VALUE rb_ary_entry(VALUE ary, long offset)
void rb_bug(const char *fmt,...)
size_t strlen(const char *)
static int run_exec_dup2_child(VALUE ary, VALUE save, char *errmsg, size_t errmsg_buflen)
void rb_update_max_fd(int fd)
#define redirect_open(pathname, flags, perm)
static VALUE save_env_i(VALUE i, VALUE ary, int argc, VALUE *argv)
rb_pid_t rb_spawn_err(int argc, VALUE *argv, char *errmsg, size_t errmsg_buflen)
void rb_define_virtual_variable(const char *, VALUE(*)(ANYARGS), void(*)(ANYARGS))
static VALUE hide_obj(VALUE obj)
static VALUE p_uid_exchange(VALUE obj)
static void after_exec(void)
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
static VALUE rb_f_system(int argc, VALUE *argv)
int rb_io_modestr_oflags(const char *modestr)
static int under_gid_switch
static VALUE pst_bitand(VALUE st1, VALUE st2)
static VALUE p_uid_exchangeable(void)
static VALUE pst_to_s(VALUE st)
static VALUE detach_process_pid(VALUE thread)
int rb_env_path_tainted(void)
#define rb_block_call(arg1, arg2, arg3, arg4, arg5, arg6)
static int check_exec_env_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
static rb_gid_t SAVED_GROUP_ID
SOCKET rb_w32_get_osfhandle(int)
static VALUE p_uid_sw_ensure(VALUE obj)
int execl(const char *path, const char *arg0,...)
static VALUE proc_waitall(void)
VALUE rb_struct_new(VALUE,...)
int rb_proc_exec(const char *str)
VALUE rb_ary_push(VALUE ary, VALUE item)
void rb_close_before_exec(int lowfd, int maxhint, VALUE noclose_fds)
int rb_proc_exec_n(int argc, VALUE *argv, const char *prog)
static void check_exec_redirect(VALUE key, VALUE val, VALUE options)
int rb_thread_alone(void)
rb_pid_t rb_w32_aspawn_flags(int, const char *, char *const *, DWORD)
static VALUE p_gid_have_saved_id(void)
static VALUE p_gid_exchangeable(void)
static VALUE INT2NUM(int v)
VALUE rb_protect(VALUE(*proc)(VALUE), VALUE data, int *state)
#define RSTRING_PTR(string)
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
void rb_raise(VALUE exc, const char *fmt,...)
rb_pid_t rb_waitpid(rb_pid_t pid, int *st, int flags)
static void check_gid_switch(void)
int rb_exec(const struct rb_exec_arg *e)
static VALUE p_gid_change_privilege(VALUE obj, VALUE id)
#define RARRAY_LEN(ARRAY)
VALUE rb_ary_new3(long n,...)
static VALUE check_exec_redirect_fd(VALUE v, int iskey)
VALUE rb_f_exit(int argc, VALUE *argv)
static VALUE pst_equal(VALUE st1, VALUE st2)
int setrlimit(int resource, const struct rlimit *rlp)
void rb_define_global_function(const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a global function.
static VALUE pst_success_p(VALUE st)
static VALUE proc_wait(int argc, VALUE *argv)
int rb_exec_err(const struct rb_exec_arg *e, char *errmsg, size_t errmsg_buflen)
static VALUE pst_wtermsig(VALUE st)
void rb_undef_method(VALUE klass, const char *name)
static int min(int a, int b)
#define GetOpenFile(obj, fp)
void rb_thread_start_timer_thread(void)
static VALUE check_exec_fds(VALUE options)
static VALUE pst_wstopsig(VALUE st)
VALUE rb_thread_local_aref(VALUE, ID)
static VALUE pst_wifsignaled(VALUE st)
rb_pid_t rb_fork(int *, int(*)(void *), void *, VALUE)
void rb_exc_raise(VALUE mesg)
static void save_env(VALUE save)
VALUE rb_singleton_class(VALUE obj)
Returns the singleton class of obj.
#define preserving_errno(stmts)
VALUE rb_last_status_get(void)
#define MEMZERO(p, type, n)
static int save_redirect_fd(int fd, VALUE save, char *errmsg, size_t errmsg_buflen)
#define RUBY_VM_CHECK_INTS()
static VALUE proc_wait2(int argc, VALUE *argv)
static rb_gid_t rb_setegid_core(rb_gid_t egid)
void rb_thread_sleep(int)
VALUE rb_marshal_dump(VALUE, VALUE)
VALUE rb_class_new_instance(int, VALUE *, VALUE)
int execv(const char *path, char *const argv[])
static rb_pid_t rb_spawn_internal(int argc, VALUE *argv, int default_close_others, char *errmsg, size_t errmsg_buflen)
static VALUE rb_f_exit_bang(int argc, VALUE *argv, VALUE obj)
int rb_block_given_p(void)
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
int getrlimit(int resource, struct rlimit *rlp)
RUBY_EXTERN VALUE rb_cObject
static VALUE rb_f_spawn(int argc, VALUE *argv)
static rb_uid_t SAVED_USER_ID
#define proc_getmaxgroups
static VALUE proc_detach(VALUE obj, VALUE pid)
const char * ruby_signal_name(int)
rb_pid_t rb_w32_spawn(int, const char *, const char *)
int rb_run_exec_options_err(const struct rb_exec_arg *e, struct rb_exec_arg *s, char *errmsg, size_t errmsg_buflen)
VALUE rb_str_cat2(VALUE, const char *)
static int run_exec_open(VALUE ary, VALUE save, char *errmsg, size_t errmsg_buflen)
static VALUE pst_wifexited(VALUE st)
VALUE rb_iv_get(VALUE, const char *)
void rb_thread_stop_timer_thread(int close_anyway)
static int proc_exec_v(char **argv, const char *prog)
#define ALLOC_ARGV_WITH_STR(n, v, s, l)
void rb_define_const(VALUE, const char *, VALUE)
void rb_ary_store(VALUE ary, long idx, VALUE val)
static VALUE pst_rshift(VALUE st1, VALUE st2)
static VALUE rb_check_exec_env(VALUE hash)
#define ALLOCA_N(type, n)
VALUE rb_obj_alloc(VALUE)
static VALUE rb_exec_arg_prepare(struct rb_exec_arg *earg, int argc, VALUE *argv, int default_close_others)
static VALUE proc_geteuid(VALUE obj)
void rb_disable_interrupt(void)
rb_pid_t rb_fork_err(int *, int(*)(void *, char *, size_t), void *, VALUE, char *, size_t)
static void before_exec(void)
VALUE rb_const_get(VALUE, ID)
static void rb_last_status_clear(void)
VALUE rb_thread_local_aset(VALUE, ID, VALUE)
static void rb_exec_fillarg(VALUE prog, int argc, VALUE *argv, VALUE env, VALUE opthash, struct rb_exec_arg *e)
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
VALUE rb_io_fdopen(int, int, const char *)
static void rb_check_exec_options(VALUE opthash, struct rb_exec_arg *e)
static VALUE rb_waitpid_blocking(void *data)
static VALUE p_uid_switch(VALUE obj)
static VALUE pst_wcoredump(VALUE st)
VALUE rb_sprintf(const char *format,...)
static int chfunc(void *data, char *errbuf, size_t errbuf_len)
static int under_uid_switch
VALUE rb_iv_set(VALUE, const char *, VALUE)
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
static VALUE pst_pid(VALUE st)
char * dln_find_exe_r(const char *, const char *, char *, size_t)
static void pst_message(VALUE str, rb_pid_t pid, int status)
unsigned char buf[MIME_BUF_SIZE]
VALUE rb_assoc_new(VALUE car, VALUE cdr)
VALUE tied_io_for_writing
static VALUE detach_process_watcher(void *arg)
static int options(unsigned char *cp)
static VALUE p_gid_switch(VALUE obj)
#define CHILD_ERRMSG_BUFLEN
const char * rb_class2name(VALUE)
char * strchr(char *, char)
#define RARRAY_PTR(ARRAY)
VALUE rb_ensure(VALUE(*b_proc)(ANYARGS), VALUE data1, VALUE(*e_proc)(ANYARGS), VALUE data2)
#define proc_setmaxgroups
#define RARRAY_LENINT(ary)
void rb_sys_fail(const char *mesg)
static int intcmp(const void *a, const void *b)
void rb_jump_tag(int tag)
void ruby_error_print(void)
static void security(const char *str)
void rb_enable_interrupt(void)
VALUE rb_define_module_under(VALUE outer, const char *name)
#define StringValueCStr(v)
static int check_exec_options_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
static rb_uid_t rb_seteuid_core(rb_uid_t euid)
static VALUE p_gid_grant_privilege(VALUE obj, VALUE id)
VALUE rb_f_abort(int argc, VALUE *argv)
void rb_thread_wait_for(struct timeval)
static st_table * pid_tbl
VALUE rb_equal(VALUE, VALUE)
RUBY_EXTERN VALUE rb_stderr
sighandler_t signal(int signum, sighandler_t handler)
void rb_last_status_set(int status, rb_pid_t pid)
static VALUE pst_wifstopped(VALUE st)
VALUE rb_check_convert_type(VALUE, int, const char *, const char *)
#define redirect_dup2(oldfd, newfd)
static VALUE p_gid_exchange(VALUE obj)
void rb_set_errinfo(VALUE err)
static VALUE p_uid_grant_privilege(VALUE obj, VALUE id)
static VALUE pst_to_i(VALUE st)
VALUE rb_check_array_type(VALUE ary)
static void check_uid_switch(void)
VALUE rb_exec_arg_init(int argc, VALUE *argv, int accept_shell, struct rb_exec_arg *e)
VALUE rb_str_catf(VALUE str, const char *format,...)
void rb_thread_reset_timer_thread(void)
VALUE rb_f_kill(int, VALUE *)
VALUE rb_check_string_type(VALUE)
static VALUE proc_getegid(VALUE obj)
static int run_exec_dup2(VALUE ary, VALUE save, char *errmsg, size_t errmsg_buflen)
static VALUE get_ppid(void)
static int waitall_each(rb_pid_t pid, int status, VALUE ary)
static VALUE p_gid_sw_ensure(VALUE obj)
static VALUE pst_inspect(VALUE st)
VALUE rb_marshal_load(VALUE)
VALUE rb_ary_dup(VALUE ary)
VALUE rb_io_puts(int, VALUE *, VALUE)
void rb_notimplement(void)
static unsigned int hash(const char *str, unsigned int len)
#define SafeStringValue(v)
VALUE rb_ary_join(VALUE ary, VALUE sep)
static VALUE rb_f_sleep(int argc, VALUE *argv)
static VALUE p_uid_change_privilege(VALUE obj, VALUE id)
static VALUE rb_exec_getargs(int *argc_p, VALUE **argv_p, int accept_shell, VALUE *env_ret, VALUE *opthash_ret, struct rb_exec_arg *e)
const char * rb_id2name(ID id)
#define StringValuePtr(v)
#define redirect_dup(oldfd)
static char fbuf[MAXPATHLEN]
RUBY_EXTERN int dup2(int, int)
int rb_exec_arg_addopt(struct rb_exec_arg *e, VALUE key, VALUE val)
#define STRCASECMP(s1, s2)
#define CONST_ID(var, str)
static VALUE proc_getuid(VALUE obj)
VALUE rb_struct_define(const char *,...)
int rb_run_exec_options(const struct rb_exec_arg *e, struct rb_exec_arg *s)
VALUE rb_define_module(const char *name)
rb_pid_t rb_spawn(int argc, VALUE *argv)
VALUE rb_str_buf_new(long)
#define try_with_sh(prog, argv)
VALUE rb_detach_process(rb_pid_t pid)
void rb_exec_arg_fixup(struct rb_exec_arg *e)
static VALUE get_pid(void)
VALUE rb_thread_create(VALUE(*)(ANYARGS), void *)
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
void ruby_setenv(const char *name, const char *value)
VALUE rb_str_new2(const char *)
void rb_warn(const char *fmt,...)
static VALUE pst_wexitstatus(VALUE st)
static rb_pid_t rb_spawn_process(struct rb_exec_arg *earg, VALUE prog, char *errmsg, size_t errmsg_buflen)
static int intrcmp(const void *a, const void *b)
rb_pid_t waitpid(rb_pid_t, int *, int)
VALUE rb_attr_get(VALUE, ID)
static int wait_each(rb_pid_t pid, int status, struct wait_data *data)
char * strrchr(const char *, const char)
void rb_thread_sleep_forever(void)
VALUE rb_f_exec(int argc, VALUE *argv)