Ruby: process.c Source File

Ruby 3.3.4p94 (2024-07-09 revision be1089c8ec5ba40e09b1553e36b3174bf4014d9d)

process.c
1/**********************************************************************
2
3 process.c -
4
5 $Author$
6 created at: Tue Aug 10 14:30:50 JST 1993
7
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
11
12**********************************************************************/
13
14#include "ruby/internal/config.h"
15
16#include "ruby/fiber/scheduler.h"
17
18#include <ctype.h>
19#include <errno.h>
20#include <signal.h>
21#include <stdarg.h>
22#include <stdio.h>
23#include <time.h>
24
25#ifdef HAVE_STDLIB_H
26# include <stdlib.h>
27#endif
28
29#ifdef HAVE_UNISTD_H
30# include <unistd.h>
31#endif
32
33#ifdef HAVE_FCNTL_H
34# include <fcntl.h>
35#endif
36
37#ifdef HAVE_PROCESS_H
38# include <process.h>
39#endif
40
41#ifndef EXIT_SUCCESS
42# define EXIT_SUCCESS 0
43#endif
44
45#ifndef EXIT_FAILURE
46# define EXIT_FAILURE 1
47#endif
48
49#ifdef HAVE_SYS_WAIT_H
50# include <sys/wait.h>
51#endif
52
53#ifdef HAVE_SYS_RESOURCE_H
54# include <sys/resource.h>
55#endif
56
57#ifdef HAVE_VFORK_H
58# include <vfork.h>
59#endif
60
61#ifdef HAVE_SYS_PARAM_H
62# include <sys/param.h>
63#endif
64
65#ifndef MAXPATHLEN
66# define MAXPATHLEN 1024
67#endif
68
69#include <sys/stat.h>
70
71#ifdef HAVE_SYS_TIME_H
72# include <sys/time.h>
73#endif
74
75#ifdef HAVE_SYS_TIMES_H
76# include <sys/times.h>
77#endif
78
79#ifdef HAVE_PWD_H
80# include <pwd.h>
81#endif
82
83#ifdef HAVE_GRP_H
84# include <grp.h>
85# ifdef __CYGWIN__
86int initgroups(const char *, rb_gid_t);
87# endif
88#endif
89
90#ifdef HAVE_SYS_ID_H
91# include <sys/id.h>
92#endif
93
94#ifdef __APPLE__
95# include <mach/mach_time.h>
96#endif
97
98#include "dln.h"
99#include "hrtime.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"
113#include "rjit.h"
114#include "ruby/io.h"
115#include "ruby/st.h"
116#include "ruby/thread.h"
117#include "ruby/util.h"
118#include "vm_core.h"
119#include "vm_sync.h"
120#include "ruby/ractor.h"
121
122/* define system APIs */
123#ifdef _WIN32
124#undef open
125#define open rb_w32_uopen
126#endif
127
128#if defined(HAVE_TIMES) || defined(_WIN32)
129/*********************************************************************
130 *
131 * Document-class: Process::Tms
132 *
133 * Placeholder for rusage
134 */
135static VALUE rb_cProcessTms;
136#endif
137
138#ifndef WIFEXITED
139#define WIFEXITED(w) (((w) & 0xff) == 0)
140#endif
141#ifndef WIFSIGNALED
142#define WIFSIGNALED(w) (((w) & 0x7f) > 0 && (((w) & 0x7f) < 0x7f))
143#endif
144#ifndef WIFSTOPPED
145#define WIFSTOPPED(w) (((w) & 0xff) == 0x7f)
146#endif
147#ifndef WEXITSTATUS
148#define WEXITSTATUS(w) (((w) >> 8) & 0xff)
149#endif
150#ifndef WTERMSIG
151#define WTERMSIG(w) ((w) & 0x7f)
152#endif
153#ifndef WSTOPSIG
154#define WSTOPSIG WEXITSTATUS
155#endif
156
157#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__)
158#define HAVE_44BSD_SETUID 1
159#define HAVE_44BSD_SETGID 1
160#endif
161
162#ifdef __NetBSD__
163#undef HAVE_SETRUID
164#undef HAVE_SETRGID
165#endif
166
167#ifdef BROKEN_SETREUID
168#define setreuid ruby_setreuid
169int setreuid(rb_uid_t ruid, rb_uid_t euid);
170#endif
171#ifdef BROKEN_SETREGID
172#define setregid ruby_setregid
173int setregid(rb_gid_t rgid, rb_gid_t egid);
174#endif
175
176#if defined(HAVE_44BSD_SETUID) || defined(__APPLE__)
177#if !defined(USE_SETREUID) && !defined(BROKEN_SETREUID)
178#define OBSOLETE_SETREUID 1
179#endif
180#if !defined(USE_SETREGID) && !defined(BROKEN_SETREGID)
181#define OBSOLETE_SETREGID 1
182#endif
183#endif
184
185static void check_uid_switch(void);
186static void check_gid_switch(void);
187static int exec_async_signal_safe(const struct rb_execarg *, char *, size_t);
188
189 VALUE rb_envtbl(void);
190 VALUE rb_env_to_hash(void);
191
192#if 1
193#define p_uid_from_name p_uid_from_name
194#define p_gid_from_name p_gid_from_name
195#endif
196
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)
204# else
205# define GETLOGIN_R_SIZE_INIT GETLOGIN_R_SIZE_DEFAULT
206# endif
207# elif defined(HAVE_GETLOGIN)
208# define USE_GETLOGIN 1
209# endif
210#endif
211
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
217# endif
218# if defined(HAVE_GETPWNAM_R)
219# define USE_GETPWNAM_R 1
220# elif defined(HAVE_GETPWNAM)
221# define USE_GETPWNAM 1
222# endif
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)
228# else
229# define GETPW_R_SIZE_INIT GETPW_R_SIZE_DEFAULT
230# endif
231# endif
232# ifdef USE_GETPWNAM_R
233# define PREPARE_GETPWNAM \
234 VALUE getpw_buf = 0
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)
239static rb_uid_t obj2uid(VALUE id, VALUE *getpw_buf);
240static inline rb_uid_t
241obj2uid0(VALUE id)
242{
243 rb_uid_t uid;
244 PREPARE_GETPWNAM;
245 uid = OBJ2UID1(id);
246 FINISH_GETPWNAM;
247 return uid;
248}
249# else
250# define PREPARE_GETPWNAM /* do nothing */
251# define FINISH_GETPWNAM /* do nothing */
252# define OBJ2UID1(id) obj2uid((id))
253# define OBJ2UID(id) obj2uid((id))
254static rb_uid_t obj2uid(VALUE id);
255# endif
256#else
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
264# endif
265#endif
266
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
273# endif
274# ifdef USE_GETGRNAM_R
275# define PREPARE_GETGRNAM \
276 VALUE getgr_buf = 0
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)
281static rb_gid_t obj2gid(VALUE id, VALUE *getgr_buf);
282static inline rb_gid_t
283obj2gid0(VALUE id)
284{
285 rb_gid_t gid;
286 PREPARE_GETGRNAM;
287 gid = OBJ2GID1(id);
288 FINISH_GETGRNAM;
289 return gid;
290}
291static rb_gid_t obj2gid(VALUE id, VALUE *getgr_buf);
292# else
293# define PREPARE_GETGRNAM /* do nothing */
294# define FINISH_GETGRNAM /* do nothing */
295# define OBJ2GID1(id) obj2gid((id))
296# define OBJ2GID(id) obj2gid((id))
297static rb_gid_t obj2gid(VALUE id);
298# endif
299#else
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
307# endif
308#endif
309
310#if SIZEOF_CLOCK_T == SIZEOF_INT
311typedef unsigned int unsigned_clock_t;
312#elif SIZEOF_CLOCK_T == SIZEOF_LONG
313typedef unsigned long unsigned_clock_t;
314#elif defined(HAVE_LONG_LONG) && SIZEOF_CLOCK_T == SIZEOF_LONG_LONG
315typedef unsigned LONG_LONG unsigned_clock_t;
316#endif
317#ifndef HAVE_SIG_T
318typedef void (*sig_t) (int);
319#endif
320
321#define id_exception idException
322static ID id_in, id_out, id_err, id_pid, id_uid, id_gid;
323static ID id_close, id_child;
324#ifdef HAVE_SETPGID
325static ID id_pgroup;
326#endif
327#ifdef _WIN32
328static ID id_new_pgroup;
329#endif
330static ID id_unsetenv_others, id_chdir, id_umask, id_close_others;
331static ID id_nanosecond, id_microsecond, id_millisecond, id_second;
332static ID id_float_microsecond, id_float_millisecond, id_float_second;
333static ID id_GETTIMEOFDAY_BASED_CLOCK_REALTIME, id_TIME_BASED_CLOCK_REALTIME;
334#ifdef CLOCK_REALTIME
335static ID id_CLOCK_REALTIME;
336# define RUBY_CLOCK_REALTIME ID2SYM(id_CLOCK_REALTIME)
337#endif
338#ifdef CLOCK_MONOTONIC
339static ID id_CLOCK_MONOTONIC;
340# define RUBY_CLOCK_MONOTONIC ID2SYM(id_CLOCK_MONOTONIC)
341#endif
342#ifdef CLOCK_PROCESS_CPUTIME_ID
343static ID id_CLOCK_PROCESS_CPUTIME_ID;
344# define RUBY_CLOCK_PROCESS_CPUTIME_ID ID2SYM(id_CLOCK_PROCESS_CPUTIME_ID)
345#endif
346#ifdef CLOCK_THREAD_CPUTIME_ID
347static ID id_CLOCK_THREAD_CPUTIME_ID;
348# define RUBY_CLOCK_THREAD_CPUTIME_ID ID2SYM(id_CLOCK_THREAD_CPUTIME_ID)
349#endif
350#ifdef HAVE_TIMES
351static ID id_TIMES_BASED_CLOCK_MONOTONIC;
352static ID id_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID;
353#endif
354#ifdef RUSAGE_SELF
355static ID id_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID;
356#endif
357static ID id_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID;
358#ifdef __APPLE__
359static 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)
361#endif
362static ID id_hertz;
363
364static rb_pid_t cached_pid;
365
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
371#else
372#define ALWAYS_NEED_ENVP 0
373#endif
374
375static void
376assert_close_on_exec(int fd)
377{
378#if VM_CHECK_MODE > 0
379#if defined(HAVE_FCNTL) && defined(F_GETFD) && defined(FD_CLOEXEC)
380 int flags = fcntl(fd, F_GETFD);
381 if (flags == -1) {
382 static const char m[] = "reserved FD closed unexpectedly?\n";
383 (void)!write(2, m, sizeof(m) - 1);
384 return;
385 }
386 if (flags & FD_CLOEXEC) return;
387 rb_bug("reserved FD did not have close-on-exec set");
388#else
389 rb_bug("reserved FD without close-on-exec support");
390#endif /* FD_CLOEXEC */
391#endif /* VM_CHECK_MODE */
392}
393
394static inline int
395close_unless_reserved(int fd)
396{
397 if (rb_reserved_fd_p(fd)) { /* async-signal-safe */
398 assert_close_on_exec(fd);
399 return 0;
400 }
401 return close(fd); /* async-signal-safe */
402}
403
404/*#define DEBUG_REDIRECT*/
405#if defined(DEBUG_REDIRECT)
406
407static void
408ttyprintf(const char *fmt, ...)
409{
410 va_list ap;
411 FILE *tty;
412 int save = errno;
413#ifdef _WIN32
414 tty = fopen("con", "w");
415#else
416 tty = fopen("/dev/tty", "w");
417#endif
418 if (!tty)
419 return;
420
421 va_start(ap, fmt);
422 vfprintf(tty, fmt, ap);
423 va_end(ap);
424 fclose(tty);
425 errno = save;
426}
427
428static int
429redirect_dup(int oldfd)
430{
431 int ret;
432 ret = dup(oldfd);
433 ttyprintf("dup(%d) => %d\n", oldfd, ret);
434 return ret;
435}
436
437static int
438redirect_dup2(int oldfd, int newfd)
439{
440 int ret;
441 ret = dup2(oldfd, newfd);
442 ttyprintf("dup2(%d, %d) => %d\n", oldfd, newfd, ret);
443 return ret;
444}
445
446static int
447redirect_cloexec_dup(int oldfd)
448{
449 int ret;
450 ret = rb_cloexec_dup(oldfd);
451 ttyprintf("cloexec_dup(%d) => %d\n", oldfd, ret);
452 return ret;
453}
454
455static int
456redirect_cloexec_dup2(int oldfd, int newfd)
457{
458 int ret;
459 ret = rb_cloexec_dup2(oldfd, newfd);
460 ttyprintf("cloexec_dup2(%d, %d) => %d\n", oldfd, newfd, ret);
461 return ret;
462}
463
464static int
465redirect_close(int fd)
466{
467 int ret;
468 ret = close_unless_reserved(fd);
469 ttyprintf("close(%d) => %d\n", fd, ret);
470 return ret;
471}
472
473static int
474parent_redirect_open(const char *pathname, int flags, mode_t perm)
475{
476 int ret;
477 ret = rb_cloexec_open(pathname, flags, perm);
478 ttyprintf("parent_open(\"%s\", 0x%x, 0%o) => %d\n", pathname, flags, perm, ret);
479 return ret;
480}
481
482static int
483parent_redirect_close(int fd)
484{
485 int ret;
486 ret = close_unless_reserved(fd);
487 ttyprintf("parent_close(%d) => %d\n", fd, ret);
488 return ret;
489}
490
491#else
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)
499#endif
500
501static VALUE
502get_pid(void)
503{
504 if (UNLIKELY(!cached_pid)) { /* 0 is not a valid pid */
505 cached_pid = getpid();
506 }
507 /* pid should be likely POSFIXABLE() */
508 return PIDT2NUM(cached_pid);
509}
510
511#if defined HAVE_WORKING_FORK || defined HAVE_DAEMON
512static void
513clear_pid_cache(void)
514{
515 cached_pid = 0;
516}
517#endif
518
519/*
520 * call-seq:
521 * Process.pid -> integer
522 *
523 * Returns the process ID of the current process:
524 *
525 * Process.pid # => 15668
526 *
527 */
528
529static VALUE
530proc_get_pid(VALUE _)
531{
532 return get_pid();
533}
534
535static VALUE
536get_ppid(void)
537{
538 return PIDT2NUM(getppid());
539}
540
541/*
542 * call-seq:
543 * Process.ppid -> integer
544 *
545 * Returns the process ID of the parent of the current process:
546 *
547 * puts "Pid is #{Process.pid}."
548 * fork { puts "Parent pid is #{Process.ppid}." }
549 *
550 * Output:
551 *
552 * Pid is 271290.
553 * Parent pid is 271290.
554 *
555 * May not return a trustworthy value on certain platforms.
556 */
557
558static VALUE
559proc_get_ppid(VALUE _)
560{
561 return get_ppid();
562}
563
564
565/*********************************************************************
566 *
567 * Document-class: Process::Status
568 *
569 * A Process::Status contains information about a system process.
570 *
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):
574 *
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
582 *
583 */
584
585static VALUE rb_cProcessStatus;
586
587 struct rb_process_status {
588 rb_pid_t pid;
589 int status;
590 int error;
591};
592
593static const rb_data_type_t rb_process_status_type = {
594 .wrap_struct_name = "Process::Status",
595 .function = {
596 .dmark = NULL,
597 .dfree = RUBY_DEFAULT_FREE,
598 .dsize = NULL,
599 },
600 .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
601};
602
603static VALUE
604rb_process_status_allocate(VALUE klass)
605{
606 struct rb_process_status *data;
607 return TypedData_Make_Struct(klass, struct rb_process_status, &rb_process_status_type, data);
608}
609
610 VALUE
611rb_last_status_get(void)
612{
613 return GET_THREAD()->last_status;
614}
615
616/*
617 * call-seq:
618 * Process.last_status -> Process::Status or nil
619 *
620 * Returns a Process::Status object representing the most recently exited
621 * child process in the current thread, or +nil+ if none:
622 *
623 * Process.spawn('ruby', '-e', 'exit 13')
624 * Process.wait
625 * Process.last_status # => #<Process::Status: pid 14396 exit 13>
626 *
627 * Process.spawn('ruby', '-e', 'exit 14')
628 * Process.wait
629 * Process.last_status # => #<Process::Status: pid 4692 exit 14>
630 *
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>
634 * Process.wait
635 * Process.last_status # => #<Process::Status: pid 1380 exit 15>
636 *
637 */
638static VALUE
639proc_s_last_status(VALUE mod)
640{
641 return rb_last_status_get();
642}
643
644 VALUE
645rb_process_status_new(rb_pid_t pid, int status, int error)
646{
647 VALUE last_status = rb_process_status_allocate(rb_cProcessStatus);
648 struct rb_process_status *data = RTYPEDDATA_GET_DATA(last_status);
649 data->pid = pid;
650 data->status = status;
651 data->error = error;
652
653 rb_obj_freeze(last_status);
654 return last_status;
655}
656
657static VALUE
658process_status_dump(VALUE status)
659{
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);
663 if (data->pid) {
664 rb_ivar_set(dump, id_status, INT2NUM(data->status));
665 rb_ivar_set(dump, id_pid, PIDT2NUM(data->pid));
666 }
667 return dump;
668}
669
670static VALUE
671process_status_load(VALUE real_obj, VALUE load_obj)
672{
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);
678 return real_obj;
679}
680
681void
682rb_last_status_set(int status, rb_pid_t pid)
683{
684 GET_THREAD()->last_status = rb_process_status_new(pid, status, 0);
685}
686
687static void
688last_status_clear(rb_thread_t *th)
689{
690 th->last_status = Qnil;
691}
692
693void
694rb_last_status_clear(void)
695{
696 last_status_clear(GET_THREAD());
697}
698
699static rb_pid_t
700pst_pid(VALUE status)
701{
702 struct rb_process_status *data;
703 TypedData_Get_Struct(status, struct rb_process_status, &rb_process_status_type, data);
704 return data->pid;
705}
706
707static int
708pst_status(VALUE status)
709{
710 struct rb_process_status *data;
711 TypedData_Get_Struct(status, struct rb_process_status, &rb_process_status_type, data);
712 return data->status;
713}
714
715/*
716 * call-seq:
717 * to_i -> integer
718 *
719 * Returns the system-dependent integer status of +self+:
720 *
721 * `cat /nop`
722 * $?.to_i # => 256
723 */
724
725static VALUE
726pst_to_i(VALUE self)
727{
728 int status = pst_status(self);
729 return RB_INT2NUM(status);
730}
731
732#define PST2INT(st) pst_status(st)
733
734/*
735 * call-seq:
736 * pid -> integer
737 *
738 * Returns the process ID of the process:
739 *
740 * system("false")
741 * $?.pid # => 1247002
742 *
743 */
744
745static VALUE
746pst_pid_m(VALUE self)
747{
748 rb_pid_t pid = pst_pid(self);
749 return PIDT2NUM(pid);
750}
751
752static VALUE pst_message_status(VALUE str, int status);
753
754static void
755pst_message(VALUE str, rb_pid_t pid, int status)
756{
757 rb_str_catf(str, "pid %ld", (long)pid);
758 pst_message_status(str, status);
759}
760
761static VALUE
762pst_message_status(VALUE str, int status)
763{
764 if (WIFSTOPPED(status)) {
765 int stopsig = WSTOPSIG(status);
766 const char *signame = ruby_signal_name(stopsig);
767 if (signame) {
768 rb_str_catf(str, " stopped SIG%s (signal %d)", signame, stopsig);
769 }
770 else {
771 rb_str_catf(str, " stopped signal %d", stopsig);
772 }
773 }
774 if (WIFSIGNALED(status)) {
775 int termsig = WTERMSIG(status);
776 const char *signame = ruby_signal_name(termsig);
777 if (signame) {
778 rb_str_catf(str, " SIG%s (signal %d)", signame, termsig);
779 }
780 else {
781 rb_str_catf(str, " signal %d", termsig);
782 }
783 }
784 if (WIFEXITED(status)) {
785 rb_str_catf(str, " exit %d", WEXITSTATUS(status));
786 }
787#ifdef WCOREDUMP
788 if (WCOREDUMP(status)) {
789 rb_str_cat2(str, " (core dumped)");
790 }
791#endif
792 return str;
793}
794
795
796/*
797 * call-seq:
798 * to_s -> string
799 *
800 * Returns a string representation of +self+:
801 *
802 * `cat /nop`
803 * $?.to_s # => "pid 1262141 exit 1"
804 *
805 *
806 */
807
808static VALUE
809pst_to_s(VALUE st)
810{
811 rb_pid_t pid;
812 int status;
813 VALUE str;
814
815 pid = pst_pid(st);
816 status = PST2INT(st);
817
818 str = rb_str_buf_new(0);
819 pst_message(str, pid, status);
820 return str;
821}
822
823
824/*
825 * call-seq:
826 * inspect -> string
827 *
828 * Returns a string representation of +self+:
829 *
830 * system("false")
831 * $?.inspect # => "#<Process::Status: pid 1303494 exit 1>"
832 *
833 */
834
835static VALUE
836pst_inspect(VALUE st)
837{
838 rb_pid_t pid;
839 int status;
840 VALUE str;
841
842 pid = pst_pid(st);
843 if (!pid) {
844 return rb_sprintf("#<%s: uninitialized>", rb_class2name(CLASS_OF(st)));
845 }
846 status = PST2INT(st);
847
848 str = rb_sprintf("#<%s: ", rb_class2name(CLASS_OF(st)));
849 pst_message(str, pid, status);
850 rb_str_cat2(str, ">");
851 return str;
852}
853
854
855/*
856 * call-seq:
857 * stat == other -> true or false
858 *
859 * Returns whether the value of #to_i == +other+:
860 *
861 * `cat /nop`
862 * stat = $? # => #<Process::Status: pid 1170366 exit 1>
863 * sprintf('%x', stat.to_i) # => "100"
864 * stat == 0x100 # => true
865 *
866 */
867
868static VALUE
869pst_equal(VALUE st1, VALUE st2)
870{
871 if (st1 == st2) return Qtrue;
872 return rb_equal(pst_to_i(st1), st2);
873}
874
875
876/*
877 * call-seq:
878 * stat & mask -> integer
879 *
880 * This method is deprecated as #to_i value is system-specific; use
881 * predicate methods like #exited? or #stopped?, or getters like #exitstatus
882 * or #stopsig.
883 *
884 * Returns the logical AND of the value of #to_i with +mask+:
885 *
886 * `cat /nop`
887 * stat = $? # => #<Process::Status: pid 1155508 exit 1>
888 * sprintf('%x', stat.to_i) # => "100"
889 * stat & 0x00 # => 0
890 *
891 * ArgumentError is raised if +mask+ is negative.
892 */
893
894static VALUE
895pst_bitand(VALUE st1, VALUE st2)
896{
897 int status = PST2INT(st1);
898 int mask = NUM2INT(st2);
899
900 if (mask < 0) {
901 rb_raise(rb_eArgError, "negative mask value: %d", mask);
902 }
903#define WARN_SUGGEST(suggest) \
904 rb_warn_deprecated_to_remove_at(3.4, "Process::Status#&", suggest)
905
906 switch (mask) {
907 case 0x80:
908 WARN_SUGGEST("Process::Status#coredump?");
909 break;
910 case 0x7f:
911 WARN_SUGGEST("Process::Status#signaled? or Process::Status#termsig");
912 break;
913 case 0xff:
914 WARN_SUGGEST("Process::Status#exited?, Process::Status#stopped? or Process::Status#coredump?");
915 break;
916 case 0xff00:
917 WARN_SUGGEST("Process::Status#exitstatus or Process::Status#stopsig");
918 break;
919 default:
920 WARN_SUGGEST("other Process::Status predicates");
921 break;
922 }
923#undef WARN_SUGGEST
924 status &= mask;
925
926 return INT2NUM(status);
927}
928
929
930/*
931 * call-seq:
932 * stat >> places -> integer
933 *
934 * This method is deprecated as #to_i value is system-specific; use
935 * predicate methods like #exited? or #stopped?, or getters like #exitstatus
936 * or #stopsig.
937 *
938 * Returns the value of #to_i, shifted +places+ to the right:
939 *
940 * `cat /nop`
941 * stat = $? # => #<Process::Status: pid 1155508 exit 1>
942 * stat.to_i # => 256
943 * stat >> 1 # => 128
944 * stat >> 2 # => 64
945 *
946 * ArgumentError is raised if +places+ is negative.
947 */
948
949static VALUE
950pst_rshift(VALUE st1, VALUE st2)
951{
952 int status = PST2INT(st1);
953 int places = NUM2INT(st2);
954
955 if (places < 0) {
956 rb_raise(rb_eArgError, "negative shift value: %d", places);
957 }
958#define WARN_SUGGEST(suggest) \
959 rb_warn_deprecated_to_remove_at(3.4, "Process::Status#>>", suggest)
960
961 switch (places) {
962 case 7:
963 WARN_SUGGEST("Process::Status#coredump?");
964 break;
965 case 8:
966 WARN_SUGGEST("Process::Status#exitstatus or Process::Status#stopsig");
967 break;
968 default:
969 WARN_SUGGEST("other Process::Status attributes");
970 break;
971 }
972#undef WARN_SUGGEST
973 status >>= places;
974
975 return INT2NUM(status);
976}
977
978
979/*
980 * call-seq:
981 * stopped? -> true or false
982 *
983 * Returns +true+ if this process is stopped,
984 * and if the corresponding #wait call had the Process::WUNTRACED flag set,
985 * +false+ otherwise.
986 */
987
988static VALUE
989pst_wifstopped(VALUE st)
990{
991 int status = PST2INT(st);
992
993 return RBOOL(WIFSTOPPED(status));
994}
995
996
997/*
998 * call-seq:
999 * stopsig -> integer or nil
1000 *
1001 * Returns the number of the signal that caused the process to stop,
1002 * or +nil+ if the process is not stopped.
1003 */
1004
1005static VALUE
1006pst_wstopsig(VALUE st)
1007{
1008 int status = PST2INT(st);
1009
1010 if (WIFSTOPPED(status))
1011 return INT2NUM(WSTOPSIG(status));
1012 return Qnil;
1013}
1014
1015
1016/*
1017 * call-seq:
1018 * signaled? -> true or false
1019 *
1020 * Returns +true+ if the process terminated because of an uncaught signal,
1021 * +false+ otherwise.
1022 */
1023
1024static VALUE
1025pst_wifsignaled(VALUE st)
1026{
1027 int status = PST2INT(st);
1028
1029 return RBOOL(WIFSIGNALED(status));
1030}
1031
1032
1033/*
1034 * call-seq:
1035 * termsig -> integer or nil
1036 *
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.
1039 */
1040
1041static VALUE
1042pst_wtermsig(VALUE st)
1043{
1044 int status = PST2INT(st);
1045
1046 if (WIFSIGNALED(status))
1047 return INT2NUM(WTERMSIG(status));
1048 return Qnil;
1049}
1050
1051
1052/*
1053 * call-seq:
1054 * exited? -> true or false
1055 *
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.
1059 */
1060
1061static VALUE
1062pst_wifexited(VALUE st)
1063{
1064 int status = PST2INT(st);
1065
1066 return RBOOL(WIFEXITED(status));
1067}
1068
1069
1070/*
1071 * call-seq:
1072 * exitstatus -> integer or nil
1073 *
1074 * Returns the least significant eight bits of the return code
1075 * of the process if it has exited;
1076 * +nil+ otherwise:
1077 *
1078 * `exit 99`
1079 * $?.exitstatus # => 99
1080 *
1081 */
1082
1083static VALUE
1084pst_wexitstatus(VALUE st)
1085{
1086 int status = PST2INT(st);
1087
1088 if (WIFEXITED(status))
1089 return INT2NUM(WEXITSTATUS(status));
1090 return Qnil;
1091}
1092
1093
1094/*
1095 * call-seq:
1096 * success? -> true, false, or nil
1097 *
1098 * Returns:
1099 *
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.
1103 *
1104 */
1105
1106static VALUE
1107pst_success_p(VALUE st)
1108{
1109 int status = PST2INT(st);
1110
1111 if (!WIFEXITED(status))
1112 return Qnil;
1113 return RBOOL(WEXITSTATUS(status) == EXIT_SUCCESS);
1114}
1115
1116
1117/*
1118 * call-seq:
1119 * coredump? -> true or false
1120 *
1121 * Returns +true+ if the process generated a coredump
1122 * when it terminated, +false+ if not.
1123 *
1124 * Not available on all platforms.
1125 */
1126
1127static VALUE
1128pst_wcoredump(VALUE st)
1129{
1130#ifdef WCOREDUMP
1131 int status = PST2INT(st);
1132
1133 return RBOOL(WCOREDUMP(status));
1134#else
1135 return Qfalse;
1136#endif
1137}
1138
1139static rb_pid_t
1140do_waitpid(rb_pid_t pid, int *st, int flags)
1141{
1142#if defined HAVE_WAITPID
1143 return waitpid(pid, st, flags);
1144#elif defined HAVE_WAIT4
1145 return wait4(pid, st, flags, NULL);
1146#else
1147# error waitpid or wait4 is required.
1148#endif
1149}
1150
1151 struct waitpid_state {
1152 struct ccan_list_node wnode;
1153 rb_execution_context_t *ec;
1154 rb_nativethread_cond_t *cond;
1155 rb_pid_t ret;
1156 rb_pid_t pid;
1157 int status;
1158 int options;
1159 int errnum;
1160};
1161
1162static void
1163waitpid_state_init(struct waitpid_state *w, rb_pid_t pid, int options)
1164{
1165 w->ret = 0;
1166 w->pid = pid;
1167 w->options = options;
1168 w->errnum = 0;
1169 w->status = 0;
1170}
1171
1172static void *
1173waitpid_blocking_no_SIGCHLD(void *x)
1174{
1175 struct waitpid_state *w = x;
1176
1177 w->ret = do_waitpid(w->pid, &w->status, w->options);
1178
1179 return 0;
1180}
1181
1182static void
1183waitpid_no_SIGCHLD(struct waitpid_state *w)
1184{
1185 if (w->options & WNOHANG) {
1186 w->ret = do_waitpid(w->pid, &w->status, w->options);
1187 }
1188 else {
1189 do {
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));
1192 }
1193 if (w->ret == -1)
1194 w->errnum = errno;
1195}
1196
1197 VALUE
1198rb_process_status_wait(rb_pid_t pid, int flags)
1199{
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;
1205 }
1206
1207 struct waitpid_state waitpid_state;
1208
1209 waitpid_state_init(&waitpid_state, pid, flags);
1210 waitpid_state.ec = GET_EC();
1211
1212 waitpid_no_SIGCHLD(&waitpid_state);
1213
1214 if (waitpid_state.ret == 0) return Qnil;
1215
1216 return rb_process_status_new(waitpid_state.ret, waitpid_state.status, waitpid_state.errnum);
1217}
1218
1219/*
1220 * call-seq:
1221 * Process::Status.wait(pid = -1, flags = 0) -> Process::Status
1222 *
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+.
1226 *
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>:
1231 *
1232 * Process.spawn('cat /nop') # => 1155880
1233 * Process::Status.wait # => #<Process::Status: pid 1155880 exit 1>
1234 * $? # => #<Process::Status: pid 1155508 exit 1>
1235 *
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>:
1240 *
1241 * Process::Status.wait # => #<Process::Status: pid -1 exit 0>
1242 * $? # => #<Process::Status: pid 1155508 exit 1> # Unchanged.
1243 *
1244 * May invoke the scheduler hook Fiber::Scheduler#process_wait.
1245 *
1246 * Not available on all platforms.
1247 */
1248
1249static VALUE
1250rb_process_status_waitv(int argc, VALUE *argv, VALUE _)
1251{
1252 rb_check_arity(argc, 0, 2);
1253
1254 rb_pid_t pid = -1;
1255 int flags = 0;
1256
1257 if (argc >= 1) {
1258 pid = NUM2PIDT(argv[0]);
1259 }
1260
1261 if (argc >= 2) {
1262 flags = RB_NUM2INT(argv[1]);
1263 }
1264
1265 return rb_process_status_wait(pid, flags);
1266}
1267
1268rb_pid_t
1269rb_waitpid(rb_pid_t pid, int *st, int flags)
1270{
1271 VALUE status = rb_process_status_wait(pid, flags);
1272 if (NIL_P(status)) return 0;
1273
1274 struct rb_process_status *data = rb_check_typeddata(status, &rb_process_status_type);
1275 pid = data->pid;
1276
1277 if (st) *st = data->status;
1278
1279 if (pid == -1) {
1280 errno = data->error;
1281 }
1282 else {
1283 GET_THREAD()->last_status = status;
1284 }
1285
1286 return pid;
1287}
1288
1289static VALUE
1290proc_wait(int argc, VALUE *argv)
1291{
1292 rb_pid_t pid;
1293 int flags, status;
1294
1295 flags = 0;
1296 if (rb_check_arity(argc, 0, 2) == 0) {
1297 pid = -1;
1298 }
1299 else {
1300 VALUE vflags;
1301 pid = NUM2PIDT(argv[0]);
1302 if (argc == 2 && !NIL_P(vflags = argv[1])) {
1303 flags = NUM2UINT(vflags);
1304 }
1305 }
1306
1307 if ((pid = rb_waitpid(pid, &status, flags)) < 0)
1308 rb_sys_fail(0);
1309
1310 if (pid == 0) {
1311 rb_last_status_clear();
1312 return Qnil;
1313 }
1314
1315 return PIDT2NUM(pid);
1316}
1317
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.
1322
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
1326 aPid:".
1327
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.
1330 */
1331
1332/*
1333 * call-seq:
1334 * Process.wait(pid = -1, flags = 0) -> integer
1335 *
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+:
1340 *
1341 * - Positive integer: Waits for the child process whose process ID is +pid+:
1342 *
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
1350 *
1351 * - <tt>0</tt>: Waits for any child process whose group ID
1352 * is the same as that of the current process:
1353 *
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)."
1360 * end
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)."
1366 * end
1367 * retrieved_pid = Process.wait(0)
1368 * puts "Process.wait(0) returned pid #{retrieved_pid}, which is child 0 pid."
1369 * begin
1370 * Process.wait(0)
1371 * rescue Errno::ECHILD => x
1372 * puts "Raised #{x.class}, because child 1 process group ID differs from parent process group ID."
1373 * end
1374 *
1375 * Output:
1376 *
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.
1384 *
1385 * - <tt>-1</tt> (default): Waits for any child process:
1386 *
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)."
1393 * end
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.
1400 * end
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)
1406 *
1407 * Output:
1408 *
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).
1414 * true
1415 * true
1416 *
1417 * - Less than <tt>-1</tt>: Waits for any child whose process group ID is <tt>-pid</tt>:
1418 *
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)."
1425 * end
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)."
1431 * end
1432 * sleep 1
1433 * retrieved_pid = Process.wait(-child1_pid)
1434 * puts "Process.wait(-child1_pid) returned pid #{retrieved_pid}, which is child 1 pid."
1435 * begin
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}."
1439 * end
1440 *
1441 * Output:
1442 *
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.
1450 *
1451 * Argument +flags+ should be given as one of the following constants,
1452 * or as the logical OR of both:
1453 *
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.
1456 *
1457 * Not all flags are available on all platforms.
1458 *
1459 * Raises Errno::ECHILD if there is no suitable child process.
1460 *
1461 * Not available on all platforms.
1462 *
1463 * Process.waitpid is an alias for Process.wait.
1464 */
1465static VALUE
1466proc_m_wait(int c, VALUE *v, VALUE _)
1467{
1468 return proc_wait(c, v);
1469}
1470
1471/*
1472 * call-seq:
1473 * Process.wait2(pid = -1, flags = 0) -> [pid, status]
1474 *
1475 * Like Process.waitpid, but returns an array
1476 * containing the child process +pid+ and Process::Status +status+:
1477 *
1478 * pid = Process.spawn('ruby', '-e', 'exit 13') # => 309581
1479 * Process.wait2(pid)
1480 * # => [309581, #<Process::Status: pid 309581 exit 13>]
1481 *
1482 * Process.waitpid2 is an alias for Process.waitpid.
1483 */
1484
1485static VALUE
1486proc_wait2(int argc, VALUE *argv, VALUE _)
1487{
1488 VALUE pid = proc_wait(argc, argv);
1489 if (NIL_P(pid)) return Qnil;
1490 return rb_assoc_new(pid, rb_last_status_get());
1491}
1492
1493
1494/*
1495 * call-seq:
1496 * Process.waitall -> array
1497 *
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:
1501 *
1502 * pid0 = Process.spawn('ruby', '-e', 'exit 13') # => 325470
1503 * pid1 = Process.spawn('ruby', '-e', 'exit 14') # => 325495
1504 * Process.waitall
1505 * # => [[325470, #<Process::Status: pid 325470 exit 13>], [325495, #<Process::Status: pid 325495 exit 14>]]
1506 *
1507 */
1508
1509static VALUE
1510proc_waitall(VALUE _)
1511{
1512 VALUE result;
1513 rb_pid_t pid;
1514 int status;
1515
1516 result = rb_ary_new();
1517 rb_last_status_clear();
1518
1519 for (pid = -1;;) {
1520 pid = rb_waitpid(-1, &status, 0);
1521 if (pid == -1) {
1522 int e = errno;
1523 if (e == ECHILD)
1524 break;
1525 rb_syserr_fail(e, 0);
1526 }
1527 rb_ary_push(result, rb_assoc_new(PIDT2NUM(pid), rb_last_status_get()));
1528 }
1529 return result;
1530}
1531
1532static VALUE rb_cWaiter;
1533
1534static VALUE
1535detach_process_pid(VALUE thread)
1536{
1537 return rb_thread_local_aref(thread, id_pid);
1538}
1539
1540static VALUE
1541detach_process_watcher(void *arg)
1542{
1543 rb_pid_t cpid, pid = (rb_pid_t)(VALUE)arg;
1544 int status;
1545
1546 while ((cpid = rb_waitpid(pid, &status, 0)) == 0) {
1547 /* wait while alive */
1548 }
1549 return rb_last_status_get();
1550}
1551
1552 VALUE
1553rb_detach_process(rb_pid_t pid)
1554{
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);
1558 return watcher;
1559}
1560
1561
1562/*
1563 * call-seq:
1564 * Process.detach(pid) -> thread
1565 *
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.
1570 *
1571 * This method is needed only when the parent process will never wait
1572 * for the child process.
1573 *
1574 * This example does not reap the second child process;
1575 * that process appears as a zombie in the process status (+ps+) output:
1576 *
1577 * pid = Process.spawn('ruby', '-e', 'exit 13') # => 312691
1578 * sleep(1)
1579 * # Find zombies.
1580 * system("ps -ho pid,state -p #{pid}")
1581 *
1582 * Output:
1583 *
1584 * 312716 Z
1585 *
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:
1588 *
1589 * pid = Process.spawn('ruby', '-e', 'exit 13') # => 313213
1590 * thread = Process.detach(pid)
1591 * sleep(1)
1592 * # => #<Process::Waiter:0x00007f038f48b838 run>
1593 * system("ps -ho pid,state -p #{pid}") # Finds no zombies.
1594 *
1595 * The waiting thread can return the pid of the detached child process:
1596 *
1597 * thread.join.pid # => 313262
1598 *
1599 */
1600
1601static VALUE
1602proc_detach(VALUE obj, VALUE pid)
1603{
1604 return rb_detach_process(NUM2PIDT(pid));
1605}
1606
1607/* This function should be async-signal-safe. Actually it is. */
1608static void
1609before_exec_async_signal_safe(void)
1610{
1611}
1612
1613static void
1614before_exec_non_async_signal_safe(void)
1615{
1616 /*
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.
1622 *
1623 * Nowadays, we always stop the timer thread completely to allow redirects.
1624 */
1625 rb_thread_stop_timer_thread();
1626}
1627
1628#define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
1629#ifdef _WIN32
1630int rb_w32_set_nonblock2(int fd, int nonblock);
1631#endif
1632
1633static int
1634set_blocking(int fd)
1635{
1636#ifdef _WIN32
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 */
1640
1641 /* EBADF ought to be possible */
1642 if (fl == -1) return fl;
1643 if (fl & O_NONBLOCK) {
1644 fl &= ~O_NONBLOCK;
1645 return fcntl(fd, F_SETFL, fl);
1646 }
1647 return 0;
1648#endif
1649}
1650
1651static void
1652stdfd_clear_nonblock(void)
1653{
1654 /* many programs cannot deal with non-blocking stdin/stdout/stderr */
1655 int fd;
1656 for (fd = 0; fd < 3; fd++) {
1657 (void)set_blocking(fd); /* can't do much about errors anyhow */
1658 }
1659}
1660
1661static void
1662before_exec(void)
1663{
1664 before_exec_non_async_signal_safe();
1665 before_exec_async_signal_safe();
1666}
1667
1668static void
1669after_exec(void)
1670{
1671 rb_thread_reset_timer_thread();
1672 rb_thread_start_timer_thread();
1673}
1674
1675#if defined HAVE_WORKING_FORK || defined HAVE_DAEMON
1676static void
1677before_fork_ruby(void)
1678{
1679 before_exec();
1680}
1681
1682static void
1683after_fork_ruby(rb_pid_t pid)
1684{
1685 if (pid == 0) {
1686 // child
1687 clear_pid_cache();
1688 rb_thread_atfork();
1689 }
1690 else {
1691 // parent
1692 after_exec();
1693 }
1694}
1695#endif
1696
1697#if defined(HAVE_WORKING_FORK)
1698
1699COMPILER_WARNING_PUSH
1700#if __has_warning("-Wdeprecated-declarations") || RBIMPL_COMPILER_IS(GCC)
1701COMPILER_WARNING_IGNORED(-Wdeprecated-declarations)
1702#endif
1703static inline rb_pid_t
1704rb_fork(void)
1705{
1706 return fork();
1707}
1708COMPILER_WARNING_POP
1709
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)
1712static void
1713exec_with_sh(const char *prog, char **argv, char **envp)
1714{
1715 *argv = (char *)prog;
1716 *--argv = (char *)"sh";
1717 if (envp)
1718 execve("/bin/sh", argv, envp); /* async-signal-safe */
1719 else
1720 execv("/bin/sh", argv); /* async-signal-safe (since SUSv4) */
1721}
1722
1723#else
1724#define try_with_sh(err, prog, argv, envp) (void)0
1725#endif
1726
1727/* This function should be async-signal-safe. Actually it is. */
1728static int
1729proc_exec_cmd(const char *prog, VALUE argv_str, VALUE envp_str)
1730{
1731 char **argv;
1732#ifndef _WIN32
1733 char **envp;
1734 int err;
1735#endif
1736
1737 argv = ARGVSTR2ARGV(argv_str);
1738
1739 if (!prog) {
1740 return ENOENT;
1741 }
1742
1743#ifdef _WIN32
1744 rb_w32_uaspawn(P_OVERLAY, prog, argv);
1745 return errno;
1746#else
1747 envp = envp_str ? RB_IMEMO_TMPBUF_PTR(envp_str) : NULL;
1748 if (envp_str)
1749 execve(prog, argv, envp); /* async-signal-safe */
1750 else
1751 execv(prog, argv); /* async-signal-safe (since SUSv4) */
1752 err = errno;
1753 try_with_sh(err, prog, argv, envp); /* try_with_sh() is async-signal-safe. */
1754 return err;
1755#endif
1756}
1757
1758/* This function should be async-signal-safe. Actually it is. */
1759static int
1760proc_exec_sh(const char *str, VALUE envp_str)
1761{
1762 const char *s;
1763
1764 s = str;
1765 while (*s == ' ' || *s == '\t' || *s == '\n')
1766 s++;
1767
1768 if (!*s) {
1769 return ENOENT;
1770 }
1771
1772#ifdef _WIN32
1773 rb_w32_uspawn(P_OVERLAY, (char *)str, 0);
1774#elif defined(__CYGWIN32__)
1775 {
1776 char fbuf[MAXPATHLEN];
1777 char *shell = dln_find_exe_r("sh", 0, fbuf, sizeof(fbuf));
1778 int status = -1;
1779 if (shell)
1780 execl(shell, "sh", "-c", str, (char *) NULL);
1781 else
1782 status = system(str);
1783 if (status != -1)
1784 exit(status);
1785 }
1786#else
1787 if (envp_str)
1788 execle("/bin/sh", "sh", "-c", str, (char *)NULL, RB_IMEMO_TMPBUF_PTR(envp_str)); /* async-signal-safe */
1789 else
1790 execl("/bin/sh", "sh", "-c", str, (char *)NULL); /* async-signal-safe (since SUSv4) */
1791#endif /* _WIN32 */
1792 return errno;
1793}
1794
1795int
1796rb_proc_exec(const char *str)
1797{
1798 int ret;
1799 before_exec();
1800 ret = proc_exec_sh(str, Qfalse);
1801 after_exec();
1802 errno = ret;
1803 return -1;
1804}
1805
1806static void
1807mark_exec_arg(void *ptr)
1808{
1809 struct rb_execarg *eargp = ptr;
1810 if (eargp->use_shell)
1811 rb_gc_mark(eargp->invoke.sh.shell_script);
1812 else {
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);
1817 }
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);
1830}
1831
1832static size_t
1833memsize_exec_arg(const void *ptr)
1834{
1835 return sizeof(struct rb_execarg);
1836}
1837
1838static const rb_data_type_t exec_arg_data_type = {
1839 "exec_arg",
1840 {mark_exec_arg, RUBY_TYPED_DEFAULT_FREE, memsize_exec_arg},
1841 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
1842};
1843
1844#ifdef _WIN32
1845# define DEFAULT_PROCESS_ENCODING rb_utf8_encoding()
1846#endif
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)
1850static VALUE
1851export_dup(VALUE str)
1852{
1853 VALUE newstr = EXPORT_STR(str);
1854 if (newstr == str) newstr = rb_str_dup(str);
1855 return newstr;
1856}
1857#else
1858# define EXPORT_STR(str) (str)
1859# define EXPORT_DUP(str) rb_str_dup(str)
1860#endif
1861
1862#if !defined(HAVE_WORKING_FORK) && defined(HAVE_SPAWNV)
1863# define USE_SPAWNV 1
1864#else
1865# define USE_SPAWNV 0
1866#endif
1867#ifndef P_NOWAIT
1868# define P_NOWAIT _P_NOWAIT
1869#endif
1870
1871#if USE_SPAWNV
1872#if defined(_WIN32)
1873#define proc_spawn_cmd_internal(argv, prog) rb_w32_uaspawn(P_NOWAIT, (prog), (argv))
1874#else
1875static rb_pid_t
1876proc_spawn_cmd_internal(char **argv, char *prog)
1877{
1878 char fbuf[MAXPATHLEN];
1879 rb_pid_t status;
1880
1881 if (!prog)
1882 prog = argv[0];
1883 prog = dln_find_exe_r(prog, 0, fbuf, sizeof(fbuf));
1884 if (!prog)
1885 return -1;
1886
1887 before_exec();
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);
1893 after_exec();
1894 if (status == -1) errno = ENOEXEC;
1895 }
1896 return status;
1897}
1898#endif
1899
1900static rb_pid_t
1901proc_spawn_cmd(char **argv, VALUE prog, struct rb_execarg *eargp)
1902{
1903 rb_pid_t pid = -1;
1904
1905 if (argv[0]) {
1906#if defined(_WIN32)
1907 DWORD flags = 0;
1908 if (eargp->new_pgroup_given && eargp->new_pgroup_flag) {
1909 flags = CREATE_NEW_PROCESS_GROUP;
1910 }
1911 pid = rb_w32_uaspawn_flags(P_NOWAIT, prog ? RSTRING_PTR(prog) : 0, argv, flags);
1912#else
1913 pid = proc_spawn_cmd_internal(argv, prog ? RSTRING_PTR(prog) : 0);
1914#endif
1915 }
1916 return pid;
1917}
1918
1919#if defined(_WIN32)
1920#define proc_spawn_sh(str) rb_w32_uspawn(P_NOWAIT, (str), 0)
1921#else
1922static rb_pid_t
1923proc_spawn_sh(char *str)
1924{
1925 char fbuf[MAXPATHLEN];
1926 rb_pid_t status;
1927
1928 char *shell = dln_find_exe_r("sh", 0, fbuf, sizeof(fbuf));
1929 before_exec();
1930 status = spawnl(P_NOWAIT, (shell ? shell : "/bin/sh"), "sh", "-c", str, (char*)NULL);
1931 after_exec();
1932 return status;
1933}
1934#endif
1935#endif
1936
1937static VALUE
1938hide_obj(VALUE obj)
1939{
1940 RBASIC_CLEAR_CLASS(obj);
1941 return obj;
1942}
1943
1944static VALUE
1945check_exec_redirect_fd(VALUE v, int iskey)
1946{
1947 VALUE tmp;
1948 int fd;
1949 if (FIXNUM_P(v)) {
1950 fd = FIX2INT(v);
1951 }
1952 else if (SYMBOL_P(v)) {
1953 ID id = rb_check_id(&v);
1954 if (id == id_in)
1955 fd = 0;
1956 else if (id == id_out)
1957 fd = 1;
1958 else if (id == id_err)
1959 fd = 2;
1960 else
1961 goto wrong;
1962 }
1963 else if (!NIL_P(tmp = rb_io_check_io(v))) {
1964 rb_io_t *fptr;
1965 GetOpenFile(tmp, fptr);
1966 if (fptr->tied_io_for_writing)
1967 rb_raise(rb_eArgError, "duplex IO redirection");
1968 fd = fptr->fd;
1969 }
1970 else {
1971 goto wrong;
1972 }
1973 if (fd < 0) {
1974 rb_raise(rb_eArgError, "negative file descriptor");
1975 }
1976#ifdef _WIN32
1977 else if (fd >= 3 && iskey) {
1978 rb_raise(rb_eArgError, "wrong file descriptor (%d)", fd);
1979 }
1980#endif
1981 return INT2FIX(fd);
1982
1983 wrong:
1984 rb_raise(rb_eArgError, "wrong exec redirect");
1985 UNREACHABLE_RETURN(Qundef);
1986}
1987
1988static VALUE
1989check_exec_redirect1(VALUE ary, VALUE key, VALUE param)
1990{
1991 if (ary == Qfalse) {
1992 ary = hide_obj(rb_ary_new());
1993 }
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)));
1997 }
1998 else {
1999 int i;
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)));
2004 }
2005 }
2006 return ary;
2007}
2008
2009static void
2010check_exec_redirect(VALUE key, VALUE val, struct rb_execarg *eargp)
2011{
2012 VALUE param;
2013 VALUE path, flags, perm;
2014 VALUE tmp;
2015 ID id;
2016
2017 switch (TYPE(val)) {
2018 case T_SYMBOL:
2019 id = rb_check_id(&val);
2020 if (id == id_close) {
2021 param = Qnil;
2022 eargp->fd_close = check_exec_redirect1(eargp->fd_close, key, param);
2023 }
2024 else if (id == id_in) {
2025 param = INT2FIX(0);
2026 eargp->fd_dup2 = check_exec_redirect1(eargp->fd_dup2, key, param);
2027 }
2028 else if (id == id_out) {
2029 param = INT2FIX(1);
2030 eargp->fd_dup2 = check_exec_redirect1(eargp->fd_dup2, key, param);
2031 }
2032 else if (id == id_err) {
2033 param = INT2FIX(2);
2034 eargp->fd_dup2 = check_exec_redirect1(eargp->fd_dup2, key, param);
2035 }
2036 else {
2037 rb_raise(rb_eArgError, "wrong exec redirect symbol: %"PRIsVALUE,
2038 val);
2039 }
2040 break;
2041
2042 case T_FILE:
2043 io:
2044 val = check_exec_redirect_fd(val, 0);
2045 /* fall through */
2046 case T_FIXNUM:
2047 param = val;
2048 eargp->fd_dup2 = check_exec_redirect1(eargp->fd_dup2, key, param);
2049 break;
2050
2051 case T_ARRAY:
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);
2057 }
2058 else {
2059 FilePathValue(path);
2060 flags = rb_ary_entry(val, 1);
2061 if (NIL_P(flags))
2062 flags = INT2NUM(O_RDONLY);
2063 else if (RB_TYPE_P(flags, T_STRING))
2064 flags = INT2NUM(rb_io_modestr_oflags(StringValueCStr(flags)));
2065 else
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);
2072 }
2073 break;
2074
2075 case T_STRING:
2076 path = val;
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)) {
2083 int i;
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;
2088 }
2089 if (i == RARRAY_LEN(key))
2090 flags = INT2NUM(O_WRONLY|O_CREAT|O_TRUNC);
2091 else
2092 flags = INT2NUM(O_RDONLY);
2093 }
2094 else
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);
2100 break;
2101
2102 default:
2103 tmp = val;
2104 val = rb_io_check_io(tmp);
2105 if (!NIL_P(val)) goto io;
2106 rb_raise(rb_eArgError, "wrong exec redirect action");
2107 }
2108
2109}
2110
2111#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
2112static int rlimit_type_by_sym(VALUE key);
2113
2114static void
2115rb_execarg_addopt_rlimit(struct rb_execarg *eargp, int rtype, VALUE val)
2116{
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());
2121 else
2122 ary = eargp->rlimit_limits;
2123 tmp = rb_check_array_type(val);
2124 if (!NIL_P(tmp)) {
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));
2130 }
2131 else {
2132 rb_raise(rb_eArgError, "wrong exec rlimit option");
2133 }
2134 }
2135 else {
2136 softlim = hardlim = rb_to_int(val);
2137 }
2138 tmp = hide_obj(rb_ary_new3(3, INT2NUM(rtype), softlim, hardlim));
2139 rb_ary_push(ary, tmp);
2140}
2141#endif
2142
2143#define TO_BOOL(val, name) (NIL_P(val) ? 0 : rb_bool_expected((val), name, TRUE))
2144int
2145rb_execarg_addopt(VALUE execarg_obj, VALUE key, VALUE val)
2146{
2147 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2148
2149 ID id;
2150
2151 switch (TYPE(key)) {
2152 case T_SYMBOL:
2153#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
2154 {
2155 int rtype = rlimit_type_by_sym(key);
2156 if (rtype != -1) {
2157 rb_execarg_addopt_rlimit(eargp, rtype, val);
2158 RB_GC_GUARD(execarg_obj);
2159 return ST_CONTINUE;
2160 }
2161 }
2162#endif
2163 if (!(id = rb_check_id(&key))) return ST_STOP;
2164#ifdef HAVE_SETPGID
2165 if (id == id_pgroup) {
2166 rb_pid_t pgroup;
2167 if (eargp->pgroup_given) {
2168 rb_raise(rb_eArgError, "pgroup option specified twice");
2169 }
2170 if (!RTEST(val))
2171 pgroup = -1; /* asis(-1) means "don't call setpgid()". */
2172 else if (val == Qtrue)
2173 pgroup = 0; /* new process group. */
2174 else {
2175 pgroup = NUM2PIDT(val);
2176 if (pgroup < 0) {
2177 rb_raise(rb_eArgError, "negative process group ID : %ld", (long)pgroup);
2178 }
2179 }
2180 eargp->pgroup_given = 1;
2181 eargp->pgroup_pgid = pgroup;
2182 }
2183 else
2184#endif
2185#ifdef _WIN32
2186 if (id == id_new_pgroup) {
2187 if (eargp->new_pgroup_given) {
2188 rb_raise(rb_eArgError, "new_pgroup option specified twice");
2189 }
2190 eargp->new_pgroup_given = 1;
2191 eargp->new_pgroup_flag = TO_BOOL(val, "new_pgroup");
2192 }
2193 else
2194#endif
2195 if (id == id_unsetenv_others) {
2196 if (eargp->unsetenv_others_given) {
2197 rb_raise(rb_eArgError, "unsetenv_others option specified twice");
2198 }
2199 eargp->unsetenv_others_given = 1;
2200 eargp->unsetenv_others_do = TO_BOOL(val, "unsetenv_others");
2201 }
2202 else if (id == id_chdir) {
2203 if (eargp->chdir_given) {
2204 rb_raise(rb_eArgError, "chdir option specified twice");
2205 }
2206 FilePathValue(val);
2207 val = rb_str_encode_ospath(val);
2208 eargp->chdir_given = 1;
2209 eargp->chdir_dir = hide_obj(EXPORT_DUP(val));
2210 }
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");
2215 }
2216 eargp->umask_given = 1;
2217 eargp->umask_mask = cmask;
2218 }
2219 else if (id == id_close_others) {
2220 if (eargp->close_others_given) {
2221 rb_raise(rb_eArgError, "close_others option specified twice");
2222 }
2223 eargp->close_others_given = 1;
2224 eargp->close_others_do = TO_BOOL(val, "close_others");
2225 }
2226 else if (id == id_in) {
2227 key = INT2FIX(0);
2228 goto redirect;
2229 }
2230 else if (id == id_out) {
2231 key = INT2FIX(1);
2232 goto redirect;
2233 }
2234 else if (id == id_err) {
2235 key = INT2FIX(2);
2236 goto redirect;
2237 }
2238 else if (id == id_uid) {
2239#ifdef HAVE_SETUID
2240 if (eargp->uid_given) {
2241 rb_raise(rb_eArgError, "uid option specified twice");
2242 }
2243 check_uid_switch();
2244 {
2245 eargp->uid = OBJ2UID(val);
2246 eargp->uid_given = 1;
2247 }
2248#else
2249 rb_raise(rb_eNotImpError,
2250 "uid option is unimplemented on this machine");
2251#endif
2252 }
2253 else if (id == id_gid) {
2254#ifdef HAVE_SETGID
2255 if (eargp->gid_given) {
2256 rb_raise(rb_eArgError, "gid option specified twice");
2257 }
2258 check_gid_switch();
2259 {
2260 eargp->gid = OBJ2GID(val);
2261 eargp->gid_given = 1;
2262 }
2263#else
2264 rb_raise(rb_eNotImpError,
2265 "gid option is unimplemented on this machine");
2266#endif
2267 }
2268 else if (id == id_exception) {
2269 if (eargp->exception_given) {
2270 rb_raise(rb_eArgError, "exception option specified twice");
2271 }
2272 eargp->exception_given = 1;
2273 eargp->exception = TO_BOOL(val, "exception");
2274 }
2275 else {
2276 return ST_STOP;
2277 }
2278 break;
2279
2280 case T_FIXNUM:
2281 case T_FILE:
2282 case T_ARRAY:
2283redirect:
2284 check_exec_redirect(key, val, eargp);
2285 break;
2286
2287 default:
2288 return ST_STOP;
2289 }
2290
2291 RB_GC_GUARD(execarg_obj);
2292 return ST_CONTINUE;
2293}
2294
2295static int
2296check_exec_options_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
2297{
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) {
2302 if (SYMBOL_P(key))
2303 rb_raise(rb_eArgError, "wrong exec option symbol: % "PRIsVALUE,
2304 key);
2305 rb_raise(rb_eArgError, "wrong exec option");
2306 }
2307 return ST_CONTINUE;
2308}
2309
2310static int
2311check_exec_options_i_extract(st_data_t st_key, st_data_t st_val, st_data_t arg)
2312{
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);
2321 }
2322 return ST_CONTINUE;
2323}
2324
2325static int
2326check_exec_fds_1(struct rb_execarg *eargp, VALUE h, int maxhint, VALUE ary)
2327{
2328 long i;
2329
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);
2336 }
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));
2343 if (maxhint < fd)
2344 maxhint = fd;
2345 if (ary == eargp->fd_dup2 || ary == eargp->fd_dup2_child) {
2346 fd = FIX2INT(RARRAY_AREF(elt, 1));
2347 if (maxhint < fd)
2348 maxhint = fd;
2349 }
2350 }
2351 }
2352 return maxhint;
2353}
2354
2355static VALUE
2356check_exec_fds(struct rb_execarg *eargp)
2357{
2358 VALUE h = rb_hash_new();
2359 VALUE ary;
2360 int maxhint = -1;
2361 long i;
2362
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);
2366
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));
2373 int lastfd = oldfd;
2374 VALUE val = rb_hash_lookup(h, INT2FIX(lastfd));
2375 long depth = 0;
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);
2381 depth++;
2382 }
2383 if (val != Qtrue)
2384 rb_raise(rb_eArgError, "child fd %d is not redirected", oldfd);
2385 if (oldfd != lastfd) {
2386 VALUE val2;
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));
2392 val = val2;
2393 }
2394 }
2395 }
2396 }
2397
2398 eargp->close_others_maxhint = maxhint;
2399 return h;
2400}
2401
2402static void
2403rb_check_exec_options(VALUE opthash, VALUE execarg_obj)
2404{
2405 if (RHASH_EMPTY_P(opthash))
2406 return;
2407 rb_hash_stlike_foreach(opthash, check_exec_options_i, (st_data_t)execarg_obj);
2408}
2409
2410 VALUE
2411rb_execarg_extract_options(VALUE execarg_obj, VALUE opthash)
2412{
2413 VALUE args[2];
2414 if (RHASH_EMPTY_P(opthash))
2415 return Qnil;
2416 args[0] = execarg_obj;
2417 args[1] = Qnil;
2418 rb_hash_stlike_foreach(opthash, check_exec_options_i_extract, (st_data_t)args);
2419 return args[1];
2420}
2421
2422#ifdef ENV_IGNORECASE
2423#define ENVMATCH(s1, s2) (STRCASECMP((s1), (s2)) == 0)
2424#else
2425#define ENVMATCH(n1, n2) (strcmp((n1), (n2)) == 0)
2426#endif
2427
2428static int
2429check_exec_env_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
2430{
2431 VALUE key = (VALUE)st_key;
2432 VALUE val = (VALUE)st_val;
2433 VALUE env = ((VALUE *)arg)[0];
2434 VALUE *path = &((VALUE *)arg)[1];
2435 char *k;
2436
2437 k = StringValueCStr(key);
2438 if (strchr(k, '='))
2439 rb_raise(rb_eArgError, "environment name contains a equal : %"PRIsVALUE, key);
2440
2441 if (!NIL_P(val))
2442 StringValueCStr(val);
2443
2444 key = EXPORT_STR(key);
2445 if (!NIL_P(val)) val = EXPORT_STR(val);
2446
2447 if (ENVMATCH(k, PATH_ENV)) {
2448 *path = val;
2449 }
2450 rb_ary_push(env, hide_obj(rb_assoc_new(key, val)));
2451
2452 return ST_CONTINUE;
2453}
2454
2455static VALUE
2456rb_check_exec_env(VALUE hash, VALUE *path)
2457{
2458 VALUE env[2];
2459
2460 env[0] = hide_obj(rb_ary_new());
2461 env[1] = Qfalse;
2462 rb_hash_stlike_foreach(hash, check_exec_env_i, (st_data_t)env);
2463 *path = env[1];
2464
2465 return env[0];
2466}
2467
2468static VALUE
2469rb_check_argv(int argc, VALUE *argv)
2470{
2471 VALUE tmp, prog;
2472 int i;
2473
2474 rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
2475
2476 prog = 0;
2477 tmp = rb_check_array_type(argv[0]);
2478 if (!NIL_P(tmp)) {
2479 if (RARRAY_LEN(tmp) != 2) {
2480 rb_raise(rb_eArgError, "wrong first argument");
2481 }
2482 prog = RARRAY_AREF(tmp, 0);
2483 argv[0] = RARRAY_AREF(tmp, 1);
2484 SafeStringValue(prog);
2485 StringValueCStr(prog);
2486 prog = rb_str_new_frozen(prog);
2487 }
2488 for (i = 0; i < argc; i++) {
2489 SafeStringValue(argv[i]);
2490 argv[i] = rb_str_new_frozen(argv[i]);
2491 StringValueCStr(argv[i]);
2492 }
2493 return prog;
2494}
2495
2496static VALUE
2497check_hash(VALUE obj)
2498{
2499 if (RB_SPECIAL_CONST_P(obj)) return Qnil;
2500 switch (RB_BUILTIN_TYPE(obj)) {
2501 case T_STRING:
2502 case T_ARRAY:
2503 return Qnil;
2504 default:
2505 break;
2506 }
2507 return rb_check_hash_type(obj);
2508}
2509
2510static VALUE
2511rb_exec_getargs(int *argc_p, VALUE **argv_p, int accept_shell, VALUE *env_ret, VALUE *opthash_ret)
2512{
2513 VALUE hash, prog;
2514
2515 if (0 < *argc_p) {
2516 hash = check_hash((*argv_p)[*argc_p-1]);
2517 if (!NIL_P(hash)) {
2518 *opthash_ret = hash;
2519 (*argc_p)--;
2520 }
2521 }
2522
2523 if (0 < *argc_p) {
2524 hash = check_hash((*argv_p)[0]);
2525 if (!NIL_P(hash)) {
2526 *env_ret = hash;
2527 (*argc_p)--;
2528 (*argv_p)++;
2529 }
2530 }
2531 prog = rb_check_argv(*argc_p, *argv_p);
2532 if (!prog) {
2533 prog = (*argv_p)[0];
2534 if (accept_shell && *argc_p == 1) {
2535 *argc_p = 0;
2536 *argv_p = 0;
2537 }
2538 }
2539 return prog;
2540}
2541
2542#ifndef _WIN32
2543 struct string_part {
2544 const char *ptr;
2545 size_t len;
2546};
2547
2548static int
2549compare_posix_sh(const void *key, const void *el)
2550{
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;
2554 return ret;
2555}
2556#endif
2557
2558static void
2559rb_exec_fillarg(VALUE prog, int argc, VALUE *argv, VALUE env, VALUE opthash, VALUE execarg_obj)
2560{
2561 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2562 char fbuf[MAXPATHLEN];
2563
2564 MEMZERO(eargp, struct rb_execarg, 1);
2565
2566 if (!NIL_P(opthash)) {
2567 rb_check_exec_options(opthash, execarg_obj);
2568 }
2569 if (!NIL_P(env)) {
2570 env = rb_check_exec_env(env, &eargp->path_env);
2571 eargp->env_modification = env;
2572 }
2573
2574 prog = EXPORT_STR(prog);
2575 eargp->use_shell = argc == 0;
2576 if (eargp->use_shell)
2577 eargp->invoke.sh.shell_script = prog;
2578 else
2579 eargp->invoke.cmd.command_name = prog;
2580
2581#ifndef _WIN32
2582 if (eargp->use_shell) {
2583 static const char posix_sh_cmds[][9] = {
2584 "!", /* reserved */
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 */
2613 };
2614 const char *p;
2615 struct string_part first = {0, 0};
2616 int has_meta = 0;
2617 /*
2618 * meta characters:
2619 *
2620 * * Pathname Expansion
2621 * ? Pathname Expansion
2622 * {} Grouping Commands
2623 * [] Pathname Expansion
2624 * <> Redirection
2625 * () Grouping Commands
2626 * ~ Tilde Expansion
2627 * & AND Lists, Asynchronous Lists
2628 * | OR Lists, Pipelines
2629 * \ Escape Character
2630 * $ Parameter Expansion
2631 * ; Sequential Lists
2632 * ' Single-Quotes
2633 * ` Command Substitution
2634 * " Double-Quotes
2635 * \n Lists
2636 *
2637 * # Comment
2638 * = Assignment preceding command name
2639 * % (used in Parameter Expansion)
2640 */
2641 for (p = RSTRING_PTR(prog); *p; p++) {
2642 if (*p == ' ' || *p == '\t') {
2643 if (first.ptr && !first.len) first.len = p - first.ptr;
2644 }
2645 else {
2646 if (!first.ptr) first.ptr = p;
2647 }
2648 if (!has_meta && strchr("*?{}[]<>()~&|\\$;'`\"\n#", *p))
2649 has_meta = 1;
2650 if (!first.len) {
2651 if (*p == '=') {
2652 has_meta = 1;
2653 }
2654 else if (*p == '/') {
2655 first.len = 0x100; /* longer than any posix_sh_cmds */
2656 }
2657 }
2658 if (has_meta)
2659 break;
2660 }
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))
2665 has_meta = 1;
2666 }
2667 if (!has_meta) {
2668 /* avoid shell since no shell meta character found. */
2669 eargp->use_shell = 0;
2670 }
2671 if (!eargp->use_shell) {
2672 VALUE argv_buf;
2673 argv_buf = hide_obj(rb_str_buf_new(0));
2674 p = RSTRING_PTR(prog);
2675 while (*p) {
2676 while (*p == ' ' || *p == '\t')
2677 p++;
2678 if (*p) {
2679 const char *w = p;
2680 while (*p && *p != ' ' && *p != '\t')
2681 p++;
2682 rb_str_buf_cat(argv_buf, w, p-w);
2683 rb_str_buf_cat(argv_buf, "", 1); /* append '\0' */
2684 }
2685 }
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);
2690 }
2691 }
2692#endif
2693
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));
2700 if (abspath)
2701 eargp->invoke.cmd.command_abspath = rb_str_new_cstr(abspath);
2702 else
2703 eargp->invoke.cmd.command_abspath = Qnil;
2704 }
2705
2706 if (!eargp->use_shell && !eargp->invoke.cmd.argv_buf) {
2707 int i;
2708 VALUE argv_buf;
2709 argv_buf = rb_str_buf_new(0);
2710 hide_obj(argv_buf);
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);
2717#endif
2718 rb_str_buf_cat(argv_buf, s, RSTRING_LEN(arg) + 1); /* include '\0' */
2719 }
2720 eargp->invoke.cmd.argv_buf = argv_buf;
2721 }
2722
2723 if (!eargp->use_shell) {
2724 const char *p, *ep, *null=NULL;
2725 VALUE argv_str;
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);
2730 while (p < ep) {
2731 rb_str_buf_cat(argv_str, (char *)&p, sizeof(p));
2732 p += strlen(p) + 1;
2733 }
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);
2737 }
2738 RB_GC_GUARD(execarg_obj);
2739}
2740
2741struct rb_execarg *
2742rb_execarg_get(VALUE execarg_obj)
2743{
2744 struct rb_execarg *eargp;
2745 TypedData_Get_Struct(execarg_obj, struct rb_execarg, &exec_arg_data_type, eargp);
2746 return eargp;
2747}
2748
2749static VALUE
2750rb_execarg_init(int argc, const VALUE *orig_argv, int accept_shell, VALUE execarg_obj)
2751{
2752 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2753 VALUE prog, ret;
2754 VALUE env = Qnil, opthash = Qnil;
2755 VALUE argv_buf;
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);
2763 return ret;
2764}
2765
2766 VALUE
2767rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt)
2768{
2769 VALUE execarg_obj;
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");
2775 }
2776 return execarg_obj;
2777}
2778
2779void
2780rb_execarg_setenv(VALUE execarg_obj, VALUE env)
2781{
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);
2786}
2787
2788static int
2789fill_envp_buf_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
2790{
2791 VALUE key = (VALUE)st_key;
2792 VALUE val = (VALUE)st_val;
2793 VALUE envp_buf = (VALUE)arg;
2794
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' */
2799
2800 return ST_CONTINUE;
2801}
2802
2803
2804static long run_exec_dup2_tmpbuf_size(long n);
2805
2806 struct open_struct {
2807 VALUE fname;
2808 int oflags;
2809 mode_t perm;
2810 int ret;
2811 int err;
2812};
2813
2814static void *
2815open_func(void *ptr)
2816{
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);
2820 data->err = errno;
2821 return NULL;
2822}
2823
2824static void
2825rb_execarg_allocate_dup2_tmpbuf(struct rb_execarg *eargp, long len)
2826{
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;
2830}
2831
2832static VALUE
2833rb_execarg_parent_start1(VALUE execarg_obj)
2834{
2835 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2836 int unsetenv_others;
2837 VALUE envopts;
2838 VALUE ary;
2839
2840 ary = eargp->fd_open;
2841 if (ary != Qfalse) {
2842 long i;
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);
2851 int fd2;
2852 if (NIL_P(fd2v)) {
2853 struct open_struct open_data;
2854 again:
2855 open_data.fname = vpath;
2856 open_data.oflags = flags;
2857 open_data.perm = perm;
2858 open_data.ret = -1;
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();
2864 goto again;
2865 }
2866 rb_syserr_fail_str(open_data.err, vpath);
2867 }
2868 fd2 = open_data.ret;
2869 rb_update_max_fd(fd2);
2870 RARRAY_ASET(param, 3, INT2FIX(fd2));
2871 rb_thread_check_ints();
2872 }
2873 else {
2874 fd2 = NUM2INT(fd2v);
2875 }
2876 rb_execarg_addopt(execarg_obj, INT2FIX(fd), INT2FIX(fd2));
2877 }
2878 }
2879
2880 eargp->redirect_fds = check_exec_fds(eargp);
2881
2882 ary = eargp->fd_dup2;
2883 if (ary != Qfalse) {
2884 rb_execarg_allocate_dup2_tmpbuf(eargp, RARRAY_LEN(ary));
2885 }
2886
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;
2891 char *p, *ep;
2892 if (unsetenv_others) {
2893 envtbl = rb_hash_new();
2894 }
2895 else {
2896 envtbl = rb_env_to_hash();
2897 }
2898 hide_obj(envtbl);
2899 if (envopts != Qfalse) {
2900 st_table *stenv = RHASH_TBL_RAW(envtbl);
2901 long i;
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);
2906 if (NIL_P(val)) {
2907 st_data_t stkey = (st_data_t)key;
2908 st_delete(stenv, &stkey, NULL);
2909 }
2910 else {
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);
2914 }
2915 }
2916 }
2917 envp_buf = rb_str_buf_new(0);
2918 hide_obj(envp_buf);
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));
2921 hide_obj(envp_str);
2922 p = RSTRING_PTR(envp_buf);
2923 ep = p + RSTRING_LEN(envp_buf);
2924 while (p < ep) {
2925 rb_str_buf_cat(envp_str, (char *)&p, sizeof(p));
2926 p += strlen(p) + 1;
2927 }
2928 p = NULL;
2929 rb_str_buf_cat(envp_str, (char *)&p, sizeof(p));
2930 eargp->envp_str =
2931 rb_imemo_tmpbuf_auto_free_pointer_new_from_an_RString(envp_str);
2932 eargp->envp_buf = envp_buf;
2933
2934 /*
2935 char **tmp_envp = (char **)RSTRING_PTR(envp_str);
2936 while (*tmp_envp) {
2937 printf("%s\n", *tmp_envp);
2938 tmp_envp++;
2939 }
2940 */
2941 }
2942
2943 RB_GC_GUARD(execarg_obj);
2944 return Qnil;
2945}
2946
2947void
2948rb_execarg_parent_start(VALUE execarg_obj)
2949{
2950 int state;
2951 rb_protect(rb_execarg_parent_start1, execarg_obj, &state);
2952 if (state) {
2953 rb_execarg_parent_end(execarg_obj);
2954 rb_jump_tag(state);
2955 }
2956}
2957
2958static VALUE
2959execarg_parent_end(VALUE execarg_obj)
2960{
2961 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2962 int err = errno;
2963 VALUE ary;
2964
2965 ary = eargp->fd_open;
2966 if (ary != Qfalse) {
2967 long i;
2968 for (i = 0; i < RARRAY_LEN(ary); i++) {
2969 VALUE elt = RARRAY_AREF(ary, i);
2970 VALUE param = RARRAY_AREF(elt, 1);
2971 VALUE fd2v;
2972 int fd2;
2973 fd2v = RARRAY_AREF(param, 3);
2974 if (!NIL_P(fd2v)) {
2975 fd2 = FIX2INT(fd2v);
2976 parent_redirect_close(fd2);
2977 RARRAY_ASET(param, 3, Qnil);
2978 }
2979 }
2980 }
2981
2982 errno = err;
2983 RB_GC_GUARD(execarg_obj);
2984 return execarg_obj;
2985}
2986
2987void
2988rb_execarg_parent_end(VALUE execarg_obj)
2989{
2990 execarg_parent_end(execarg_obj);
2991 RB_GC_GUARD(execarg_obj);
2992}
2993
2994static void
2995rb_exec_fail(struct rb_execarg *eargp, int err, const char *errmsg)
2996{
2997 if (!errmsg || !*errmsg) return;
2998 if (strcmp(errmsg, "chdir") == 0) {
2999 rb_sys_fail_str(eargp->chdir_dir);
3000 }
3001 rb_sys_fail(errmsg);
3002}
3003
3004#if 0
3005void
3006rb_execarg_fail(VALUE execarg_obj, int err, const char *errmsg)
3007{
3008 if (!errmsg || !*errmsg) return;
3009 rb_exec_fail(rb_execarg_get(execarg_obj), err, errmsg);
3010 RB_GC_GUARD(execarg_obj);
3011}
3012#endif
3013
3014 VALUE
3015rb_f_exec(int argc, const VALUE *argv)
3016{
3017 VALUE execarg_obj, fail_str;
3018 struct rb_execarg *eargp;
3019#define CHILD_ERRMSG_BUFLEN 80
3020 char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' };
3021 int err, state;
3022
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 */
3026
3027 rb_protect(rb_execarg_parent_start1, execarg_obj, &state);
3028 if (state) {
3029 execarg_parent_end(execarg_obj);
3030 after_exec(); /* restart timer thread */
3031 rb_jump_tag(state);
3032 }
3033
3034 fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
3035
3036 err = exec_async_signal_safe(eargp, errmsg, sizeof(errmsg));
3037 after_exec(); /* restart timer thread */
3038
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);
3043}
3044
3045NORETURN(static VALUE f_exec(int c, const VALUE *a, VALUE _));
3046
3047/*
3048 * call-seq:
3049 * exec([env, ] command_line, options = {})
3050 * exec([env, ] exe_path, *args, options = {})
3051 *
3052 * Replaces the current process by doing one of the following:
3053 *
3054 * - Passing string +command_line+ to the shell.
3055 * - Invoking the executable at +exe_path+.
3056 *
3057 * This method has potential security vulnerabilities if called with untrusted input;
3058 * see {Command Injection}[rdoc-ref:command_injection.rdoc].
3059 *
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).
3064 *
3065 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
3066 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
3067 *
3068 * Argument +options+ is a hash of options for the new process;
3069 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
3070 *
3071 * The first required argument is one of the following:
3072 *
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.
3077 *
3078 * <b>Argument +command_line+</b>
3079 *
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:
3083 *
3084 * exec('if true; then echo "Foo"; fi') # Shell reserved word.
3085 * exec('echo') # Built-in.
3086 * exec('date > date.tmp') # Contains meta character.
3087 *
3088 * The command line may also contain arguments and options for the command:
3089 *
3090 * exec('echo "Foo"')
3091 *
3092 * Output:
3093 *
3094 * Foo
3095 *
3096 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
3097 *
3098 * Raises an exception if the new process could not execute.
3099 *
3100 * <b>Argument +exe_path+</b>
3101 *
3102 * Argument +exe_path+ is one of the following:
3103 *
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.
3107 *
3108 * Example:
3109 *
3110 * exec('/usr/bin/date')
3111 *
3112 * Output:
3113 *
3114 * Sat Aug 26 09:38:00 AM CDT 2023
3115 *
3116 * Ruby invokes the executable directly, with no shell and no shell expansion:
3117 *
3118 * exec('doesnt_exist') # Raises Errno::ENOENT
3119 *
3120 * If one or more +args+ is given, each is an argument or option
3121 * to be passed to the executable:
3122 *
3123 * exec('echo', 'C*')
3124 * exec('echo', 'hello', 'world')
3125 *
3126 * Output:
3127 *
3128 * C*
3129 * hello world
3130 *
3131 * Raises an exception if the new process could not execute.
3132 */
3133
3134static VALUE
3135f_exec(int c, const VALUE *a, VALUE _)
3136{
3137 rb_f_exec(c, a);
3138 UNREACHABLE_RETURN(Qnil);
3139}
3140
3141#define ERRMSG(str) do { if (errmsg && 0 < errmsg_buflen) strlcpy(errmsg, (str), errmsg_buflen); } while (0)
3142#define ERRMSG1(str, a) do { if (errmsg && 0 < errmsg_buflen) snprintf(errmsg, errmsg_buflen, (str), (a)); } while (0)
3143#define ERRMSG2(str, a, b) do { if (errmsg && 0 < errmsg_buflen) snprintf(errmsg, errmsg_buflen, (str), (a), (b)); } while (0)
3144
3145static int fd_get_cloexec(int fd, char *errmsg, size_t errmsg_buflen);
3146static int fd_set_cloexec(int fd, char *errmsg, size_t errmsg_buflen);
3147static int fd_clear_cloexec(int fd, char *errmsg, size_t errmsg_buflen);
3148
3149static int
3150save_redirect_fd(int fd, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3151{
3152 if (sargp) {
3153 VALUE newary, redirection;
3154 int save_fd = redirect_cloexec_dup(fd), cloexec;
3155 if (save_fd == -1) {
3156 if (errno == EBADF)
3157 return 0;
3158 ERRMSG("dup");
3159 return -1;
3160 }
3161 rb_update_max_fd(save_fd);
3162 newary = sargp->fd_dup2;
3163 if (newary == Qfalse) {
3164 newary = hide_obj(rb_ary_new());
3165 sargp->fd_dup2 = newary;
3166 }
3167 cloexec = fd_get_cloexec(fd, errmsg, errmsg_buflen);
3168 redirection = hide_obj(rb_assoc_new(INT2FIX(fd), INT2FIX(save_fd)));
3169 if (cloexec) rb_ary_push(redirection, Qtrue);
3170 rb_ary_push(newary, redirection);
3171
3172 newary = sargp->fd_close;
3173 if (newary == Qfalse) {
3174 newary = hide_obj(rb_ary_new());
3175 sargp->fd_close = newary;
3176 }
3177 rb_ary_push(newary, hide_obj(rb_assoc_new(INT2FIX(save_fd), Qnil)));
3178 }
3179
3180 return 0;
3181}
3182
3183static int
3184intcmp(const void *a, const void *b)
3185{
3186 return *(int*)a - *(int*)b;
3187}
3188
3189static int
3190intrcmp(const void *a, const void *b)
3191{
3192 return *(int*)b - *(int*)a;
3193}
3194
3195 struct run_exec_dup2_fd_pair {
3196 int oldfd;
3197 int newfd;
3198 long older_index;
3199 long num_newer;
3200 int cloexec;
3201};
3202
3203static long
3204run_exec_dup2_tmpbuf_size(long n)
3205{
3206 return sizeof(struct run_exec_dup2_fd_pair) * n;
3207}
3208
3209/* This function should be async-signal-safe. Actually it is. */
3210static int
3211fd_get_cloexec(int fd, char *errmsg, size_t errmsg_buflen)
3212{
3213#ifdef F_GETFD
3214 int ret = 0;
3215 ret = fcntl(fd, F_GETFD); /* async-signal-safe */
3216 if (ret == -1) {
3217 ERRMSG("fcntl(F_GETFD)");
3218 return -1;
3219 }
3220 if (ret & FD_CLOEXEC) return 1;
3221#endif
3222 return 0;
3223}
3224
3225/* This function should be async-signal-safe. Actually it is. */
3226static int
3227fd_set_cloexec(int fd, char *errmsg, size_t errmsg_buflen)
3228{
3229#ifdef F_GETFD
3230 int ret = 0;
3231 ret = fcntl(fd, F_GETFD); /* async-signal-safe */
3232 if (ret == -1) {
3233 ERRMSG("fcntl(F_GETFD)");
3234 return -1;
3235 }
3236 if (!(ret & FD_CLOEXEC)) {
3237 ret |= FD_CLOEXEC;
3238 ret = fcntl(fd, F_SETFD, ret); /* async-signal-safe */
3239 if (ret == -1) {
3240 ERRMSG("fcntl(F_SETFD)");
3241 return -1;
3242 }
3243 }
3244#endif
3245 return 0;
3246}
3247
3248/* This function should be async-signal-safe. Actually it is. */
3249static int
3250fd_clear_cloexec(int fd, char *errmsg, size_t errmsg_buflen)
3251{
3252#ifdef F_GETFD
3253 int ret;
3254 ret = fcntl(fd, F_GETFD); /* async-signal-safe */
3255 if (ret == -1) {
3256 ERRMSG("fcntl(F_GETFD)");
3257 return -1;
3258 }
3259 if (ret & FD_CLOEXEC) {
3260 ret &= ~FD_CLOEXEC;
3261 ret = fcntl(fd, F_SETFD, ret); /* async-signal-safe */
3262 if (ret == -1) {
3263 ERRMSG("fcntl(F_SETFD)");
3264 return -1;
3265 }
3266 }
3267#endif
3268 return 0;
3269}
3270
3271/* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3272static int
3273run_exec_dup2(VALUE ary, VALUE tmpbuf, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3274{
3275 long n, i;
3276 int ret;
3277 int extra_fd = -1;
3278 struct rb_imemo_tmpbuf_struct *buf = (void *)tmpbuf;
3279 struct run_exec_dup2_fd_pair *pairs = (void *)buf->ptr;
3280
3281 n = RARRAY_LEN(ary);
3282
3283 /* initialize oldfd and newfd: O(n) */
3284 for (i = 0; i < n; i++) {
3285 VALUE elt = RARRAY_AREF(ary, i);
3286 pairs[i].oldfd = FIX2INT(RARRAY_AREF(elt, 1));
3287 pairs[i].newfd = FIX2INT(RARRAY_AREF(elt, 0)); /* unique */
3288 pairs[i].cloexec = RARRAY_LEN(elt) > 2 && RTEST(RARRAY_AREF(elt, 2));
3289 pairs[i].older_index = -1;
3290 }
3291
3292 /* sort the table by oldfd: O(n log n) */
3293 if (!sargp)
3294 qsort(pairs, n, sizeof(struct run_exec_dup2_fd_pair), intcmp); /* hopefully async-signal-safe */
3295 else
3296 qsort(pairs, n, sizeof(struct run_exec_dup2_fd_pair), intrcmp);
3297
3298 /* initialize older_index and num_newer: O(n log n) */
3299 for (i = 0; i < n; i++) {
3300 int newfd = pairs[i].newfd;
3301 struct run_exec_dup2_fd_pair key, *found;
3302 key.oldfd = newfd;
3303 found = bsearch(&key, pairs, n, sizeof(struct run_exec_dup2_fd_pair), intcmp); /* hopefully async-signal-safe */
3304 pairs[i].num_newer = 0;
3305 if (found) {
3306 while (pairs < found && (found-1)->oldfd == newfd)
3307 found--;
3308 while (found < pairs+n && found->oldfd == newfd) {
3309 pairs[i].num_newer++;
3310 found->older_index = i;
3311 found++;
3312 }
3313 }
3314 }
3315
3316 /* non-cyclic redirection: O(n) */
3317 for (i = 0; i < n; i++) {
3318 long j = i;
3319 while (j != -1 && pairs[j].oldfd != -1 && pairs[j].num_newer == 0) {
3320 if (save_redirect_fd(pairs[j].newfd, sargp, errmsg, errmsg_buflen) < 0) /* async-signal-safe */
3321 goto fail;
3322 ret = redirect_dup2(pairs[j].oldfd, pairs[j].newfd); /* async-signal-safe */
3323 if (ret == -1) {
3324 ERRMSG("dup2");
3325 goto fail;
3326 }
3327 if (pairs[j].cloexec &&
3328 fd_set_cloexec(pairs[j].newfd, errmsg, errmsg_buflen)) {
3329 goto fail;
3330 }
3331 rb_update_max_fd(pairs[j].newfd); /* async-signal-safe but don't need to call it in a child process. */
3332 pairs[j].oldfd = -1;
3333 j = pairs[j].older_index;
3334 if (j != -1)
3335 pairs[j].num_newer--;
3336 }
3337 }
3338
3339 /* cyclic redirection: O(n) */
3340 for (i = 0; i < n; i++) {
3341 long j;
3342 if (pairs[i].oldfd == -1)
3343 continue;
3344 if (pairs[i].oldfd == pairs[i].newfd) { /* self cycle */
3345 if (fd_clear_cloexec(pairs[i].oldfd, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3346 goto fail;
3347 pairs[i].oldfd = -1;
3348 continue;
3349 }
3350 if (extra_fd == -1) {
3351 extra_fd = redirect_dup(pairs[i].oldfd); /* async-signal-safe */
3352 if (extra_fd == -1) {
3353 ERRMSG("dup");
3354 goto fail;
3355 }
3356 // without this, kqueue timer_th.event_fd fails with a reserved FD did not have close-on-exec
3357 // in #assert_close_on_exec because the FD_CLOEXEC is not dup'd by default
3358 if (fd_get_cloexec(pairs[i].oldfd, errmsg, errmsg_buflen)) {
3359 if (fd_set_cloexec(extra_fd, errmsg, errmsg_buflen)) {
3360 goto fail;
3361 }
3362 }
3363 rb_update_max_fd(extra_fd);
3364 }
3365 else {
3366 ret = redirect_dup2(pairs[i].oldfd, extra_fd); /* async-signal-safe */
3367 if (ret == -1) {
3368 ERRMSG("dup2");
3369 goto fail;
3370 }
3371 rb_update_max_fd(extra_fd);
3372 }
3373 pairs[i].oldfd = extra_fd;
3374 j = pairs[i].older_index;
3375 pairs[i].older_index = -1;
3376 while (j != -1) {
3377 ret = redirect_dup2(pairs[j].oldfd, pairs[j].newfd); /* async-signal-safe */
3378 if (ret == -1) {
3379 ERRMSG("dup2");
3380 goto fail;
3381 }
3382 rb_update_max_fd(ret);
3383 pairs[j].oldfd = -1;
3384 j = pairs[j].older_index;
3385 }
3386 }
3387 if (extra_fd != -1) {
3388 ret = redirect_close(extra_fd); /* async-signal-safe */
3389 if (ret == -1) {
3390 ERRMSG("close");
3391 goto fail;
3392 }
3393 }
3394
3395 return 0;
3396
3397 fail:
3398 return -1;
3399}
3400
3401/* This function should be async-signal-safe. Actually it is. */
3402static int
3403run_exec_close(VALUE ary, char *errmsg, size_t errmsg_buflen)
3404{
3405 long i;
3406 int ret;
3407
3408 for (i = 0; i < RARRAY_LEN(ary); i++) {
3409 VALUE elt = RARRAY_AREF(ary, i);
3410 int fd = FIX2INT(RARRAY_AREF(elt, 0));
3411 ret = redirect_close(fd); /* async-signal-safe */
3412 if (ret == -1) {
3413 ERRMSG("close");
3414 return -1;
3415 }
3416 }
3417 return 0;
3418}
3419
3420/* This function should be async-signal-safe when sargp is NULL. Actually it is. */
3421static int
3422run_exec_dup2_child(VALUE ary, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3423{
3424 long i;
3425 int ret;
3426
3427 for (i = 0; i < RARRAY_LEN(ary); i++) {
3428 VALUE elt = RARRAY_AREF(ary, i);
3429 int newfd = FIX2INT(RARRAY_AREF(elt, 0));
3430 int oldfd = FIX2INT(RARRAY_AREF(elt, 1));
3431
3432 if (save_redirect_fd(newfd, sargp, errmsg, errmsg_buflen) < 0) /* async-signal-safe */
3433 return -1;
3434 ret = redirect_dup2(oldfd, newfd); /* async-signal-safe */
3435 if (ret == -1) {
3436 ERRMSG("dup2");
3437 return -1;
3438 }
3439 rb_update_max_fd(newfd);
3440 }
3441 return 0;
3442}
3443
3444#ifdef HAVE_SETPGID
3445/* This function should be async-signal-safe when sargp is NULL. Actually it is. */
3446static int
3447run_exec_pgroup(const struct rb_execarg *eargp, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3448{
3449 /*
3450 * If FD_CLOEXEC is available, rb_fork_async_signal_safe waits the child's execve.
3451 * So setpgid is done in the child when rb_fork_async_signal_safe is returned in
3452 * the parent.
3453 * No race condition, even without setpgid from the parent.
3454 * (Is there an environment which has setpgid but no FD_CLOEXEC?)
3455 */
3456 int ret;
3457 rb_pid_t pgroup;
3458
3459 pgroup = eargp->pgroup_pgid;
3460 if (pgroup == -1)
3461 return 0;
3462
3463 if (sargp) {
3464 /* maybe meaningless with no fork environment... */
3465 sargp->pgroup_given = 1;
3466 sargp->pgroup_pgid = getpgrp();
3467 }
3468
3469 if (pgroup == 0) {
3470 pgroup = getpid(); /* async-signal-safe */
3471 }
3472 ret = setpgid(getpid(), pgroup); /* async-signal-safe */
3473 if (ret == -1) ERRMSG("setpgid");
3474 return ret;
3475}
3476#endif
3477
3478#if defined(HAVE_SETRLIMIT) && defined(RLIM2NUM)
3479/* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3480static int
3481run_exec_rlimit(VALUE ary, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3482{
3483 long i;
3484 for (i = 0; i < RARRAY_LEN(ary); i++) {
3485 VALUE elt = RARRAY_AREF(ary, i);
3486 int rtype = NUM2INT(RARRAY_AREF(elt, 0));
3487 struct rlimit rlim;
3488 if (sargp) {
3489 VALUE tmp, newary;
3490 if (getrlimit(rtype, &rlim) == -1) {
3491 ERRMSG("getrlimit");
3492 return -1;
3493 }
3494 tmp = hide_obj(rb_ary_new3(3, RARRAY_AREF(elt, 0),
3495 RLIM2NUM(rlim.rlim_cur),
3496 RLIM2NUM(rlim.rlim_max)));
3497 if (sargp->rlimit_limits == Qfalse)
3498 newary = sargp->rlimit_limits = hide_obj(rb_ary_new());
3499 else
3500 newary = sargp->rlimit_limits;
3501 rb_ary_push(newary, tmp);
3502 }
3503 rlim.rlim_cur = NUM2RLIM(RARRAY_AREF(elt, 1));
3504 rlim.rlim_max = NUM2RLIM(RARRAY_AREF(elt, 2));
3505 if (setrlimit(rtype, &rlim) == -1) { /* hopefully async-signal-safe */
3506 ERRMSG("setrlimit");
3507 return -1;
3508 }
3509 }
3510 return 0;
3511}
3512#endif
3513
3514#if !defined(HAVE_WORKING_FORK)
3515static VALUE
3516save_env_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
3517{
3518 rb_ary_push(ary, hide_obj(rb_ary_dup(argv[0])));
3519 return Qnil;
3520}
3521
3522static void
3523save_env(struct rb_execarg *sargp)
3524{
3525 if (!sargp)
3526 return;
3527 if (sargp->env_modification == Qfalse) {
3528 VALUE env = rb_envtbl();
3529 if (RTEST(env)) {
3530 VALUE ary = hide_obj(rb_ary_new());
3531 rb_block_call(env, idEach, 0, 0, save_env_i,
3532 (VALUE)ary);
3533 sargp->env_modification = ary;
3534 }
3535 sargp->unsetenv_others_given = 1;
3536 sargp->unsetenv_others_do = 1;
3537 }
3538}
3539#endif
3540
3541#ifdef _WIN32
3542#undef chdir
3543#define chdir(p) rb_w32_uchdir(p)
3544#endif
3545
3546/* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3547int
3548rb_execarg_run_options(const struct rb_execarg *eargp, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3549{
3550 VALUE obj;
3551
3552 if (sargp) {
3553 /* assume that sargp is always NULL on fork-able environments */
3554 MEMZERO(sargp, struct rb_execarg, 1);
3555 sargp->redirect_fds = Qnil;
3556 }
3557
3558#ifdef HAVE_SETPGID
3559 if (eargp->pgroup_given) {
3560 if (run_exec_pgroup(eargp, sargp, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3561 return -1;
3562 }
3563#endif
3564
3565#if defined(HAVE_SETRLIMIT) && defined(RLIM2NUM)
3566 obj = eargp->rlimit_limits;
3567 if (obj != Qfalse) {
3568 if (run_exec_rlimit(obj, sargp, errmsg, errmsg_buflen) == -1) /* hopefully async-signal-safe */
3569 return -1;
3570 }
3571#endif
3572
3573#if !defined(HAVE_WORKING_FORK)
3574 if (eargp->unsetenv_others_given && eargp->unsetenv_others_do) {
3575 save_env(sargp);
3576 rb_env_clear();
3577 }
3578
3579 obj = eargp->env_modification;
3580 if (obj != Qfalse) {
3581 long i;
3582 save_env(sargp);
3583 for (i = 0; i < RARRAY_LEN(obj); i++) {
3584 VALUE pair = RARRAY_AREF(obj, i);
3585 VALUE key = RARRAY_AREF(pair, 0);
3586 VALUE val = RARRAY_AREF(pair, 1);
3587 if (NIL_P(val))
3588 ruby_setenv(StringValueCStr(key), 0);
3589 else
3590 ruby_setenv(StringValueCStr(key), StringValueCStr(val));
3591 }
3592 }
3593#endif
3594
3595 if (eargp->umask_given) {
3596 mode_t mask = eargp->umask_mask;
3597 mode_t oldmask = umask(mask); /* never fail */ /* async-signal-safe */
3598 if (sargp) {
3599 sargp->umask_given = 1;
3600 sargp->umask_mask = oldmask;
3601 }
3602 }
3603
3604 obj = eargp->fd_dup2;
3605 if (obj != Qfalse) {
3606 if (run_exec_dup2(obj, eargp->dup2_tmpbuf, sargp, errmsg, errmsg_buflen) == -1) /* hopefully async-signal-safe */
3607 return -1;
3608 }
3609
3610 obj = eargp->fd_close;
3611 if (obj != Qfalse) {
3612 if (sargp)
3613 rb_warn("cannot close fd before spawn");
3614 else {
3615 if (run_exec_close(obj, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3616 return -1;
3617 }
3618 }
3619
3620#ifdef HAVE_WORKING_FORK
3621 if (eargp->close_others_do) {
3622 rb_close_before_exec(3, eargp->close_others_maxhint, eargp->redirect_fds); /* async-signal-safe */
3623 }
3624#endif
3625
3626 obj = eargp->fd_dup2_child;
3627 if (obj != Qfalse) {
3628 if (run_exec_dup2_child(obj, sargp, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3629 return -1;
3630 }
3631
3632 if (eargp->chdir_given) {
3633 if (sargp) {
3634 sargp->chdir_given = 1;
3635 sargp->chdir_dir = hide_obj(rb_dir_getwd_ospath());
3636 }
3637 if (chdir(RSTRING_PTR(eargp->chdir_dir)) == -1) { /* async-signal-safe */
3638 ERRMSG("chdir");
3639 return -1;
3640 }
3641 }
3642
3643#ifdef HAVE_SETGID
3644 if (eargp->gid_given) {
3645 if (setgid(eargp->gid) < 0) {
3646 ERRMSG("setgid");
3647 return -1;
3648 }
3649 }
3650#endif
3651#ifdef HAVE_SETUID
3652 if (eargp->uid_given) {
3653 if (setuid(eargp->uid) < 0) {
3654 ERRMSG("setuid");
3655 return -1;
3656 }
3657 }
3658#endif
3659
3660 if (sargp) {
3661 VALUE ary = sargp->fd_dup2;
3662 if (ary != Qfalse) {
3663 rb_execarg_allocate_dup2_tmpbuf(sargp, RARRAY_LEN(ary));
3664 }
3665 }
3666 {
3667 int preserve = errno;
3668 stdfd_clear_nonblock();
3669 errno = preserve;
3670 }
3671
3672 return 0;
3673}
3674
3675/* This function should be async-signal-safe. Hopefully it is. */
3676int
3677rb_exec_async_signal_safe(const struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
3678{
3679 errno = exec_async_signal_safe(eargp, errmsg, errmsg_buflen);
3680 return -1;
3681}
3682
3683static int
3684exec_async_signal_safe(const struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
3685{
3686#if !defined(HAVE_WORKING_FORK)
3687 struct rb_execarg sarg, *const sargp = &sarg;
3688#else
3689 struct rb_execarg *const sargp = NULL;
3690#endif
3691 int err;
3692
3693 if (rb_execarg_run_options(eargp, sargp, errmsg, errmsg_buflen) < 0) { /* hopefully async-signal-safe */
3694 return errno;
3695 }
3696
3697 if (eargp->use_shell) {
3698 err = proc_exec_sh(RSTRING_PTR(eargp->invoke.sh.shell_script), eargp->envp_str); /* async-signal-safe */
3699 }
3700 else {
3701 char *abspath = NULL;
3702 if (!NIL_P(eargp->invoke.cmd.command_abspath))
3703 abspath = RSTRING_PTR(eargp->invoke.cmd.command_abspath);
3704 err = proc_exec_cmd(abspath, eargp->invoke.cmd.argv_str, eargp->envp_str); /* async-signal-safe */
3705 }
3706#if !defined(HAVE_WORKING_FORK)
3707 rb_execarg_run_options(sargp, NULL, errmsg, errmsg_buflen);
3708#endif
3709
3710 return err;
3711}
3712
3713#ifdef HAVE_WORKING_FORK
3714/* This function should be async-signal-safe. Hopefully it is. */
3715static int
3716rb_exec_atfork(void* arg, char *errmsg, size_t errmsg_buflen)
3717{
3718 return rb_exec_async_signal_safe(arg, errmsg, errmsg_buflen); /* hopefully async-signal-safe */
3719}
3720
3721static VALUE
3722proc_syswait(VALUE pid)
3723{
3724 rb_syswait((rb_pid_t)pid);
3725 return Qnil;
3726}
3727
3728static int
3729move_fds_to_avoid_crash(int *fdp, int n, VALUE fds)
3730{
3731 int min = 0;
3732 int i;
3733 for (i = 0; i < n; i++) {
3734 int ret;
3735 while (RTEST(rb_hash_lookup(fds, INT2FIX(fdp[i])))) {
3736 if (min <= fdp[i])
3737 min = fdp[i]+1;
3738 while (RTEST(rb_hash_lookup(fds, INT2FIX(min))))
3739 min++;
3740 ret = rb_cloexec_fcntl_dupfd(fdp[i], min);
3741 if (ret == -1)
3742 return -1;
3743 rb_update_max_fd(ret);
3744 close(fdp[i]);
3745 fdp[i] = ret;
3746 }
3747 }
3748 return 0;
3749}
3750
3751static int
3752pipe_nocrash(int filedes[2], VALUE fds)
3753{
3754 int ret;
3755 ret = rb_pipe(filedes);
3756 if (ret == -1)
3757 return -1;
3758 if (RTEST(fds)) {
3759 int save = errno;
3760 if (move_fds_to_avoid_crash(filedes, 2, fds) == -1) {
3761 close(filedes[0]);
3762 close(filedes[1]);
3763 return -1;
3764 }
3765 errno = save;
3766 }
3767 return ret;
3768}
3769
3770#ifndef O_BINARY
3771#define O_BINARY 0
3772#endif
3773
3774static VALUE
3775rb_thread_sleep_that_takes_VALUE_as_sole_argument(VALUE n)
3776{
3777 rb_thread_sleep(NUM2INT(n));
3778 return Qundef;
3779}
3780
3781static int
3782handle_fork_error(int err, struct rb_process_status *status, int *ep, volatile int *try_gc_p)
3783{
3784 int state = 0;
3785
3786 switch (err) {
3787 case ENOMEM:
3788 if ((*try_gc_p)-- > 0 && !rb_during_gc()) {
3789 rb_gc();
3790 return 0;
3791 }
3792 break;
3793 case EAGAIN:
3794#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
3795 case EWOULDBLOCK:
3796#endif
3797 if (!status && !ep) {
3798 rb_thread_sleep(1);
3799 return 0;
3800 }
3801 else {
3802 rb_protect(rb_thread_sleep_that_takes_VALUE_as_sole_argument, INT2FIX(1), &state);
3803 if (status) status->status = state;
3804 if (!state) return 0;
3805 }
3806 break;
3807 }
3808 if (ep) {
3809 close(ep[0]);
3810 close(ep[1]);
3811 errno = err;
3812 }
3813 if (state && !status) rb_jump_tag(state);
3814 return -1;
3815}
3816
3817#define prefork() ( \
3818 rb_io_flush(rb_stdout), \
3819 rb_io_flush(rb_stderr) \
3820 )
3821
3822/*
3823 * Forks child process, and returns the process ID in the parent
3824 * process.
3825 *
3826 * If +status+ is given, protects from any exceptions and sets the
3827 * jump status to it, and returns -1. If failed to fork new process
3828 * but no exceptions occurred, sets 0 to it. Otherwise, if forked
3829 * successfully, the value of +status+ is undetermined.
3830 *
3831 * In the child process, just returns 0 if +chfunc+ is +NULL+.
3832 * Otherwise +chfunc+ will be called with +charg+, and then the child
3833 * process exits with +EXIT_SUCCESS+ when it returned zero.
3834 *
3835 * In the case of the function is called and returns non-zero value,
3836 * the child process exits with non-+EXIT_SUCCESS+ value (normally
3837 * 127). And, on the platforms where +FD_CLOEXEC+ is available,
3838 * +errno+ is propagated to the parent process, and this function
3839 * returns -1 in the parent process. On the other platforms, just
3840 * returns pid.
3841 *
3842 * If fds is not Qnil, internal pipe for the errno propagation is
3843 * arranged to avoid conflicts of the hash keys in +fds+.
3844 *
3845 * +chfunc+ must not raise any exceptions.
3846 */
3847
3848static ssize_t
3849write_retry(int fd, const void *buf, size_t len)
3850{
3851 ssize_t w;
3852
3853 do {
3854 w = write(fd, buf, len);
3855 } while (w < 0 && errno == EINTR);
3856
3857 return w;
3858}
3859
3860static ssize_t
3861read_retry(int fd, void *buf, size_t len)
3862{
3863 ssize_t r;
3864
3865 if (set_blocking(fd) != 0) {
3866#ifndef _WIN32
3867 rb_async_bug_errno("set_blocking failed reading child error", errno);
3868#endif
3869 }
3870
3871 do {
3872 r = read(fd, buf, len);
3873 } while (r < 0 && errno == EINTR);
3874
3875 return r;
3876}
3877
3878static void
3879send_child_error(int fd, char *errmsg, size_t errmsg_buflen)
3880{
3881 int err;
3882
3883 err = errno;
3884 if (write_retry(fd, &err, sizeof(err)) < 0) err = errno;
3885 if (errmsg && 0 < errmsg_buflen) {
3886 errmsg[errmsg_buflen-1] = '\0';
3887 errmsg_buflen = strlen(errmsg);
3888 if (errmsg_buflen > 0 && write_retry(fd, errmsg, errmsg_buflen) < 0)
3889 err = errno;
3890 }
3891}
3892
3893static int
3894recv_child_error(int fd, int *errp, char *errmsg, size_t errmsg_buflen)
3895{
3896 int err;
3897 ssize_t size;
3898 if ((size = read_retry(fd, &err, sizeof(err))) < 0) {
3899 err = errno;
3900 }
3901 *errp = err;
3902 if (size == sizeof(err) &&
3903 errmsg && 0 < errmsg_buflen) {
3904 ssize_t ret = read_retry(fd, errmsg, errmsg_buflen-1);
3905 if (0 <= ret) {
3906 errmsg[ret] = '\0';
3907 }
3908 }
3909 close(fd);
3910 return size != 0;
3911}
3912
3913#ifdef HAVE_WORKING_VFORK
3914#if !defined(HAVE_GETRESUID) && defined(HAVE_GETUIDX)
3915/* AIX 7.1 */
3916static int
3917getresuid(rb_uid_t *ruid, rb_uid_t *euid, rb_uid_t *suid)
3918{
3919 rb_uid_t ret;
3920
3921 *ruid = getuid();
3922 *euid = geteuid();
3923 ret = getuidx(ID_SAVED);
3924 if (ret == (rb_uid_t)-1)
3925 return -1;
3926 *suid = ret;
3927 return 0;
3928}
3929#define HAVE_GETRESUID
3930#endif
3931
3932#if !defined(HAVE_GETRESGID) && defined(HAVE_GETGIDX)
3933/* AIX 7.1 */
3934static int
3935getresgid(rb_gid_t *rgid, rb_gid_t *egid, rb_gid_t *sgid)
3936{
3937 rb_gid_t ret;
3938
3939 *rgid = getgid();
3940 *egid = getegid();
3941 ret = getgidx(ID_SAVED);
3942 if (ret == (rb_gid_t)-1)
3943 return -1;
3944 *sgid = ret;
3945 return 0;
3946}
3947#define HAVE_GETRESGID
3948#endif
3949
3950static int
3951has_privilege(void)
3952{
3953 /*
3954 * has_privilege() is used to choose vfork() or fork().
3955 *
3956 * If the process has privilege, the parent process or
3957 * the child process can change UID/GID.
3958 * If vfork() is used to create the child process and
3959 * the parent or child process change effective UID/GID,
3960 * different privileged processes shares memory.
3961 * It is a bad situation.
3962 * So, fork() should be used.
3963 */
3964
3965 rb_uid_t ruid, euid;
3966 rb_gid_t rgid, egid;
3967
3968#if defined HAVE_ISSETUGID
3969 if (issetugid())
3970 return 1;
3971#endif
3972
3973#ifdef HAVE_GETRESUID
3974 {
3975 int ret;
3976 rb_uid_t suid;
3977 ret = getresuid(&ruid, &euid, &suid);
3978 if (ret == -1)
3979 rb_sys_fail("getresuid(2)");
3980 if (euid != suid)
3981 return 1;
3982 }
3983#else
3984 ruid = getuid();
3985 euid = geteuid();
3986#endif
3987
3988 if (euid == 0 || euid != ruid)
3989 return 1;
3990
3991#ifdef HAVE_GETRESGID
3992 {
3993 int ret;
3994 rb_gid_t sgid;
3995 ret = getresgid(&rgid, &egid, &sgid);
3996 if (ret == -1)
3997 rb_sys_fail("getresgid(2)");
3998 if (egid != sgid)
3999 return 1;
4000 }
4001#else
4002 rgid = getgid();
4003 egid = getegid();
4004#endif
4005
4006 if (egid != rgid)
4007 return 1;
4008
4009 return 0;
4010}
4011#endif
4012
4013struct child_handler_disabler_state
4014{
4015 sigset_t sigmask;
4016};
4017
4018static void
4019disable_child_handler_before_fork(struct child_handler_disabler_state *old)
4020{
4021#ifdef HAVE_PTHREAD_SIGMASK
4022 int ret;
4023 sigset_t all;
4024
4025 ret = sigfillset(&all);
4026 if (ret == -1)
4027 rb_sys_fail("sigfillset");
4028
4029 ret = pthread_sigmask(SIG_SETMASK, &all, &old->sigmask); /* not async-signal-safe */
4030 if (ret != 0) {
4031 rb_syserr_fail(ret, "pthread_sigmask");
4032 }
4033#else
4034# pragma GCC warning "pthread_sigmask on fork is not available. potentially dangerous"
4035#endif
4036}
4037
4038static void
4039disable_child_handler_fork_parent(struct child_handler_disabler_state *old)
4040{
4041#ifdef HAVE_PTHREAD_SIGMASK
4042 int ret;
4043
4044 ret = pthread_sigmask(SIG_SETMASK, &old->sigmask, NULL); /* not async-signal-safe */
4045 if (ret != 0) {
4046 rb_syserr_fail(ret, "pthread_sigmask");
4047 }
4048#else
4049# pragma GCC warning "pthread_sigmask on fork is not available. potentially dangerous"
4050#endif
4051}
4052
4053/* This function should be async-signal-safe. Actually it is. */
4054static int
4055disable_child_handler_fork_child(struct child_handler_disabler_state *old, char *errmsg, size_t errmsg_buflen)
4056{
4057 int sig;
4058 int ret;
4059
4060 for (sig = 1; sig < NSIG; sig++) {
4061 sig_t handler = signal(sig, SIG_DFL);
4062
4063 if (handler == SIG_ERR && errno == EINVAL) {
4064 continue; /* Ignore invalid signal number */
4065 }
4066 if (handler == SIG_ERR) {
4067 ERRMSG("signal to obtain old action");
4068 return -1;
4069 }
4070#ifdef SIGPIPE
4071 if (sig == SIGPIPE) {
4072 continue;
4073 }
4074#endif
4075 /* it will be reset to SIG_DFL at execve time, instead */
4076 if (handler == SIG_IGN) {
4077 signal(sig, SIG_IGN);
4078 }
4079 }
4080
4081 /* non-Ruby child process, ensure cmake can see SIGCHLD */
4082 sigemptyset(&old->sigmask);
4083 ret = sigprocmask(SIG_SETMASK, &old->sigmask, NULL); /* async-signal-safe */
4084 if (ret != 0) {
4085 ERRMSG("sigprocmask");
4086 return -1;
4087 }
4088 return 0;
4089}
4090
4091static rb_pid_t
4092retry_fork_async_signal_safe(struct rb_process_status *status, int *ep,
4093 int (*chfunc)(void*, char *, size_t), void *charg,
4094 char *errmsg, size_t errmsg_buflen,
4095 struct waitpid_state *w)
4096{
4097 rb_pid_t pid;
4098 volatile int try_gc = 1;
4099 struct child_handler_disabler_state old;
4100 int err;
4101
4102 while (1) {
4103 prefork();
4104 disable_child_handler_before_fork(&old);
4105#ifdef HAVE_WORKING_VFORK
4106 if (!has_privilege())
4107 pid = vfork();
4108 else
4109 pid = rb_fork();
4110#else
4111 pid = rb_fork();
4112#endif
4113 if (pid == 0) {/* fork succeed, child process */
4114 int ret;
4115 close(ep[0]);
4116 ret = disable_child_handler_fork_child(&old, errmsg, errmsg_buflen); /* async-signal-safe */
4117 if (ret == 0) {
4118 ret = chfunc(charg, errmsg, errmsg_buflen);
4119 if (!ret) _exit(EXIT_SUCCESS);
4120 }
4121 send_child_error(ep[1], errmsg, errmsg_buflen);
4122#if EXIT_SUCCESS == 127
4123 _exit(EXIT_FAILURE);
4124#else
4125 _exit(127);
4126#endif
4127 }
4128 err = errno;
4129 disable_child_handler_fork_parent(&old);
4130 if (0 < pid) /* fork succeed, parent process */
4131 return pid;
4132 /* fork failed */
4133 if (handle_fork_error(err, status, ep, &try_gc))
4134 return -1;
4135 }
4136}
4137
4138static rb_pid_t
4139fork_check_err(struct rb_process_status *status, int (*chfunc)(void*, char *, size_t), void *charg,
4140 VALUE fds, char *errmsg, size_t errmsg_buflen,
4141 struct rb_execarg *eargp)
4142{
4143 rb_pid_t pid;
4144 int err;
4145 int ep[2];
4146 int error_occurred;
4147
4148 struct waitpid_state *w = eargp && eargp->waitpid_state ? eargp->waitpid_state : 0;
4149
4150 if (status) status->status = 0;
4151
4152 if (pipe_nocrash(ep, fds)) return -1;
4153
4154 pid = retry_fork_async_signal_safe(status, ep, chfunc, charg, errmsg, errmsg_buflen, w);
4155
4156 if (status) status->pid = pid;
4157
4158 if (pid < 0) {
4159 if (status) status->error = errno;
4160
4161 return pid;
4162 }
4163
4164 close(ep[1]);
4165
4166 error_occurred = recv_child_error(ep[0], &err, errmsg, errmsg_buflen);
4167
4168 if (error_occurred) {
4169 if (status) {
4170 int state = 0;
4171 status->error = err;
4172
4173 VM_ASSERT((w == 0) && "only used by extensions");
4174 rb_protect(proc_syswait, (VALUE)pid, &state);
4175
4176 status->status = state;
4177 }
4178 else if (!w) {
4179 rb_syswait(pid);
4180 }
4181
4182 errno = err;
4183 return -1;
4184 }
4185
4186 return pid;
4187}
4188
4189/*
4190 * The "async_signal_safe" name is a lie, but it is used by pty.c and
4191 * maybe other exts. fork() is not async-signal-safe due to pthread_atfork
4192 * and future POSIX revisions will remove it from a list of signal-safe
4193 * functions. rb_waitpid is not async-signal-safe since RJIT, either.
4194 * For our purposes, we do not need async-signal-safety, here
4195 */
4196rb_pid_t
4197rb_fork_async_signal_safe(int *status,
4198 int (*chfunc)(void*, char *, size_t), void *charg,
4199 VALUE fds, char *errmsg, size_t errmsg_buflen)
4200{
4201 struct rb_process_status process_status;
4202
4203 rb_pid_t result = fork_check_err(&process_status, chfunc, charg, fds, errmsg, errmsg_buflen, 0);
4204
4205 if (status) {
4206 *status = process_status.status;
4207 }
4208
4209 return result;
4210}
4211
4212static rb_pid_t
4213rb_fork_ruby2(struct rb_process_status *status)
4214{
4215 rb_pid_t pid;
4216 int try_gc = 1, err;
4217 struct child_handler_disabler_state old;
4218
4219 if (status) status->status = 0;
4220
4221 while (1) {
4222 prefork();
4223
4224 before_fork_ruby();
4225 disable_child_handler_before_fork(&old);
4226 {
4227 pid = rb_fork();
4228 err = errno;
4229 if (status) {
4230 status->pid = pid;
4231 status->error = err;
4232 }
4233 }
4234 disable_child_handler_fork_parent(&old); /* yes, bad name */
4235 after_fork_ruby(pid);
4236
4237 if (pid >= 0) { /* fork succeed */
4238 return pid;
4239 }
4240
4241 /* fork failed */
4242 if (handle_fork_error(err, status, NULL, &try_gc)) {
4243 return -1;
4244 }
4245 }
4246}
4247
4248rb_pid_t
4249rb_fork_ruby(int *status)
4250{
4251 struct rb_process_status process_status = {0};
4252
4253 rb_pid_t pid = rb_fork_ruby2(&process_status);
4254
4255 if (status) *status = process_status.status;
4256
4257 return pid;
4258}
4259
4260static rb_pid_t
4261proc_fork_pid(void)
4262{
4263 rb_pid_t pid = rb_fork_ruby(NULL);
4264
4265 if (pid == -1) {
4266 rb_sys_fail("fork(2)");
4267 }
4268
4269 return pid;
4270}
4271
4272rb_pid_t
4273rb_call_proc__fork(void)
4274{
4275 ID id__fork;
4276 CONST_ID(id__fork, "_fork");
4277 if (rb_method_basic_definition_p(CLASS_OF(rb_mProcess), id__fork)) {
4278 return proc_fork_pid();
4279 }
4280 else {
4281 VALUE pid = rb_funcall(rb_mProcess, id__fork, 0);
4282 return NUM2PIDT(pid);
4283 }
4284}
4285#endif
4286
4287#if defined(HAVE_WORKING_FORK) && !defined(CANNOT_FORK_WITH_PTHREAD)
4288/*
4289 * call-seq:
4290 * Process._fork -> integer
4291 *
4292 * An internal API for fork. Do not call this method directly.
4293 * Currently, this is called via Kernel#fork, Process.fork, and
4294 * IO.popen with <tt>"-"</tt>.
4295 *
4296 * This method is not for casual code but for application monitoring
4297 * libraries. You can add custom code before and after fork events
4298 * by overriding this method.
4299 *
4300 * Note: Process.daemon may be implemented using fork(2) BUT does not go
4301 * through this method.
4302 * Thus, depending on your reason to hook into this method, you
4303 * may also want to hook into that one.
4304 * See {this issue}[https://bugs.ruby-lang.org/issues/18911] for a
4305 * more detailed discussion of this.
4306 */
4307 VALUE
4308rb_proc__fork(VALUE _obj)
4309{
4310 rb_pid_t pid = proc_fork_pid();
4311 return PIDT2NUM(pid);
4312}
4313
4314/*
4315 * call-seq:
4316 * Process.fork { ... } -> integer or nil
4317 * Process.fork -> integer or nil
4318 *
4319 * Creates a child process.
4320 *
4321 * With a block given, runs the block in the child process;
4322 * on block exit, the child terminates with a status of zero:
4323 *
4324 * puts "Before the fork: #{Process.pid}"
4325 * fork do
4326 * puts "In the child process: #{Process.pid}"
4327 * end # => 382141
4328 * puts "After the fork: #{Process.pid}"
4329 *
4330 * Output:
4331 *
4332 * Before the fork: 420496
4333 * After the fork: 420496
4334 * In the child process: 420520
4335 *
4336 * With no block given, the +fork+ call returns twice:
4337 *
4338 * - Once in the parent process, returning the pid of the child process.
4339 * - Once in the child process, returning +nil+.
4340 *
4341 * Example:
4342 *
4343 * puts "This is the first line before the fork (pid #{Process.pid})"
4344 * puts fork
4345 * puts "This is the second line after the fork (pid #{Process.pid})"
4346 *
4347 * Output:
4348 *
4349 * This is the first line before the fork (pid 420199)
4350 * 420223
4351 * This is the second line after the fork (pid 420199)
4352 *
4353 * This is the second line after the fork (pid 420223)
4354 *
4355 * In either case, the child process may exit using
4356 * Kernel.exit! to avoid the call to Kernel#at_exit.
4357 *
4358 * To avoid zombie processes, the parent process should call either:
4359 *
4360 * - Process.wait, to collect the termination statuses of its children.
4361 * - Process.detach, to register disinterest in their status.
4362 *
4363 * The thread calling +fork+ is the only thread in the created child process;
4364 * +fork+ doesn't copy other threads.
4365 *
4366 * Note that method +fork+ is available on some platforms,
4367 * but not on others:
4368 *
4369 * Process.respond_to?(:fork) # => true # Would be false on some.
4370 *
4371 * If not, you may use ::spawn instead of +fork+.
4372 */
4373
4374static VALUE
4375rb_f_fork(VALUE obj)
4376{
4377 rb_pid_t pid;
4378
4379 pid = rb_call_proc__fork();
4380
4381 if (pid == 0) {
4382 if (rb_block_given_p()) {
4383 int status;
4384 rb_protect(rb_yield, Qundef, &status);
4385 ruby_stop(status);
4386 }
4387 return Qnil;
4388 }
4389
4390 return PIDT2NUM(pid);
4391}
4392#else
4393#define rb_proc__fork rb_f_notimplement
4394#define rb_f_fork rb_f_notimplement
4395#endif
4396
4397static int
4398exit_status_code(VALUE status)
4399{
4400 int istatus;
4401
4402 switch (status) {
4403 case Qtrue:
4404 istatus = EXIT_SUCCESS;
4405 break;
4406 case Qfalse:
4407 istatus = EXIT_FAILURE;
4408 break;
4409 default:
4410 istatus = NUM2INT(status);
4411#if EXIT_SUCCESS != 0
4412 if (istatus == 0)
4413 istatus = EXIT_SUCCESS;
4414#endif
4415 break;
4416 }
4417 return istatus;
4418}
4419
4420NORETURN(static VALUE rb_f_exit_bang(int argc, VALUE *argv, VALUE obj));
4421/*
4422 * call-seq:
4423 * exit!(status = false)
4424 * Process.exit!(status = false)
4425 *
4426 * Exits the process immediately; no exit handlers are called.
4427 * Returns exit status +status+ to the underlying operating system.
4428 *
4429 * Process.exit!(true)
4430 *
4431 * Values +true+ and +false+ for argument +status+
4432 * indicate, respectively, success and failure;
4433 * The meanings of integer values are system-dependent.
4434 *
4435 */
4436
4437static VALUE
4438rb_f_exit_bang(int argc, VALUE *argv, VALUE obj)
4439{
4440 int istatus;
4441
4442 if (rb_check_arity(argc, 0, 1) == 1) {
4443 istatus = exit_status_code(argv[0]);
4444 }
4445 else {
4446 istatus = EXIT_FAILURE;
4447 }
4448 _exit(istatus);
4449
4450 UNREACHABLE_RETURN(Qnil);
4451}
4452
4453void
4454rb_exit(int status)
4455{
4456 if (GET_EC()->tag) {
4457 VALUE args[2];
4458
4459 args[0] = INT2NUM(status);
4460 args[1] = rb_str_new2("exit");
4461 rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit));
4462 }
4463 ruby_stop(status);
4464}
4465
4466 VALUE
4467rb_f_exit(int argc, const VALUE *argv)
4468{
4469 int istatus;
4470
4471 if (rb_check_arity(argc, 0, 1) == 1) {
4472 istatus = exit_status_code(argv[0]);
4473 }
4474 else {
4475 istatus = EXIT_SUCCESS;
4476 }
4477 rb_exit(istatus);
4478
4479 UNREACHABLE_RETURN(Qnil);
4480}
4481
4482NORETURN(static VALUE f_exit(int c, const VALUE *a, VALUE _));
4483/*
4484 * call-seq:
4485 * exit(status = true)
4486 * Process.exit(status = true)
4487 *
4488 * Initiates termination of the Ruby script by raising SystemExit;
4489 * the exception may be caught.
4490 * Returns exit status +status+ to the underlying operating system.
4491 *
4492 * Values +true+ and +false+ for argument +status+
4493 * indicate, respectively, success and failure;
4494 * The meanings of integer values are system-dependent.
4495 *
4496 * Example:
4497 *
4498 * begin
4499 * exit
4500 * puts 'Never get here.'
4501 * rescue SystemExit
4502 * puts 'Rescued a SystemExit exception.'
4503 * end
4504 * puts 'After begin block.'
4505 *
4506 * Output:
4507 *
4508 * Rescued a SystemExit exception.
4509 * After begin block.
4510 *
4511 * Just prior to final termination,
4512 * Ruby executes any at-exit procedures (see Kernel::at_exit)
4513 * and any object finalizers (see ObjectSpace::define_finalizer).
4514 *
4515 * Example:
4516 *
4517 * at_exit { puts 'In at_exit function.' }
4518 * ObjectSpace.define_finalizer('string', proc { puts 'In finalizer.' })
4519 * exit
4520 *
4521 * Output:
4522 *
4523 * In at_exit function.
4524 * In finalizer.
4525 *
4526 */
4527
4528static VALUE
4529f_exit(int c, const VALUE *a, VALUE _)
4530{
4531 rb_f_exit(c, a);
4532 UNREACHABLE_RETURN(Qnil);
4533}
4534
4535 VALUE
4536rb_f_abort(int argc, const VALUE *argv)
4537{
4538 rb_check_arity(argc, 0, 1);
4539 if (argc == 0) {
4540 rb_execution_context_t *ec = GET_EC();
4541 VALUE errinfo = rb_ec_get_errinfo(ec);
4542 if (!NIL_P(errinfo)) {
4543 rb_ec_error_print(ec, errinfo);
4544 }
4545 rb_exit(EXIT_FAILURE);
4546 }
4547 else {
4548 VALUE args[2];
4549
4550 args[1] = args[0] = argv[0];
4551 StringValue(args[0]);
4552 rb_io_puts(1, args, rb_ractor_stderr());
4553 args[0] = INT2NUM(EXIT_FAILURE);
4554 rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit));
4555 }
4556
4557 UNREACHABLE_RETURN(Qnil);
4558}
4559
4560NORETURN(static VALUE f_abort(int c, const VALUE *a, VALUE _));
4561
4562/*
4563 * call-seq:
4564 * abort
4565 * Process.abort(msg = nil)
4566 *
4567 * Terminates execution immediately, effectively by calling
4568 * <tt>Kernel.exit(false)</tt>.
4569 *
4570 * If string argument +msg+ is given,
4571 * it is written to STDERR prior to termination;
4572 * otherwise, if an exception was raised,
4573 * prints its message and backtrace.
4574 */
4575
4576static VALUE
4577f_abort(int c, const VALUE *a, VALUE _)
4578{
4579 rb_f_abort(c, a);
4580 UNREACHABLE_RETURN(Qnil);
4581}
4582
4583void
4584rb_syswait(rb_pid_t pid)
4585{
4586 int status;
4587
4588 rb_waitpid(pid, &status, 0);
4589}
4590
4591#if !defined HAVE_WORKING_FORK && !defined HAVE_SPAWNV && !defined __EMSCRIPTEN__
4592char *
4593rb_execarg_commandline(const struct rb_execarg *eargp, VALUE *prog)
4594{
4595 VALUE cmd = *prog;
4596 if (eargp && !eargp->use_shell) {
4597 VALUE str = eargp->invoke.cmd.argv_str;
4598 VALUE buf = eargp->invoke.cmd.argv_buf;
4599 char *p, **argv = ARGVSTR2ARGV(str);
4600 long i, argc = ARGVSTR2ARGC(str);
4601 const char *start = RSTRING_PTR(buf);
4602 cmd = rb_str_new(start, RSTRING_LEN(buf));
4603 p = RSTRING_PTR(cmd);
4604 for (i = 1; i < argc; ++i) {
4605 p[argv[i] - start - 1] = ' ';
4606 }
4607 *prog = cmd;
4608 return p;
4609 }
4610 return StringValueCStr(*prog);
4611}
4612#endif
4613
4614static rb_pid_t
4615rb_spawn_process(struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
4616{
4617 rb_pid_t pid;
4618#if !defined HAVE_WORKING_FORK || USE_SPAWNV
4619 VALUE prog;
4620 struct rb_execarg sarg;
4621# if !defined HAVE_SPAWNV
4622 int status;
4623# endif
4624#endif
4625
4626#if defined HAVE_WORKING_FORK && !USE_SPAWNV
4627 pid = fork_check_err(eargp->status, rb_exec_atfork, eargp, eargp->redirect_fds, errmsg, errmsg_buflen, eargp);
4628#else
4629 prog = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
4630
4631 if (rb_execarg_run_options(eargp, &sarg, errmsg, errmsg_buflen) < 0) {
4632 return -1;
4633 }
4634
4635 if (prog && !eargp->use_shell) {
4636 char **argv = ARGVSTR2ARGV(eargp->invoke.cmd.argv_str);
4637 argv[0] = RSTRING_PTR(prog);
4638 }
4639# if defined HAVE_SPAWNV
4640 if (eargp->use_shell) {
4641 pid = proc_spawn_sh(RSTRING_PTR(prog));
4642 }
4643 else {
4644 char **argv = ARGVSTR2ARGV(eargp->invoke.cmd.argv_str);
4645 pid = proc_spawn_cmd(argv, prog, eargp);
4646 }
4647
4648 if (pid == -1) {
4649 rb_last_status_set(0x7f << 8, pid);
4650 }
4651# else
4652 status = system(rb_execarg_commandline(eargp, &prog));
4653 pid = 1; /* dummy */
4654 rb_last_status_set((status & 0xff) << 8, pid);
4655# endif
4656
4657 if (eargp->waitpid_state) {
4658 eargp->waitpid_state->pid = pid;
4659 }
4660
4661 rb_execarg_run_options(&sarg, NULL, errmsg, errmsg_buflen);
4662#endif
4663
4664 return pid;
4665}
4666
4667 struct spawn_args {
4668 VALUE execarg;
4669 struct {
4670 char *ptr;
4671 size_t buflen;
4672 } errmsg;
4673};
4674
4675static VALUE
4676do_spawn_process(VALUE arg)
4677{
4678 struct spawn_args *argp = (struct spawn_args *)arg;
4679
4680 rb_execarg_parent_start1(argp->execarg);
4681
4682 return (VALUE)rb_spawn_process(rb_execarg_get(argp->execarg),
4683 argp->errmsg.ptr, argp->errmsg.buflen);
4684}
4685
4686NOINLINE(static rb_pid_t
4687 rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen));
4688
4689static rb_pid_t
4690rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen)
4691{
4692 struct spawn_args args;
4693
4694 args.execarg = execarg_obj;
4695 args.errmsg.ptr = errmsg;
4696 args.errmsg.buflen = errmsg_buflen;
4697
4698 rb_pid_t r = (rb_pid_t)rb_ensure(do_spawn_process, (VALUE)&args,
4699 execarg_parent_end, execarg_obj);
4700 return r;
4701}
4702
4703static rb_pid_t
4704rb_spawn_internal(int argc, const VALUE *argv, char *errmsg, size_t errmsg_buflen)
4705{
4706 VALUE execarg_obj;
4707
4708 execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
4709 return rb_execarg_spawn(execarg_obj, errmsg, errmsg_buflen);
4710}
4711
4712rb_pid_t
4713rb_spawn_err(int argc, const VALUE *argv, char *errmsg, size_t errmsg_buflen)
4714{
4715 return rb_spawn_internal(argc, argv, errmsg, errmsg_buflen);
4716}
4717
4718rb_pid_t
4719rb_spawn(int argc, const VALUE *argv)
4720{
4721 return rb_spawn_internal(argc, argv, NULL, 0);
4722}
4723
4724/*
4725 * call-seq:
4726 * system([env, ] command_line, options = {}, exception: false) -> true, false, or nil
4727 * system([env, ] exe_path, *args, options = {}, exception: false) -> true, false, or nil
4728 *
4729 * Creates a new child process by doing one of the following
4730 * in that process:
4731 *
4732 * - Passing string +command_line+ to the shell.
4733 * - Invoking the executable at +exe_path+.
4734 *
4735 * This method has potential security vulnerabilities if called with untrusted input;
4736 * see {Command Injection}[rdoc-ref:command_injection.rdoc].
4737 *
4738 * Returns:
4739 *
4740 * - +true+ if the command exits with status zero.
4741 * - +false+ if the exit status is a non-zero integer.
4742 * - +nil+ if the command could not execute.
4743 *
4744 * Raises an exception (instead of returning +false+ or +nil+)
4745 * if keyword argument +exception+ is set to +true+.
4746 *
4747 * Assigns the command's error status to <tt>$?</tt>.
4748 *
4749 * The new process is created using the
4750 * {system system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/system.html];
4751 * it may inherit some of its environment from the calling program
4752 * (possibly including open file descriptors).
4753 *
4754 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
4755 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
4756 *
4757 * Argument +options+ is a hash of options for the new process;
4758 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
4759 *
4760 * The first required argument is one of the following:
4761 *
4762 * - +command_line+ if it is a string,
4763 * and if it begins with a shell reserved word or special built-in,
4764 * or if it contains one or more meta characters.
4765 * - +exe_path+ otherwise.
4766 *
4767 * <b>Argument +command_line+</b>
4768 *
4769 * \String argument +command_line+ is a command line to be passed to a shell;
4770 * it must begin with a shell reserved word, begin with a special built-in,
4771 * or contain meta characters:
4772 *
4773 * system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
4774 * system('echo') # => true # Built-in.
4775 * system('date > /tmp/date.tmp') # => true # Contains meta character.
4776 * system('date > /nop/date.tmp') # => false
4777 * system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
4778 *
4779 * Assigns the command's error status to <tt>$?</tt>:
4780 *
4781 * system('echo') # => true # Built-in.
4782 * $? # => #<Process::Status: pid 640610 exit 0>
4783 * system('date > /nop/date.tmp') # => false
4784 * $? # => #<Process::Status: pid 640742 exit 2>
4785 *
4786 * The command line may also contain arguments and options for the command:
4787 *
4788 * system('echo "Foo"') # => true
4789 *
4790 * Output:
4791 *
4792 * Foo
4793 *
4794 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
4795 *
4796 * Raises an exception if the new process could not execute.
4797 *
4798 * <b>Argument +exe_path+</b>
4799 *
4800 * Argument +exe_path+ is one of the following:
4801 *
4802 * - The string path to an executable to be called.
4803 * - A 2-element array containing the path to an executable
4804 * and the string to be used as the name of the executing process.
4805 *
4806 * Example:
4807 *
4808 * system('/usr/bin/date') # => true # Path to date on Unix-style system.
4809 * system('foo') # => nil # Command failed.
4810 *
4811 * Output:
4812 *
4813 * Mon Aug 28 11:43:10 AM CDT 2023
4814 *
4815 * Assigns the command's error status to <tt>$?</tt>:
4816 *
4817 * system('/usr/bin/date') # => true
4818 * $? # => #<Process::Status: pid 645605 exit 0>
4819 * system('foo') # => nil
4820 * $? # => #<Process::Status: pid 645608 exit 127>
4821 *
4822 * Ruby invokes the executable directly, with no shell and no shell expansion:
4823 *
4824 * system('doesnt_exist') # => nil
4825 *
4826 * If one or more +args+ is given, each is an argument or option
4827 * to be passed to the executable:
4828 *
4829 * system('echo', 'C*') # => true
4830 * system('echo', 'hello', 'world') # => true
4831 *
4832 * Output:
4833 *
4834 * C*
4835 * hello world
4836 *
4837 * Raises an exception if the new process could not execute.
4838 */
4839
4840static VALUE
4841rb_f_system(int argc, VALUE *argv, VALUE _)
4842{
4843 rb_thread_t *th = GET_THREAD();
4844 VALUE execarg_obj = rb_execarg_new(argc, argv, TRUE, TRUE);
4845 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
4846
4847 struct rb_process_status status = {0};
4848 eargp->status = &status;
4849
4850 last_status_clear(th);
4851
4852 // This function can set the thread's last status.
4853 // May be different from waitpid_state.pid on exec failure.
4854 rb_pid_t pid = rb_execarg_spawn(execarg_obj, 0, 0);
4855
4856 if (pid > 0) {
4857 VALUE status = rb_process_status_wait(pid, 0);
4858 struct rb_process_status *data = rb_check_typeddata(status, &rb_process_status_type);
4859 // Set the last status:
4860 rb_obj_freeze(status);
4861 th->last_status = status;
4862
4863 if (data->status == EXIT_SUCCESS) {
4864 return Qtrue;
4865 }
4866
4867 if (data->error != 0) {
4868 if (eargp->exception) {
4869 VALUE command = eargp->invoke.sh.shell_script;
4870 RB_GC_GUARD(execarg_obj);
4871 rb_syserr_fail_str(data->error, command);
4872 }
4873 else {
4874 return Qnil;
4875 }
4876 }
4877 else if (eargp->exception) {
4878 VALUE command = eargp->invoke.sh.shell_script;
4879 VALUE str = rb_str_new_cstr("Command failed with");
4880 rb_str_cat_cstr(pst_message_status(str, data->status), ": ");
4881 rb_str_append(str, command);
4882 RB_GC_GUARD(execarg_obj);
4883 rb_exc_raise(rb_exc_new_str(rb_eRuntimeError, str));
4884 }
4885 else {
4886 return Qfalse;
4887 }
4888
4889 RB_GC_GUARD(status);
4890 }
4891
4892 if (eargp->exception) {
4893 VALUE command = eargp->invoke.sh.shell_script;
4894 RB_GC_GUARD(execarg_obj);
4895 rb_syserr_fail_str(errno, command);
4896 }
4897 else {
4898 return Qnil;
4899 }
4900}
4901
4902/*
4903 * call-seq:
4904 * spawn([env, ] command_line, options = {}) -> pid
4905 * spawn([env, ] exe_path, *args, options = {}) -> pid
4906 *
4907 * Creates a new child process by doing one of the following
4908 * in that process:
4909 *
4910 * - Passing string +command_line+ to the shell.
4911 * - Invoking the executable at +exe_path+.
4912 *
4913 * This method has potential security vulnerabilities if called with untrusted input;
4914 * see {Command Injection}[rdoc-ref:command_injection.rdoc].
4915 *
4916 * Returns the process ID (pid) of the new process,
4917 * without waiting for it to complete.
4918 *
4919 * To avoid zombie processes, the parent process should call either:
4920 *
4921 * - Process.wait, to collect the termination statuses of its children.
4922 * - Process.detach, to register disinterest in their status.
4923 *
4924 * The new process is created using the
4925 * {exec system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/execve.html];
4926 * it may inherit some of its environment from the calling program
4927 * (possibly including open file descriptors).
4928 *
4929 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
4930 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
4931 *
4932 * Argument +options+ is a hash of options for the new process;
4933 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
4934 *
4935 * The first required argument is one of the following:
4936 *
4937 * - +command_line+ if it is a string,
4938 * and if it begins with a shell reserved word or special built-in,
4939 * or if it contains one or more meta characters.
4940 * - +exe_path+ otherwise.
4941 *
4942 * <b>Argument +command_line+</b>
4943 *
4944 * \String argument +command_line+ is a command line to be passed to a shell;
4945 * it must begin with a shell reserved word, begin with a special built-in,
4946 * or contain meta characters:
4947 *
4948 * spawn('if true; then echo "Foo"; fi') # => 798847 # Shell reserved word.
4949 * Process.wait # => 798847
4950 * spawn('echo') # => 798848 # Built-in.
4951 * Process.wait # => 798848
4952 * spawn('date > /tmp/date.tmp') # => 798879 # Contains meta character.
4953 * Process.wait # => 798849
4954 * spawn('date > /nop/date.tmp') # => 798882 # Issues error message.
4955 * Process.wait # => 798882
4956 *
4957 * The command line may also contain arguments and options for the command:
4958 *
4959 * spawn('echo "Foo"') # => 799031
4960 * Process.wait # => 799031
4961 *
4962 * Output:
4963 *
4964 * Foo
4965 *
4966 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
4967 *
4968 * Raises an exception if the new process could not execute.
4969 *
4970 * <b>Argument +exe_path+</b>
4971 *
4972 * Argument +exe_path+ is one of the following:
4973 *
4974 * - The string path to an executable to be called:
4975 *
4976 * spawn('/usr/bin/date') # Path to date on Unix-style system.
4977 * Process.wait
4978 *
4979 * Output:
4980 *
4981 * Thu Aug 31 10:06:48 AM CDT 2023
4982 *
4983 * - A 2-element array containing the path to an executable
4984 * and the string to be used as the name of the executing process:
4985 *
4986 * pid = spawn(['sleep', 'Hello!'], '1') # 2-element array.
4987 * p `ps -p #{pid} -o command=`
4988 *
4989 * Output:
4990 *
4991 * "Hello! 1\n"
4992 *
4993 * Ruby invokes the executable directly, with no shell and no shell expansion.
4994 *
4995 * If one or more +args+ is given, each is an argument or option
4996 * to be passed to the executable:
4997 *
4998 * spawn('echo', 'C*') # => 799392
4999 * Process.wait # => 799392
5000 * spawn('echo', 'hello', 'world') # => 799393
5001 * Process.wait # => 799393
5002 *
5003 * Output:
5004 *
5005 * C*
5006 * hello world
5007 *
5008 * Raises an exception if the new process could not execute.
5009 */
5010
5011static VALUE
5012rb_f_spawn(int argc, VALUE *argv, VALUE _)
5013{
5014 rb_pid_t pid;
5015 char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' };
5016 VALUE execarg_obj, fail_str;
5017 struct rb_execarg *eargp;
5018
5019 execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
5020 eargp = rb_execarg_get(execarg_obj);
5021 fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
5022
5023 pid = rb_execarg_spawn(execarg_obj, errmsg, sizeof(errmsg));
5024
5025 if (pid == -1) {
5026 int err = errno;
5027 rb_exec_fail(eargp, err, errmsg);
5028 RB_GC_GUARD(execarg_obj);
5029 rb_syserr_fail_str(err, fail_str);
5030 }
5031#if defined(HAVE_WORKING_FORK) || defined(HAVE_SPAWNV)
5032 return PIDT2NUM(pid);
5033#else
5034 return Qnil;
5035#endif
5036}
5037
5038/*
5039 * call-seq:
5040 * sleep(secs = nil) -> slept_secs
5041 *
5042 * Suspends execution of the current thread for the number of seconds
5043 * specified by numeric argument +secs+, or forever if +secs+ is +nil+;
5044 * returns the integer number of seconds suspended (rounded).
5045 *
5046 * Time.new # => 2008-03-08 19:56:19 +0900
5047 * sleep 1.2 # => 1
5048 * Time.new # => 2008-03-08 19:56:20 +0900
5049 * sleep 1.9 # => 2
5050 * Time.new # => 2008-03-08 19:56:22 +0900
5051 *
5052 */
5053
5054static VALUE
5055rb_f_sleep(int argc, VALUE *argv, VALUE _)
5056{
5057 time_t beg = time(0);
5058 VALUE scheduler = rb_fiber_scheduler_current();
5059
5060 if (scheduler != Qnil) {
5061 rb_fiber_scheduler_kernel_sleepv(scheduler, argc, argv);
5062 }
5063 else {
5064 if (argc == 0 || (argc == 1 && NIL_P(argv[0]))) {
5065 rb_thread_sleep_forever();
5066 }
5067 else {
5068 rb_check_arity(argc, 0, 1);
5069 rb_thread_wait_for(rb_time_interval(argv[0]));
5070 }
5071 }
5072
5073 time_t end = time(0) - beg;
5074
5075 return TIMET2NUM(end);
5076}
5077
5078
5079#if (defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)) || defined(HAVE_GETPGID)
5080/*
5081 * call-seq:
5082 * Process.getpgrp -> integer
5083 *
5084 * Returns the process group ID for the current process:
5085 *
5086 * Process.getpgid(0) # => 25527
5087 * Process.getpgrp # => 25527
5088 *
5089 */
5090
5091static VALUE
5092proc_getpgrp(VALUE _)
5093{
5094 rb_pid_t pgrp;
5095
5096#if defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)
5097 pgrp = getpgrp();
5098 if (pgrp < 0) rb_sys_fail(0);
5099 return PIDT2NUM(pgrp);
5100#else /* defined(HAVE_GETPGID) */
5101 pgrp = getpgid(0);
5102 if (pgrp < 0) rb_sys_fail(0);
5103 return PIDT2NUM(pgrp);
5104#endif
5105}
5106#else
5107#define proc_getpgrp rb_f_notimplement
5108#endif
5109
5110
5111#if defined(HAVE_SETPGID) || (defined(HAVE_SETPGRP) && defined(SETPGRP_VOID))
5112/*
5113 * call-seq:
5114 * Process.setpgrp -> 0
5115 *
5116 * Equivalent to <tt>setpgid(0, 0)</tt>.
5117 *
5118 * Not available on all platforms.
5119 */
5120
5121static VALUE
5122proc_setpgrp(VALUE _)
5123{
5124 /* check for posix setpgid() first; this matches the posix */
5125 /* getpgrp() above. It appears that configure will set SETPGRP_VOID */
5126 /* even though setpgrp(0,0) would be preferred. The posix call avoids */
5127 /* this confusion. */
5128#ifdef HAVE_SETPGID
5129 if (setpgid(0,0) < 0) rb_sys_fail(0);
5130#elif defined(HAVE_SETPGRP) && defined(SETPGRP_VOID)
5131 if (setpgrp() < 0) rb_sys_fail(0);
5132#endif
5133 return INT2FIX(0);
5134}
5135#else
5136#define proc_setpgrp rb_f_notimplement
5137#endif
5138
5139
5140#if defined(HAVE_GETPGID)
5141/*
5142 * call-seq:
5143 * Process.getpgid(pid) -> integer
5144 *
5145 * Returns the process group ID for the given process ID +pid+:
5146 *
5147 * Process.getpgid(Process.ppid) # => 25527
5148 *
5149 * Not available on all platforms.
5150 */
5151
5152static VALUE
5153proc_getpgid(VALUE obj, VALUE pid)
5154{
5155 rb_pid_t i;
5156
5157 i = getpgid(NUM2PIDT(pid));
5158 if (i < 0) rb_sys_fail(0);
5159 return PIDT2NUM(i);
5160}
5161#else
5162#define proc_getpgid rb_f_notimplement
5163#endif
5164
5165
5166#ifdef HAVE_SETPGID
5167/*
5168 * call-seq:
5169 * Process.setpgid(pid, pgid) -> 0
5170 *
5171 * Sets the process group ID for the process given by process ID +pid+
5172 * to +pgid+.
5173 *
5174 * Not available on all platforms.
5175 */
5176
5177static VALUE
5178proc_setpgid(VALUE obj, VALUE pid, VALUE pgrp)
5179{
5180 rb_pid_t ipid, ipgrp;
5181
5182 ipid = NUM2PIDT(pid);
5183 ipgrp = NUM2PIDT(pgrp);
5184
5185 if (setpgid(ipid, ipgrp) < 0) rb_sys_fail(0);
5186 return INT2FIX(0);
5187}
5188#else
5189#define proc_setpgid rb_f_notimplement
5190#endif
5191
5192
5193#ifdef HAVE_GETSID
5194/*
5195 * call-seq:
5196 * Process.getsid(pid = nil) -> integer
5197 *
5198 * Returns the session ID of the given process ID +pid+,
5199 * or of the current process if not given:
5200 *
5201 * Process.getsid # => 27422
5202 * Process.getsid(0) # => 27422
5203 * Process.getsid(Process.pid()) # => 27422
5204 *
5205 * Not available on all platforms.
5206 */
5207static VALUE
5208proc_getsid(int argc, VALUE *argv, VALUE _)
5209{
5210 rb_pid_t sid;
5211 rb_pid_t pid = 0;
5212
5213 if (rb_check_arity(argc, 0, 1) == 1 && !NIL_P(argv[0]))
5214 pid = NUM2PIDT(argv[0]);
5215
5216 sid = getsid(pid);
5217 if (sid < 0) rb_sys_fail(0);
5218 return PIDT2NUM(sid);
5219}
5220#else
5221#define proc_getsid rb_f_notimplement
5222#endif
5223
5224
5225#if defined(HAVE_SETSID) || (defined(HAVE_SETPGRP) && defined(TIOCNOTTY))
5226#if !defined(HAVE_SETSID)
5227static rb_pid_t ruby_setsid(void);
5228#define setsid() ruby_setsid()
5229#endif
5230/*
5231 * call-seq:
5232 * Process.setsid -> integer
5233 *
5234 * Establishes the current process as a new session and process group leader,
5235 * with no controlling tty;
5236 * returns the session ID:
5237 *
5238 * Process.setsid # => 27422
5239 *
5240 * Not available on all platforms.
5241 */
5242
5243static VALUE
5244proc_setsid(VALUE _)
5245{
5246 rb_pid_t pid;
5247
5248 pid = setsid();
5249 if (pid < 0) rb_sys_fail(0);
5250 return PIDT2NUM(pid);
5251}
5252
5253#if !defined(HAVE_SETSID)
5254#define HAVE_SETSID 1
5255static rb_pid_t
5256ruby_setsid(void)
5257{
5258 rb_pid_t pid;
5259 int ret;
5260
5261 pid = getpid();
5262#if defined(SETPGRP_VOID)
5263 ret = setpgrp();
5264 /* If `pid_t setpgrp(void)' is equivalent to setsid(),
5265 `ret' will be the same value as `pid', and following open() will fail.
5266 In Linux, `int setpgrp(void)' is equivalent to setpgid(0, 0). */
5267#else
5268 ret = setpgrp(0, pid);
5269#endif
5270 if (ret == -1) return -1;
5271
5272 if ((fd = rb_cloexec_open("/dev/tty", O_RDWR, 0)) >= 0) {
5273 rb_update_max_fd(fd);
5274 ioctl(fd, TIOCNOTTY, NULL);
5275 close(fd);
5276 }
5277 return pid;
5278}
5279#endif
5280#else
5281#define proc_setsid rb_f_notimplement
5282#endif
5283
5284
5285#ifdef HAVE_GETPRIORITY
5286/*
5287 * call-seq:
5288 * Process.getpriority(kind, id) -> integer
5289 *
5290 * Returns the scheduling priority for specified process, process group,
5291 * or user.
5292 *
5293 * Argument +kind+ is one of:
5294 *
5295 * - Process::PRIO_PROCESS: return priority for process.
5296 * - Process::PRIO_PGRP: return priority for process group.
5297 * - Process::PRIO_USER: return priority for user.
5298 *
5299 * Argument +id+ is the ID for the process, process group, or user;
5300 * zero specified the current ID for +kind+.
5301 *
5302 * Examples:
5303 *
5304 * Process.getpriority(Process::PRIO_USER, 0) # => 19
5305 * Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
5306 *
5307 * Not available on all platforms.
5308 */
5309
5310static VALUE
5311proc_getpriority(VALUE obj, VALUE which, VALUE who)
5312{
5313 int prio, iwhich, iwho;
5314
5315 iwhich = NUM2INT(which);
5316 iwho = NUM2INT(who);
5317
5318 errno = 0;
5319 prio = getpriority(iwhich, iwho);
5320 if (errno) rb_sys_fail(0);
5321 return INT2FIX(prio);
5322}
5323#else
5324#define proc_getpriority rb_f_notimplement
5325#endif
5326
5327
5328#ifdef HAVE_GETPRIORITY
5329/*
5330 * call-seq:
5331 * Process.setpriority(kind, integer, priority) -> 0
5332 *
5333 * See Process.getpriority.
5334 *
5335 * Examples:
5336 *
5337 * Process.setpriority(Process::PRIO_USER, 0, 19) # => 0
5338 * Process.setpriority(Process::PRIO_PROCESS, 0, 19) # => 0
5339 * Process.getpriority(Process::PRIO_USER, 0) # => 19
5340 * Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
5341 *
5342 * Not available on all platforms.
5343 */
5344
5345static VALUE
5346proc_setpriority(VALUE obj, VALUE which, VALUE who, VALUE prio)
5347{
5348 int iwhich, iwho, iprio;
5349
5350 iwhich = NUM2INT(which);
5351 iwho = NUM2INT(who);
5352 iprio = NUM2INT(prio);
5353
5354 if (setpriority(iwhich, iwho, iprio) < 0)
5355 rb_sys_fail(0);
5356 return INT2FIX(0);
5357}
5358#else
5359#define proc_setpriority rb_f_notimplement
5360#endif
5361
5362#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
5363static int
5364rlimit_resource_name2int(const char *name, long len, int casetype)
5365{
5366 int resource;
5367 const char *p;
5368#define RESCHECK(r) \
5369 do { \
5370 if (len == rb_strlen_lit(#r) && STRCASECMP(name, #r) == 0) { \
5371 resource = RLIMIT_##r; \
5372 goto found; \
5373 } \
5374 } while (0)
5375
5376 switch (TOUPPER(*name)) {
5377 case 'A':
5378#ifdef RLIMIT_AS
5379 RESCHECK(AS);
5380#endif
5381 break;
5382
5383 case 'C':
5384#ifdef RLIMIT_CORE
5385 RESCHECK(CORE);
5386#endif
5387#ifdef RLIMIT_CPU
5388 RESCHECK(CPU);
5389#endif
5390 break;
5391
5392 case 'D':
5393#ifdef RLIMIT_DATA
5394 RESCHECK(DATA);
5395#endif
5396 break;
5397
5398 case 'F':
5399#ifdef RLIMIT_FSIZE
5400 RESCHECK(FSIZE);
5401#endif
5402 break;
5403
5404 case 'M':
5405#ifdef RLIMIT_MEMLOCK
5406 RESCHECK(MEMLOCK);
5407#endif
5408#ifdef RLIMIT_MSGQUEUE
5409 RESCHECK(MSGQUEUE);
5410#endif
5411 break;
5412
5413 case 'N':
5414#ifdef RLIMIT_NOFILE
5415 RESCHECK(NOFILE);
5416#endif
5417#ifdef RLIMIT_NPROC
5418 RESCHECK(NPROC);
5419#endif
5420#ifdef RLIMIT_NPTS
5421 RESCHECK(NPTS);
5422#endif
5423#ifdef RLIMIT_NICE
5424 RESCHECK(NICE);
5425#endif
5426 break;
5427
5428 case 'R':
5429#ifdef RLIMIT_RSS
5430 RESCHECK(RSS);
5431#endif
5432#ifdef RLIMIT_RTPRIO
5433 RESCHECK(RTPRIO);
5434#endif
5435#ifdef RLIMIT_RTTIME
5436 RESCHECK(RTTIME);
5437#endif
5438 break;
5439
5440 case 'S':
5441#ifdef RLIMIT_STACK
5442 RESCHECK(STACK);
5443#endif
5444#ifdef RLIMIT_SBSIZE
5445 RESCHECK(SBSIZE);
5446#endif
5447#ifdef RLIMIT_SIGPENDING
5448 RESCHECK(SIGPENDING);
5449#endif
5450 break;
5451 }
5452 return -1;
5453
5454 found:
5455 switch (casetype) {
5456 case 0:
5457 for (p = name; *p; p++)
5458 if (!ISUPPER(*p))
5459 return -1;
5460 break;
5461
5462 case 1:
5463 for (p = name; *p; p++)
5464 if (!ISLOWER(*p))
5465 return -1;
5466 break;
5467
5468 default:
5469 rb_bug("unexpected casetype");
5470 }
5471 return resource;
5472#undef RESCHECK
5473}
5474
5475static int
5476rlimit_type_by_hname(const char *name, long len)
5477{
5478 return rlimit_resource_name2int(name, len, 0);
5479}
5480
5481static int
5482rlimit_type_by_lname(const char *name, long len)
5483{
5484 return rlimit_resource_name2int(name, len, 1);
5485}
5486
5487static int
5488rlimit_type_by_sym(VALUE key)
5489{
5490 VALUE name = rb_sym2str(key);
5491 const char *rname = RSTRING_PTR(name);
5492 long len = RSTRING_LEN(name);
5493 int rtype = -1;
5494 static const char prefix[] = "rlimit_";
5495 enum {prefix_len = sizeof(prefix)-1};
5496
5497 if (len > prefix_len && strncmp(prefix, rname, prefix_len) == 0) {
5498 rtype = rlimit_type_by_lname(rname + prefix_len, len - prefix_len);
5499 }
5500
5501 RB_GC_GUARD(key);
5502 return rtype;
5503}
5504
5505static int
5506rlimit_resource_type(VALUE rtype)
5507{
5508 const char *name;
5509 long len;
5510 VALUE v;
5511 int r;
5512
5513 switch (TYPE(rtype)) {
5514 case T_SYMBOL:
5515 v = rb_sym2str(rtype);
5516 name = RSTRING_PTR(v);
5517 len = RSTRING_LEN(v);
5518 break;
5519
5520 default:
5521 v = rb_check_string_type(rtype);
5522 if (!NIL_P(v)) {
5523 rtype = v;
5524 case T_STRING:
5525 name = StringValueCStr(rtype);
5526 len = RSTRING_LEN(rtype);
5527 break;
5528 }
5529 /* fall through */
5530
5531 case T_FIXNUM:
5532 case T_BIGNUM:
5533 return NUM2INT(rtype);
5534 }
5535
5536 r = rlimit_type_by_hname(name, len);
5537 if (r != -1)
5538 return r;
5539
5540 rb_raise(rb_eArgError, "invalid resource name: % "PRIsVALUE, rtype);
5541
5542 UNREACHABLE_RETURN(-1);
5543}
5544
5545static rlim_t
5546rlimit_resource_value(VALUE rval)
5547{
5548 const char *name;
5549 VALUE v;
5550
5551 switch (TYPE(rval)) {
5552 case T_SYMBOL:
5553 v = rb_sym2str(rval);
5554 name = RSTRING_PTR(v);
5555 break;
5556
5557 default:
5558 v = rb_check_string_type(rval);
5559 if (!NIL_P(v)) {
5560 rval = v;
5561 case T_STRING:
5562 name = StringValueCStr(rval);
5563 break;
5564 }
5565 /* fall through */
5566
5567 case T_FIXNUM:
5568 case T_BIGNUM:
5569 return NUM2RLIM(rval);
5570 }
5571
5572#ifdef RLIM_INFINITY
5573 if (strcmp(name, "INFINITY") == 0) return RLIM_INFINITY;
5574#endif
5575#ifdef RLIM_SAVED_MAX
5576 if (strcmp(name, "SAVED_MAX") == 0) return RLIM_SAVED_MAX;
5577#endif
5578#ifdef RLIM_SAVED_CUR
5579 if (strcmp(name, "SAVED_CUR") == 0) return RLIM_SAVED_CUR;
5580#endif
5581 rb_raise(rb_eArgError, "invalid resource value: %"PRIsVALUE, rval);
5582
5583 UNREACHABLE_RETURN((rlim_t)-1);
5584}
5585#endif
5586
5587#if defined(HAVE_GETRLIMIT) && defined(RLIM2NUM)
5588/*
5589 * call-seq:
5590 * Process.getrlimit(resource) -> [cur_limit, max_limit]
5591 *
5592 * Returns a 2-element array of the current (soft) limit
5593 * and maximum (hard) limit for the given +resource+.
5594 *
5595 * Argument +resource+ specifies the resource whose limits are to be returned;
5596 * see Process.setrlimit.
5597 *
5598 * Each of the returned values +cur_limit+ and +max_limit+ is an integer;
5599 * see Process.setrlimit.
5600 *
5601 * Example:
5602 *
5603 * Process.getrlimit(:CORE) # => [0, 18446744073709551615]
5604 *
5605 * See Process.setrlimit.
5606 *
5607 * Not available on all platforms.
5608 */
5609
5610static VALUE
5611proc_getrlimit(VALUE obj, VALUE resource)
5612{
5613 struct rlimit rlim;
5614
5615 if (getrlimit(rlimit_resource_type(resource), &rlim) < 0) {
5616 rb_sys_fail("getrlimit");
5617 }
5618 return rb_assoc_new(RLIM2NUM(rlim.rlim_cur), RLIM2NUM(rlim.rlim_max));
5619}
5620#else
5621#define proc_getrlimit rb_f_notimplement
5622#endif
5623
5624#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
5625/*
5626 * call-seq:
5627 * Process.setrlimit(resource, cur_limit, max_limit = cur_limit) -> nil
5628 *
5629 * Sets limits for the current process for the given +resource+
5630 * to +cur_limit+ (soft limit) and +max_limit+ (hard limit);
5631 * returns +nil+.
5632 *
5633 * Argument +resource+ specifies the resource whose limits are to be set;
5634 * the argument may be given as a symbol, as a string, or as a constant
5635 * beginning with <tt>Process::RLIMIT_</tt>
5636 * (e.g., +:CORE+, <tt>'CORE'</tt>, or <tt>Process::RLIMIT_CORE</tt>.
5637 *
5638 * The resources available and supported are system-dependent,
5639 * and may include (here expressed as symbols):
5640 *
5641 * - +:AS+: Total available memory (bytes) (SUSv3, NetBSD, FreeBSD, OpenBSD except 4.4BSD-Lite).
5642 * - +:CORE+: Core size (bytes) (SUSv3).
5643 * - +:CPU+: CPU time (seconds) (SUSv3).
5644 * - +:DATA+: Data segment (bytes) (SUSv3).
5645 * - +:FSIZE+: File size (bytes) (SUSv3).
5646 * - +:MEMLOCK+: Total size for mlock(2) (bytes) (4.4BSD, GNU/Linux).
5647 * - +:MSGQUEUE+: Allocation for POSIX message queues (bytes) (GNU/Linux).
5648 * - +:NICE+: Ceiling on process's nice(2) value (number) (GNU/Linux).
5649 * - +:NOFILE+: File descriptors (number) (SUSv3).
5650 * - +:NPROC+: Number of processes for the user (number) (4.4BSD, GNU/Linux).
5651 * - +:NPTS+: Number of pseudo terminals (number) (FreeBSD).
5652 * - +:RSS+: Resident memory size (bytes) (4.2BSD, GNU/Linux).
5653 * - +:RTPRIO+: Ceiling on the process's real-time priority (number) (GNU/Linux).
5654 * - +:RTTIME+: CPU time for real-time process (us) (GNU/Linux).
5655 * - +:SBSIZE+: All socket buffers (bytes) (NetBSD, FreeBSD).
5656 * - +:SIGPENDING+: Number of queued signals allowed (signals) (GNU/Linux).
5657 * - +:STACK+: Stack size (bytes) (SUSv3).
5658 *
5659 * Arguments +cur_limit+ and +max_limit+ may be:
5660 *
5661 * - Integers (+max_limit+ should not be smaller than +cur_limit+).
5662 * - Symbol +:SAVED_MAX+, string <tt>'SAVED_MAX'</tt>,
5663 * or constant <tt>Process::RLIM_SAVED_MAX</tt>: saved maximum limit.
5664 * - Symbol +:SAVED_CUR+, string <tt>'SAVED_CUR'</tt>,
5665 * or constant <tt>Process::RLIM_SAVED_CUR</tt>: saved current limit.
5666 * - Symbol +:INFINITY+, string <tt>'INFINITY'</tt>,
5667 * or constant <tt>Process::RLIM_INFINITY</tt>: no limit on resource.
5668 *
5669 * This example raises the soft limit of core size to
5670 * the hard limit to try to make core dump possible:
5671 *
5672 * Process.setrlimit(:CORE, Process.getrlimit(:CORE)[1])
5673 *
5674 * Not available on all platforms.
5675 */
5676
5677static VALUE
5678proc_setrlimit(int argc, VALUE *argv, VALUE obj)
5679{
5680 VALUE resource, rlim_cur, rlim_max;
5681 struct rlimit rlim;
5682
5683 rb_check_arity(argc, 2, 3);
5684 resource = argv[0];
5685 rlim_cur = argv[1];
5686 if (argc < 3 || NIL_P(rlim_max = argv[2]))
5687 rlim_max = rlim_cur;
5688
5689 rlim.rlim_cur = rlimit_resource_value(rlim_cur);
5690 rlim.rlim_max = rlimit_resource_value(rlim_max);
5691
5692 if (setrlimit(rlimit_resource_type(resource), &rlim) < 0) {
5693 rb_sys_fail("setrlimit");
5694 }
5695 return Qnil;
5696}
5697#else
5698#define proc_setrlimit rb_f_notimplement
5699#endif
5700
5701static int under_uid_switch = 0;
5702static void
5703check_uid_switch(void)
5704{
5705 if (under_uid_switch) {
5706 rb_raise(rb_eRuntimeError, "can't handle UID while evaluating block given to Process::UID.switch method");
5707 }
5708}
5709
5710static int under_gid_switch = 0;
5711static void
5712check_gid_switch(void)
5713{
5714 if (under_gid_switch) {
5715 rb_raise(rb_eRuntimeError, "can't handle GID while evaluating block given to Process::UID.switch method");
5716 }
5717}
5718
5719
5720#if defined(HAVE_PWD_H)
5726 VALUE
5727rb_getlogin(void)
5728{
5729#if ( !defined(USE_GETLOGIN_R) && !defined(USE_GETLOGIN) )
5730 return Qnil;
5731#else
5732 char MAYBE_UNUSED(*login) = NULL;
5733
5734# ifdef USE_GETLOGIN_R
5735
5736#if defined(__FreeBSD__)
5737 typedef int getlogin_r_size_t;
5738#else
5739 typedef size_t getlogin_r_size_t;
5740#endif
5741
5742 long loginsize = GETLOGIN_R_SIZE_INIT; /* maybe -1 */
5743
5744 if (loginsize < 0)
5745 loginsize = GETLOGIN_R_SIZE_DEFAULT;
5746
5747 VALUE maybe_result = rb_str_buf_new(loginsize);
5748
5749 login = RSTRING_PTR(maybe_result);
5750 loginsize = rb_str_capacity(maybe_result);
5751 rb_str_set_len(maybe_result, loginsize);
5752
5753 int gle;
5754 errno = 0;
5755 while ((gle = getlogin_r(login, (getlogin_r_size_t)loginsize)) != 0) {
5756
5757 if (gle == ENOTTY || gle == ENXIO || gle == ENOENT) {
5758 rb_str_resize(maybe_result, 0);
5759 return Qnil;
5760 }
5761
5762 if (gle != ERANGE || loginsize >= GETLOGIN_R_SIZE_LIMIT) {
5763 rb_str_resize(maybe_result, 0);
5764 rb_syserr_fail(gle, "getlogin_r");
5765 }
5766
5767 rb_str_modify_expand(maybe_result, loginsize);
5768 login = RSTRING_PTR(maybe_result);
5769 loginsize = rb_str_capacity(maybe_result);
5770 }
5771
5772 if (login == NULL) {
5773 rb_str_resize(maybe_result, 0);
5774 return Qnil;
5775 }
5776
5777 return maybe_result;
5778
5779# elif USE_GETLOGIN
5780
5781 errno = 0;
5782 login = getlogin();
5783 if (errno) {
5784 if (errno == ENOTTY || errno == ENXIO || errno == ENOENT) {
5785 return Qnil;
5786 }
5787 rb_syserr_fail(errno, "getlogin");
5788 }
5789
5790 return login ? rb_str_new_cstr(login) : Qnil;
5791# endif
5792
5793#endif
5794}
5795
5796 VALUE
5797rb_getpwdirnam_for_login(VALUE login_name)
5798{
5799#if ( !defined(USE_GETPWNAM_R) && !defined(USE_GETPWNAM) )
5800 return Qnil;
5801#else
5802
5803 if (NIL_P(login_name)) {
5804 /* nothing to do; no name with which to query the password database */
5805 return Qnil;
5806 }
5807
5808 char *login = RSTRING_PTR(login_name);
5809
5810 struct passwd *pwptr;
5811
5812# ifdef USE_GETPWNAM_R
5813
5814 struct passwd pwdnm;
5815 char *bufnm;
5816 long bufsizenm = GETPW_R_SIZE_INIT; /* maybe -1 */
5817
5818 if (bufsizenm < 0)
5819 bufsizenm = GETPW_R_SIZE_DEFAULT;
5820
5821 VALUE getpwnm_tmp = rb_str_tmp_new(bufsizenm);
5822
5823 bufnm = RSTRING_PTR(getpwnm_tmp);
5824 bufsizenm = rb_str_capacity(getpwnm_tmp);
5825 rb_str_set_len(getpwnm_tmp, bufsizenm);
5826
5827 int enm;
5828 errno = 0;
5829 while ((enm = getpwnam_r(login, &pwdnm, bufnm, bufsizenm, &pwptr)) != 0) {
5830
5831 if (enm == ENOENT || enm== ESRCH || enm == EBADF || enm == EPERM) {
5832 /* not found; non-errors */
5833 rb_str_resize(getpwnm_tmp, 0);
5834 return Qnil;
5835 }
5836
5837 if (enm != ERANGE || bufsizenm >= GETPW_R_SIZE_LIMIT) {
5838 rb_str_resize(getpwnm_tmp, 0);
5839 rb_syserr_fail(enm, "getpwnam_r");
5840 }
5841
5842 rb_str_modify_expand(getpwnm_tmp, bufsizenm);
5843 bufnm = RSTRING_PTR(getpwnm_tmp);
5844 bufsizenm = rb_str_capacity(getpwnm_tmp);
5845 }
5846
5847 if (pwptr == NULL) {
5848 /* no record in the password database for the login name */
5849 rb_str_resize(getpwnm_tmp, 0);
5850 return Qnil;
5851 }
5852
5853 /* found it */
5854 VALUE result = rb_str_new_cstr(pwptr->pw_dir);
5855 rb_str_resize(getpwnm_tmp, 0);
5856 return result;
5857
5858# elif USE_GETPWNAM
5859
5860 errno = 0;
5861 pwptr = getpwnam(login);
5862 if (pwptr) {
5863 /* found it */
5864 return rb_str_new_cstr(pwptr->pw_dir);
5865 }
5866 if (errno
5867 /* avoid treating as errors errno values that indicate "not found" */
5868 && ( errno != ENOENT && errno != ESRCH && errno != EBADF && errno != EPERM)) {
5869 rb_syserr_fail(errno, "getpwnam");
5870 }
5871
5872 return Qnil; /* not found */
5873# endif
5874
5875#endif
5876}
5877
5881 VALUE
5882rb_getpwdiruid(void)
5883{
5884# if !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID)
5885 /* Should never happen... </famous-last-words> */
5886 return Qnil;
5887# else
5888 uid_t ruid = getuid();
5889
5890 struct passwd *pwptr;
5891
5892# ifdef USE_GETPWUID_R
5893
5894 struct passwd pwdid;
5895 char *bufid;
5896 long bufsizeid = GETPW_R_SIZE_INIT; /* maybe -1 */
5897
5898 if (bufsizeid < 0)
5899 bufsizeid = GETPW_R_SIZE_DEFAULT;
5900
5901 VALUE getpwid_tmp = rb_str_tmp_new(bufsizeid);
5902
5903 bufid = RSTRING_PTR(getpwid_tmp);
5904 bufsizeid = rb_str_capacity(getpwid_tmp);
5905 rb_str_set_len(getpwid_tmp, bufsizeid);
5906
5907 int eid;
5908 errno = 0;
5909 while ((eid = getpwuid_r(ruid, &pwdid, bufid, bufsizeid, &pwptr)) != 0) {
5910
5911 if (eid == ENOENT || eid== ESRCH || eid == EBADF || eid == EPERM) {
5912 /* not found; non-errors */
5913 rb_str_resize(getpwid_tmp, 0);
5914 return Qnil;
5915 }
5916
5917 if (eid != ERANGE || bufsizeid >= GETPW_R_SIZE_LIMIT) {
5918 rb_str_resize(getpwid_tmp, 0);
5919 rb_syserr_fail(eid, "getpwuid_r");
5920 }
5921
5922 rb_str_modify_expand(getpwid_tmp, bufsizeid);
5923 bufid = RSTRING_PTR(getpwid_tmp);
5924 bufsizeid = rb_str_capacity(getpwid_tmp);
5925 }
5926
5927 if (pwptr == NULL) {
5928 /* no record in the password database for the uid */
5929 rb_str_resize(getpwid_tmp, 0);
5930 return Qnil;
5931 }
5932
5933 /* found it */
5934 VALUE result = rb_str_new_cstr(pwptr->pw_dir);
5935 rb_str_resize(getpwid_tmp, 0);
5936 return result;
5937
5938# elif defined(USE_GETPWUID)
5939
5940 errno = 0;
5941 pwptr = getpwuid(ruid);
5942 if (pwptr) {
5943 /* found it */
5944 return rb_str_new_cstr(pwptr->pw_dir);
5945 }
5946 if (errno
5947 /* avoid treating as errors errno values that indicate "not found" */
5948 && ( errno == ENOENT || errno == ESRCH || errno == EBADF || errno == EPERM)) {
5949 rb_syserr_fail(errno, "getpwuid");
5950 }
5951
5952 return Qnil; /* not found */
5953# endif
5954
5955#endif /* !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID) */
5956}
5957#endif /* HAVE_PWD_H */
5958
5959
5960/*********************************************************************
5961 * Document-class: Process::Sys
5962 *
5963 * The Process::Sys module contains UID and GID
5964 * functions which provide direct bindings to the system calls of the
5965 * same names instead of the more-portable versions of the same
5966 * functionality found in the Process,
5967 * Process::UID, and Process::GID modules.
5968 */
5969
5970#if defined(HAVE_PWD_H)
5971static rb_uid_t
5972obj2uid(VALUE id
5973# ifdef USE_GETPWNAM_R
5974 , VALUE *getpw_tmp
5975# endif
5976 )
5977{
5978 rb_uid_t uid;
5979 VALUE tmp;
5980
5981 if (FIXNUM_P(id) || NIL_P(tmp = rb_check_string_type(id))) {
5982 uid = NUM2UIDT(id);
5983 }
5984 else {
5985 const char *usrname = StringValueCStr(id);
5986 struct passwd *pwptr;
5987#ifdef USE_GETPWNAM_R
5988 struct passwd pwbuf;
5989 char *getpw_buf;
5990 long getpw_buf_len;
5991 int e;
5992 if (!*getpw_tmp) {
5993 getpw_buf_len = GETPW_R_SIZE_INIT;
5994 if (getpw_buf_len < 0) getpw_buf_len = GETPW_R_SIZE_DEFAULT;
5995 *getpw_tmp = rb_str_tmp_new(getpw_buf_len);
5996 }
5997 getpw_buf = RSTRING_PTR(*getpw_tmp);
5998 getpw_buf_len = rb_str_capacity(*getpw_tmp);
5999 rb_str_set_len(*getpw_tmp, getpw_buf_len);
6000 errno = 0;
6001 while ((e = getpwnam_r(usrname, &pwbuf, getpw_buf, getpw_buf_len, &pwptr)) != 0) {
6002 if (e != ERANGE || getpw_buf_len >= GETPW_R_SIZE_LIMIT) {
6003 rb_str_resize(*getpw_tmp, 0);
6004 rb_syserr_fail(e, "getpwnam_r");
6005 }
6006 rb_str_modify_expand(*getpw_tmp, getpw_buf_len);
6007 getpw_buf = RSTRING_PTR(*getpw_tmp);
6008 getpw_buf_len = rb_str_capacity(*getpw_tmp);
6009 }
6010#else
6011 pwptr = getpwnam(usrname);
6012#endif
6013 if (!pwptr) {
6014#ifndef USE_GETPWNAM_R
6015 endpwent();
6016#endif
6017 rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, id);
6018 }
6019 uid = pwptr->pw_uid;
6020#ifndef USE_GETPWNAM_R
6021 endpwent();
6022#endif
6023 }
6024 return uid;
6025}
6026
6027# ifdef p_uid_from_name
6028/*
6029 * call-seq:
6030 * Process::UID.from_name(name) -> uid
6031 *
6032 * Get the user ID by the _name_.
6033 * If the user is not found, +ArgumentError+ will be raised.
6034 *
6035 * Process::UID.from_name("root") #=> 0
6036 * Process::UID.from_name("nosuchuser") #=> can't find user for nosuchuser (ArgumentError)
6037 */
6038
6039static VALUE
6040p_uid_from_name(VALUE self, VALUE id)
6041{
6042 return UIDT2NUM(OBJ2UID(id));
6043}
6044# endif
6045#endif
6046
6047#if defined(HAVE_GRP_H)
6048static rb_gid_t
6049obj2gid(VALUE id
6050# ifdef USE_GETGRNAM_R
6051 , VALUE *getgr_tmp
6052# endif
6053 )
6054{
6055 rb_gid_t gid;
6056 VALUE tmp;
6057
6058 if (FIXNUM_P(id) || NIL_P(tmp = rb_check_string_type(id))) {
6059 gid = NUM2GIDT(id);
6060 }
6061 else {
6062 const char *grpname = StringValueCStr(id);
6063 struct group *grptr;
6064#ifdef USE_GETGRNAM_R
6065 struct group grbuf;
6066 char *getgr_buf;
6067 long getgr_buf_len;
6068 int e;
6069 if (!*getgr_tmp) {
6070 getgr_buf_len = GETGR_R_SIZE_INIT;
6071 if (getgr_buf_len < 0) getgr_buf_len = GETGR_R_SIZE_DEFAULT;
6072 *getgr_tmp = rb_str_tmp_new(getgr_buf_len);
6073 }
6074 getgr_buf = RSTRING_PTR(*getgr_tmp);
6075 getgr_buf_len = rb_str_capacity(*getgr_tmp);
6076 rb_str_set_len(*getgr_tmp, getgr_buf_len);
6077 errno = 0;
6078 while ((e = getgrnam_r(grpname, &grbuf, getgr_buf, getgr_buf_len, &grptr)) != 0) {
6079 if (e != ERANGE || getgr_buf_len >= GETGR_R_SIZE_LIMIT) {
6080 rb_str_resize(*getgr_tmp, 0);
6081 rb_syserr_fail(e, "getgrnam_r");
6082 }
6083 rb_str_modify_expand(*getgr_tmp, getgr_buf_len);
6084 getgr_buf = RSTRING_PTR(*getgr_tmp);
6085 getgr_buf_len = rb_str_capacity(*getgr_tmp);
6086 }
6087#elif defined(HAVE_GETGRNAM)
6088 grptr = getgrnam(grpname);
6089#else
6090 grptr = NULL;
6091#endif
6092 if (!grptr) {
6093#if !defined(USE_GETGRNAM_R) && defined(HAVE_ENDGRENT)
6094 endgrent();
6095#endif
6096 rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, id);
6097 }
6098 gid = grptr->gr_gid;
6099#if !defined(USE_GETGRNAM_R) && defined(HAVE_ENDGRENT)
6100 endgrent();
6101#endif
6102 }
6103 return gid;
6104}
6105
6106# ifdef p_gid_from_name
6107/*
6108 * call-seq:
6109 * Process::GID.from_name(name) -> gid
6110 *
6111 * Get the group ID by the _name_.
6112 * If the group is not found, +ArgumentError+ will be raised.
6113 *
6114 * Process::GID.from_name("wheel") #=> 0
6115 * Process::GID.from_name("nosuchgroup") #=> can't find group for nosuchgroup (ArgumentError)
6116 */
6117
6118static VALUE
6119p_gid_from_name(VALUE self, VALUE id)
6120{
6121 return GIDT2NUM(OBJ2GID(id));
6122}
6123# endif
6124#endif
6125
6126#if defined HAVE_SETUID
6127/*
6128 * call-seq:
6129 * Process::Sys.setuid(user) -> nil
6130 *
6131 * Set the user ID of the current process to _user_. Not
6132 * available on all platforms.
6133 *
6134 */
6135
6136static VALUE
6137p_sys_setuid(VALUE obj, VALUE id)
6138{
6139 check_uid_switch();
6140 if (setuid(OBJ2UID(id)) != 0) rb_sys_fail(0);
6141 return Qnil;
6142}
6143#else
6144#define p_sys_setuid rb_f_notimplement
6145#endif
6146
6147
6148#if defined HAVE_SETRUID
6149/*
6150 * call-seq:
6151 * Process::Sys.setruid(user) -> nil
6152 *
6153 * Set the real user ID of the calling process to _user_.
6154 * Not available on all platforms.
6155 *
6156 */
6157
6158static VALUE
6159p_sys_setruid(VALUE obj, VALUE id)
6160{
6161 check_uid_switch();
6162 if (setruid(OBJ2UID(id)) != 0) rb_sys_fail(0);
6163 return Qnil;
6164}
6165#else
6166#define p_sys_setruid rb_f_notimplement
6167#endif
6168
6169
6170#if defined HAVE_SETEUID
6171/*
6172 * call-seq:
6173 * Process::Sys.seteuid(user) -> nil
6174 *
6175 * Set the effective user ID of the calling process to
6176 * _user_. Not available on all platforms.
6177 *
6178 */
6179
6180static VALUE
6181p_sys_seteuid(VALUE obj, VALUE id)
6182{
6183 check_uid_switch();
6184 if (seteuid(OBJ2UID(id)) != 0) rb_sys_fail(0);
6185 return Qnil;
6186}
6187#else
6188#define p_sys_seteuid rb_f_notimplement
6189#endif
6190
6191
6192#if defined HAVE_SETREUID
6193/*
6194 * call-seq:
6195 * Process::Sys.setreuid(rid, eid) -> nil
6196 *
6197 * Sets the (user) real and/or effective user IDs of the current
6198 * process to _rid_ and _eid_, respectively. A value of
6199 * <code>-1</code> for either means to leave that ID unchanged. Not
6200 * available on all platforms.
6201 *
6202 */
6203
6204static VALUE
6205p_sys_setreuid(VALUE obj, VALUE rid, VALUE eid)
6206{
6207 rb_uid_t ruid, euid;
6208 PREPARE_GETPWNAM;
6209 check_uid_switch();
6210 ruid = OBJ2UID1(rid);
6211 euid = OBJ2UID1(eid);
6212 FINISH_GETPWNAM;
6213 if (setreuid(ruid, euid) != 0) rb_sys_fail(0);
6214 return Qnil;
6215}
6216#else
6217#define p_sys_setreuid rb_f_notimplement
6218#endif
6219
6220
6221#if defined HAVE_SETRESUID
6222/*
6223 * call-seq:
6224 * Process::Sys.setresuid(rid, eid, sid) -> nil
6225 *
6226 * Sets the (user) real, effective, and saved user IDs of the
6227 * current process to _rid_, _eid_, and _sid_ respectively. A
6228 * value of <code>-1</code> for any value means to
6229 * leave that ID unchanged. Not available on all platforms.
6230 *
6231 */
6232
6233static VALUE
6234p_sys_setresuid(VALUE obj, VALUE rid, VALUE eid, VALUE sid)
6235{
6236 rb_uid_t ruid, euid, suid;
6237 PREPARE_GETPWNAM;
6238 check_uid_switch();
6239 ruid = OBJ2UID1(rid);
6240 euid = OBJ2UID1(eid);
6241 suid = OBJ2UID1(sid);
6242 FINISH_GETPWNAM;
6243 if (setresuid(ruid, euid, suid) != 0) rb_sys_fail(0);
6244 return Qnil;
6245}
6246#else
6247#define p_sys_setresuid rb_f_notimplement
6248#endif
6249
6250
6251/*
6252 * call-seq:
6253 * Process.uid -> integer
6254 * Process::UID.rid -> integer
6255 * Process::Sys.getuid -> integer
6256 *
6257 * Returns the (real) user ID of the current process.
6258 *
6259 * Process.uid # => 1000
6260 *
6261 */
6262
6263static VALUE
6264proc_getuid(VALUE obj)
6265{
6266 rb_uid_t uid = getuid();
6267 return UIDT2NUM(uid);
6268}
6269
6270
6271#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRUID) || defined(HAVE_SETUID)
6272/*
6273 * call-seq:
6274 * Process.uid = new_uid -> new_uid
6275 *
6276 * Sets the (user) user ID for the current process to +new_uid+:
6277 *
6278 * Process.uid = 1000 # => 1000
6279 *
6280 * Not available on all platforms.
6281 */
6282
6283static VALUE
6284proc_setuid(VALUE obj, VALUE id)
6285{
6286 rb_uid_t uid;
6287
6288 check_uid_switch();
6289
6290 uid = OBJ2UID(id);
6291#if defined(HAVE_SETRESUID)
6292 if (setresuid(uid, -1, -1) < 0) rb_sys_fail(0);
6293#elif defined HAVE_SETREUID
6294 if (setreuid(uid, -1) < 0) rb_sys_fail(0);
6295#elif defined HAVE_SETRUID
6296 if (setruid(uid) < 0) rb_sys_fail(0);
6297#elif defined HAVE_SETUID
6298 {
6299 if (geteuid() == uid) {
6300 if (setuid(uid) < 0) rb_sys_fail(0);
6301 }
6302 else {
6303 rb_notimplement();
6304 }
6305 }
6306#endif
6307 return id;
6308}
6309#else
6310#define proc_setuid rb_f_notimplement
6311#endif
6312
6313
6314/********************************************************************
6315 *
6316 * Document-class: Process::UID
6317 *
6318 * The Process::UID module contains a collection of
6319 * module functions which can be used to portably get, set, and
6320 * switch the current process's real, effective, and saved user IDs.
6321 *
6322 */
6323
6324static rb_uid_t SAVED_USER_ID = -1;
6325
6326#ifdef BROKEN_SETREUID
6327int
6328setreuid(rb_uid_t ruid, rb_uid_t euid)
6329{
6330 if (ruid != (rb_uid_t)-1 && ruid != getuid()) {
6331 if (euid == (rb_uid_t)-1) euid = geteuid();
6332 if (setuid(ruid) < 0) return -1;
6333 }
6334 if (euid != (rb_uid_t)-1 && euid != geteuid()) {
6335 if (seteuid(euid) < 0) return -1;
6336 }
6337 return 0;
6338}
6339#endif
6340
6341/*
6342 * call-seq:
6343 * Process::UID.change_privilege(user) -> integer
6344 *
6345 * Change the current process's real and effective user ID to that
6346 * specified by _user_. Returns the new user ID. Not
6347 * available on all platforms.
6348 *
6349 * [Process.uid, Process.euid] #=> [0, 0]
6350 * Process::UID.change_privilege(31) #=> 31
6351 * [Process.uid, Process.euid] #=> [31, 31]
6352 */
6353
6354static VALUE
6355p_uid_change_privilege(VALUE obj, VALUE id)
6356{
6357 rb_uid_t uid;
6358
6359 check_uid_switch();
6360
6361 uid = OBJ2UID(id);
6362
6363 if (geteuid() == 0) { /* root-user */
6364#if defined(HAVE_SETRESUID)
6365 if (setresuid(uid, uid, uid) < 0) rb_sys_fail(0);
6366 SAVED_USER_ID = uid;
6367#elif defined(HAVE_SETUID)
6368 if (setuid(uid) < 0) rb_sys_fail(0);
6369 SAVED_USER_ID = uid;
6370#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
6371 if (getuid() == uid) {
6372 if (SAVED_USER_ID == uid) {
6373 if (setreuid(-1, uid) < 0) rb_sys_fail(0);
6374 }
6375 else {
6376 if (uid == 0) { /* (r,e,s) == (root, root, x) */
6377 if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
6378 if (setreuid(SAVED_USER_ID, 0) < 0) rb_sys_fail(0);
6379 SAVED_USER_ID = 0; /* (r,e,s) == (x, root, root) */
6380 if (setreuid(uid, uid) < 0) rb_sys_fail(0);
6381 SAVED_USER_ID = uid;
6382 }
6383 else {
6384 if (setreuid(0, -1) < 0) rb_sys_fail(0);
6385 SAVED_USER_ID = 0;
6386 if (setreuid(uid, uid) < 0) rb_sys_fail(0);
6387 SAVED_USER_ID = uid;
6388 }
6389 }
6390 }
6391 else {
6392 if (setreuid(uid, uid) < 0) rb_sys_fail(0);
6393 SAVED_USER_ID = uid;
6394 }
6395#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
6396 if (getuid() == uid) {
6397 if (SAVED_USER_ID == uid) {
6398 if (seteuid(uid) < 0) rb_sys_fail(0);
6399 }
6400 else {
6401 if (uid == 0) {
6402 if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
6403 SAVED_USER_ID = 0;
6404 if (setruid(0) < 0) rb_sys_fail(0);
6405 }
6406 else {
6407 if (setruid(0) < 0) rb_sys_fail(0);
6408 SAVED_USER_ID = 0;
6409 if (seteuid(uid) < 0) rb_sys_fail(0);
6410 if (setruid(uid) < 0) rb_sys_fail(0);
6411 SAVED_USER_ID = uid;
6412 }
6413 }
6414 }
6415 else {
6416 if (seteuid(uid) < 0) rb_sys_fail(0);
6417 if (setruid(uid) < 0) rb_sys_fail(0);
6418 SAVED_USER_ID = uid;
6419 }
6420#else
6421 (void)uid;
6422 rb_notimplement();
6423#endif
6424 }
6425 else { /* unprivileged user */
6426#if defined(HAVE_SETRESUID)
6427 if (setresuid((getuid() == uid)? (rb_uid_t)-1: uid,
6428 (geteuid() == uid)? (rb_uid_t)-1: uid,
6429 (SAVED_USER_ID == uid)? (rb_uid_t)-1: uid) < 0) rb_sys_fail(0);
6430 SAVED_USER_ID = uid;
6431#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
6432 if (SAVED_USER_ID == uid) {
6433 if (setreuid((getuid() == uid)? (rb_uid_t)-1: uid,
6434 (geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
6435 rb_sys_fail(0);
6436 }
6437 else if (getuid() != uid) {
6438 if (setreuid(uid, (geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
6439 rb_sys_fail(0);
6440 SAVED_USER_ID = uid;
6441 }
6442 else if (/* getuid() == uid && */ geteuid() != uid) {
6443 if (setreuid(geteuid(), uid) < 0) rb_sys_fail(0);
6444 SAVED_USER_ID = uid;
6445 if (setreuid(uid, -1) < 0) rb_sys_fail(0);
6446 }
6447 else { /* getuid() == uid && geteuid() == uid */
6448 if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
6449 if (setreuid(SAVED_USER_ID, uid) < 0) rb_sys_fail(0);
6450 SAVED_USER_ID = uid;
6451 if (setreuid(uid, -1) < 0) rb_sys_fail(0);
6452 }
6453#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
6454 if (SAVED_USER_ID == uid) {
6455 if (geteuid() != uid && seteuid(uid) < 0) rb_sys_fail(0);
6456 if (getuid() != uid && setruid(uid) < 0) rb_sys_fail(0);
6457 }
6458 else if (/* SAVED_USER_ID != uid && */ geteuid() == uid) {
6459 if (getuid() != uid) {
6460 if (setruid(uid) < 0) rb_sys_fail(0);
6461 SAVED_USER_ID = uid;
6462 }
6463 else {
6464 if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
6465 SAVED_USER_ID = uid;
6466 if (setruid(uid) < 0) rb_sys_fail(0);
6467 }
6468 }
6469 else if (/* geteuid() != uid && */ getuid() == uid) {
6470 if (seteuid(uid) < 0) rb_sys_fail(0);
6471 if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
6472 SAVED_USER_ID = uid;
6473 if (setruid(uid) < 0) rb_sys_fail(0);
6474 }
6475 else {
6476 rb_syserr_fail(EPERM, 0);
6477 }
6478#elif defined HAVE_44BSD_SETUID
6479 if (getuid() == uid) {
6480 /* (r,e,s)==(uid,?,?) ==> (uid,uid,uid) */
6481 if (setuid(uid) < 0) rb_sys_fail(0);
6482 SAVED_USER_ID = uid;
6483 }
6484 else {
6485 rb_syserr_fail(EPERM, 0);
6486 }
6487#elif defined HAVE_SETEUID
6488 if (getuid() == uid && SAVED_USER_ID == uid) {
6489 if (seteuid(uid) < 0) rb_sys_fail(0);
6490 }
6491 else {
6492 rb_syserr_fail(EPERM, 0);
6493 }
6494#elif defined HAVE_SETUID
6495 if (getuid() == uid && SAVED_USER_ID == uid) {
6496 if (setuid(uid) < 0) rb_sys_fail(0);
6497 }
6498 else {
6499 rb_syserr_fail(EPERM, 0);
6500 }
6501#else
6502 rb_notimplement();
6503#endif
6504 }
6505 return id;
6506}
6507
6508
6509
6510#if defined HAVE_SETGID
6511/*
6512 * call-seq:
6513 * Process::Sys.setgid(group) -> nil
6514 *
6515 * Set the group ID of the current process to _group_. Not
6516 * available on all platforms.
6517 *
6518 */
6519
6520static VALUE
6521p_sys_setgid(VALUE obj, VALUE id)
6522{
6523 check_gid_switch();
6524 if (setgid(OBJ2GID(id)) != 0) rb_sys_fail(0);
6525 return Qnil;
6526}
6527#else
6528#define p_sys_setgid rb_f_notimplement
6529#endif
6530
6531
6532#if defined HAVE_SETRGID
6533/*
6534 * call-seq:
6535 * Process::Sys.setrgid(group) -> nil
6536 *
6537 * Set the real group ID of the calling process to _group_.
6538 * Not available on all platforms.
6539 *
6540 */
6541
6542static VALUE
6543p_sys_setrgid(VALUE obj, VALUE id)
6544{
6545 check_gid_switch();
6546 if (setrgid(OBJ2GID(id)) != 0) rb_sys_fail(0);
6547 return Qnil;
6548}
6549#else
6550#define p_sys_setrgid rb_f_notimplement
6551#endif
6552
6553
6554#if defined HAVE_SETEGID
6555/*
6556 * call-seq:
6557 * Process::Sys.setegid(group) -> nil
6558 *
6559 * Set the effective group ID of the calling process to
6560 * _group_. Not available on all platforms.
6561 *
6562 */
6563
6564static VALUE
6565p_sys_setegid(VALUE obj, VALUE id)
6566{
6567 check_gid_switch();
6568 if (setegid(OBJ2GID(id)) != 0) rb_sys_fail(0);
6569 return Qnil;
6570}
6571#else
6572#define p_sys_setegid rb_f_notimplement
6573#endif
6574
6575
6576#if defined HAVE_SETREGID
6577/*
6578 * call-seq:
6579 * Process::Sys.setregid(rid, eid) -> nil
6580 *
6581 * Sets the (group) real and/or effective group IDs of the current
6582 * process to <em>rid</em> and <em>eid</em>, respectively. A value of
6583 * <code>-1</code> for either means to leave that ID unchanged. Not
6584 * available on all platforms.
6585 *
6586 */
6587
6588static VALUE
6589p_sys_setregid(VALUE obj, VALUE rid, VALUE eid)
6590{
6591 rb_gid_t rgid, egid;
6592 check_gid_switch();
6593 rgid = OBJ2GID(rid);
6594 egid = OBJ2GID(eid);
6595 if (setregid(rgid, egid) != 0) rb_sys_fail(0);
6596 return Qnil;
6597}
6598#else
6599#define p_sys_setregid rb_f_notimplement
6600#endif
6601
6602#if defined HAVE_SETRESGID
6603/*
6604 * call-seq:
6605 * Process::Sys.setresgid(rid, eid, sid) -> nil
6606 *
6607 * Sets the (group) real, effective, and saved user IDs of the
6608 * current process to <em>rid</em>, <em>eid</em>, and <em>sid</em>
6609 * respectively. A value of <code>-1</code> for any value means to
6610 * leave that ID unchanged. Not available on all platforms.
6611 *
6612 */
6613
6614static VALUE
6615p_sys_setresgid(VALUE obj, VALUE rid, VALUE eid, VALUE sid)
6616{
6617 rb_gid_t rgid, egid, sgid;
6618 check_gid_switch();
6619 rgid = OBJ2GID(rid);
6620 egid = OBJ2GID(eid);
6621 sgid = OBJ2GID(sid);
6622 if (setresgid(rgid, egid, sgid) != 0) rb_sys_fail(0);
6623 return Qnil;
6624}
6625#else
6626#define p_sys_setresgid rb_f_notimplement
6627#endif
6628
6629
6630#if defined HAVE_ISSETUGID
6631/*
6632 * call-seq:
6633 * Process::Sys.issetugid -> true or false
6634 *
6635 * Returns +true+ if the process was created as a result
6636 * of an execve(2) system call which had either of the setuid or
6637 * setgid bits set (and extra privileges were given as a result) or
6638 * if it has changed any of its real, effective or saved user or
6639 * group IDs since it began execution.
6640 *
6641 */
6642
6643static VALUE
6644p_sys_issetugid(VALUE obj)
6645{
6646 return RBOOL(issetugid());
6647}
6648#else
6649#define p_sys_issetugid rb_f_notimplement
6650#endif
6651
6652
6653/*
6654 * call-seq:
6655 * Process.gid -> integer
6656 * Process::GID.rid -> integer
6657 * Process::Sys.getgid -> integer
6658 *
6659 * Returns the (real) group ID for the current process:
6660 *
6661 * Process.gid # => 1000
6662 *
6663 */
6664
6665static VALUE
6666proc_getgid(VALUE obj)
6667{
6668 rb_gid_t gid = getgid();
6669 return GIDT2NUM(gid);
6670}
6671
6672
6673#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETRGID) || defined(HAVE_SETGID)
6674/*
6675 * call-seq:
6676 * Process.gid = new_gid -> new_gid
6677 *
6678 * Sets the group ID for the current process to +new_gid+:
6679 *
6680 * Process.gid = 1000 # => 1000
6681 *
6682 */
6683
6684static VALUE
6685proc_setgid(VALUE obj, VALUE id)
6686{
6687 rb_gid_t gid;
6688
6689 check_gid_switch();
6690
6691 gid = OBJ2GID(id);
6692#if defined(HAVE_SETRESGID)
6693 if (setresgid(gid, -1, -1) < 0) rb_sys_fail(0);
6694#elif defined HAVE_SETREGID
6695 if (setregid(gid, -1) < 0) rb_sys_fail(0);
6696#elif defined HAVE_SETRGID
6697 if (setrgid(gid) < 0) rb_sys_fail(0);
6698#elif defined HAVE_SETGID
6699 {
6700 if (getegid() == gid) {
6701 if (setgid(gid) < 0) rb_sys_fail(0);
6702 }
6703 else {
6704 rb_notimplement();
6705 }
6706 }
6707#endif
6708 return GIDT2NUM(gid);
6709}
6710#else
6711#define proc_setgid rb_f_notimplement
6712#endif
6713
6714
6715#if defined(_SC_NGROUPS_MAX) || defined(NGROUPS_MAX)
6716/*
6717 * Maximum supplementary groups are platform dependent.
6718 * FWIW, 65536 is enough big for our supported OSs.
6719 *
6720 * OS Name max groups
6721 * -----------------------------------------------
6722 * Linux Kernel >= 2.6.3 65536
6723 * Linux Kernel < 2.6.3 32
6724 * IBM AIX 5.2 64
6725 * IBM AIX 5.3 ... 6.1 128
6726 * IBM AIX 7.1 128 (can be configured to be up to 2048)
6727 * OpenBSD, NetBSD 16
6728 * FreeBSD < 8.0 16
6729 * FreeBSD >=8.0 1023
6730 * Darwin (Mac OS X) 16
6731 * Sun Solaris 7,8,9,10 16
6732 * Sun Solaris 11 / OpenSolaris 1024
6733 * Windows 1015
6734 */
6735static int _maxgroups = -1;
6736static int
6737get_sc_ngroups_max(void)
6738{
6739#ifdef _SC_NGROUPS_MAX
6740 return (int)sysconf(_SC_NGROUPS_MAX);
6741#elif defined(NGROUPS_MAX)
6742 return (int)NGROUPS_MAX;
6743#else
6744 return -1;
6745#endif
6746}
6747static int
6748maxgroups(void)
6749{
6750 if (_maxgroups < 0) {
6751 _maxgroups = get_sc_ngroups_max();
6752 if (_maxgroups < 0)
6753 _maxgroups = RB_MAX_GROUPS;
6754 }
6755
6756 return _maxgroups;
6757}
6758#endif
6759
6760
6761
6762#ifdef HAVE_GETGROUPS
6763/*
6764 * call-seq:
6765 * Process.groups -> array
6766 *
6767 * Returns an array of the group IDs
6768 * in the supplemental group access list for the current process:
6769 *
6770 * Process.groups # => [4, 24, 27, 30, 46, 122, 135, 136, 1000]
6771 *
6772 * These properties of the returned array are system-dependent:
6773 *
6774 * - Whether (and how) the array is sorted.
6775 * - Whether the array includes effective group IDs.
6776 * - Whether the array includes duplicate group IDs.
6777 * - Whether the array size exceeds the value of Process.maxgroups.
6778 *
6779 * Use this call to get a sorted and unique array:
6780 *
6781 * Process.groups.uniq.sort
6782 *
6783 */
6784
6785static VALUE
6786proc_getgroups(VALUE obj)
6787{
6788 VALUE ary, tmp;
6789 int i, ngroups;
6790 rb_gid_t *groups;
6791
6792 ngroups = getgroups(0, NULL);
6793 if (ngroups == -1)
6794 rb_sys_fail(0);
6795
6796 groups = ALLOCV_N(rb_gid_t, tmp, ngroups);
6797
6798 ngroups = getgroups(ngroups, groups);
6799 if (ngroups == -1)
6800 rb_sys_fail(0);
6801
6802 ary = rb_ary_new();
6803 for (i = 0; i < ngroups; i++)
6804 rb_ary_push(ary, GIDT2NUM(groups[i]));
6805
6806 ALLOCV_END(tmp);
6807
6808 return ary;
6809}
6810#else
6811#define proc_getgroups rb_f_notimplement
6812#endif
6813
6814
6815#ifdef HAVE_SETGROUPS
6816/*
6817 * call-seq:
6818 * Process.groups = new_groups -> new_groups
6819 *
6820 * Sets the supplemental group access list to the given
6821 * array of group IDs.
6822 *
6823 * Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
6824 * Process.groups = [27, 6, 10, 11] # => [27, 6, 10, 11]
6825 * Process.groups # => [27, 6, 10, 11]
6826 *
6827 */
6828
6829static VALUE
6830proc_setgroups(VALUE obj, VALUE ary)
6831{
6832 int ngroups, i;
6833 rb_gid_t *groups;
6834 VALUE tmp;
6835 PREPARE_GETGRNAM;
6836
6837 Check_Type(ary, T_ARRAY);
6838
6839 ngroups = RARRAY_LENINT(ary);
6840 if (ngroups > maxgroups())
6841 rb_raise(rb_eArgError, "too many groups, %d max", maxgroups());
6842
6843 groups = ALLOCV_N(rb_gid_t, tmp, ngroups);
6844
6845 for (i = 0; i < ngroups; i++) {
6846 VALUE g = RARRAY_AREF(ary, i);
6847
6848 groups[i] = OBJ2GID1(g);
6849 }
6850 FINISH_GETGRNAM;
6851
6852 if (setgroups(ngroups, groups) == -1) /* ngroups <= maxgroups */
6853 rb_sys_fail(0);
6854
6855 ALLOCV_END(tmp);
6856
6857 return proc_getgroups(obj);
6858}
6859#else
6860#define proc_setgroups rb_f_notimplement
6861#endif
6862
6863
6864#ifdef HAVE_INITGROUPS
6865/*
6866 * call-seq:
6867 * Process.initgroups(username, gid) -> array
6868 *
6869 * Sets the supplemental group access list;
6870 * the new list includes:
6871 *
6872 * - The group IDs of those groups to which the user given by +username+ belongs.
6873 * - The group ID +gid+.
6874 *
6875 * Example:
6876 *
6877 * Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
6878 * Process.initgroups('me', 30) # => [30, 6, 10, 11]
6879 * Process.groups # => [30, 6, 10, 11]
6880 *
6881 * Not available on all platforms.
6882 */
6883
6884static VALUE
6885proc_initgroups(VALUE obj, VALUE uname, VALUE base_grp)
6886{
6887 if (initgroups(StringValueCStr(uname), OBJ2GID(base_grp)) != 0) {
6888 rb_sys_fail(0);
6889 }
6890 return proc_getgroups(obj);
6891}
6892#else
6893#define proc_initgroups rb_f_notimplement
6894#endif
6895
6896#if defined(_SC_NGROUPS_MAX) || defined(NGROUPS_MAX)
6897/*
6898 * call-seq:
6899 * Process.maxgroups -> integer
6900 *
6901 * Returns the maximum number of group IDs allowed
6902 * in the supplemental group access list:
6903 *
6904 * Process.maxgroups # => 32
6905 *
6906 */
6907
6908static VALUE
6909proc_getmaxgroups(VALUE obj)
6910{
6911 return INT2FIX(maxgroups());
6912}
6913#else
6914#define proc_getmaxgroups rb_f_notimplement
6915#endif
6916
6917#ifdef HAVE_SETGROUPS
6918/*
6919 * call-seq:
6920 * Process.maxgroups = new_max -> new_max
6921 *
6922 * Sets the maximum number of group IDs allowed
6923 * in the supplemental group access list.
6924 */
6925
6926static VALUE
6927proc_setmaxgroups(VALUE obj, VALUE val)
6928{
6929 int ngroups = FIX2INT(val);
6930 int ngroups_max = get_sc_ngroups_max();
6931
6932 if (ngroups <= 0)
6933 rb_raise(rb_eArgError, "maxgroups %d should be positive", ngroups);
6934
6935 if (ngroups > RB_MAX_GROUPS)
6936 ngroups = RB_MAX_GROUPS;
6937
6938 if (ngroups_max > 0 && ngroups > ngroups_max)
6939 ngroups = ngroups_max;
6940
6941 _maxgroups = ngroups;
6942
6943 return INT2FIX(_maxgroups);
6944}
6945#else
6946#define proc_setmaxgroups rb_f_notimplement
6947#endif
6948
6949#if defined(HAVE_DAEMON) || (defined(HAVE_WORKING_FORK) && defined(HAVE_SETSID))
6950static int rb_daemon(int nochdir, int noclose);
6951
6952/*
6953 * call-seq:
6954 * Process.daemon(nochdir = nil, noclose = nil) -> 0
6955 *
6956 * Detaches the current process from its controlling terminal
6957 * and runs it in the background as system daemon;
6958 * returns zero.
6959 *
6960 * By default:
6961 *
6962 * - Changes the current working directory to the root directory.
6963 * - Redirects $stdin, $stdout, and $stderr to the null device.
6964 *
6965 * If optional argument +nochdir+ is +true+,
6966 * does not change the current working directory.
6967 *
6968 * If optional argument +noclose+ is +true+,
6969 * does not redirect $stdin, $stdout, or $stderr.
6970 */
6971
6972static VALUE
6973proc_daemon(int argc, VALUE *argv, VALUE _)
6974{
6975 int n, nochdir = FALSE, noclose = FALSE;
6976
6977 switch (rb_check_arity(argc, 0, 2)) {
6978 case 2: noclose = TO_BOOL(argv[1], "noclose");
6979 case 1: nochdir = TO_BOOL(argv[0], "nochdir");
6980 }
6981
6982 prefork();
6983 n = rb_daemon(nochdir, noclose);
6984 if (n < 0) rb_sys_fail("daemon");
6985 return INT2FIX(n);
6986}
6987
6988extern const char ruby_null_device[];
6989
6990static int
6991rb_daemon(int nochdir, int noclose)
6992{
6993 int err = 0;
6994#ifdef HAVE_DAEMON
6995 before_fork_ruby();
6996 err = daemon(nochdir, noclose);
6997 after_fork_ruby(0);
6998#else
6999 int n;
7000
7001 switch (rb_fork_ruby(NULL)) {
7002 case -1: return -1;
7003 case 0: break;
7004 default: _exit(EXIT_SUCCESS);
7005 }
7006
7007 /* ignore EPERM which means already being process-leader */
7008 if (setsid() < 0) (void)0;
7009
7010 if (!nochdir)
7011 err = chdir("/");
7012
7013 if (!noclose && (n = rb_cloexec_open(ruby_null_device, O_RDWR, 0)) != -1) {
7014 rb_update_max_fd(n);
7015 (void)dup2(n, 0);
7016 (void)dup2(n, 1);
7017 (void)dup2(n, 2);
7018 if (n > 2)
7019 (void)close (n);
7020 }
7021#endif
7022 return err;
7023}
7024#else
7025#define proc_daemon rb_f_notimplement
7026#endif
7027
7028/********************************************************************
7029 *
7030 * Document-class: Process::GID
7031 *
7032 * The Process::GID module contains a collection of
7033 * module functions which can be used to portably get, set, and
7034 * switch the current process's real, effective, and saved group IDs.
7035 *
7036 */
7037
7038static rb_gid_t SAVED_GROUP_ID = -1;
7039
7040#ifdef BROKEN_SETREGID
7041int
7042setregid(rb_gid_t rgid, rb_gid_t egid)
7043{
7044 if (rgid != (rb_gid_t)-1 && rgid != getgid()) {
7045 if (egid == (rb_gid_t)-1) egid = getegid();
7046 if (setgid(rgid) < 0) return -1;
7047 }
7048 if (egid != (rb_gid_t)-1 && egid != getegid()) {
7049 if (setegid(egid) < 0) return -1;
7050 }
7051 return 0;
7052}
7053#endif
7054
7055/*
7056 * call-seq:
7057 * Process::GID.change_privilege(group) -> integer
7058 *
7059 * Change the current process's real and effective group ID to that
7060 * specified by _group_. Returns the new group ID. Not
7061 * available on all platforms.
7062 *
7063 * [Process.gid, Process.egid] #=> [0, 0]
7064 * Process::GID.change_privilege(33) #=> 33
7065 * [Process.gid, Process.egid] #=> [33, 33]
7066 */
7067
7068static VALUE
7069p_gid_change_privilege(VALUE obj, VALUE id)
7070{
7071 rb_gid_t gid;
7072
7073 check_gid_switch();
7074
7075 gid = OBJ2GID(id);
7076
7077 if (geteuid() == 0) { /* root-user */
7078#if defined(HAVE_SETRESGID)
7079 if (setresgid(gid, gid, gid) < 0) rb_sys_fail(0);
7080 SAVED_GROUP_ID = gid;
7081#elif defined HAVE_SETGID
7082 if (setgid(gid) < 0) rb_sys_fail(0);
7083 SAVED_GROUP_ID = gid;
7084#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7085 if (getgid() == gid) {
7086 if (SAVED_GROUP_ID == gid) {
7087 if (setregid(-1, gid) < 0) rb_sys_fail(0);
7088 }
7089 else {
7090 if (gid == 0) { /* (r,e,s) == (root, y, x) */
7091 if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7092 if (setregid(SAVED_GROUP_ID, 0) < 0) rb_sys_fail(0);
7093 SAVED_GROUP_ID = 0; /* (r,e,s) == (x, root, root) */
7094 if (setregid(gid, gid) < 0) rb_sys_fail(0);
7095 SAVED_GROUP_ID = gid;
7096 }
7097 else { /* (r,e,s) == (z, y, x) */
7098 if (setregid(0, 0) < 0) rb_sys_fail(0);
7099 SAVED_GROUP_ID = 0;
7100 if (setregid(gid, gid) < 0) rb_sys_fail(0);
7101 SAVED_GROUP_ID = gid;
7102 }
7103 }
7104 }
7105 else {
7106 if (setregid(gid, gid) < 0) rb_sys_fail(0);
7107 SAVED_GROUP_ID = gid;
7108 }
7109#elif defined(HAVE_SETRGID) && defined (HAVE_SETEGID)
7110 if (getgid() == gid) {
7111 if (SAVED_GROUP_ID == gid) {
7112 if (setegid(gid) < 0) rb_sys_fail(0);
7113 }
7114 else {
7115 if (gid == 0) {
7116 if (setegid(gid) < 0) rb_sys_fail(0);
7117 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7118 SAVED_GROUP_ID = 0;
7119 if (setrgid(0) < 0) rb_sys_fail(0);
7120 }
7121 else {
7122 if (setrgid(0) < 0) rb_sys_fail(0);
7123 SAVED_GROUP_ID = 0;
7124 if (setegid(gid) < 0) rb_sys_fail(0);
7125 if (setrgid(gid) < 0) rb_sys_fail(0);
7126 SAVED_GROUP_ID = gid;
7127 }
7128 }
7129 }
7130 else {
7131 if (setegid(gid) < 0) rb_sys_fail(0);
7132 if (setrgid(gid) < 0) rb_sys_fail(0);
7133 SAVED_GROUP_ID = gid;
7134 }
7135#else
7136 rb_notimplement();
7137#endif
7138 }
7139 else { /* unprivileged user */
7140#if defined(HAVE_SETRESGID)
7141 if (setresgid((getgid() == gid)? (rb_gid_t)-1: gid,
7142 (getegid() == gid)? (rb_gid_t)-1: gid,
7143 (SAVED_GROUP_ID == gid)? (rb_gid_t)-1: gid) < 0) rb_sys_fail(0);
7144 SAVED_GROUP_ID = gid;
7145#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7146 if (SAVED_GROUP_ID == gid) {
7147 if (setregid((getgid() == gid)? (rb_uid_t)-1: gid,
7148 (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
7149 rb_sys_fail(0);
7150 }
7151 else if (getgid() != gid) {
7152 if (setregid(gid, (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
7153 rb_sys_fail(0);
7154 SAVED_GROUP_ID = gid;
7155 }
7156 else if (/* getgid() == gid && */ getegid() != gid) {
7157 if (setregid(getegid(), gid) < 0) rb_sys_fail(0);
7158 SAVED_GROUP_ID = gid;
7159 if (setregid(gid, -1) < 0) rb_sys_fail(0);
7160 }
7161 else { /* getgid() == gid && getegid() == gid */
7162 if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7163 if (setregid(SAVED_GROUP_ID, gid) < 0) rb_sys_fail(0);
7164 SAVED_GROUP_ID = gid;
7165 if (setregid(gid, -1) < 0) rb_sys_fail(0);
7166 }
7167#elif defined(HAVE_SETRGID) && defined(HAVE_SETEGID)
7168 if (SAVED_GROUP_ID == gid) {
7169 if (getegid() != gid && setegid(gid) < 0) rb_sys_fail(0);
7170 if (getgid() != gid && setrgid(gid) < 0) rb_sys_fail(0);
7171 }
7172 else if (/* SAVED_GROUP_ID != gid && */ getegid() == gid) {
7173 if (getgid() != gid) {
7174 if (setrgid(gid) < 0) rb_sys_fail(0);
7175 SAVED_GROUP_ID = gid;
7176 }
7177 else {
7178 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7179 SAVED_GROUP_ID = gid;
7180 if (setrgid(gid) < 0) rb_sys_fail(0);
7181 }
7182 }
7183 else if (/* getegid() != gid && */ getgid() == gid) {
7184 if (setegid(gid) < 0) rb_sys_fail(0);
7185 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7186 SAVED_GROUP_ID = gid;
7187 if (setrgid(gid) < 0) rb_sys_fail(0);
7188 }
7189 else {
7190 rb_syserr_fail(EPERM, 0);
7191 }
7192#elif defined HAVE_44BSD_SETGID
7193 if (getgid() == gid) {
7194 /* (r,e,s)==(gid,?,?) ==> (gid,gid,gid) */
7195 if (setgid(gid) < 0) rb_sys_fail(0);
7196 SAVED_GROUP_ID = gid;
7197 }
7198 else {
7199 rb_syserr_fail(EPERM, 0);
7200 }
7201#elif defined HAVE_SETEGID
7202 if (getgid() == gid && SAVED_GROUP_ID == gid) {
7203 if (setegid(gid) < 0) rb_sys_fail(0);
7204 }
7205 else {
7206 rb_syserr_fail(EPERM, 0);
7207 }
7208#elif defined HAVE_SETGID
7209 if (getgid() == gid && SAVED_GROUP_ID == gid) {
7210 if (setgid(gid) < 0) rb_sys_fail(0);
7211 }
7212 else {
7213 rb_syserr_fail(EPERM, 0);
7214 }
7215#else
7216 (void)gid;
7217 rb_notimplement();
7218#endif
7219 }
7220 return id;
7221}
7222
7223
7224/*
7225 * call-seq:
7226 * Process.euid -> integer
7227 * Process::UID.eid -> integer
7228 * Process::Sys.geteuid -> integer
7229 *
7230 * Returns the effective user ID for the current process.
7231 *
7232 * Process.euid # => 501
7233 *
7234 */
7235
7236static VALUE
7237proc_geteuid(VALUE obj)
7238{
7239 rb_uid_t euid = geteuid();
7240 return UIDT2NUM(euid);
7241}
7242
7243#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID) || defined(_POSIX_SAVED_IDS)
7244static void
7245proc_seteuid(rb_uid_t uid)
7246{
7247#if defined(HAVE_SETRESUID)
7248 if (setresuid(-1, uid, -1) < 0) rb_sys_fail(0);
7249#elif defined HAVE_SETREUID
7250 if (setreuid(-1, uid) < 0) rb_sys_fail(0);
7251#elif defined HAVE_SETEUID
7252 if (seteuid(uid) < 0) rb_sys_fail(0);
7253#elif defined HAVE_SETUID
7254 if (uid == getuid()) {
7255 if (setuid(uid) < 0) rb_sys_fail(0);
7256 }
7257 else {
7258 rb_notimplement();
7259 }
7260#else
7261 rb_notimplement();
7262#endif
7263}
7264#endif
7265
7266#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID)
7267/*
7268 * call-seq:
7269 * Process.euid = new_euid -> new_euid
7270 *
7271 * Sets the effective user ID for the current process.
7272 *
7273 * Not available on all platforms.
7274 */
7275
7276static VALUE
7277proc_seteuid_m(VALUE mod, VALUE euid)
7278{
7279 check_uid_switch();
7280 proc_seteuid(OBJ2UID(euid));
7281 return euid;
7282}
7283#else
7284#define proc_seteuid_m rb_f_notimplement
7285#endif
7286
7287static rb_uid_t
7288rb_seteuid_core(rb_uid_t euid)
7289{
7290#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7291 rb_uid_t uid;
7292#endif
7293
7294 check_uid_switch();
7295
7296#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7297 uid = getuid();
7298#endif
7299
7300#if defined(HAVE_SETRESUID)
7301 if (uid != euid) {
7302 if (setresuid(-1,euid,euid) < 0) rb_sys_fail(0);
7303 SAVED_USER_ID = euid;
7304 }
7305 else {
7306 if (setresuid(-1,euid,-1) < 0) rb_sys_fail(0);
7307 }
7308#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7309 if (setreuid(-1, euid) < 0) rb_sys_fail(0);
7310 if (uid != euid) {
7311 if (setreuid(euid,uid) < 0) rb_sys_fail(0);
7312 if (setreuid(uid,euid) < 0) rb_sys_fail(0);
7313 SAVED_USER_ID = euid;
7314 }
7315#elif defined HAVE_SETEUID
7316 if (seteuid(euid) < 0) rb_sys_fail(0);
7317#elif defined HAVE_SETUID
7318 if (geteuid() == 0) rb_sys_fail(0);
7319 if (setuid(euid) < 0) rb_sys_fail(0);
7320#else
7321 rb_notimplement();
7322#endif
7323 return euid;
7324}
7325
7326
7327/*
7328 * call-seq:
7329 * Process::UID.grant_privilege(user) -> integer
7330 * Process::UID.eid= user -> integer
7331 *
7332 * Set the effective user ID, and if possible, the saved user ID of
7333 * the process to the given _user_. Returns the new
7334 * effective user ID. Not available on all platforms.
7335 *
7336 * [Process.uid, Process.euid] #=> [0, 0]
7337 * Process::UID.grant_privilege(31) #=> 31
7338 * [Process.uid, Process.euid] #=> [0, 31]
7339 */
7340
7341static VALUE
7342p_uid_grant_privilege(VALUE obj, VALUE id)
7343{
7344 rb_seteuid_core(OBJ2UID(id));
7345 return id;
7346}
7347
7348
7349/*
7350 * call-seq:
7351 * Process.egid -> integer
7352 * Process::GID.eid -> integer
7353 * Process::Sys.geteid -> integer
7354 *
7355 * Returns the effective group ID for the current process:
7356 *
7357 * Process.egid # => 500
7358 *
7359 * Not available on all platforms.
7360 */
7361
7362static VALUE
7363proc_getegid(VALUE obj)
7364{
7365 rb_gid_t egid = getegid();
7366
7367 return GIDT2NUM(egid);
7368}
7369
7370#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID) || defined(_POSIX_SAVED_IDS)
7371/*
7372 * call-seq:
7373 * Process.egid = new_egid -> new_egid
7374 *
7375 * Sets the effective group ID for the current process.
7376 *
7377 * Not available on all platforms.
7378 */
7379
7380static VALUE
7381proc_setegid(VALUE obj, VALUE egid)
7382{
7383#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7384 rb_gid_t gid;
7385#endif
7386
7387 check_gid_switch();
7388
7389#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7390 gid = OBJ2GID(egid);
7391#endif
7392
7393#if defined(HAVE_SETRESGID)
7394 if (setresgid(-1, gid, -1) < 0) rb_sys_fail(0);
7395#elif defined HAVE_SETREGID
7396 if (setregid(-1, gid) < 0) rb_sys_fail(0);
7397#elif defined HAVE_SETEGID
7398 if (setegid(gid) < 0) rb_sys_fail(0);
7399#elif defined HAVE_SETGID
7400 if (gid == getgid()) {
7401 if (setgid(gid) < 0) rb_sys_fail(0);
7402 }
7403 else {
7404 rb_notimplement();
7405 }
7406#else
7407 rb_notimplement();
7408#endif
7409 return egid;
7410}
7411#endif
7412
7413#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7414#define proc_setegid_m proc_setegid
7415#else
7416#define proc_setegid_m rb_f_notimplement
7417#endif
7418
7419static rb_gid_t
7420rb_setegid_core(rb_gid_t egid)
7421{
7422#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7423 rb_gid_t gid;
7424#endif
7425
7426 check_gid_switch();
7427
7428#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7429 gid = getgid();
7430#endif
7431
7432#if defined(HAVE_SETRESGID)
7433 if (gid != egid) {
7434 if (setresgid(-1,egid,egid) < 0) rb_sys_fail(0);
7435 SAVED_GROUP_ID = egid;
7436 }
7437 else {
7438 if (setresgid(-1,egid,-1) < 0) rb_sys_fail(0);
7439 }
7440#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7441 if (setregid(-1, egid) < 0) rb_sys_fail(0);
7442 if (gid != egid) {
7443 if (setregid(egid,gid) < 0) rb_sys_fail(0);
7444 if (setregid(gid,egid) < 0) rb_sys_fail(0);
7445 SAVED_GROUP_ID = egid;
7446 }
7447#elif defined HAVE_SETEGID
7448 if (setegid(egid) < 0) rb_sys_fail(0);
7449#elif defined HAVE_SETGID
7450 if (geteuid() == 0 /* root user */) rb_sys_fail(0);
7451 if (setgid(egid) < 0) rb_sys_fail(0);
7452#else
7453 rb_notimplement();
7454#endif
7455 return egid;
7456}
7457
7458
7459/*
7460 * call-seq:
7461 * Process::GID.grant_privilege(group) -> integer
7462 * Process::GID.eid = group -> integer
7463 *
7464 * Set the effective group ID, and if possible, the saved group ID of
7465 * the process to the given _group_. Returns the new
7466 * effective group ID. Not available on all platforms.
7467 *
7468 * [Process.gid, Process.egid] #=> [0, 0]
7469 * Process::GID.grant_privilege(31) #=> 33
7470 * [Process.gid, Process.egid] #=> [0, 33]
7471 */
7472
7473static VALUE
7474p_gid_grant_privilege(VALUE obj, VALUE id)
7475{
7476 rb_setegid_core(OBJ2GID(id));
7477 return id;
7478}
7479
7480
7481/*
7482 * call-seq:
7483 * Process::UID.re_exchangeable? -> true or false
7484 *
7485 * Returns +true+ if the real and effective user IDs of a
7486 * process may be exchanged on the current platform.
7487 *
7488 */
7489
7490static VALUE
7491p_uid_exchangeable(VALUE _)
7492{
7493#if defined(HAVE_SETRESUID)
7494 return Qtrue;
7495#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7496 return Qtrue;
7497#else
7498 return Qfalse;
7499#endif
7500}
7501
7502
7503/*
7504 * call-seq:
7505 * Process::UID.re_exchange -> integer
7506 *
7507 * Exchange real and effective user IDs and return the new effective
7508 * user ID. Not available on all platforms.
7509 *
7510 * [Process.uid, Process.euid] #=> [0, 31]
7511 * Process::UID.re_exchange #=> 0
7512 * [Process.uid, Process.euid] #=> [31, 0]
7513 */
7514
7515static VALUE
7516p_uid_exchange(VALUE obj)
7517{
7518 rb_uid_t uid;
7519#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7520 rb_uid_t euid;
7521#endif
7522
7523 check_uid_switch();
7524
7525 uid = getuid();
7526#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7527 euid = geteuid();
7528#endif
7529
7530#if defined(HAVE_SETRESUID)
7531 if (setresuid(euid, uid, uid) < 0) rb_sys_fail(0);
7532 SAVED_USER_ID = uid;
7533#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7534 if (setreuid(euid,uid) < 0) rb_sys_fail(0);
7535 SAVED_USER_ID = uid;
7536#else
7537 rb_notimplement();
7538#endif
7539 return UIDT2NUM(uid);
7540}
7541
7542
7543/*
7544 * call-seq:
7545 * Process::GID.re_exchangeable? -> true or false
7546 *
7547 * Returns +true+ if the real and effective group IDs of a
7548 * process may be exchanged on the current platform.
7549 *
7550 */
7551
7552static VALUE
7553p_gid_exchangeable(VALUE _)
7554{
7555#if defined(HAVE_SETRESGID)
7556 return Qtrue;
7557#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7558 return Qtrue;
7559#else
7560 return Qfalse;
7561#endif
7562}
7563
7564
7565/*
7566 * call-seq:
7567 * Process::GID.re_exchange -> integer
7568 *
7569 * Exchange real and effective group IDs and return the new effective
7570 * group ID. Not available on all platforms.
7571 *
7572 * [Process.gid, Process.egid] #=> [0, 33]
7573 * Process::GID.re_exchange #=> 0
7574 * [Process.gid, Process.egid] #=> [33, 0]
7575 */
7576
7577static VALUE
7578p_gid_exchange(VALUE obj)
7579{
7580 rb_gid_t gid;
7581#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7582 rb_gid_t egid;
7583#endif
7584
7585 check_gid_switch();
7586
7587 gid = getgid();
7588#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7589 egid = getegid();
7590#endif
7591
7592#if defined(HAVE_SETRESGID)
7593 if (setresgid(egid, gid, gid) < 0) rb_sys_fail(0);
7594 SAVED_GROUP_ID = gid;
7595#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7596 if (setregid(egid,gid) < 0) rb_sys_fail(0);
7597 SAVED_GROUP_ID = gid;
7598#else
7599 rb_notimplement();
7600#endif
7601 return GIDT2NUM(gid);
7602}
7603
7604/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
7605
7606/*
7607 * call-seq:
7608 * Process::UID.sid_available? -> true or false
7609 *
7610 * Returns +true+ if the current platform has saved user
7611 * ID functionality.
7612 *
7613 */
7614
7615static VALUE
7616p_uid_have_saved_id(VALUE _)
7617{
7618#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
7619 return Qtrue;
7620#else
7621 return Qfalse;
7622#endif
7623}
7624
7625
7626#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
7627static VALUE
7628p_uid_sw_ensure(VALUE i)
7629{
7630 rb_uid_t id = (rb_uid_t/* narrowing */)i;
7631 under_uid_switch = 0;
7632 id = rb_seteuid_core(id);
7633 return UIDT2NUM(id);
7634}
7635
7636
7637/*
7638 * call-seq:
7639 * Process::UID.switch -> integer
7640 * Process::UID.switch {|| block} -> object
7641 *
7642 * Switch the effective and real user IDs of the current process. If
7643 * a <em>block</em> is given, the user IDs will be switched back
7644 * after the block is executed. Returns the new effective user ID if
7645 * called without a block, and the return value of the block if one
7646 * is given.
7647 *
7648 */
7649
7650static VALUE
7651p_uid_switch(VALUE obj)
7652{
7653 rb_uid_t uid, euid;
7654
7655 check_uid_switch();
7656
7657 uid = getuid();
7658 euid = geteuid();
7659
7660 if (uid != euid) {
7661 proc_seteuid(uid);
7662 if (rb_block_given_p()) {
7663 under_uid_switch = 1;
7664 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, SAVED_USER_ID);
7665 }
7666 else {
7667 return UIDT2NUM(euid);
7668 }
7669 }
7670 else if (euid != SAVED_USER_ID) {
7671 proc_seteuid(SAVED_USER_ID);
7672 if (rb_block_given_p()) {
7673 under_uid_switch = 1;
7674 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, euid);
7675 }
7676 else {
7677 return UIDT2NUM(uid);
7678 }
7679 }
7680 else {
7681 rb_syserr_fail(EPERM, 0);
7682 }
7683
7684 UNREACHABLE_RETURN(Qnil);
7685}
7686#else
7687static VALUE
7688p_uid_sw_ensure(VALUE obj)
7689{
7690 under_uid_switch = 0;
7691 return p_uid_exchange(obj);
7692}
7693
7694static VALUE
7695p_uid_switch(VALUE obj)
7696{
7697 rb_uid_t uid, euid;
7698
7699 check_uid_switch();
7700
7701 uid = getuid();
7702 euid = geteuid();
7703
7704 if (uid == euid) {
7705 rb_syserr_fail(EPERM, 0);
7706 }
7707 p_uid_exchange(obj);
7708 if (rb_block_given_p()) {
7709 under_uid_switch = 1;
7710 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, obj);
7711 }
7712 else {
7713 return UIDT2NUM(euid);
7714 }
7715}
7716#endif
7717
7718
7719/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
7720
7721/*
7722 * call-seq:
7723 * Process::GID.sid_available? -> true or false
7724 *
7725 * Returns +true+ if the current platform has saved group
7726 * ID functionality.
7727 *
7728 */
7729
7730static VALUE
7731p_gid_have_saved_id(VALUE _)
7732{
7733#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
7734 return Qtrue;
7735#else
7736 return Qfalse;
7737#endif
7738}
7739
7740#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
7741static VALUE
7742p_gid_sw_ensure(VALUE i)
7743{
7744 rb_gid_t id = (rb_gid_t/* narrowing */)i;
7745 under_gid_switch = 0;
7746 id = rb_setegid_core(id);
7747 return GIDT2NUM(id);
7748}
7749
7750
7751/*
7752 * call-seq:
7753 * Process::GID.switch -> integer
7754 * Process::GID.switch {|| block} -> object
7755 *
7756 * Switch the effective and real group IDs of the current process. If
7757 * a <em>block</em> is given, the group IDs will be switched back
7758 * after the block is executed. Returns the new effective group ID if
7759 * called without a block, and the return value of the block if one
7760 * is given.
7761 *
7762 */
7763
7764static VALUE
7765p_gid_switch(VALUE obj)
7766{
7767 rb_gid_t gid, egid;
7768
7769 check_gid_switch();
7770
7771 gid = getgid();
7772 egid = getegid();
7773
7774 if (gid != egid) {
7775 proc_setegid(obj, GIDT2NUM(gid));
7776 if (rb_block_given_p()) {
7777 under_gid_switch = 1;
7778 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, SAVED_GROUP_ID);
7779 }
7780 else {
7781 return GIDT2NUM(egid);
7782 }
7783 }
7784 else if (egid != SAVED_GROUP_ID) {
7785 proc_setegid(obj, GIDT2NUM(SAVED_GROUP_ID));
7786 if (rb_block_given_p()) {
7787 under_gid_switch = 1;
7788 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, egid);
7789 }
7790 else {
7791 return GIDT2NUM(gid);
7792 }
7793 }
7794 else {
7795 rb_syserr_fail(EPERM, 0);
7796 }
7797
7798 UNREACHABLE_RETURN(Qnil);
7799}
7800#else
7801static VALUE
7802p_gid_sw_ensure(VALUE obj)
7803{
7804 under_gid_switch = 0;
7805 return p_gid_exchange(obj);
7806}
7807
7808static VALUE
7809p_gid_switch(VALUE obj)
7810{
7811 rb_gid_t gid, egid;
7812
7813 check_gid_switch();
7814
7815 gid = getgid();
7816 egid = getegid();
7817
7818 if (gid == egid) {
7819 rb_syserr_fail(EPERM, 0);
7820 }
7821 p_gid_exchange(obj);
7822 if (rb_block_given_p()) {
7823 under_gid_switch = 1;
7824 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, obj);
7825 }
7826 else {
7827 return GIDT2NUM(egid);
7828 }
7829}
7830#endif
7831
7832
7833#if defined(HAVE_TIMES)
7834static long
7835get_clk_tck(void)
7836{
7837#ifdef HAVE__SC_CLK_TCK
7838 return sysconf(_SC_CLK_TCK);
7839#elif defined CLK_TCK
7840 return CLK_TCK;
7841#elif defined HZ
7842 return HZ;
7843#else
7844 return 60;
7845#endif
7846}
7847
7848/*
7849 * call-seq:
7850 * Process.times -> process_tms
7851 *
7852 * Returns a Process::Tms structure that contains user and system CPU times
7853 * for the current process, and for its children processes:
7854 *
7855 * Process.times
7856 * # => #<struct Process::Tms utime=55.122118, stime=35.533068, cutime=0.0, cstime=0.002846>
7857 *
7858 * The precision is platform-defined.
7859 */
7860
7861 VALUE
7862rb_proc_times(VALUE obj)
7863{
7864 VALUE utime, stime, cutime, cstime, ret;
7865#if defined(RUSAGE_SELF) && defined(RUSAGE_CHILDREN)
7866 struct rusage usage_s, usage_c;
7867
7868 if (getrusage(RUSAGE_SELF, &usage_s) != 0 || getrusage(RUSAGE_CHILDREN, &usage_c) != 0)
7869 rb_sys_fail("getrusage");
7870 utime = DBL2NUM((double)usage_s.ru_utime.tv_sec + (double)usage_s.ru_utime.tv_usec/1e6);
7871 stime = DBL2NUM((double)usage_s.ru_stime.tv_sec + (double)usage_s.ru_stime.tv_usec/1e6);
7872 cutime = DBL2NUM((double)usage_c.ru_utime.tv_sec + (double)usage_c.ru_utime.tv_usec/1e6);
7873 cstime = DBL2NUM((double)usage_c.ru_stime.tv_sec + (double)usage_c.ru_stime.tv_usec/1e6);
7874#else
7875 const double hertz = (double)get_clk_tck();
7876 struct tms buf;
7877
7878 times(&buf);
7879 utime = DBL2NUM(buf.tms_utime / hertz);
7880 stime = DBL2NUM(buf.tms_stime / hertz);
7881 cutime = DBL2NUM(buf.tms_cutime / hertz);
7882 cstime = DBL2NUM(buf.tms_cstime / hertz);
7883#endif
7884 ret = rb_struct_new(rb_cProcessTms, utime, stime, cutime, cstime);
7885 RB_GC_GUARD(utime);
7886 RB_GC_GUARD(stime);
7887 RB_GC_GUARD(cutime);
7888 RB_GC_GUARD(cstime);
7889 return ret;
7890}
7891#else
7892#define rb_proc_times rb_f_notimplement
7893#endif
7894
7895#ifdef HAVE_LONG_LONG
7896typedef LONG_LONG timetick_int_t;
7897#define TIMETICK_INT_MIN LLONG_MIN
7898#define TIMETICK_INT_MAX LLONG_MAX
7899#define TIMETICK_INT2NUM(v) LL2NUM(v)
7900#define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_LONG_P(a, b)
7901#else
7902typedef long timetick_int_t;
7903#define TIMETICK_INT_MIN LONG_MIN
7904#define TIMETICK_INT_MAX LONG_MAX
7905#define TIMETICK_INT2NUM(v) LONG2NUM(v)
7906#define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_P(a, b)
7907#endif
7908
7909CONSTFUNC(static timetick_int_t gcd_timetick_int(timetick_int_t, timetick_int_t));
7910static timetick_int_t
7911gcd_timetick_int(timetick_int_t a, timetick_int_t b)
7912{
7913 timetick_int_t t;
7914
7915 if (a < b) {
7916 t = a;
7917 a = b;
7918 b = t;
7919 }
7920
7921 while (1) {
7922 t = a % b;
7923 if (t == 0)
7924 return b;
7925 a = b;
7926 b = t;
7927 }
7928}
7929
7930static void
7931reduce_fraction(timetick_int_t *np, timetick_int_t *dp)
7932{
7933 timetick_int_t gcd = gcd_timetick_int(*np, *dp);
7934 if (gcd != 1) {
7935 *np /= gcd;
7936 *dp /= gcd;
7937 }
7938}
7939
7940static void
7941reduce_factors(timetick_int_t *numerators, int num_numerators,
7942 timetick_int_t *denominators, int num_denominators)
7943{
7944 int i, j;
7945 for (i = 0; i < num_numerators; i++) {
7946 if (numerators[i] == 1)
7947 continue;
7948 for (j = 0; j < num_denominators; j++) {
7949 if (denominators[j] == 1)
7950 continue;
7951 reduce_fraction(&numerators[i], &denominators[j]);
7952 }
7953 }
7954}
7955
7956 struct timetick {
7957 timetick_int_t giga_count;
7958 int32_t count; /* 0 .. 999999999 */
7959};
7960
7961static VALUE
7962timetick2dblnum(struct timetick *ttp,
7963 timetick_int_t *numerators, int num_numerators,
7964 timetick_int_t *denominators, int num_denominators)
7965{
7966 double d;
7967 int i;
7968
7969 reduce_factors(numerators, num_numerators,
7970 denominators, num_denominators);
7971
7972 d = ttp->giga_count * 1e9 + ttp->count;
7973
7974 for (i = 0; i < num_numerators; i++)
7975 d *= numerators[i];
7976 for (i = 0; i < num_denominators; i++)
7977 d /= denominators[i];
7978
7979 return DBL2NUM(d);
7980}
7981
7982static VALUE
7983timetick2dblnum_reciprocal(struct timetick *ttp,
7984 timetick_int_t *numerators, int num_numerators,
7985 timetick_int_t *denominators, int num_denominators)
7986{
7987 double d;
7988 int i;
7989
7990 reduce_factors(numerators, num_numerators,
7991 denominators, num_denominators);
7992
7993 d = 1.0;
7994 for (i = 0; i < num_denominators; i++)
7995 d *= denominators[i];
7996 for (i = 0; i < num_numerators; i++)
7997 d /= numerators[i];
7998 d /= ttp->giga_count * 1e9 + ttp->count;
7999
8000 return DBL2NUM(d);
8001}
8002
8003#define NDIV(x,y) (-(-((x)+1)/(y))-1)
8004#define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d))
8005
8006static VALUE
8007timetick2integer(struct timetick *ttp,
8008 timetick_int_t *numerators, int num_numerators,
8009 timetick_int_t *denominators, int num_denominators)
8010{
8011 VALUE v;
8012 int i;
8013
8014 reduce_factors(numerators, num_numerators,
8015 denominators, num_denominators);
8016
8017 if (!MUL_OVERFLOW_SIGNED_INTEGER_P(1000000000, ttp->giga_count,
8018 TIMETICK_INT_MIN, TIMETICK_INT_MAX-ttp->count)) {
8019 timetick_int_t t = ttp->giga_count * 1000000000 + ttp->count;
8020 for (i = 0; i < num_numerators; i++) {
8021 timetick_int_t factor = numerators[i];
8022 if (MUL_OVERFLOW_TIMETICK_P(factor, t))
8023 goto generic;
8024 t *= factor;
8025 }
8026 for (i = 0; i < num_denominators; i++) {
8027 t = DIV(t, denominators[i]);
8028 }
8029 return TIMETICK_INT2NUM(t);
8030 }
8031
8032 generic:
8033 v = TIMETICK_INT2NUM(ttp->giga_count);
8034 v = rb_funcall(v, '*', 1, LONG2FIX(1000000000));
8035 v = rb_funcall(v, '+', 1, LONG2FIX(ttp->count));
8036 for (i = 0; i < num_numerators; i++) {
8037 timetick_int_t factor = numerators[i];
8038 if (factor == 1)
8039 continue;
8040 v = rb_funcall(v, '*', 1, TIMETICK_INT2NUM(factor));
8041 }
8042 for (i = 0; i < num_denominators; i++) {
8043 v = rb_funcall(v, '/', 1, TIMETICK_INT2NUM(denominators[i])); /* Ruby's '/' is div. */
8044 }
8045 return v;
8046}
8047
8048static VALUE
8049make_clock_result(struct timetick *ttp,
8050 timetick_int_t *numerators, int num_numerators,
8051 timetick_int_t *denominators, int num_denominators,
8052 VALUE unit)
8053{
8054 if (unit == ID2SYM(id_nanosecond)) {
8055 numerators[num_numerators++] = 1000000000;
8056 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8057 }
8058 else if (unit == ID2SYM(id_microsecond)) {
8059 numerators[num_numerators++] = 1000000;
8060 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8061 }
8062 else if (unit == ID2SYM(id_millisecond)) {
8063 numerators[num_numerators++] = 1000;
8064 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8065 }
8066 else if (unit == ID2SYM(id_second)) {
8067 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8068 }
8069 else if (unit == ID2SYM(id_float_microsecond)) {
8070 numerators[num_numerators++] = 1000000;
8071 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
8072 }
8073 else if (unit == ID2SYM(id_float_millisecond)) {
8074 numerators[num_numerators++] = 1000;
8075 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
8076 }
8077 else if (NIL_P(unit) || unit == ID2SYM(id_float_second)) {
8078 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
8079 }
8080 else
8081 rb_raise(rb_eArgError, "unexpected unit: %"PRIsVALUE, unit);
8082}
8083
8084#ifdef __APPLE__
8085static const mach_timebase_info_data_t *
8086get_mach_timebase_info(void)
8087{
8088 static mach_timebase_info_data_t sTimebaseInfo;
8089
8090 if ( sTimebaseInfo.denom == 0 ) {
8091 (void) mach_timebase_info(&sTimebaseInfo);
8092 }
8093
8094 return &sTimebaseInfo;
8095}
8096
8097double
8098ruby_real_ms_time(void)
8099{
8100 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8101 uint64_t t = mach_absolute_time();
8102 return (double)t * info->numer / info->denom / 1e6;
8103}
8104#endif
8105
8106#if defined(NUM2CLOCKID)
8107# define NUMERIC_CLOCKID 1
8108#else
8109# define NUMERIC_CLOCKID 0
8110# define NUM2CLOCKID(x) 0
8111#endif
8112
8113#define clock_failed(name, err, arg) do { \
8114 int clock_error = (err); \
8115 rb_syserr_fail_str(clock_error, rb_sprintf("clock_" name "(%+"PRIsVALUE")", (arg))); \
8116 } while (0)
8117
8118/*
8119 * call-seq:
8120 * Process.clock_gettime(clock_id, unit = :float_second) -> number
8121 *
8122 * Returns a clock time as determined by POSIX function
8123 * {clock_gettime()}[https://man7.org/linux/man-pages/man3/clock_gettime.3.html]:
8124 *
8125 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID) # => 198.650379677
8126 *
8127 * Argument +clock_id+ should be a symbol or a constant that specifies
8128 * the clock whose time is to be returned;
8129 * see below.
8130 *
8131 * Optional argument +unit+ should be a symbol that specifies
8132 * the unit to be used in the returned clock time;
8133 * see below.
8134 *
8135 * <b>Argument +clock_id+</b>
8136 *
8137 * Argument +clock_id+ specifies the clock whose time is to be returned;
8138 * it may be a constant such as <tt>Process::CLOCK_REALTIME</tt>,
8139 * or a symbol shorthand such as +:CLOCK_REALTIME+.
8140 *
8141 * The supported clocks depend on the underlying operating system;
8142 * this method supports the following clocks on the indicated platforms
8143 * (raises Errno::EINVAL if called with an unsupported clock):
8144 *
8145 * - +:CLOCK_BOOTTIME+: Linux 2.6.39.
8146 * - +:CLOCK_BOOTTIME_ALARM+: Linux 3.0.
8147 * - +:CLOCK_MONOTONIC+: SUSv3 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4, macOS 10.12, Windows-2000.
8148 * - +:CLOCK_MONOTONIC_COARSE+: Linux 2.6.32.
8149 * - +:CLOCK_MONOTONIC_FAST+: FreeBSD 8.1.
8150 * - +:CLOCK_MONOTONIC_PRECISE+: FreeBSD 8.1.
8151 * - +:CLOCK_MONOTONIC_RAW+: Linux 2.6.28, macOS 10.12.
8152 * - +:CLOCK_MONOTONIC_RAW_APPROX+: macOS 10.12.
8153 * - +:CLOCK_PROCESS_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 9.3, OpenBSD 5.4, macOS 10.12.
8154 * - +:CLOCK_PROF+: FreeBSD 3.0, OpenBSD 2.1.
8155 * - +: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.
8156 * Time.now is recommended over +:CLOCK_REALTIME:.
8157 * - +:CLOCK_REALTIME_ALARM+: Linux 3.0.
8158 * - +:CLOCK_REALTIME_COARSE+: Linux 2.6.32.
8159 * - +:CLOCK_REALTIME_FAST+: FreeBSD 8.1.
8160 * - +:CLOCK_REALTIME_PRECISE+: FreeBSD 8.1.
8161 * - +:CLOCK_SECOND+: FreeBSD 8.1.
8162 * - +:CLOCK_TAI+: Linux 3.10.
8163 * - +:CLOCK_THREAD_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 7.1, OpenBSD 5.4, macOS 10.12.
8164 * - +:CLOCK_UPTIME+: FreeBSD 7.0, OpenBSD 5.5.
8165 * - +:CLOCK_UPTIME_FAST+: FreeBSD 8.1.
8166 * - +:CLOCK_UPTIME_PRECISE+: FreeBSD 8.1.
8167 * - +:CLOCK_UPTIME_RAW+: macOS 10.12.
8168 * - +:CLOCK_UPTIME_RAW_APPROX+: macOS 10.12.
8169 * - +:CLOCK_VIRTUAL+: FreeBSD 3.0, OpenBSD 2.1.
8170 *
8171 * Note that SUS stands for Single Unix Specification.
8172 * SUS contains POSIX and clock_gettime is defined in the POSIX part.
8173 * SUS defines +:CLOCK_REALTIME+ as mandatory but
8174 * +:CLOCK_MONOTONIC+, +:CLOCK_PROCESS_CPUTIME_ID+,
8175 * and +:CLOCK_THREAD_CPUTIME_ID+ are optional.
8176 *
8177 * Certain emulations are used when the given +clock_id+
8178 * is not supported directly:
8179 *
8180 * - Emulations for +:CLOCK_REALTIME+:
8181 *
8182 * - +:GETTIMEOFDAY_BASED_CLOCK_REALTIME+:
8183 * Use gettimeofday() defined by SUS (deprecated in SUSv4).
8184 * The resolution is 1 microsecond.
8185 * - +:TIME_BASED_CLOCK_REALTIME+:
8186 * Use time() defined by ISO C.
8187 * The resolution is 1 second.
8188 *
8189 * - Emulations for +:CLOCK_MONOTONIC+:
8190 *
8191 * - +:MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC+:
8192 * Use mach_absolute_time(), available on Darwin.
8193 * The resolution is CPU dependent.
8194 * - +:TIMES_BASED_CLOCK_MONOTONIC+:
8195 * Use the result value of times() defined by POSIX, thus:
8196 * >>>
8197 * Upon successful completion, times() shall return the elapsed real time,
8198 * in clock ticks, since an arbitrary point in the past
8199 * (for example, system start-up time).
8200 *
8201 * For example, GNU/Linux returns a value based on jiffies and it is monotonic.
8202 * However, 4.4BSD uses gettimeofday() and it is not monotonic.
8203 * (FreeBSD uses +:CLOCK_MONOTONIC+ instead, though.)
8204 *
8205 * The resolution is the clock tick.
8206 * "getconf CLK_TCK" command shows the clock ticks per second.
8207 * (The clock ticks-per-second is defined by HZ macro in older systems.)
8208 * If it is 100 and clock_t is 32 bits integer type,
8209 * the resolution is 10 millisecond and cannot represent over 497 days.
8210 *
8211 * - Emulations for +:CLOCK_PROCESS_CPUTIME_ID+:
8212 *
8213 * - +:GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8214 * Use getrusage() defined by SUS.
8215 * getrusage() is used with RUSAGE_SELF to obtain the time only for
8216 * the calling process (excluding the time for child processes).
8217 * The result is addition of user time (ru_utime) and system time (ru_stime).
8218 * The resolution is 1 microsecond.
8219 * - +:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8220 * Use times() defined by POSIX.
8221 * The result is addition of user time (tms_utime) and system time (tms_stime).
8222 * tms_cutime and tms_cstime are ignored to exclude the time for child processes.
8223 * The resolution is the clock tick.
8224 * "getconf CLK_TCK" command shows the clock ticks per second.
8225 * (The clock ticks per second is defined by HZ macro in older systems.)
8226 * If it is 100, the resolution is 10 millisecond.
8227 * - +:CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8228 * Use clock() defined by ISO C.
8229 * The resolution is <tt>1/CLOCKS_PER_SEC</tt>.
8230 * +CLOCKS_PER_SEC+ is the C-level macro defined by time.h.
8231 * SUS defines +CLOCKS_PER_SEC+ as 1000000;
8232 * other systems may define it differently.
8233 * If +CLOCKS_PER_SEC+ is 1000000 (as in SUS),
8234 * the resolution is 1 microsecond.
8235 * If +CLOCKS_PER_SEC+ is 1000000 and clock_t is a 32-bit integer type,
8236 * it cannot represent over 72 minutes.
8237 *
8238 * <b>Argument +unit+</b>
8239 *
8240 * Optional argument +unit+ (default +:float_second+)
8241 * specifies the unit for the returned value.
8242 *
8243 * - +:float_microsecond+: Number of microseconds as a float.
8244 * - +:float_millisecond+: Number of milliseconds as a float.
8245 * - +:float_second+: Number of seconds as a float.
8246 * - +:microsecond+: Number of microseconds as an integer.
8247 * - +:millisecond+: Number of milliseconds as an integer.
8248 * - +:nanosecond+: Number of nanoseconds as an integer.
8249 * - +::second+: Number of seconds as an integer.
8250 *
8251 * Examples:
8252 *
8253 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond)
8254 * # => 203605054.825
8255 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond)
8256 * # => 203643.696848
8257 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_second)
8258 * # => 203.762181929
8259 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :microsecond)
8260 * # => 204123212
8261 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :millisecond)
8262 * # => 204298
8263 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond)
8264 * # => 204602286036
8265 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :second)
8266 * # => 204
8267 *
8268 * The underlying function, clock_gettime(), returns a number of nanoseconds.
8269 * Float object (IEEE 754 double) is not enough to represent
8270 * the return value for +:CLOCK_REALTIME+.
8271 * If the exact nanoseconds value is required, use +:nanosecond+ as the +unit+.
8272 *
8273 * The origin (time zero) of the returned value is system-dependent,
8274 * and may be, for example, system start up time,
8275 * process start up time, the Epoch, etc.
8276 *
8277 * The origin in +:CLOCK_REALTIME+ is defined as the Epoch:
8278 * <tt>1970-01-01 00:00:00 UTC</tt>;
8279 * some systems count leap seconds and others don't,
8280 * so the result may vary across systems.
8281 */
8282static VALUE
8283rb_clock_gettime(int argc, VALUE *argv, VALUE _)
8284{
8285 int ret;
8286
8287 struct timetick tt;
8288 timetick_int_t numerators[2];
8289 timetick_int_t denominators[2];
8290 int num_numerators = 0;
8291 int num_denominators = 0;
8292
8293 VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
8294 VALUE clk_id = argv[0];
8295#ifdef HAVE_CLOCK_GETTIME
8296 clockid_t c;
8297#endif
8298
8299 if (SYMBOL_P(clk_id)) {
8300#ifdef CLOCK_REALTIME
8301 if (clk_id == RUBY_CLOCK_REALTIME) {
8302 c = CLOCK_REALTIME;
8303 goto gettime;
8304 }
8305#endif
8306
8307#ifdef CLOCK_MONOTONIC
8308 if (clk_id == RUBY_CLOCK_MONOTONIC) {
8309 c = CLOCK_MONOTONIC;
8310 goto gettime;
8311 }
8312#endif
8313
8314#ifdef CLOCK_PROCESS_CPUTIME_ID
8315 if (clk_id == RUBY_CLOCK_PROCESS_CPUTIME_ID) {
8316 c = CLOCK_PROCESS_CPUTIME_ID;
8317 goto gettime;
8318 }
8319#endif
8320
8321#ifdef CLOCK_THREAD_CPUTIME_ID
8322 if (clk_id == RUBY_CLOCK_THREAD_CPUTIME_ID) {
8323 c = CLOCK_THREAD_CPUTIME_ID;
8324 goto gettime;
8325 }
8326#endif
8327
8328 /*
8329 * Non-clock_gettime clocks are provided by symbol clk_id.
8330 */
8331#ifdef HAVE_GETTIMEOFDAY
8332 /*
8333 * GETTIMEOFDAY_BASED_CLOCK_REALTIME is used for
8334 * CLOCK_REALTIME if clock_gettime is not available.
8335 */
8336#define RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME ID2SYM(id_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
8337 if (clk_id == RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME) {
8338 struct timeval tv;
8339 ret = gettimeofday(&tv, 0);
8340 if (ret != 0)
8341 rb_sys_fail("gettimeofday");
8342 tt.giga_count = tv.tv_sec;
8343 tt.count = (int32_t)tv.tv_usec * 1000;
8344 denominators[num_denominators++] = 1000000000;
8345 goto success;
8346 }
8347#endif
8348
8349#define RUBY_TIME_BASED_CLOCK_REALTIME ID2SYM(id_TIME_BASED_CLOCK_REALTIME)
8350 if (clk_id == RUBY_TIME_BASED_CLOCK_REALTIME) {
8351 time_t t;
8352 t = time(NULL);
8353 if (t == (time_t)-1)
8354 rb_sys_fail("time");
8355 tt.giga_count = t;
8356 tt.count = 0;
8357 denominators[num_denominators++] = 1000000000;
8358 goto success;
8359 }
8360
8361#ifdef HAVE_TIMES
8362#define RUBY_TIMES_BASED_CLOCK_MONOTONIC \
8363 ID2SYM(id_TIMES_BASED_CLOCK_MONOTONIC)
8364 if (clk_id == RUBY_TIMES_BASED_CLOCK_MONOTONIC) {
8365 struct tms buf;
8366 clock_t c;
8367 unsigned_clock_t uc;
8368 c = times(&buf);
8369 if (c == (clock_t)-1)
8370 rb_sys_fail("times");
8371 uc = (unsigned_clock_t)c;
8372 tt.count = (int32_t)(uc % 1000000000);
8373 tt.giga_count = (uc / 1000000000);
8374 denominators[num_denominators++] = get_clk_tck();
8375 goto success;
8376 }
8377#endif
8378
8379#ifdef RUSAGE_SELF
8380#define RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID \
8381 ID2SYM(id_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
8382 if (clk_id == RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8383 struct rusage usage;
8384 int32_t usec;
8385 ret = getrusage(RUSAGE_SELF, &usage);
8386 if (ret != 0)
8387 rb_sys_fail("getrusage");
8388 tt.giga_count = usage.ru_utime.tv_sec + usage.ru_stime.tv_sec;
8389 usec = (int32_t)(usage.ru_utime.tv_usec + usage.ru_stime.tv_usec);
8390 if (1000000 <= usec) {
8391 tt.giga_count++;
8392 usec -= 1000000;
8393 }
8394 tt.count = usec * 1000;
8395 denominators[num_denominators++] = 1000000000;
8396 goto success;
8397 }
8398#endif
8399
8400#ifdef HAVE_TIMES
8401#define RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID \
8402 ID2SYM(id_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID)
8403 if (clk_id == RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8404 struct tms buf;
8405 unsigned_clock_t utime, stime;
8406 if (times(&buf) == (clock_t)-1)
8407 rb_sys_fail("times");
8408 utime = (unsigned_clock_t)buf.tms_utime;
8409 stime = (unsigned_clock_t)buf.tms_stime;
8410 tt.count = (int32_t)((utime % 1000000000) + (stime % 1000000000));
8411 tt.giga_count = (utime / 1000000000) + (stime / 1000000000);
8412 if (1000000000 <= tt.count) {
8413 tt.count -= 1000000000;
8414 tt.giga_count++;
8415 }
8416 denominators[num_denominators++] = get_clk_tck();
8417 goto success;
8418 }
8419#endif
8420
8421#define RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID \
8422 ID2SYM(id_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID)
8423 if (clk_id == RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8424 clock_t c;
8425 unsigned_clock_t uc;
8426 errno = 0;
8427 c = clock();
8428 if (c == (clock_t)-1)
8429 rb_sys_fail("clock");
8430 uc = (unsigned_clock_t)c;
8431 tt.count = (int32_t)(uc % 1000000000);
8432 tt.giga_count = uc / 1000000000;
8433 denominators[num_denominators++] = CLOCKS_PER_SEC;
8434 goto success;
8435 }
8436
8437#ifdef __APPLE__
8438 if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
8439 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8440 uint64_t t = mach_absolute_time();
8441 tt.count = (int32_t)(t % 1000000000);
8442 tt.giga_count = t / 1000000000;
8443 numerators[num_numerators++] = info->numer;
8444 denominators[num_denominators++] = info->denom;
8445 denominators[num_denominators++] = 1000000000;
8446 goto success;
8447 }
8448#endif
8449 }
8450 else if (NUMERIC_CLOCKID) {
8451#if defined(HAVE_CLOCK_GETTIME)
8452 struct timespec ts;
8453 c = NUM2CLOCKID(clk_id);
8454 gettime:
8455 ret = clock_gettime(c, &ts);
8456 if (ret == -1)
8457 clock_failed("gettime", errno, clk_id);
8458 tt.count = (int32_t)ts.tv_nsec;
8459 tt.giga_count = ts.tv_sec;
8460 denominators[num_denominators++] = 1000000000;
8461 goto success;
8462#endif
8463 }
8464 else {
8465 rb_unexpected_type(clk_id, T_SYMBOL);
8466 }
8467 clock_failed("gettime", EINVAL, clk_id);
8468
8469 success:
8470 return make_clock_result(&tt, numerators, num_numerators, denominators, num_denominators, unit);
8471}
8472
8473/*
8474 * call-seq:
8475 * Process.clock_getres(clock_id, unit = :float_second) -> number
8476 *
8477 * Returns a clock resolution as determined by POSIX function
8478 * {clock_getres()}[https://man7.org/linux/man-pages/man3/clock_getres.3.html]:
8479 *
8480 * Process.clock_getres(:CLOCK_REALTIME) # => 1.0e-09
8481 *
8482 * See Process.clock_gettime for the values of +clock_id+ and +unit+.
8483 *
8484 * Examples:
8485 *
8486 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond) # => 0.001
8487 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond) # => 1.0e-06
8488 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 1.0e-09
8489 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :microsecond) # => 0
8490 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :millisecond) # => 0
8491 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond) # => 1
8492 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :second) # => 0
8493 *
8494 * In addition to the values for +unit+ supported in Process.clock_gettime,
8495 * this method supports +:hertz+, the integer number of clock ticks per second
8496 * (which is the reciprocal of +:float_second+):
8497 *
8498 * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz) # => 100.0
8499 * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 0.01
8500 *
8501 * <b>Accuracy</b>:
8502 * Note that the returned resolution may be inaccurate on some platforms
8503 * due to underlying bugs.
8504 * Inaccurate resolutions have been reported for various clocks including
8505 * +:CLOCK_MONOTONIC+ and +:CLOCK_MONOTONIC_RAW+
8506 * on Linux, macOS, BSD or AIX platforms, when using ARM processors,
8507 * or when using virtualization.
8508 */
8509static VALUE
8510rb_clock_getres(int argc, VALUE *argv, VALUE _)
8511{
8512 int ret;
8513
8514 struct timetick tt;
8515 timetick_int_t numerators[2];
8516 timetick_int_t denominators[2];
8517 int num_numerators = 0;
8518 int num_denominators = 0;
8519#ifdef HAVE_CLOCK_GETRES
8520 clockid_t c;
8521#endif
8522
8523 VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
8524 VALUE clk_id = argv[0];
8525
8526 if (SYMBOL_P(clk_id)) {
8527#ifdef CLOCK_REALTIME
8528 if (clk_id == RUBY_CLOCK_REALTIME) {
8529 c = CLOCK_REALTIME;
8530 goto getres;
8531 }
8532#endif
8533
8534#ifdef CLOCK_MONOTONIC
8535 if (clk_id == RUBY_CLOCK_MONOTONIC) {
8536 c = CLOCK_MONOTONIC;
8537 goto getres;
8538 }
8539#endif
8540
8541#ifdef CLOCK_PROCESS_CPUTIME_ID
8542 if (clk_id == RUBY_CLOCK_PROCESS_CPUTIME_ID) {
8543 c = CLOCK_PROCESS_CPUTIME_ID;
8544 goto getres;
8545 }
8546#endif
8547
8548#ifdef CLOCK_THREAD_CPUTIME_ID
8549 if (clk_id == RUBY_CLOCK_THREAD_CPUTIME_ID) {
8550 c = CLOCK_THREAD_CPUTIME_ID;
8551 goto getres;
8552 }
8553#endif
8554
8555#ifdef RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
8556 if (clk_id == RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME) {
8557 tt.giga_count = 0;
8558 tt.count = 1000;
8559 denominators[num_denominators++] = 1000000000;
8560 goto success;
8561 }
8562#endif
8563
8564#ifdef RUBY_TIME_BASED_CLOCK_REALTIME
8565 if (clk_id == RUBY_TIME_BASED_CLOCK_REALTIME) {
8566 tt.giga_count = 1;
8567 tt.count = 0;
8568 denominators[num_denominators++] = 1000000000;
8569 goto success;
8570 }
8571#endif
8572
8573#ifdef RUBY_TIMES_BASED_CLOCK_MONOTONIC
8574 if (clk_id == RUBY_TIMES_BASED_CLOCK_MONOTONIC) {
8575 tt.count = 1;
8576 tt.giga_count = 0;
8577 denominators[num_denominators++] = get_clk_tck();
8578 goto success;
8579 }
8580#endif
8581
8582#ifdef RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
8583 if (clk_id == RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8584 tt.giga_count = 0;
8585 tt.count = 1000;
8586 denominators[num_denominators++] = 1000000000;
8587 goto success;
8588 }
8589#endif
8590
8591#ifdef RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
8592 if (clk_id == RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8593 tt.count = 1;
8594 tt.giga_count = 0;
8595 denominators[num_denominators++] = get_clk_tck();
8596 goto success;
8597 }
8598#endif
8599
8600#ifdef RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
8601 if (clk_id == RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8602 tt.count = 1;
8603 tt.giga_count = 0;
8604 denominators[num_denominators++] = CLOCKS_PER_SEC;
8605 goto success;
8606 }
8607#endif
8608
8609#ifdef RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
8610 if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
8611 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8612 tt.count = 1;
8613 tt.giga_count = 0;
8614 numerators[num_numerators++] = info->numer;
8615 denominators[num_denominators++] = info->denom;
8616 denominators[num_denominators++] = 1000000000;
8617 goto success;
8618 }
8619#endif
8620 }
8621 else if (NUMERIC_CLOCKID) {
8622#if defined(HAVE_CLOCK_GETRES)
8623 struct timespec ts;
8624 c = NUM2CLOCKID(clk_id);
8625 getres:
8626 ret = clock_getres(c, &ts);
8627 if (ret == -1)
8628 clock_failed("getres", errno, clk_id);
8629 tt.count = (int32_t)ts.tv_nsec;
8630 tt.giga_count = ts.tv_sec;
8631 denominators[num_denominators++] = 1000000000;
8632 goto success;
8633#endif
8634 }
8635 else {
8636 rb_unexpected_type(clk_id, T_SYMBOL);
8637 }
8638 clock_failed("getres", EINVAL, clk_id);
8639
8640 success:
8641 if (unit == ID2SYM(id_hertz)) {
8642 return timetick2dblnum_reciprocal(&tt, numerators, num_numerators, denominators, num_denominators);
8643 }
8644 else {
8645 return make_clock_result(&tt, numerators, num_numerators, denominators, num_denominators, unit);
8646 }
8647}
8648
8649static VALUE
8650get_CHILD_STATUS(ID _x, VALUE *_y)
8651{
8652 return rb_last_status_get();
8653}
8654
8655static VALUE
8656get_PROCESS_ID(ID _x, VALUE *_y)
8657{
8658 return get_pid();
8659}
8660
8661/*
8662 * call-seq:
8663 * Process.kill(signal, *ids) -> count
8664 *
8665 * Sends a signal to each process specified by +ids+
8666 * (which must specify at least one ID);
8667 * returns the count of signals sent.
8668 *
8669 * For each given +id+, if +id+ is:
8670 *
8671 * - Positive, sends the signal to the process whose process ID is +id+.
8672 * - Zero, send the signal to all processes in the current process group.
8673 * - Negative, sends the signal to a system-dependent collection of processes.
8674 *
8675 * Argument +signal+ specifies the signal to be sent;
8676 * the argument may be:
8677 *
8678 * - An integer signal number: e.g., +-29+, +0+, +29+.
8679 * - A signal name (string), with or without leading <tt>'SIG'</tt>,
8680 * and with or without a further prefixed minus sign (<tt>'-'</tt>):
8681 * e.g.:
8682 *
8683 * - <tt>'SIGPOLL'</tt>.
8684 * - <tt>'POLL'</tt>,
8685 * - <tt>'-SIGPOLL'</tt>.
8686 * - <tt>'-POLL'</tt>.
8687 *
8688 * - A signal symbol, with or without leading <tt>'SIG'</tt>,
8689 * and with or without a further prefixed minus sign (<tt>'-'</tt>):
8690 * e.g.:
8691 *
8692 * - +:SIGPOLL+.
8693 * - +:POLL+.
8694 * - <tt>:'-SIGPOLL'</tt>.
8695 * - <tt>:'-POLL'</tt>.
8696 *
8697 * If +signal+ is:
8698 *
8699 * - A non-negative integer, or a signal name or symbol
8700 * without prefixed <tt>'-'</tt>,
8701 * each process with process ID +id+ is signalled.
8702 * - A negative integer, or a signal name or symbol
8703 * with prefixed <tt>'-'</tt>,
8704 * each process group with group ID +id+ is signalled.
8705 *
8706 * Use method Signal.list to see which signals are supported
8707 * by Ruby on the underlying platform;
8708 * the method returns a hash of the string names
8709 * and non-negative integer values of the supported signals.
8710 * The size and content of the returned hash varies widely
8711 * among platforms.
8712 *
8713 * Additionally, signal +0+ is useful to determine if the process exists.
8714 *
8715 * Example:
8716 *
8717 * pid = fork do
8718 * Signal.trap('HUP') { puts 'Ouch!'; exit }
8719 * # ... do some work ...
8720 * end
8721 * # ...
8722 * Process.kill('HUP', pid)
8723 * Process.wait
8724 *
8725 * Output:
8726 *
8727 * Ouch!
8728 *
8729 * Exceptions:
8730 *
8731 * - Raises Errno::EINVAL or RangeError if +signal+ is an integer
8732 * but invalid.
8733 * - Raises ArgumentError if +signal+ is a string or symbol
8734 * but invalid.
8735 * - Raises Errno::ESRCH or RangeError if one of +ids+ is invalid.
8736 * - Raises Errno::EPERM if needed permissions are not in force.
8737 *
8738 * In the last two cases, signals may have been sent to some processes.
8739 */
8740
8741static VALUE
8742proc_rb_f_kill(int c, const VALUE *v, VALUE _)
8743{
8744 return rb_f_kill(c, v);
8745}
8746
8747 VALUE rb_mProcess;
8748static VALUE rb_mProcUID;
8749static VALUE rb_mProcGID;
8750static VALUE rb_mProcID_Syscall;
8751
8752/*
8753 * call-seq:
8754 * Process.warmup -> true
8755 *
8756 * Notify the Ruby virtual machine that the boot sequence is finished,
8757 * and that now is a good time to optimize the application. This is useful
8758 * for long running applications.
8759 *
8760 * This method is expected to be called at the end of the application boot.
8761 * If the application is deployed using a pre-forking model, +Process.warmup+
8762 * should be called in the original process before the first fork.
8763 *
8764 * The actual optimizations performed are entirely implementation specific
8765 * and may change in the future without notice.
8766 *
8767 * On CRuby, +Process.warmup+:
8768 *
8769 * * Performs a major GC.
8770 * * Compacts the heap.
8771 * * Promotes all surviving objects to the old generation.
8772 * * Precomputes the coderange of all strings.
8773 * * Frees all empty heap pages and increments the allocatable pages counter
8774 * by the number of pages freed.
8775 * * Invoke +malloc_trim+ if available to free empty malloc pages.
8776 */
8777
8778static VALUE
8779proc_warmup(VALUE _)
8780{
8781 RB_VM_LOCK_ENTER();
8782 rb_gc_prepare_heap();
8783 RB_VM_LOCK_LEAVE();
8784 return Qtrue;
8785}
8786
8787/*
8788 * Document-module: Process
8789 *
8790 * \Module +Process+ represents a process in the underlying operating system.
8791 * Its methods support management of the current process and its child processes.
8792 *
8793 * == \Process Creation
8794 *
8795 * Each of the following methods executes a given command in a new process or subshell,
8796 * or multiple commands in new processes and/or subshells.
8797 * The choice of process or subshell depends on the form of the command;
8798 * see {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
8799 *
8800 * - Process.spawn, Kernel#spawn: Executes the command;
8801 * returns the new pid without waiting for completion.
8802 * - Process.exec: Replaces the current process by executing the command.
8803 *
8804 * In addition:
8805 *
8806 * - \Method Kernel#system executes a given command-line (string) in a subshell;
8807 * returns +true+, +false+, or +nil+.
8808 * - \Method Kernel#` executes a given command-line (string) in a subshell;
8809 * returns its $stdout string.
8810 * - \Module Open3 supports creating child processes
8811 * with access to their $stdin, $stdout, and $stderr streams.
8812 *
8813 * === Execution Environment
8814 *
8815 * Optional leading argument +env+ is a hash of name/value pairs,
8816 * where each name is a string and each value is a string or +nil+;
8817 * each name/value pair is added to ENV in the new process.
8818 *
8819 * Process.spawn( 'ruby -e "p ENV[\"Foo\"]"')
8820 * Process.spawn({'Foo' => '0'}, 'ruby -e "p ENV[\"Foo\"]"')
8821 *
8822 * Output:
8823 *
8824 * "0"
8825 *
8826 * The effect is usually similar to that of calling ENV#update with argument +env+,
8827 * where each named environment variable is created or updated
8828 * (if the value is non-+nil+),
8829 * or deleted (if the value is +nil+).
8830 *
8831 * However, some modifications to the calling process may remain
8832 * if the new process fails.
8833 * For example, hard resource limits are not restored.
8834 *
8835 * === Argument +command_line+ or +exe_path+
8836 *
8837 * The required string argument is one of the following:
8838 *
8839 * - +command_line+ if it begins with a shell reserved word or special built-in,
8840 * or if it contains one or more meta characters.
8841 * - +exe_path+ otherwise.
8842 *
8843 * <b>Argument +command_line+</b>
8844 *
8845 * \String argument +command_line+ is a command line to be passed to a shell;
8846 * it must begin with a shell reserved word, begin with a special built-in,
8847 * or contain meta characters:
8848 *
8849 * system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
8850 * system('echo') # => true # Built-in.
8851 * system('date > /tmp/date.tmp') # => true # Contains meta character.
8852 * system('date > /nop/date.tmp') # => false
8853 * system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
8854 *
8855 * The command line may also contain arguments and options for the command:
8856 *
8857 * system('echo "Foo"') # => true
8858 *
8859 * Output:
8860 *
8861 * Foo
8862 *
8863 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
8864 *
8865 * <b>Argument +exe_path+</b>
8866 *
8867 * Argument +exe_path+ is one of the following:
8868 *
8869 * - The string path to an executable to be called.
8870 * - A 2-element array containing the path to an executable to be called,
8871 * and the string to be used as the name of the executing process.
8872 *
8873 * Example:
8874 *
8875 * system('/usr/bin/date') # => true # Path to date on Unix-style system.
8876 * system('foo') # => nil # Command failed.
8877 *
8878 * Output:
8879 *
8880 * Mon Aug 28 11:43:10 AM CDT 2023
8881 *
8882 * === Execution Options
8883 *
8884 * Optional trailing argument +options+ is a hash of execution options.
8885 *
8886 * ==== Working Directory (+:chdir+)
8887 *
8888 * By default, the working directory for the new process is the same as
8889 * that of the current process:
8890 *
8891 * Dir.chdir('/var')
8892 * Process.spawn('ruby -e "puts Dir.pwd"')
8893 *
8894 * Output:
8895 *
8896 * /var
8897 *
8898 * Use option +:chdir+ to set the working directory for the new process:
8899 *
8900 * Process.spawn('ruby -e "puts Dir.pwd"', {chdir: '/tmp'})
8901 *
8902 * Output:
8903 *
8904 * /tmp
8905 *
8906 * The working directory of the current process is not changed:
8907 *
8908 * Dir.pwd # => "/var"
8909 *
8910 * ==== \File Redirection (\File Descriptor)
8911 *
8912 * Use execution options for file redirection in the new process.
8913 *
8914 * The key for such an option may be an integer file descriptor (fd),
8915 * specifying a source,
8916 * or an array of fds, specifying multiple sources.
8917 *
8918 * An integer source fd may be specified as:
8919 *
8920 * - _n_: Specifies file descriptor _n_.
8921 *
8922 * There are these shorthand symbols for fds:
8923 *
8924 * - +:in+: Specifies file descriptor 0 (STDIN).
8925 * - +:out+: Specifies file descriptor 1 (STDOUT).
8926 * - +:err+: Specifies file descriptor 2 (STDERR).
8927 *
8928 * The value given with a source is one of:
8929 *
8930 * - _n_:
8931 * Redirects to fd _n_ in the parent process.
8932 * - +filepath+:
8933 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>,
8934 * where +mode+ is <tt>'r'</tt> for source +:in+,
8935 * or <tt>'w'</tt> for source +:out+ or +:err+.
8936 * - <tt>[filepath]</tt>:
8937 * Redirects from the file at +filepath+ via <tt>open(filepath, 'r', 0644)</tt>.
8938 * - <tt>[filepath, mode]</tt>:
8939 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>.
8940 * - <tt>[filepath, mode, perm]</tt>:
8941 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, perm)</tt>.
8942 * - <tt>[:child, fd]</tt>:
8943 * Redirects to the redirected +fd+.
8944 * - +:close+: Closes the file descriptor in child process.
8945 *
8946 * See {Access Modes}[rdoc-ref:File@Access+Modes]
8947 * and {File Permissions}[rdoc-ref:File@File+Permissions].
8948 *
8949 * ==== Environment Variables (+:unsetenv_others+)
8950 *
8951 * By default, the new process inherits environment variables
8952 * from the parent process;
8953 * use execution option key +:unsetenv_others+ with value +true+
8954 * to clear environment variables in the new process.
8955 *
8956 * Any changes specified by execution option +env+ are made after the new process
8957 * inherits or clears its environment variables;
8958 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
8959 *
8960 * ==== \File-Creation Access (+:umask+)
8961 *
8962 * Use execution option +:umask+ to set the file-creation access
8963 * for the new process;
8964 * see {Access Modes}[rdoc-ref:File@Access+Modes]:
8965 *
8966 * command = 'ruby -e "puts sprintf(\"0%o\", File.umask)"'
8967 * options = {:umask => 0644}
8968 * Process.spawn(command, options)
8969 *
8970 * Output:
8971 *
8972 * 0644
8973 *
8974 * ==== \Process Groups (+:pgroup+ and +:new_pgroup+)
8975 *
8976 * By default, the new process belongs to the same
8977 * {process group}[https://en.wikipedia.org/wiki/Process_group]
8978 * as the parent process.
8979 *
8980 * To specify a different process group.
8981 * use execution option +:pgroup+ with one of the following values:
8982 *
8983 * - +true+: Create a new process group for the new process.
8984 * - _pgid_: Create the new process in the process group
8985 * whose id is _pgid_.
8986 *
8987 * On Windows only, use execution option +:new_pgroup+ with value +true+
8988 * to create a new process group for the new process.
8989 *
8990 * ==== Resource Limits
8991 *
8992 * Use execution options to set resource limits.
8993 *
8994 * The keys for these options are symbols of the form
8995 * <tt>:rlimit_<i>resource_name</i></tt>,
8996 * where _resource_name_ is the downcased form of one of the string
8997 * resource names described at method Process.setrlimit.
8998 * For example, key +:rlimit_cpu+ corresponds to resource limit <tt>'CPU'</tt>.
8999 *
9000 * The value for such as key is one of:
9001 *
9002 * - An integer, specifying both the current and maximum limits.
9003 * - A 2-element array of integers, specifying the current and maximum limits.
9004 *
9005 * ==== \File Descriptor Inheritance
9006 *
9007 * By default, the new process inherits file descriptors from the parent process.
9008 *
9009 * Use execution option <tt>:close_others => true</tt> to modify that inheritance
9010 * by closing non-standard fds (3 and greater) that are not otherwise redirected.
9011 *
9012 * === Execution Shell
9013 *
9014 * On a Unix-like system, the shell invoked is <tt>/bin/sh</tt>;
9015 * otherwise the shell invoked is determined by environment variable
9016 * <tt>ENV['RUBYSHELL']</tt>, if defined, or <tt>ENV['COMSPEC']</tt> otherwise.
9017 *
9018 * Except for the +COMSPEC+ case,
9019 * the entire string +command_line+ is passed as an argument
9020 * to {shell option -c}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/sh.html].
9021 *
9022 * The shell performs normal shell expansion on the command line:
9023 *
9024 * spawn('echo C*') # => 799139
9025 * Process.wait # => 799139
9026 *
9027 * Output:
9028 *
9029 * CONTRIBUTING.md COPYING COPYING.ja
9030 *
9031 * == What's Here
9032 *
9033 * === Current-Process Getters
9034 *
9035 * - ::argv0: Returns the process name as a frozen string.
9036 * - ::egid: Returns the effective group ID.
9037 * - ::euid: Returns the effective user ID.
9038 * - ::getpgrp: Return the process group ID.
9039 * - ::getrlimit: Returns the resource limit.
9040 * - ::gid: Returns the (real) group ID.
9041 * - ::pid: Returns the process ID.
9042 * - ::ppid: Returns the process ID of the parent process.
9043 * - ::uid: Returns the (real) user ID.
9044 *
9045 * === Current-Process Setters
9046 *
9047 * - ::egid=: Sets the effective group ID.
9048 * - ::euid=: Sets the effective user ID.
9049 * - ::gid=: Sets the (real) group ID.
9050 * - ::setproctitle: Sets the process title.
9051 * - ::setpgrp: Sets the process group ID of the process to zero.
9052 * - ::setrlimit: Sets a resource limit.
9053 * - ::setsid: Establishes the process as a new session and process group leader,
9054 * with no controlling tty.
9055 * - ::uid=: Sets the user ID.
9056 *
9057 * === Current-Process Execution
9058 *
9059 * - ::abort: Immediately terminates the process.
9060 * - ::daemon: Detaches the process from its controlling terminal
9061 * and continues running it in the background as system daemon.
9062 * - ::exec: Replaces the process by running a given external command.
9063 * - ::exit: Initiates process termination by raising exception SystemExit
9064 * (which may be caught).
9065 * - ::exit!: Immediately exits the process.
9066 * - ::warmup: Notifies the Ruby virtual machine that the boot sequence
9067 * for the application is completed,
9068 * and that the VM may begin optimizing the application.
9069 *
9070 * === Child Processes
9071 *
9072 * - ::detach: Guards against a child process becoming a zombie.
9073 * - ::fork: Creates a child process.
9074 * - ::kill: Sends a given signal to processes.
9075 * - ::spawn: Creates a child process.
9076 * - ::wait, ::waitpid: Waits for a child process to exit; returns its process ID.
9077 * - ::wait2, ::waitpid2: Waits for a child process to exit; returns its process ID and status.
9078 * - ::waitall: Waits for all child processes to exit;
9079 * returns their process IDs and statuses.
9080 *
9081 * === \Process Groups
9082 *
9083 * - ::getpgid: Returns the process group ID for a process.
9084 * - ::getpriority: Returns the scheduling priority
9085 * for a process, process group, or user.
9086 * - ::getsid: Returns the session ID for a process.
9087 * - ::groups: Returns an array of the group IDs
9088 * in the supplemental group access list for this process.
9089 * - ::groups=: Sets the supplemental group access list
9090 * to the given array of group IDs.
9091 * - ::initgroups: Initializes the supplemental group access list.
9092 * - ::last_status: Returns the status of the last executed child process
9093 * in the current thread.
9094 * - ::maxgroups: Returns the maximum number of group IDs allowed
9095 * in the supplemental group access list.
9096 * - ::maxgroups=: Sets the maximum number of group IDs allowed
9097 * in the supplemental group access list.
9098 * - ::setpgid: Sets the process group ID of a process.
9099 * - ::setpriority: Sets the scheduling priority
9100 * for a process, process group, or user.
9101 *
9102 * === Timing
9103 *
9104 * - ::clock_getres: Returns the resolution of a system clock.
9105 * - ::clock_gettime: Returns the time from a system clock.
9106 * - ::times: Returns a Process::Tms object containing times
9107 * for the current process and its child processes.
9108 *
9109 */
9110
9111void
9112InitVM_process(void)
9113{
9114 rb_define_virtual_variable("$?", get_CHILD_STATUS, 0);
9115 rb_define_virtual_variable("$$", get_PROCESS_ID, 0);
9116
9117 rb_gvar_ractor_local("$$");
9118 rb_gvar_ractor_local("$?");
9119
9120 rb_define_global_function("exec", f_exec, -1);
9121 rb_define_global_function("fork", rb_f_fork, 0);
9122 rb_define_global_function("exit!", rb_f_exit_bang, -1);
9123 rb_define_global_function("system", rb_f_system, -1);
9124 rb_define_global_function("spawn", rb_f_spawn, -1);
9125 rb_define_global_function("sleep", rb_f_sleep, -1);
9126 rb_define_global_function("exit", f_exit, -1);
9127 rb_define_global_function("abort", f_abort, -1);
9128
9129 rb_mProcess = rb_define_module("Process");
9130
9131#ifdef WNOHANG
9132 /* see Process.wait */
9133 rb_define_const(rb_mProcess, "WNOHANG", INT2FIX(WNOHANG));
9134#else
9135 /* see Process.wait */
9136 rb_define_const(rb_mProcess, "WNOHANG", INT2FIX(0));
9137#endif
9138#ifdef WUNTRACED
9139 /* see Process.wait */
9140 rb_define_const(rb_mProcess, "WUNTRACED", INT2FIX(WUNTRACED));
9141#else
9142 /* see Process.wait */
9143 rb_define_const(rb_mProcess, "WUNTRACED", INT2FIX(0));
9144#endif
9145
9146 rb_define_singleton_method(rb_mProcess, "exec", f_exec, -1);
9147 rb_define_singleton_method(rb_mProcess, "fork", rb_f_fork, 0);
9148 rb_define_singleton_method(rb_mProcess, "spawn", rb_f_spawn, -1);
9149 rb_define_singleton_method(rb_mProcess, "exit!", rb_f_exit_bang, -1);
9150 rb_define_singleton_method(rb_mProcess, "exit", f_exit, -1);
9151 rb_define_singleton_method(rb_mProcess, "abort", f_abort, -1);
9152 rb_define_singleton_method(rb_mProcess, "last_status", proc_s_last_status, 0);
9153 rb_define_singleton_method(rb_mProcess, "_fork", rb_proc__fork, 0);
9154
9155 rb_define_module_function(rb_mProcess, "kill", proc_rb_f_kill, -1);
9156 rb_define_module_function(rb_mProcess, "wait", proc_m_wait, -1);
9157 rb_define_module_function(rb_mProcess, "wait2", proc_wait2, -1);
9158 rb_define_module_function(rb_mProcess, "waitpid", proc_m_wait, -1);
9159 rb_define_module_function(rb_mProcess, "waitpid2", proc_wait2, -1);
9160 rb_define_module_function(rb_mProcess, "waitall", proc_waitall, 0);
9161 rb_define_module_function(rb_mProcess, "detach", proc_detach, 1);
9162
9163 /* :nodoc: */
9164 rb_cWaiter = rb_define_class_under(rb_mProcess, "Waiter", rb_cThread);
9165 rb_undef_alloc_func(rb_cWaiter);
9166 rb_undef_method(CLASS_OF(rb_cWaiter), "new");
9167 rb_define_method(rb_cWaiter, "pid", detach_process_pid, 0);
9168
9169 rb_cProcessStatus = rb_define_class_under(rb_mProcess, "Status", rb_cObject);
9170 rb_define_alloc_func(rb_cProcessStatus, rb_process_status_allocate);
9171 rb_undef_method(CLASS_OF(rb_cProcessStatus), "new");
9172 rb_marshal_define_compat(rb_cProcessStatus, rb_cObject,
9173 process_status_dump, process_status_load);
9174
9175 rb_define_singleton_method(rb_cProcessStatus, "wait", rb_process_status_waitv, -1);
9176
9177 rb_define_method(rb_cProcessStatus, "==", pst_equal, 1);
9178 rb_define_method(rb_cProcessStatus, "&", pst_bitand, 1);
9179 rb_define_method(rb_cProcessStatus, ">>", pst_rshift, 1);
9180 rb_define_method(rb_cProcessStatus, "to_i", pst_to_i, 0);
9181 rb_define_method(rb_cProcessStatus, "to_s", pst_to_s, 0);
9182 rb_define_method(rb_cProcessStatus, "inspect", pst_inspect, 0);
9183
9184 rb_define_method(rb_cProcessStatus, "pid", pst_pid_m, 0);
9185
9186 rb_define_method(rb_cProcessStatus, "stopped?", pst_wifstopped, 0);
9187 rb_define_method(rb_cProcessStatus, "stopsig", pst_wstopsig, 0);
9188 rb_define_method(rb_cProcessStatus, "signaled?", pst_wifsignaled, 0);
9189 rb_define_method(rb_cProcessStatus, "termsig", pst_wtermsig, 0);
9190 rb_define_method(rb_cProcessStatus, "exited?", pst_wifexited, 0);
9191 rb_define_method(rb_cProcessStatus, "exitstatus", pst_wexitstatus, 0);
9192 rb_define_method(rb_cProcessStatus, "success?", pst_success_p, 0);
9193 rb_define_method(rb_cProcessStatus, "coredump?", pst_wcoredump, 0);
9194
9195 rb_define_module_function(rb_mProcess, "pid", proc_get_pid, 0);
9196 rb_define_module_function(rb_mProcess, "ppid", proc_get_ppid, 0);
9197
9198 rb_define_module_function(rb_mProcess, "getpgrp", proc_getpgrp, 0);
9199 rb_define_module_function(rb_mProcess, "setpgrp", proc_setpgrp, 0);
9200 rb_define_module_function(rb_mProcess, "getpgid", proc_getpgid, 1);
9201 rb_define_module_function(rb_mProcess, "setpgid", proc_setpgid, 2);
9202
9203 rb_define_module_function(rb_mProcess, "getsid", proc_getsid, -1);
9204 rb_define_module_function(rb_mProcess, "setsid", proc_setsid, 0);
9205
9206 rb_define_module_function(rb_mProcess, "getpriority", proc_getpriority, 2);
9207 rb_define_module_function(rb_mProcess, "setpriority", proc_setpriority, 3);
9208
9209 rb_define_module_function(rb_mProcess, "warmup", proc_warmup, 0);
9210
9211#ifdef HAVE_GETPRIORITY
9212 /* see Process.setpriority */
9213 rb_define_const(rb_mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS));
9214 /* see Process.setpriority */
9215 rb_define_const(rb_mProcess, "PRIO_PGRP", INT2FIX(PRIO_PGRP));
9216 /* see Process.setpriority */
9217 rb_define_const(rb_mProcess, "PRIO_USER", INT2FIX(PRIO_USER));
9218#endif
9219
9220 rb_define_module_function(rb_mProcess, "getrlimit", proc_getrlimit, 1);
9221 rb_define_module_function(rb_mProcess, "setrlimit", proc_setrlimit, -1);
9222#if defined(RLIM2NUM) && defined(RLIM_INFINITY)
9223 {
9224 VALUE inf = RLIM2NUM(RLIM_INFINITY);
9225#ifdef RLIM_SAVED_MAX
9226 {
9227 VALUE v = RLIM_INFINITY == RLIM_SAVED_MAX ? inf : RLIM2NUM(RLIM_SAVED_MAX);
9228 /* see Process.setrlimit */
9229 rb_define_const(rb_mProcess, "RLIM_SAVED_MAX", v);
9230 }
9231#endif
9232 /* see Process.setrlimit */
9233 rb_define_const(rb_mProcess, "RLIM_INFINITY", inf);
9234#ifdef RLIM_SAVED_CUR
9235 {
9236 VALUE v = RLIM_INFINITY == RLIM_SAVED_CUR ? inf : RLIM2NUM(RLIM_SAVED_CUR);
9237 /* see Process.setrlimit */
9238 rb_define_const(rb_mProcess, "RLIM_SAVED_CUR", v);
9239 }
9240#endif
9241 }
9242#ifdef RLIMIT_AS
9243 /* Maximum size of the process's virtual memory (address space) in bytes.
9244 *
9245 * see the system getrlimit(2) manual for details.
9246 */
9247 rb_define_const(rb_mProcess, "RLIMIT_AS", INT2FIX(RLIMIT_AS));
9248#endif
9249#ifdef RLIMIT_CORE
9250 /* Maximum size of the core file.
9251 *
9252 * see the system getrlimit(2) manual for details.
9253 */
9254 rb_define_const(rb_mProcess, "RLIMIT_CORE", INT2FIX(RLIMIT_CORE));
9255#endif
9256#ifdef RLIMIT_CPU
9257 /* CPU time limit in seconds.
9258 *
9259 * see the system getrlimit(2) manual for details.
9260 */
9261 rb_define_const(rb_mProcess, "RLIMIT_CPU", INT2FIX(RLIMIT_CPU));
9262#endif
9263#ifdef RLIMIT_DATA
9264 /* Maximum size of the process's data segment.
9265 *
9266 * see the system getrlimit(2) manual for details.
9267 */
9268 rb_define_const(rb_mProcess, "RLIMIT_DATA", INT2FIX(RLIMIT_DATA));
9269#endif
9270#ifdef RLIMIT_FSIZE
9271 /* Maximum size of files that the process may create.
9272 *
9273 * see the system getrlimit(2) manual for details.
9274 */
9275 rb_define_const(rb_mProcess, "RLIMIT_FSIZE", INT2FIX(RLIMIT_FSIZE));
9276#endif
9277#ifdef RLIMIT_MEMLOCK
9278 /* Maximum number of bytes of memory that may be locked into RAM.
9279 *
9280 * see the system getrlimit(2) manual for details.
9281 */
9282 rb_define_const(rb_mProcess, "RLIMIT_MEMLOCK", INT2FIX(RLIMIT_MEMLOCK));
9283#endif
9284#ifdef RLIMIT_MSGQUEUE
9285 /* Specifies the limit on the number of bytes that can be allocated
9286 * for POSIX message queues for the real user ID of the calling process.
9287 *
9288 * see the system getrlimit(2) manual for details.
9289 */
9290 rb_define_const(rb_mProcess, "RLIMIT_MSGQUEUE", INT2FIX(RLIMIT_MSGQUEUE));
9291#endif
9292#ifdef RLIMIT_NICE
9293 /* Specifies a ceiling to which the process's nice value can be raised.
9294 *
9295 * see the system getrlimit(2) manual for details.
9296 */
9297 rb_define_const(rb_mProcess, "RLIMIT_NICE", INT2FIX(RLIMIT_NICE));
9298#endif
9299#ifdef RLIMIT_NOFILE
9300 /* Specifies a value one greater than the maximum file descriptor
9301 * number that can be opened by this process.
9302 *
9303 * see the system getrlimit(2) manual for details.
9304 */
9305 rb_define_const(rb_mProcess, "RLIMIT_NOFILE", INT2FIX(RLIMIT_NOFILE));
9306#endif
9307#ifdef RLIMIT_NPROC
9308 /* The maximum number of processes that can be created for the
9309 * real user ID of the calling process.
9310 *
9311 * see the system getrlimit(2) manual for details.
9312 */
9313 rb_define_const(rb_mProcess, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC));
9314#endif
9315#ifdef RLIMIT_NPTS
9316 /* The maximum number of pseudo-terminals that can be created for the
9317 * real user ID of the calling process.
9318 *
9319 * see the system getrlimit(2) manual for details.
9320 */
9321 rb_define_const(rb_mProcess, "RLIMIT_NPTS", INT2FIX(RLIMIT_NPTS));
9322#endif
9323#ifdef RLIMIT_RSS
9324 /* Specifies the limit (in pages) of the process's resident set.
9325 *
9326 * see the system getrlimit(2) manual for details.
9327 */
9328 rb_define_const(rb_mProcess, "RLIMIT_RSS", INT2FIX(RLIMIT_RSS));
9329#endif
9330#ifdef RLIMIT_RTPRIO
9331 /* Specifies a ceiling on the real-time priority that may be set for this process.
9332 *
9333 * see the system getrlimit(2) manual for details.
9334 */
9335 rb_define_const(rb_mProcess, "RLIMIT_RTPRIO", INT2FIX(RLIMIT_RTPRIO));
9336#endif
9337#ifdef RLIMIT_RTTIME
9338 /* Specifies limit on CPU time this process scheduled under a real-time
9339 * scheduling policy can consume.
9340 *
9341 * see the system getrlimit(2) manual for details.
9342 */
9343 rb_define_const(rb_mProcess, "RLIMIT_RTTIME", INT2FIX(RLIMIT_RTTIME));
9344#endif
9345#ifdef RLIMIT_SBSIZE
9346 /* Maximum size of the socket buffer.
9347 */
9348 rb_define_const(rb_mProcess, "RLIMIT_SBSIZE", INT2FIX(RLIMIT_SBSIZE));
9349#endif
9350#ifdef RLIMIT_SIGPENDING
9351 /* Specifies a limit on the number of signals that may be queued for
9352 * the real user ID of the calling process.
9353 *
9354 * see the system getrlimit(2) manual for details.
9355 */
9356 rb_define_const(rb_mProcess, "RLIMIT_SIGPENDING", INT2FIX(RLIMIT_SIGPENDING));
9357#endif
9358#ifdef RLIMIT_STACK
9359 /* Maximum size of the stack, in bytes.
9360 *
9361 * see the system getrlimit(2) manual for details.
9362 */
9363 rb_define_const(rb_mProcess, "RLIMIT_STACK", INT2FIX(RLIMIT_STACK));
9364#endif
9365#endif
9366
9367 rb_define_module_function(rb_mProcess, "uid", proc_getuid, 0);
9368 rb_define_module_function(rb_mProcess, "uid=", proc_setuid, 1);
9369 rb_define_module_function(rb_mProcess, "gid", proc_getgid, 0);
9370 rb_define_module_function(rb_mProcess, "gid=", proc_setgid, 1);
9371 rb_define_module_function(rb_mProcess, "euid", proc_geteuid, 0);
9372 rb_define_module_function(rb_mProcess, "euid=", proc_seteuid_m, 1);
9373 rb_define_module_function(rb_mProcess, "egid", proc_getegid, 0);
9374 rb_define_module_function(rb_mProcess, "egid=", proc_setegid_m, 1);
9375 rb_define_module_function(rb_mProcess, "initgroups", proc_initgroups, 2);
9376 rb_define_module_function(rb_mProcess, "groups", proc_getgroups, 0);
9377 rb_define_module_function(rb_mProcess, "groups=", proc_setgroups, 1);
9378 rb_define_module_function(rb_mProcess, "maxgroups", proc_getmaxgroups, 0);
9379 rb_define_module_function(rb_mProcess, "maxgroups=", proc_setmaxgroups, 1);
9380
9381 rb_define_module_function(rb_mProcess, "daemon", proc_daemon, -1);
9382
9383 rb_define_module_function(rb_mProcess, "times", rb_proc_times, 0);
9384
9385#if defined(RUBY_CLOCK_REALTIME)
9386#elif defined(RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
9387# define RUBY_CLOCK_REALTIME RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
9388#elif defined(RUBY_TIME_BASED_CLOCK_REALTIME)
9389# define RUBY_CLOCK_REALTIME RUBY_TIME_BASED_CLOCK_REALTIME
9390#endif
9391#if defined(CLOCK_REALTIME) && defined(CLOCKID2NUM)
9392 /* see Process.clock_gettime */
9393 rb_define_const(rb_mProcess, "CLOCK_REALTIME", CLOCKID2NUM(CLOCK_REALTIME));
9394#elif defined(RUBY_CLOCK_REALTIME)
9395 rb_define_const(rb_mProcess, "CLOCK_REALTIME", RUBY_CLOCK_REALTIME);
9396#endif
9397
9398#if defined(RUBY_CLOCK_MONOTONIC)
9399#elif defined(RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC)
9400# define RUBY_CLOCK_MONOTONIC RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
9401#endif
9402#if defined(CLOCK_MONOTONIC) && defined(CLOCKID2NUM)
9403 /* see Process.clock_gettime */
9404 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC", CLOCKID2NUM(CLOCK_MONOTONIC));
9405#elif defined(RUBY_CLOCK_MONOTONIC)
9406 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC", RUBY_CLOCK_MONOTONIC);
9407#endif
9408
9409#if defined(RUBY_CLOCK_PROCESS_CPUTIME_ID)
9410#elif defined(RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
9411# define RUBY_CLOCK_PROCESS_CPUTIME_ID RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
9412#endif
9413#if defined(CLOCK_PROCESS_CPUTIME_ID) && defined(CLOCKID2NUM)
9414 /* see Process.clock_gettime */
9415 rb_define_const(rb_mProcess, "CLOCK_PROCESS_CPUTIME_ID", CLOCKID2NUM(CLOCK_PROCESS_CPUTIME_ID));
9416#elif defined(RUBY_CLOCK_PROCESS_CPUTIME_ID)
9417 rb_define_const(rb_mProcess, "CLOCK_PROCESS_CPUTIME_ID", RUBY_CLOCK_PROCESS_CPUTIME_ID);
9418#endif
9419
9420#if defined(CLOCK_THREAD_CPUTIME_ID) && defined(CLOCKID2NUM)
9421 /* see Process.clock_gettime */
9422 rb_define_const(rb_mProcess, "CLOCK_THREAD_CPUTIME_ID", CLOCKID2NUM(CLOCK_THREAD_CPUTIME_ID));
9423#elif defined(RUBY_CLOCK_THREAD_CPUTIME_ID)
9424 rb_define_const(rb_mProcess, "CLOCK_THREAD_CPUTIME_ID", RUBY_CLOCK_THREAD_CPUTIME_ID);
9425#endif
9426
9427#ifdef CLOCKID2NUM
9428#ifdef CLOCK_VIRTUAL
9429 /* see Process.clock_gettime */
9430 rb_define_const(rb_mProcess, "CLOCK_VIRTUAL", CLOCKID2NUM(CLOCK_VIRTUAL));
9431#endif
9432#ifdef CLOCK_PROF
9433 /* see Process.clock_gettime */
9434 rb_define_const(rb_mProcess, "CLOCK_PROF", CLOCKID2NUM(CLOCK_PROF));
9435#endif
9436#ifdef CLOCK_REALTIME_FAST
9437 /* see Process.clock_gettime */
9438 rb_define_const(rb_mProcess, "CLOCK_REALTIME_FAST", CLOCKID2NUM(CLOCK_REALTIME_FAST));
9439#endif
9440#ifdef CLOCK_REALTIME_PRECISE
9441 /* see Process.clock_gettime */
9442 rb_define_const(rb_mProcess, "CLOCK_REALTIME_PRECISE", CLOCKID2NUM(CLOCK_REALTIME_PRECISE));
9443#endif
9444#ifdef CLOCK_REALTIME_COARSE
9445 /* see Process.clock_gettime */
9446 rb_define_const(rb_mProcess, "CLOCK_REALTIME_COARSE", CLOCKID2NUM(CLOCK_REALTIME_COARSE));
9447#endif
9448#ifdef CLOCK_REALTIME_ALARM
9449 /* see Process.clock_gettime */
9450 rb_define_const(rb_mProcess, "CLOCK_REALTIME_ALARM", CLOCKID2NUM(CLOCK_REALTIME_ALARM));
9451#endif
9452#ifdef CLOCK_MONOTONIC_FAST
9453 /* see Process.clock_gettime */
9454 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_FAST", CLOCKID2NUM(CLOCK_MONOTONIC_FAST));
9455#endif
9456#ifdef CLOCK_MONOTONIC_PRECISE
9457 /* see Process.clock_gettime */
9458 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_PRECISE", CLOCKID2NUM(CLOCK_MONOTONIC_PRECISE));
9459#endif
9460#ifdef CLOCK_MONOTONIC_RAW
9461 /* see Process.clock_gettime */
9462 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_RAW", CLOCKID2NUM(CLOCK_MONOTONIC_RAW));
9463#endif
9464#ifdef CLOCK_MONOTONIC_RAW_APPROX
9465 /* see Process.clock_gettime */
9466 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_RAW_APPROX", CLOCKID2NUM(CLOCK_MONOTONIC_RAW_APPROX));
9467#endif
9468#ifdef CLOCK_MONOTONIC_COARSE
9469 /* see Process.clock_gettime */
9470 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_COARSE", CLOCKID2NUM(CLOCK_MONOTONIC_COARSE));
9471#endif
9472#ifdef CLOCK_BOOTTIME
9473 /* see Process.clock_gettime */
9474 rb_define_const(rb_mProcess, "CLOCK_BOOTTIME", CLOCKID2NUM(CLOCK_BOOTTIME));
9475#endif
9476#ifdef CLOCK_BOOTTIME_ALARM
9477 /* see Process.clock_gettime */
9478 rb_define_const(rb_mProcess, "CLOCK_BOOTTIME_ALARM", CLOCKID2NUM(CLOCK_BOOTTIME_ALARM));
9479#endif
9480#ifdef CLOCK_UPTIME
9481 /* see Process.clock_gettime */
9482 rb_define_const(rb_mProcess, "CLOCK_UPTIME", CLOCKID2NUM(CLOCK_UPTIME));
9483#endif
9484#ifdef CLOCK_UPTIME_FAST
9485 /* see Process.clock_gettime */
9486 rb_define_const(rb_mProcess, "CLOCK_UPTIME_FAST", CLOCKID2NUM(CLOCK_UPTIME_FAST));
9487#endif
9488#ifdef CLOCK_UPTIME_PRECISE
9489 /* see Process.clock_gettime */
9490 rb_define_const(rb_mProcess, "CLOCK_UPTIME_PRECISE", CLOCKID2NUM(CLOCK_UPTIME_PRECISE));
9491#endif
9492#ifdef CLOCK_UPTIME_RAW
9493 /* see Process.clock_gettime */
9494 rb_define_const(rb_mProcess, "CLOCK_UPTIME_RAW", CLOCKID2NUM(CLOCK_UPTIME_RAW));
9495#endif
9496#ifdef CLOCK_UPTIME_RAW_APPROX
9497 /* see Process.clock_gettime */
9498 rb_define_const(rb_mProcess, "CLOCK_UPTIME_RAW_APPROX", CLOCKID2NUM(CLOCK_UPTIME_RAW_APPROX));
9499#endif
9500#ifdef CLOCK_SECOND
9501 /* see Process.clock_gettime */
9502 rb_define_const(rb_mProcess, "CLOCK_SECOND", CLOCKID2NUM(CLOCK_SECOND));
9503#endif
9504#ifdef CLOCK_TAI
9505 /* see Process.clock_gettime */
9506 rb_define_const(rb_mProcess, "CLOCK_TAI", CLOCKID2NUM(CLOCK_TAI));
9507#endif
9508#endif
9509 rb_define_module_function(rb_mProcess, "clock_gettime", rb_clock_gettime, -1);
9510 rb_define_module_function(rb_mProcess, "clock_getres", rb_clock_getres, -1);
9511
9512#if defined(HAVE_TIMES) || defined(_WIN32)
9513 rb_cProcessTms = rb_struct_define_under(rb_mProcess, "Tms", "utime", "stime", "cutime", "cstime", NULL);
9514#if 0 /* for RDoc */
9515 /* user time used in this process */
9516 rb_define_attr(rb_cProcessTms, "utime", TRUE, TRUE);
9517 /* system time used in this process */
9518 rb_define_attr(rb_cProcessTms, "stime", TRUE, TRUE);
9519 /* user time used in the child processes */
9520 rb_define_attr(rb_cProcessTms, "cutime", TRUE, TRUE);
9521 /* system time used in the child processes */
9522 rb_define_attr(rb_cProcessTms, "cstime", TRUE, TRUE);
9523#endif
9524#endif
9525
9526 SAVED_USER_ID = geteuid();
9527 SAVED_GROUP_ID = getegid();
9528
9529 rb_mProcUID = rb_define_module_under(rb_mProcess, "UID");
9530 rb_mProcGID = rb_define_module_under(rb_mProcess, "GID");
9531
9532 rb_define_module_function(rb_mProcUID, "rid", proc_getuid, 0);
9533 rb_define_module_function(rb_mProcGID, "rid", proc_getgid, 0);
9534 rb_define_module_function(rb_mProcUID, "eid", proc_geteuid, 0);
9535 rb_define_module_function(rb_mProcGID, "eid", proc_getegid, 0);
9536 rb_define_module_function(rb_mProcUID, "change_privilege", p_uid_change_privilege, 1);
9537 rb_define_module_function(rb_mProcGID, "change_privilege", p_gid_change_privilege, 1);
9538 rb_define_module_function(rb_mProcUID, "grant_privilege", p_uid_grant_privilege, 1);
9539 rb_define_module_function(rb_mProcGID, "grant_privilege", p_gid_grant_privilege, 1);
9540 rb_define_alias(rb_singleton_class(rb_mProcUID), "eid=", "grant_privilege");
9541 rb_define_alias(rb_singleton_class(rb_mProcGID), "eid=", "grant_privilege");
9542 rb_define_module_function(rb_mProcUID, "re_exchange", p_uid_exchange, 0);
9543 rb_define_module_function(rb_mProcGID, "re_exchange", p_gid_exchange, 0);
9544 rb_define_module_function(rb_mProcUID, "re_exchangeable?", p_uid_exchangeable, 0);
9545 rb_define_module_function(rb_mProcGID, "re_exchangeable?", p_gid_exchangeable, 0);
9546 rb_define_module_function(rb_mProcUID, "sid_available?", p_uid_have_saved_id, 0);
9547 rb_define_module_function(rb_mProcGID, "sid_available?", p_gid_have_saved_id, 0);
9548 rb_define_module_function(rb_mProcUID, "switch", p_uid_switch, 0);
9549 rb_define_module_function(rb_mProcGID, "switch", p_gid_switch, 0);
9550#ifdef p_uid_from_name
9551 rb_define_module_function(rb_mProcUID, "from_name", p_uid_from_name, 1);
9552#endif
9553#ifdef p_gid_from_name
9554 rb_define_module_function(rb_mProcGID, "from_name", p_gid_from_name, 1);
9555#endif
9556
9557 rb_mProcID_Syscall = rb_define_module_under(rb_mProcess, "Sys");
9558
9559 rb_define_module_function(rb_mProcID_Syscall, "getuid", proc_getuid, 0);
9560 rb_define_module_function(rb_mProcID_Syscall, "geteuid", proc_geteuid, 0);
9561 rb_define_module_function(rb_mProcID_Syscall, "getgid", proc_getgid, 0);
9562 rb_define_module_function(rb_mProcID_Syscall, "getegid", proc_getegid, 0);
9563
9564 rb_define_module_function(rb_mProcID_Syscall, "setuid", p_sys_setuid, 1);
9565 rb_define_module_function(rb_mProcID_Syscall, "setgid", p_sys_setgid, 1);
9566
9567 rb_define_module_function(rb_mProcID_Syscall, "setruid", p_sys_setruid, 1);
9568 rb_define_module_function(rb_mProcID_Syscall, "setrgid", p_sys_setrgid, 1);
9569
9570 rb_define_module_function(rb_mProcID_Syscall, "seteuid", p_sys_seteuid, 1);
9571 rb_define_module_function(rb_mProcID_Syscall, "setegid", p_sys_setegid, 1);
9572
9573 rb_define_module_function(rb_mProcID_Syscall, "setreuid", p_sys_setreuid, 2);
9574 rb_define_module_function(rb_mProcID_Syscall, "setregid", p_sys_setregid, 2);
9575
9576 rb_define_module_function(rb_mProcID_Syscall, "setresuid", p_sys_setresuid, 3);
9577 rb_define_module_function(rb_mProcID_Syscall, "setresgid", p_sys_setresgid, 3);
9578 rb_define_module_function(rb_mProcID_Syscall, "issetugid", p_sys_issetugid, 0);
9579}
9580
9581void
9582Init_process(void)
9583{
9584#define define_id(name) id_##name = rb_intern_const(#name)
9585 define_id(in);
9586 define_id(out);
9587 define_id(err);
9588 define_id(pid);
9589 define_id(uid);
9590 define_id(gid);
9591 define_id(close);
9592 define_id(child);
9593#ifdef HAVE_SETPGID
9594 define_id(pgroup);
9595#endif
9596#ifdef _WIN32
9597 define_id(new_pgroup);
9598#endif
9599 define_id(unsetenv_others);
9600 define_id(chdir);
9601 define_id(umask);
9602 define_id(close_others);
9603 define_id(nanosecond);
9604 define_id(microsecond);
9605 define_id(millisecond);
9606 define_id(second);
9607 define_id(float_microsecond);
9608 define_id(float_millisecond);
9609 define_id(float_second);
9610 define_id(GETTIMEOFDAY_BASED_CLOCK_REALTIME);
9611 define_id(TIME_BASED_CLOCK_REALTIME);
9612#ifdef CLOCK_REALTIME
9613 define_id(CLOCK_REALTIME);
9614#endif
9615#ifdef CLOCK_MONOTONIC
9616 define_id(CLOCK_MONOTONIC);
9617#endif
9618#ifdef CLOCK_PROCESS_CPUTIME_ID
9619 define_id(CLOCK_PROCESS_CPUTIME_ID);
9620#endif
9621#ifdef CLOCK_THREAD_CPUTIME_ID
9622 define_id(CLOCK_THREAD_CPUTIME_ID);
9623#endif
9624#ifdef HAVE_TIMES
9625 define_id(TIMES_BASED_CLOCK_MONOTONIC);
9626 define_id(TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID);
9627#endif
9628#ifdef RUSAGE_SELF
9629 define_id(GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID);
9630#endif
9631 define_id(CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID);
9632#ifdef __APPLE__
9633 define_id(MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC);
9634#endif
9635 define_id(hertz);
9636
9637 InitVM(process);
9638}
LONG_LONG
#define LONG_LONG
Definition long_long.h:38
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition cxxanyargs.hpp:670
rb_define_singleton_method
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition cxxanyargs.hpp:685
rb_define_module_function
#define rb_define_module_function(klass, mid, func, arity)
Defines klass#mid and makes it a module function.
Definition cxxanyargs.hpp:689
rb_define_global_function
#define rb_define_global_function(mid, func, arity)
Defines rb_mKernel #mid.
Definition cxxanyargs.hpp:695
PATH_ENV
#define PATH_ENV
Definition dosish.h:63
GIDT2NUM
#define GIDT2NUM
Converts a C's gid_t into an instance of rb_cInteger.
Definition gid_t.h:28
NUM2GIDT
#define NUM2GIDT
Converts an instance of rb_cNumeric into C's gid_t.
Definition gid_t.h:33
rb_singleton_class
VALUE rb_singleton_class(VALUE obj)
Finds or creates the singleton class of the passed object.
Definition class.c:2288
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1002
rb_define_module
VALUE rb_define_module(const char *name)
Defines a top-level module.
Definition class.c:1085
rb_define_module_under
VALUE rb_define_module_under(VALUE outer, const char *name)
Defines a module under the namespace of outer.
Definition class.c:1109
rb_define_alias
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition class.c:2336
rb_define_attr
void rb_define_attr(VALUE klass, const char *name, int read, int write)
Defines public accessor method(s) for an attribute.
Definition class.c:2342
rb_undef_method
void rb_undef_method(VALUE klass, const char *name)
Defines an undef of a method.
Definition class.c:2160
rb_block_given_p
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:866
rb_str_new2
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1675
TYPE
#define TYPE(_)
Old name of rb_type.
Definition value_type.h:107
T_FILE
#define T_FILE
Old name of RUBY_T_FILE.
Definition value_type.h:62
rb_str_buf_cat2
#define rb_str_buf_cat2
Old name of rb_usascii_str_new_cstr.
Definition string.h:1682
T_STRING
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
Qundef
#define Qundef
Old name of RUBY_Qundef.
Definition special_consts.h:62
INT2FIX
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
rb_str_cat2
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1683
ISUPPER
#define ISUPPER
Old name of rb_isupper.
Definition ctype.h:89
ID2SYM
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
T_BIGNUM
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
Definition value_type.h:57
T_FIXNUM
#define T_FIXNUM
Old name of RUBY_T_FIXNUM.
Definition value_type.h:63
UNREACHABLE_RETURN
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition assume.h:29
CLASS_OF
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:203
LONG2FIX
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
FIX2INT
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
TOUPPER
#define TOUPPER
Old name of rb_toupper.
Definition ctype.h:100
NUM2UINT
#define NUM2UINT
Old name of RB_NUM2UINT.
Definition int.h:45
ISLOWER
#define ISLOWER
Old name of rb_islower.
Definition ctype.h:90
rb_ary_new3
#define rb_ary_new3
Old name of rb_ary_new_from_args.
Definition array.h:652
Qtrue
#define Qtrue
Old name of RUBY_Qtrue.
Definition special_consts.h:61
NUM2INT
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
INT2NUM
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
Qnil
#define Qnil
Old name of RUBY_Qnil.
Definition special_consts.h:60
Qfalse
#define Qfalse
Old name of RUBY_Qfalse.
Definition special_consts.h:59
T_ARRAY
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
NIL_P
#define NIL_P
Old name of RB_NIL_P.
Definition special_consts.h:55
ALLOCV_N
#define ALLOCV_N
Old name of RB_ALLOCV_N.
Definition memory.h:399
T_SYMBOL
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
Definition value_type.h:80
DBL2NUM
#define DBL2NUM
Old name of rb_float_new.
Definition double.h:29
FIXNUM_P
#define FIXNUM_P
Old name of RB_FIXNUM_P.
Definition special_consts.h:53
CONST_ID
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
ALLOCV_END
#define ALLOCV_END
Old name of RB_ALLOCV_END.
Definition memory.h:400
SYMBOL_P
#define SYMBOL_P
Old name of RB_SYMBOL_P.
Definition value_type.h:88
ruby_stop
void ruby_stop(int ex)
Calls ruby_cleanup() and exits the process.
Definition eval.c:296
rb_notimplement
void rb_notimplement(void)
Definition error.c:3498
rb_eNotImpError
VALUE rb_eNotImpError
NotImplementedError exception.
Definition error.c:1354
rb_syserr_fail
void rb_syserr_fail(int e, const char *mesg)
Raises appropriate exception that represents a C errno.
Definition error.c:3567
rb_eSystemExit
VALUE rb_eSystemExit
SystemExit exception.
Definition error.c:1337
rb_syserr_fail_str
void rb_syserr_fail_str(int e, VALUE mesg)
Identical to rb_syserr_fail(), except it takes the message in Ruby's String instead of C's.
Definition error.c:3573
rb_eRuntimeError
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1342
rb_check_typeddata
void * rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type)
Identical to rb_typeddata_is_kind_of(), except it raises exceptions instead of returning false.
Definition error.c:1311
rb_warn
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:423
rb_exc_new_str
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
Definition error.c:1395
rb_unexpected_type
void rb_unexpected_type(VALUE x, int t)
Fails with the given object's type incompatibility to the type.
Definition error.c:1274
rb_exit
void rb_exit(int status)
Terminates the current execution context.
Definition process.c:4454
rb_mProcess
VALUE rb_mProcess
Process module.
Definition process.c:8747
rb_class_new_instance
VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
Allocates, then initialises an instance of the given class.
Definition object.c:2099
rb_cThread
VALUE rb_cThread
Thread class.
Definition vm.c:524
rb_equal
VALUE rb_equal(VALUE lhs, VALUE rhs)
This function is an optimised version of calling #==.
Definition object.c:147
rb_to_int
VALUE rb_to_int(VALUE val)
Identical to rb_check_to_int(), except it raises in case of conversion mismatch.
Definition object.c:3145
RB_OBJ_WRITTEN
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:631
rb_funcall
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1121
UNLIMITED_ARGUMENTS
#define UNLIMITED_ARGUMENTS
This macro is used in conjunction with rb_check_arity().
Definition error.h:35
rb_check_arity
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
Definition error.h:280
rb_f_abort
VALUE rb_f_abort(int argc, const VALUE *argv)
This is similar to rb_f_exit().
Definition process.c:4536
rb_f_exit
VALUE rb_f_exit(int argc, const VALUE *argv)
Identical to rb_exit(), except how arguments are passed.
Definition process.c:4467
rb_cloexec_dup2
int rb_cloexec_dup2(int oldfd, int newfd)
Identical to rb_cloexec_dup(), except you can specify the destination file descriptor.
Definition io.c:352
rb_update_max_fd
void rb_update_max_fd(int fd)
Informs the interpreter that the passed fd can be the max.
Definition io.c:226
rb_cloexec_open
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Opens a file that closes on exec.
Definition io.c:306
rb_close_before_exec
void rb_close_before_exec(int lowfd, int maxhint, VALUE noclose_fds)
Closes everything.
rb_reserved_fd_p
int rb_reserved_fd_p(int fd)
Queries if the given FD is reserved or not.
rb_pipe
int rb_pipe(int *pipes)
This is an rb_cloexec_pipe() + rb_update_max_fd() combo.
Definition io.c:7300
rb_cloexec_fcntl_dupfd
int rb_cloexec_fcntl_dupfd(int fd, int minfd)
Duplicates a file descriptor with closing on exec.
Definition io.c:439
rb_cloexec_dup
int rb_cloexec_dup(int oldfd)
Identical to rb_cloexec_fcntl_dupfd(), except it implies minfd is 3.
Definition io.c:345
rb_proc_exec
int rb_proc_exec(const char *cmd)
Executes a shell command.
Definition process.c:1796
rb_last_status_get
VALUE rb_last_status_get(void)
Queries the "last status", or the $?.
Definition process.c:611
rb_waitpid
rb_pid_t rb_waitpid(rb_pid_t pid, int *status, int flags)
Waits for a process, with releasing GVL.
Definition process.c:1269
rb_spawn_err
rb_pid_t rb_spawn_err(int argc, const VALUE *argv, char *errbuf, size_t buflen)
Identical to rb_spawn(), except you can additionally know the detailed situation in case of abnormal ...
Definition process.c:4713
rb_syswait
void rb_syswait(rb_pid_t pid)
This is a shorthand of rb_waitpid without status and flags.
Definition process.c:4584
rb_f_exec
VALUE rb_f_exec(int argc, const VALUE *argv)
Replaces the current process by running the given external command.
Definition process.c:3015
rb_spawn
rb_pid_t rb_spawn(int argc, const VALUE *argv)
Identical to rb_f_exec(), except it spawns a child process instead of replacing the current one.
Definition process.c:4719
rb_process_status_wait
VALUE rb_process_status_wait(rb_pid_t pid, int flags)
Wait for the specified process to terminate, reap it, and return its status.
Definition process.c:1198
rb_last_status_set
void rb_last_status_set(int status, rb_pid_t pid)
Sets the "last status", or the $?.
Definition process.c:682
rb_detach_process
VALUE rb_detach_process(rb_pid_t pid)
"Detaches" a subprocess.
Definition process.c:1553
ruby_signal_name
const char * ruby_signal_name(int signo)
Queries the name of the signal.
Definition signal.c:317
rb_f_kill
VALUE rb_f_kill(int argc, const VALUE *argv)
Sends a signal ("kills") to processes.
Definition signal.c:430
rb_str_append
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
Definition string.c:3409
rb_str_tmp_new
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1532
rb_str_new
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1498
rb_str_buf_cat
#define rb_str_buf_cat
Just another name of rb_str_cat
Definition string.h:1681
rb_str_capacity
size_t rb_str_capacity(VALUE str)
Queries the capacity of the given string.
Definition string.c:815
rb_check_string_type
VALUE rb_check_string_type(VALUE obj)
Try converting an object to its stringised representation using its to_str method,...
Definition string.c:2681
rb_str_cat_cstr
#define rb_str_cat_cstr(buf, str)
Identical to rb_str_cat(), except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1656
rb_str_modify_expand
void rb_str_modify_expand(VALUE str, long capa)
Identical to rb_str_modify(), except it additionally expands the capacity of the receiver.
Definition string.c:2486
rb_str_new_cstr
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1514
rb_struct_define_under
VALUE rb_struct_define_under(VALUE space, const char *name,...)
Identical to rb_struct_define(), except it defines the class under the specified namespace instead of...
Definition struct.c:505
rb_struct_new
VALUE rb_struct_new(VALUE klass,...)
Creates an instance of the given struct.
Definition struct.c:842
rb_thread_local_aref
VALUE rb_thread_local_aref(VALUE thread, ID key)
This badly named function reads from a Fiber local storage.
Definition thread.c:3510
RUBY_UBF_IO
#define RUBY_UBF_IO
A special UBF for blocking IO operations.
Definition thread.h:382
rb_thread_sleep_forever
void rb_thread_sleep_forever(void)
Blocks indefinitely.
Definition thread.c:1371
rb_thread_wait_for
void rb_thread_wait_for(struct timeval time)
Identical to rb_thread_sleep(), except it takes struct timeval instead.
Definition thread.c:1403
rb_thread_check_ints
void rb_thread_check_ints(void)
Checks for interrupts.
Definition thread.c:1418
rb_thread_atfork
void rb_thread_atfork(void)
A pthread_atfork(3posix)-like API.
Definition thread.c:4730
rb_thread_local_aset
VALUE rb_thread_local_aset(VALUE thread, ID key, VALUE val)
This badly named function writes to a Fiber local storage.
Definition thread.c:3658
RUBY_UBF_PROCESS
#define RUBY_UBF_PROCESS
A special UBF for blocking process operations.
Definition thread.h:389
rb_thread_sleep
void rb_thread_sleep(int sec)
Blocks for the given period of time.
Definition thread.c:1441
rb_time_interval
struct timeval rb_time_interval(VALUE num)
Creates a "time interval".
Definition time.c:2875
rb_ivar_set
VALUE rb_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
Definition variable.c:1854
rb_undef_alloc_func
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1274
rb_define_alloc_func
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
rb_check_id
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition symbol.c:1095
rb_sym2str
VALUE rb_sym2str(VALUE id)
Identical to rb_id2str(), except it takes an instance of rb_cSymbol rather than an ID.
Definition symbol.c:953
rb_define_const
void rb_define_const(VALUE klass, const char *name, VALUE val)
Defines a Ruby level constant under a namespace.
Definition variable.c:3690
io.h
rb_io_modestr_oflags
int rb_io_modestr_oflags(const char *modestr)
Identical to rb_io_modestr_fmode(), except it returns a mixture of O_ flags.
Definition io.c:6527
GetOpenFile
#define GetOpenFile
This is an old name of RB_IO_POINTER.
Definition io.h:402
rb_io_check_io
VALUE rb_io_check_io(VALUE io)
Try converting an object to its IO representation using its to_io method, if any.
Definition io.c:798
len
int len
Length of the buffer.
Definition io.h:8
ractor.h
thread.h
rb_thread_call_without_gvl2
void * rb_thread_call_without_gvl2(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
Identical to rb_thread_call_without_gvl(), except it does not interface with signals etc.
Definition thread.c:1651
rb_thread_call_without_gvl
void * rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
Allows the passed function to run in parallel with other Ruby threads.
Definition thread.c:1658
util.h
RB_NUM2INT
#define RB_NUM2INT
Just another name of rb_num2int_inline
Definition int.h:38
RB_INT2NUM
#define RB_INT2NUM
Just another name of rb_int2num_inline
Definition int.h:37
RB_BLOCK_CALL_FUNC_ARGLIST
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
Shim for block function parameters.
Definition iterator.h:58
rb_yield
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1376
rb_marshal_define_compat
void rb_marshal_define_compat(VALUE newclass, VALUE oldclass, VALUE(*dumper)(VALUE), VALUE(*loader)(VALUE, VALUE))
Marshal format compatibility layer.
Definition marshal.c:150
MEMCPY
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:366
MEMZERO
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:354
RB_GC_GUARD
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:161
NUM2MODET
#define NUM2MODET
Converts a C's mode_t into an instance of rb_cInteger.
Definition mode_t.h:28
ruby::backward::cxxanyargs::rb_thread_create
VALUE rb_thread_create(type *q, void *w)
Creates a rb_cThread instance.
Definition cxxanyargs.hpp:410
ruby::backward::cxxanyargs::rb_block_call
VALUE rb_block_call(VALUE q, ID w, int e, const VALUE *r, type *t, VALUE y)
Call a method with a block.
Definition cxxanyargs.hpp:232
ruby::backward::cxxanyargs::rb_define_virtual_variable
void rb_define_virtual_variable(const char *q, type *w, void_type *e)
Define a function-backended global variable.
Definition cxxanyargs.hpp:73
ruby::backward::cxxanyargs::rb_ensure
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
Definition cxxanyargs.hpp:310
PIDT2NUM
#define PIDT2NUM
Converts a C's pid_t into an instance of rb_cInteger.
Definition pid_t.h:28
NUM2PIDT
#define NUM2PIDT
Converts an instance of rb_cNumeric into C's pid_t.
Definition pid_t.h:33
RARRAY_LEN
#define RARRAY_LEN
Just another name of rb_array_len
Definition rarray.h:51
RARRAY_LENINT
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:281
RARRAY_ASET
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:386
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition rarray.h:403
RUBY_DEFAULT_FREE
#define RUBY_DEFAULT_FREE
This is a value you can set to RData::dfree.
Definition rdata.h:82
RHASH_SIZE
#define RHASH_SIZE(h)
Queries the size of the hash.
Definition rhash.h:69
RHASH_EMPTY_P
#define RHASH_EMPTY_P(h)
Checks if the hash is empty.
Definition rhash.h:79
SafeStringValue
#define SafeStringValue(v)
Definition rstring.h:98
StringValue
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
StringValueCStr
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
RUBY_TYPED_DEFAULT_FREE
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
Definition rtypeddata.h:79
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:515
TypedData_Make_Struct
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:497
rb_class2name
const char * rb_class2name(VALUE klass)
Queries the name of the passed class.
Definition variable.c:408
FilePathValue
#define FilePathValue(v)
Ensures that the parameter object is a path.
Definition ruby.h:90
errno
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
InitVM
#define InitVM(ext)
This macro is for internal use.
Definition ruby.h:231
scheduler.h
Scheduler APIs.
rb_fiber_scheduler_current
VALUE rb_fiber_scheduler_current(void)
Identical to rb_fiber_scheduler_get(), except it also returns RUBY_Qnil in case of a blocking fiber.
Definition scheduler.c:219
rb_fiber_scheduler_kernel_sleepv
VALUE rb_fiber_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE *argv)
Identical to rb_fiber_scheduler_kernel_sleep(), except it can pass multiple arguments.
Definition scheduler.c:289
rb_fiber_scheduler_process_wait
VALUE rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags)
Non-blocking waitpid.
Definition scheduler.c:359
RB_SPECIAL_CONST_P
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
Definition special_consts.h:327
RTEST
#define RTEST
This is an old name of RB_TEST.
Definition special_consts.h:51
stdarg.h
Defines old _.
_
#define _(args)
This was a transition path from K&R to ANSI.
Definition stdarg.h:35
__sFILE
Definition vsnprintf.c:160
open_struct
Definition process.c:2806
rb_data_type_struct
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:200
rb_data_type_struct::wrap_struct_name
const char * wrap_struct_name
Name of structs of this kind.
Definition rtypeddata.h:207
rb_execarg
Definition process.h:30
rb_execution_context_struct
Definition vm_core.h:986
rb_imemo_tmpbuf_struct
Definition imemo.h:91
rb_io
Ruby's IO, metadata and buffers.
Definition io.h:143
rb_io::tied_io_for_writing
VALUE tied_io_for_writing
Duplex IO object, if set.
Definition io.h:193
rb_io::fd
int fd
file descriptor.
Definition io.h:154
rb_process_status
Definition process.c:587
rb_thread_struct
Definition vm_core.h:1071
run_exec_dup2_fd_pair
Definition process.c:3195
spawn_args
Definition process.c:4667
st_table
Definition st.h:79
string_part
Definition process.c:2543
timespec
Definition missing.h:74
timetick
Definition process.c:7956
timeval
Definition missing.h:63
tms
Definition win32.h:700
waitpid_state
Definition process.c:1151
UIDT2NUM
#define UIDT2NUM
Converts a C's uid_t into an instance of rb_cInteger.
Definition uid_t.h:28
NUM2UIDT
#define NUM2UIDT
Converts an instance of rb_cNumeric into C's uid_t.
Definition uid_t.h:33
ID
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
VALUE
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
RB_BUILTIN_TYPE
static enum ruby_value_type RB_BUILTIN_TYPE(VALUE obj)
Queries the type of the object.
Definition value_type.h:181
Check_Type
static void Check_Type(VALUE v, enum ruby_value_type t)
Identical to RB_TYPE_P(), except it raises exceptions on predication failure.
Definition value_type.h:432