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