Ruby 4.1.0dev (2026-06-13 revision 9a90ac21ffd17ef8165ab8324514d2f4c7ed81a8)
process.c (9a90ac21ffd17ef8165ab8324514d2f4c7ed81a8)
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{
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: %"PRIsVALUE, rb_obj_class(key));
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 rb_alloc_tmp_buffer(&eargp->dup2_tmpbuf, run_exec_dup2_tmpbuf_size(len));
2730}
2731
2732static VALUE
2733rb_execarg_parent_start1(VALUE execarg_obj)
2734{
2735 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2736 int unsetenv_others;
2737 VALUE envopts;
2738 VALUE ary;
2739
2740 ary = eargp->fd_open;
2741 if (ary != Qfalse) {
2742 long i;
2743 for (i = 0; i < RARRAY_LEN(ary); i++) {
2744 VALUE elt = RARRAY_AREF(ary, i);
2745 int fd = FIX2INT(RARRAY_AREF(elt, 0));
2746 VALUE param = RARRAY_AREF(elt, 1);
2747 VALUE vpath = RARRAY_AREF(param, 0);
2748 int flags = NUM2INT(RARRAY_AREF(param, 1));
2749 mode_t perm = NUM2MODET(RARRAY_AREF(param, 2));
2750 VALUE fd2v = RARRAY_AREF(param, 3);
2751 int fd2;
2752 if (NIL_P(fd2v)) {
2753 struct open_struct open_data;
2754 again:
2755 open_data.fname = vpath;
2756 open_data.oflags = flags;
2757 open_data.perm = perm;
2758 open_data.ret = -1;
2759 open_data.err = EINTR;
2760 rb_thread_call_without_gvl2(open_func, (void *)&open_data, RUBY_UBF_IO, 0);
2761 if (open_data.ret == -1) {
2762 if (open_data.err == EINTR) {
2764 goto again;
2765 }
2766 rb_syserr_fail_str(open_data.err, vpath);
2767 }
2768 fd2 = open_data.ret;
2769 rb_update_max_fd(fd2);
2770 RARRAY_ASET(param, 3, INT2FIX(fd2));
2772 }
2773 else {
2774 fd2 = NUM2INT(fd2v);
2775 }
2776 rb_execarg_addopt(execarg_obj, INT2FIX(fd), INT2FIX(fd2));
2777 }
2778 }
2779
2780 eargp->redirect_fds = check_exec_fds(eargp);
2781
2782 ary = eargp->fd_dup2;
2783 if (ary != Qfalse) {
2784 rb_execarg_allocate_dup2_tmpbuf(eargp, RARRAY_LEN(ary));
2785 }
2786
2787 unsetenv_others = eargp->unsetenv_others_given && eargp->unsetenv_others_do;
2788 envopts = eargp->env_modification;
2789 if (ALWAYS_NEED_ENVP || unsetenv_others || envopts != Qfalse) {
2790 VALUE envtbl, envp_str, envp_buf;
2791 char *p, *ep;
2792 if (unsetenv_others) {
2793 envtbl = rb_hash_new();
2794 }
2795 else {
2796 envtbl = rb_env_to_hash();
2797 }
2798 hide_obj(envtbl);
2799 if (envopts != Qfalse) {
2800 st_table *stenv = RHASH_TBL_RAW(envtbl);
2801 long i;
2802 for (i = 0; i < RARRAY_LEN(envopts); i++) {
2803 VALUE pair = RARRAY_AREF(envopts, i);
2804 VALUE key = RARRAY_AREF(pair, 0);
2805 VALUE val = RARRAY_AREF(pair, 1);
2806 if (NIL_P(val)) {
2807 st_data_t stkey = (st_data_t)key;
2808 st_delete(stenv, &stkey, NULL);
2809 }
2810 else {
2811 st_insert(stenv, (st_data_t)key, (st_data_t)val);
2812 RB_OBJ_WRITTEN(envtbl, Qundef, key);
2813 RB_OBJ_WRITTEN(envtbl, Qundef, val);
2814 }
2815 }
2816 }
2817 envp_buf = rb_str_buf_new(0);
2818 hide_obj(envp_buf);
2819 rb_hash_stlike_foreach(envtbl, fill_envp_buf_i, (st_data_t)envp_buf);
2820 envp_str = rb_str_buf_new(sizeof(char*) * (RHASH_SIZE(envtbl) + 1));
2821 hide_obj(envp_str);
2822 p = RSTRING_PTR(envp_buf);
2823 ep = p + RSTRING_LEN(envp_buf);
2824 while (p < ep) {
2825 rb_str_buf_cat(envp_str, (char *)&p, sizeof(p));
2826 p += strlen(p) + 1;
2827 }
2828 p = NULL;
2829 rb_str_buf_cat(envp_str, (char *)&p, sizeof(p));
2830 eargp->envp_str =
2831 rb_imemo_tmpbuf_new_from_an_RString(envp_str);
2832 eargp->envp_buf = envp_buf;
2833
2834 /*
2835 char **tmp_envp = (char **)RSTRING_PTR(envp_str);
2836 while (*tmp_envp) {
2837 printf("%s\n", *tmp_envp);
2838 tmp_envp++;
2839 }
2840 */
2841 }
2842
2843 RB_GC_GUARD(execarg_obj);
2844 return Qnil;
2845}
2846
2847void
2848rb_execarg_parent_start(VALUE execarg_obj)
2849{
2850 int state;
2851 rb_protect(rb_execarg_parent_start1, execarg_obj, &state);
2852 if (state) {
2853 rb_execarg_parent_end(execarg_obj);
2854 rb_jump_tag(state);
2855 }
2856}
2857
2858static VALUE
2859execarg_parent_end(VALUE execarg_obj)
2860{
2861 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2862 int err = errno;
2863 VALUE ary;
2864
2865 ary = eargp->fd_open;
2866 if (ary != Qfalse) {
2867 long i;
2868 for (i = 0; i < RARRAY_LEN(ary); i++) {
2869 VALUE elt = RARRAY_AREF(ary, i);
2870 VALUE param = RARRAY_AREF(elt, 1);
2871 VALUE fd2v;
2872 int fd2;
2873 fd2v = RARRAY_AREF(param, 3);
2874 if (!NIL_P(fd2v)) {
2875 fd2 = FIX2INT(fd2v);
2876 parent_redirect_close(fd2);
2877 RARRAY_ASET(param, 3, Qnil);
2878 }
2879 }
2880 }
2881
2882 errno = err;
2883 RB_GC_GUARD(execarg_obj);
2884 return execarg_obj;
2885}
2886
2887void
2888rb_execarg_parent_end(VALUE execarg_obj)
2889{
2890 execarg_parent_end(execarg_obj);
2891}
2892
2893static void
2894rb_exec_fail(struct rb_execarg *eargp, int err, const char *errmsg)
2895{
2896 if (!errmsg || !*errmsg) return;
2897 if (strcmp(errmsg, "chdir") == 0) {
2898 rb_sys_fail_str(eargp->chdir_dir);
2899 }
2900 rb_sys_fail(errmsg);
2901}
2902
2903#if 0
2904void
2905rb_execarg_fail(VALUE execarg_obj, int err, const char *errmsg)
2906{
2907 if (!errmsg || !*errmsg) return;
2908 rb_exec_fail(rb_execarg_get(execarg_obj), err, errmsg);
2909 RB_GC_GUARD(execarg_obj);
2910}
2911#endif
2912
2913VALUE
2914rb_f_exec(int argc, const VALUE *argv)
2915{
2916 VALUE execarg_obj, fail_str;
2917 struct rb_execarg *eargp;
2918#define CHILD_ERRMSG_BUFLEN 80
2919 char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' };
2920 int err, state;
2921
2922 execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
2923 eargp = rb_execarg_get(execarg_obj);
2924 before_exec(); /* stop timer thread before redirects */
2925
2926 rb_protect(rb_execarg_parent_start1, execarg_obj, &state);
2927 if (state) {
2928 execarg_parent_end(execarg_obj);
2929 after_exec(); /* restart timer thread */
2930 rb_jump_tag(state);
2931 }
2932
2933 fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
2934
2935 err = exec_async_signal_safe(eargp, errmsg, sizeof(errmsg));
2936 after_exec(); /* restart timer thread */
2937
2938 rb_exec_fail(eargp, err, errmsg);
2939 RB_GC_GUARD(execarg_obj);
2940 rb_syserr_fail_str(err, fail_str);
2942}
2943
2944NORETURN(static VALUE f_exec(int c, const VALUE *a, VALUE _));
2945
2946/*
2947 * call-seq:
2948 * exec([env, ] command_line, options = {})
2949 * exec([env, ] exe_path, *args, options = {})
2950 *
2951 * Replaces the current process by doing one of the following:
2952 *
2953 * - Passing string +command_line+ to the shell.
2954 * - Invoking the executable at +exe_path+.
2955 *
2956 * This method has potential security vulnerabilities if called with untrusted input;
2957 * see {Command Injection}[rdoc-ref:security/command_injection.rdoc].
2958 *
2959 * The new process is created using the
2960 * {exec system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/execve.html];
2961 * it may inherit some of its environment from the calling program
2962 * (possibly including open file descriptors).
2963 *
2964 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
2965 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
2966 *
2967 * Argument +options+ is a hash of options for the new process;
2968 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
2969 *
2970 * The first required argument is one of the following:
2971 *
2972 * - +command_line+ if it is a string,
2973 * and if it begins with a shell reserved word or special built-in,
2974 * or if it contains one or more meta characters.
2975 * - +exe_path+ otherwise.
2976 *
2977 * <b>Argument +command_line+</b>
2978 *
2979 * \String argument +command_line+ is a command line to be passed to a shell;
2980 * it must begin with a shell reserved word, begin with a special built-in,
2981 * or contain meta characters:
2982 *
2983 * exec('if true; then echo "Foo"; fi') # Shell reserved word.
2984 * exec('exit') # Built-in.
2985 * exec('date > date.tmp') # Contains meta character.
2986 *
2987 * The command line may also contain arguments and options for the command:
2988 *
2989 * exec('echo "Foo"')
2990 *
2991 * Output:
2992 *
2993 * Foo
2994 *
2995 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
2996 *
2997 * Raises an exception if the new process could not execute.
2998 *
2999 * <b>Argument +exe_path+</b>
3000 *
3001 * Argument +exe_path+ is one of the following:
3002 *
3003 * - The string path to an executable to be called.
3004 * - A 2-element array containing the path to an executable
3005 * and the string to be used as the name of the executing process.
3006 *
3007 * Example:
3008 *
3009 * exec('/usr/bin/date')
3010 *
3011 * Output:
3012 *
3013 * Sat Aug 26 09:38:00 AM CDT 2023
3014 *
3015 * Ruby invokes the executable directly.
3016 * This form does not use the shell;
3017 * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
3018 *
3019 * exec('doesnt_exist') # Raises Errno::ENOENT
3020 *
3021 * If one or more +args+ is given, each is an argument or option
3022 * to be passed to the executable:
3023 *
3024 * exec('echo', 'C*')
3025 * exec('echo', 'hello', 'world')
3026 *
3027 * Output:
3028 *
3029 * C*
3030 * hello world
3031 *
3032 * Raises an exception if the new process could not execute.
3033 */
3034
3035static VALUE
3036f_exec(int c, const VALUE *a, VALUE _)
3037{
3038 rb_f_exec(c, a);
3040}
3041
3042#define ERRMSG(str) \
3043 ((errmsg && 0 < errmsg_buflen) ? \
3044 (void)strlcpy(errmsg, (str), errmsg_buflen) : (void)0)
3045
3046#define ERRMSG_FMT(...) \
3047 ((errmsg && 0 < errmsg_buflen) ? \
3048 (void)snprintf(errmsg, errmsg_buflen, __VA_ARGS__) : (void)0)
3049
3050static int fd_get_cloexec(int fd, char *errmsg, size_t errmsg_buflen);
3051static int fd_set_cloexec(int fd, char *errmsg, size_t errmsg_buflen);
3052static int fd_clear_cloexec(int fd, char *errmsg, size_t errmsg_buflen);
3053
3054static int
3055save_redirect_fd(int fd, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3056{
3057 if (sargp) {
3058 VALUE newary, redirection;
3059 int save_fd = redirect_cloexec_dup(fd), cloexec;
3060 if (save_fd == -1) {
3061 if (errno == EBADF)
3062 return 0;
3063 ERRMSG("dup");
3064 return -1;
3065 }
3066 rb_update_max_fd(save_fd);
3067 newary = sargp->fd_dup2;
3068 if (newary == Qfalse) {
3069 newary = hide_obj(rb_ary_new());
3070 sargp->fd_dup2 = newary;
3071 }
3072 cloexec = fd_get_cloexec(fd, errmsg, errmsg_buflen);
3073 redirection = hide_obj(rb_assoc_new(INT2FIX(fd), INT2FIX(save_fd)));
3074 if (cloexec) rb_ary_push(redirection, Qtrue);
3075 rb_ary_push(newary, redirection);
3076
3077 newary = sargp->fd_close;
3078 if (newary == Qfalse) {
3079 newary = hide_obj(rb_ary_new());
3080 sargp->fd_close = newary;
3081 }
3082 rb_ary_push(newary, hide_obj(rb_assoc_new(INT2FIX(save_fd), Qnil)));
3083 }
3084
3085 return 0;
3086}
3087
3088static int
3089intcmp(const void *a, const void *b)
3090{
3091 return *(int*)a - *(int*)b;
3092}
3093
3094static int
3095intrcmp(const void *a, const void *b)
3096{
3097 return *(int*)b - *(int*)a;
3098}
3099
3101 int oldfd;
3102 int newfd;
3103 long older_index;
3104 long num_newer;
3105 int cloexec;
3106};
3107
3108static long
3109run_exec_dup2_tmpbuf_size(long n)
3110{
3111 return sizeof(struct run_exec_dup2_fd_pair) * n;
3112}
3113
3114/* This function should be async-signal-safe. Actually it is. */
3115static int
3116fd_get_cloexec(int fd, char *errmsg, size_t errmsg_buflen)
3117{
3118#ifdef F_GETFD
3119 int ret = 0;
3120 ret = fcntl(fd, F_GETFD); /* async-signal-safe */
3121 if (ret == -1) {
3122 ERRMSG("fcntl(F_GETFD)");
3123 return -1;
3124 }
3125 if (ret & FD_CLOEXEC) return 1;
3126#endif
3127 return 0;
3128}
3129
3130/* This function should be async-signal-safe. Actually it is. */
3131static int
3132fd_set_cloexec(int fd, char *errmsg, size_t errmsg_buflen)
3133{
3134#ifdef F_GETFD
3135 int ret = 0;
3136 ret = fcntl(fd, F_GETFD); /* async-signal-safe */
3137 if (ret == -1) {
3138 ERRMSG("fcntl(F_GETFD)");
3139 return -1;
3140 }
3141 if (!(ret & FD_CLOEXEC)) {
3142 ret |= FD_CLOEXEC;
3143 ret = fcntl(fd, F_SETFD, ret); /* async-signal-safe */
3144 if (ret == -1) {
3145 ERRMSG("fcntl(F_SETFD)");
3146 return -1;
3147 }
3148 }
3149#endif
3150 return 0;
3151}
3152
3153/* This function should be async-signal-safe. Actually it is. */
3154static int
3155fd_clear_cloexec(int fd, char *errmsg, size_t errmsg_buflen)
3156{
3157#ifdef F_GETFD
3158 int ret;
3159 ret = fcntl(fd, F_GETFD); /* async-signal-safe */
3160 if (ret == -1) {
3161 ERRMSG("fcntl(F_GETFD)");
3162 return -1;
3163 }
3164 if (ret & FD_CLOEXEC) {
3165 ret &= ~FD_CLOEXEC;
3166 ret = fcntl(fd, F_SETFD, ret); /* async-signal-safe */
3167 if (ret == -1) {
3168 ERRMSG("fcntl(F_SETFD)");
3169 return -1;
3170 }
3171 }
3172#endif
3173 return 0;
3174}
3175
3176/* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3177static int
3178run_exec_dup2(VALUE ary, VALUE tmpbuf, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3179{
3180 long n, i;
3181 int ret;
3182 int extra_fd = -1;
3183 struct run_exec_dup2_fd_pair *pairs = RB_IMEMO_TMPBUF_PTR(tmpbuf);
3184
3185 n = RARRAY_LEN(ary);
3186
3187 /* initialize oldfd and newfd: O(n) */
3188 for (i = 0; i < n; i++) {
3189 VALUE elt = RARRAY_AREF(ary, i);
3190 pairs[i].oldfd = FIX2INT(RARRAY_AREF(elt, 1));
3191 pairs[i].newfd = FIX2INT(RARRAY_AREF(elt, 0)); /* unique */
3192 pairs[i].cloexec = RARRAY_LEN(elt) > 2 && RTEST(RARRAY_AREF(elt, 2));
3193 pairs[i].older_index = -1;
3194 }
3195
3196 /* sort the table by oldfd: O(n log n) */
3197 if (!sargp)
3198 qsort(pairs, n, sizeof(struct run_exec_dup2_fd_pair), intcmp); /* hopefully async-signal-safe */
3199 else
3200 qsort(pairs, n, sizeof(struct run_exec_dup2_fd_pair), intrcmp);
3201
3202 /* initialize older_index and num_newer: O(n log n) */
3203 for (i = 0; i < n; i++) {
3204 int newfd = pairs[i].newfd;
3205 struct run_exec_dup2_fd_pair key, *found;
3206 key.oldfd = newfd;
3207 found = bsearch(&key, pairs, n, sizeof(struct run_exec_dup2_fd_pair), intcmp); /* hopefully async-signal-safe */
3208 pairs[i].num_newer = 0;
3209 if (found) {
3210 while (pairs < found && (found-1)->oldfd == newfd)
3211 found--;
3212 while (found < pairs+n && found->oldfd == newfd) {
3213 pairs[i].num_newer++;
3214 found->older_index = i;
3215 found++;
3216 }
3217 }
3218 }
3219
3220 /* non-cyclic redirection: O(n) */
3221 for (i = 0; i < n; i++) {
3222 long j = i;
3223 while (j != -1 && pairs[j].oldfd != -1 && pairs[j].num_newer == 0) {
3224 if (save_redirect_fd(pairs[j].newfd, sargp, errmsg, errmsg_buflen) < 0) /* async-signal-safe */
3225 goto fail;
3226 ret = redirect_dup2(pairs[j].oldfd, pairs[j].newfd); /* async-signal-safe */
3227 if (ret == -1) {
3228 ERRMSG("dup2");
3229 goto fail;
3230 }
3231 if (pairs[j].cloexec &&
3232 fd_set_cloexec(pairs[j].newfd, errmsg, errmsg_buflen)) {
3233 goto fail;
3234 }
3235 rb_update_max_fd(pairs[j].newfd); /* async-signal-safe but don't need to call it in a child process. */
3236 pairs[j].oldfd = -1;
3237 j = pairs[j].older_index;
3238 if (j != -1)
3239 pairs[j].num_newer--;
3240 }
3241 }
3242
3243 /* cyclic redirection: O(n) */
3244 for (i = 0; i < n; i++) {
3245 long j;
3246 if (pairs[i].oldfd == -1)
3247 continue;
3248 if (pairs[i].oldfd == pairs[i].newfd) { /* self cycle */
3249 if (fd_clear_cloexec(pairs[i].oldfd, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3250 goto fail;
3251 pairs[i].oldfd = -1;
3252 continue;
3253 }
3254 if (extra_fd == -1) {
3255 extra_fd = redirect_dup(pairs[i].oldfd); /* async-signal-safe */
3256 if (extra_fd == -1) {
3257 ERRMSG("dup");
3258 goto fail;
3259 }
3260 // without this, kqueue timer_th.event_fd fails with a reserved FD did not have close-on-exec
3261 // in #assert_close_on_exec because the FD_CLOEXEC is not dup'd by default
3262 if (fd_get_cloexec(pairs[i].oldfd, errmsg, errmsg_buflen)) {
3263 if (fd_set_cloexec(extra_fd, errmsg, errmsg_buflen)) {
3264 close(extra_fd);
3265 goto fail;
3266 }
3267 }
3268 rb_update_max_fd(extra_fd);
3269 }
3270 else {
3271 ret = redirect_dup2(pairs[i].oldfd, extra_fd); /* async-signal-safe */
3272 if (ret == -1) {
3273 ERRMSG("dup2");
3274 goto fail;
3275 }
3276 rb_update_max_fd(extra_fd);
3277 }
3278 pairs[i].oldfd = extra_fd;
3279 j = pairs[i].older_index;
3280 pairs[i].older_index = -1;
3281 while (j != -1) {
3282 ret = redirect_dup2(pairs[j].oldfd, pairs[j].newfd); /* async-signal-safe */
3283 if (ret == -1) {
3284 ERRMSG("dup2");
3285 goto fail;
3286 }
3287 rb_update_max_fd(ret);
3288 pairs[j].oldfd = -1;
3289 j = pairs[j].older_index;
3290 }
3291 }
3292 if (extra_fd != -1) {
3293 ret = redirect_close(extra_fd); /* async-signal-safe */
3294 if (ret == -1) {
3295 ERRMSG("close");
3296 goto fail;
3297 }
3298 }
3299
3300 return 0;
3301
3302 fail:
3303 return -1;
3304}
3305
3306/* This function should be async-signal-safe. Actually it is. */
3307static int
3308run_exec_close(VALUE ary, char *errmsg, size_t errmsg_buflen)
3309{
3310 long i;
3311 int ret;
3312
3313 for (i = 0; i < RARRAY_LEN(ary); i++) {
3314 VALUE elt = RARRAY_AREF(ary, i);
3315 int fd = FIX2INT(RARRAY_AREF(elt, 0));
3316 ret = redirect_close(fd); /* async-signal-safe */
3317 if (ret == -1) {
3318 ERRMSG("close");
3319 return -1;
3320 }
3321 }
3322 return 0;
3323}
3324
3325/* This function should be async-signal-safe when sargp is NULL. Actually it is. */
3326static int
3327run_exec_dup2_child(VALUE ary, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3328{
3329 long i;
3330 int ret;
3331
3332 for (i = 0; i < RARRAY_LEN(ary); i++) {
3333 VALUE elt = RARRAY_AREF(ary, i);
3334 int newfd = FIX2INT(RARRAY_AREF(elt, 0));
3335 int oldfd = FIX2INT(RARRAY_AREF(elt, 1));
3336
3337 if (save_redirect_fd(newfd, sargp, errmsg, errmsg_buflen) < 0) /* async-signal-safe */
3338 return -1;
3339 ret = redirect_dup2(oldfd, newfd); /* async-signal-safe */
3340 if (ret == -1) {
3341 ERRMSG("dup2");
3342 return -1;
3343 }
3344 rb_update_max_fd(newfd);
3345 }
3346 return 0;
3347}
3348
3349#ifdef HAVE_SETPGID
3350/* This function should be async-signal-safe when sargp is NULL. Actually it is. */
3351static int
3352run_exec_pgroup(const struct rb_execarg *eargp, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3353{
3354 /*
3355 * If FD_CLOEXEC is available, rb_fork_async_signal_safe waits the child's execve.
3356 * So setpgid is done in the child when rb_fork_async_signal_safe is returned in
3357 * the parent.
3358 * No race condition, even without setpgid from the parent.
3359 * (Is there an environment which has setpgid but no FD_CLOEXEC?)
3360 */
3361 int ret;
3362 rb_pid_t pgroup;
3363
3364 pgroup = eargp->pgroup_pgid;
3365 if (pgroup == -1)
3366 return 0;
3367
3368 if (sargp) {
3369 /* maybe meaningless with no fork environment... */
3370 sargp->pgroup_given = 1;
3371 sargp->pgroup_pgid = getpgrp();
3372 }
3373
3374 if (pgroup == 0) {
3375 pgroup = getpid(); /* async-signal-safe */
3376 }
3377 ret = setpgid(getpid(), pgroup); /* async-signal-safe */
3378 if (ret == -1) ERRMSG("setpgid");
3379 return ret;
3380}
3381#endif
3382
3383#if defined(HAVE_SETRLIMIT) && defined(RLIM2NUM)
3384/* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3385static int
3386run_exec_rlimit(VALUE ary, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3387{
3388 long i;
3389 for (i = 0; i < RARRAY_LEN(ary); i++) {
3390 VALUE elt = RARRAY_AREF(ary, i);
3391 int rtype = NUM2INT(RARRAY_AREF(elt, 0));
3392 struct rlimit rlim;
3393 if (sargp) {
3394 VALUE tmp, newary;
3395 if (getrlimit(rtype, &rlim) == -1) {
3396 ERRMSG("getrlimit");
3397 return -1;
3398 }
3399 tmp = hide_obj(rb_ary_new3(3, RARRAY_AREF(elt, 0),
3400 RLIM2NUM(rlim.rlim_cur),
3401 RLIM2NUM(rlim.rlim_max)));
3402 if (sargp->rlimit_limits == Qfalse)
3403 newary = sargp->rlimit_limits = hide_obj(rb_ary_new());
3404 else
3405 newary = sargp->rlimit_limits;
3406 rb_ary_push(newary, tmp);
3407 }
3408 rlim.rlim_cur = NUM2RLIM(RARRAY_AREF(elt, 1));
3409 rlim.rlim_max = NUM2RLIM(RARRAY_AREF(elt, 2));
3410 if (setrlimit(rtype, &rlim) == -1) { /* hopefully async-signal-safe */
3411 ERRMSG("setrlimit");
3412 return -1;
3413 }
3414 }
3415 return 0;
3416}
3417#endif
3418
3419#if !defined(HAVE_WORKING_FORK)
3420static VALUE
3421save_env_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
3422{
3423 rb_ary_push(ary, hide_obj(rb_ary_dup(argv[0])));
3424 return Qnil;
3425}
3426
3427static void
3428save_env(struct rb_execarg *sargp)
3429{
3430 if (!sargp)
3431 return;
3432 if (sargp->env_modification == Qfalse) {
3433 VALUE env = rb_envtbl();
3434 if (RTEST(env)) {
3435 VALUE ary = hide_obj(rb_ary_new());
3436 rb_block_call(env, idEach, 0, 0, save_env_i,
3437 (VALUE)ary);
3438 sargp->env_modification = ary;
3439 }
3440 sargp->unsetenv_others_given = 1;
3441 sargp->unsetenv_others_do = 1;
3442 }
3443}
3444#endif
3445
3446#ifdef _WIN32
3447#undef chdir
3448#define chdir(p) rb_w32_uchdir(p)
3449#endif
3450
3451/* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3452int
3453rb_execarg_run_options(const struct rb_execarg *eargp, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3454{
3455 VALUE obj;
3456
3457 if (sargp) {
3458 /* assume that sargp is always NULL on fork-able environments */
3459 MEMZERO(sargp, struct rb_execarg, 1);
3460 sargp->redirect_fds = Qnil;
3461 }
3462
3463#ifdef HAVE_SETPGID
3464 if (eargp->pgroup_given) {
3465 if (run_exec_pgroup(eargp, sargp, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3466 return -1;
3467 }
3468#endif
3469
3470#if defined(HAVE_SETRLIMIT) && defined(RLIM2NUM)
3471 obj = eargp->rlimit_limits;
3472 if (obj != Qfalse) {
3473 if (run_exec_rlimit(obj, sargp, errmsg, errmsg_buflen) == -1) /* hopefully async-signal-safe */
3474 return -1;
3475 }
3476#endif
3477
3478#if !defined(HAVE_WORKING_FORK)
3479 if (eargp->unsetenv_others_given && eargp->unsetenv_others_do) {
3480 save_env(sargp);
3481 rb_env_clear();
3482 }
3483
3484 obj = eargp->env_modification;
3485 if (obj != Qfalse) {
3486 long i;
3487 save_env(sargp);
3488 for (i = 0; i < RARRAY_LEN(obj); i++) {
3489 VALUE pair = RARRAY_AREF(obj, i);
3490 VALUE key = RARRAY_AREF(pair, 0);
3491 VALUE val = RARRAY_AREF(pair, 1);
3492 if (NIL_P(val))
3493 ruby_setenv(StringValueCStr(key), 0);
3494 else
3495 ruby_setenv(StringValueCStr(key), StringValueCStr(val));
3496 }
3497 }
3498#endif
3499
3500 if (eargp->umask_given) {
3501 mode_t mask = eargp->umask_mask;
3502 mode_t oldmask = umask(mask); /* never fail */ /* async-signal-safe */
3503 if (sargp) {
3504 sargp->umask_given = 1;
3505 sargp->umask_mask = oldmask;
3506 }
3507 }
3508
3509 obj = eargp->fd_dup2;
3510 if (obj != Qfalse) {
3511 if (run_exec_dup2(obj, eargp->dup2_tmpbuf, sargp, errmsg, errmsg_buflen) == -1) /* hopefully async-signal-safe */
3512 return -1;
3513 }
3514
3515 obj = eargp->fd_close;
3516 if (obj != Qfalse) {
3517 if (sargp)
3518 rb_warn("cannot close fd before spawn");
3519 else {
3520 if (run_exec_close(obj, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3521 return -1;
3522 }
3523 }
3524
3525#ifdef HAVE_WORKING_FORK
3526 if (eargp->close_others_do) {
3527 rb_close_before_exec(3, eargp->close_others_maxhint, eargp->redirect_fds); /* async-signal-safe */
3528 }
3529#endif
3530
3531 obj = eargp->fd_dup2_child;
3532 if (obj != Qfalse) {
3533 if (run_exec_dup2_child(obj, sargp, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3534 return -1;
3535 }
3536
3537 if (eargp->chdir_given) {
3538 if (sargp) {
3539 sargp->chdir_given = 1;
3540 sargp->chdir_dir = hide_obj(rb_dir_getwd_ospath());
3541 }
3542 if (chdir(RSTRING_PTR(eargp->chdir_dir)) == -1) { /* async-signal-safe */
3543 ERRMSG("chdir");
3544 return -1;
3545 }
3546 }
3547
3548#ifdef HAVE_SETGID
3549 if (eargp->gid_given) {
3550 if (setgid(eargp->gid) < 0) {
3551 ERRMSG("setgid");
3552 return -1;
3553 }
3554 }
3555#endif
3556#ifdef HAVE_SETUID
3557 if (eargp->uid_given) {
3558 if (setuid(eargp->uid) < 0) {
3559 ERRMSG("setuid");
3560 return -1;
3561 }
3562 }
3563#endif
3564
3565 if (sargp) {
3566 VALUE ary = sargp->fd_dup2;
3567 if (ary != Qfalse) {
3568 rb_execarg_allocate_dup2_tmpbuf(sargp, RARRAY_LEN(ary));
3569 }
3570 }
3571 {
3572 int preserve = errno;
3573 stdfd_clear_nonblock();
3574 errno = preserve;
3575 }
3576
3577 return 0;
3578}
3579
3580/* This function should be async-signal-safe. Hopefully it is. */
3581int
3582rb_exec_async_signal_safe(const struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
3583{
3584 errno = exec_async_signal_safe(eargp, errmsg, errmsg_buflen);
3585 return -1;
3586}
3587
3588static int
3589exec_async_signal_safe(const struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
3590{
3591#if !defined(HAVE_WORKING_FORK)
3592 struct rb_execarg sarg, *const sargp = &sarg;
3593#else
3594 struct rb_execarg *const sargp = NULL;
3595#endif
3596 int err;
3597
3598 if (rb_execarg_run_options(eargp, sargp, errmsg, errmsg_buflen) < 0) { /* hopefully async-signal-safe */
3599 return errno;
3600 }
3601
3602 if (eargp->use_shell) {
3603 err = proc_exec_sh(RSTRING_PTR(eargp->invoke.sh.shell_script), eargp->envp_str); /* async-signal-safe */
3604 }
3605 else {
3606 char *abspath = NULL;
3607 if (!NIL_P(eargp->invoke.cmd.command_abspath))
3608 abspath = RSTRING_PTR(eargp->invoke.cmd.command_abspath);
3609 err = proc_exec_cmd(abspath, eargp->invoke.cmd.argv_str, eargp->envp_str); /* async-signal-safe */
3610 }
3611#if !defined(HAVE_WORKING_FORK)
3612 rb_execarg_run_options(sargp, NULL, errmsg, errmsg_buflen);
3613#endif
3614
3615 return err;
3616}
3617
3618#ifdef HAVE_WORKING_FORK
3619/* This function should be async-signal-safe. Hopefully it is. */
3620static int
3621rb_exec_atfork(void* arg, char *errmsg, size_t errmsg_buflen)
3622{
3623 return rb_exec_async_signal_safe(arg, errmsg, errmsg_buflen); /* hopefully async-signal-safe */
3624}
3625
3626static VALUE
3627proc_syswait(VALUE pid)
3628{
3629 rb_syswait((rb_pid_t)pid);
3630 return Qnil;
3631}
3632
3633static int
3634move_fds_to_avoid_crash(int *fdp, int n, VALUE fds)
3635{
3636 int min = 0;
3637 int i;
3638 for (i = 0; i < n; i++) {
3639 int ret;
3640 while (RTEST(rb_hash_lookup(fds, INT2FIX(fdp[i])))) {
3641 if (min <= fdp[i])
3642 min = fdp[i]+1;
3643 while (RTEST(rb_hash_lookup(fds, INT2FIX(min))))
3644 min++;
3645 ret = rb_cloexec_fcntl_dupfd(fdp[i], min);
3646 if (ret == -1)
3647 return -1;
3648 rb_update_max_fd(ret);
3649 close(fdp[i]);
3650 fdp[i] = ret;
3651 }
3652 }
3653 return 0;
3654}
3655
3656static int
3657pipe_nocrash(int filedes[2], VALUE fds)
3658{
3659 int ret;
3660 ret = rb_pipe(filedes);
3661 if (ret == -1)
3662 return -1;
3663 if (RTEST(fds)) {
3664 int save = errno;
3665 if (move_fds_to_avoid_crash(filedes, 2, fds) == -1) {
3666 close(filedes[0]);
3667 close(filedes[1]);
3668 return -1;
3669 }
3670 errno = save;
3671 }
3672 return ret;
3673}
3674
3675#ifndef O_BINARY
3676#define O_BINARY 0
3677#endif
3678
3679static VALUE
3680rb_thread_sleep_that_takes_VALUE_as_sole_argument(VALUE n)
3681{
3683 return Qundef;
3684}
3685
3686static int
3687handle_fork_error(int err, struct rb_process_status *status, int *ep, volatile int *try_gc_p)
3688{
3689 int state = 0;
3690
3691 switch (err) {
3692 case ENOMEM:
3693 if ((*try_gc_p)-- > 0 && !rb_during_gc()) {
3694 rb_gc();
3695 return 0;
3696 }
3697 break;
3698 case EAGAIN:
3699#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
3700 case EWOULDBLOCK:
3701#endif
3702 if (!status && !ep) {
3703 rb_thread_sleep(1);
3704 return 0;
3705 }
3706 else {
3707 rb_protect(rb_thread_sleep_that_takes_VALUE_as_sole_argument, INT2FIX(1), &state);
3708 if (status) status->status = state;
3709 if (!state) return 0;
3710 }
3711 break;
3712 }
3713 if (ep) {
3714 close(ep[0]);
3715 close(ep[1]);
3716 errno = err;
3717 }
3718 if (state && !status) rb_jump_tag(state);
3719 return -1;
3720}
3721
3722#define prefork() ( \
3723 rb_io_flush(rb_stdout), \
3724 rb_io_flush(rb_stderr) \
3725 )
3726
3727/*
3728 * Forks child process, and returns the process ID in the parent
3729 * process.
3730 *
3731 * If +status+ is given, protects from any exceptions and sets the
3732 * jump status to it, and returns -1. If failed to fork new process
3733 * but no exceptions occurred, sets 0 to it. Otherwise, if forked
3734 * successfully, the value of +status+ is undetermined.
3735 *
3736 * In the child process, just returns 0 if +chfunc+ is +NULL+.
3737 * Otherwise +chfunc+ will be called with +charg+, and then the child
3738 * process exits with +EXIT_SUCCESS+ when it returned zero.
3739 *
3740 * In the case of the function is called and returns non-zero value,
3741 * the child process exits with non-+EXIT_SUCCESS+ value (normally
3742 * 127). And, on the platforms where +FD_CLOEXEC+ is available,
3743 * +errno+ is propagated to the parent process, and this function
3744 * returns -1 in the parent process. On the other platforms, just
3745 * returns pid.
3746 *
3747 * If fds is not Qnil, internal pipe for the errno propagation is
3748 * arranged to avoid conflicts of the hash keys in +fds+.
3749 *
3750 * +chfunc+ must not raise any exceptions.
3751 */
3752
3753static ssize_t
3754write_retry(int fd, const void *buf, size_t len)
3755{
3756 ssize_t w;
3757
3758 do {
3759 w = write(fd, buf, len);
3760 } while (w < 0 && errno == EINTR);
3761
3762 return w;
3763}
3764
3765static ssize_t
3766read_retry(int fd, void *buf, size_t len)
3767{
3768 ssize_t r;
3769
3770 if (set_blocking(fd) != 0) {
3771#ifndef _WIN32
3772 rb_async_bug_errno("set_blocking failed reading child error", errno);
3773#endif
3774 }
3775
3776 do {
3777 r = read(fd, buf, len);
3778 } while (r < 0 && errno == EINTR);
3779
3780 return r;
3781}
3782
3783static void
3784send_child_error(int fd, char *errmsg, size_t errmsg_buflen)
3785{
3786 int err;
3787
3788 err = errno;
3789 if (write_retry(fd, &err, sizeof(err)) < 0) err = errno;
3790 if (errmsg && 0 < errmsg_buflen) {
3791 errmsg[errmsg_buflen-1] = '\0';
3792 errmsg_buflen = strlen(errmsg);
3793 if (errmsg_buflen > 0 && write_retry(fd, errmsg, errmsg_buflen) < 0)
3794 err = errno;
3795 }
3796}
3797
3798static int
3799recv_child_error(int fd, int *errp, char *errmsg, size_t errmsg_buflen)
3800{
3801 int err;
3802 ssize_t size;
3803 if ((size = read_retry(fd, &err, sizeof(err))) < 0) {
3804 err = errno;
3805 }
3806 *errp = err;
3807 if (size == sizeof(err) &&
3808 errmsg && 0 < errmsg_buflen) {
3809 ssize_t ret = read_retry(fd, errmsg, errmsg_buflen-1);
3810 if (0 <= ret) {
3811 errmsg[ret] = '\0';
3812 }
3813 }
3814 close(fd);
3815 return size != 0;
3816}
3817
3818#ifdef HAVE_WORKING_VFORK
3819#if !defined(HAVE_GETRESUID) && defined(HAVE_GETUIDX)
3820/* AIX 7.1 */
3821static int
3822getresuid(rb_uid_t *ruid, rb_uid_t *euid, rb_uid_t *suid)
3823{
3824 rb_uid_t ret;
3825
3826 *ruid = getuid();
3827 *euid = geteuid();
3828 ret = getuidx(ID_SAVED);
3829 if (ret == (rb_uid_t)-1)
3830 return -1;
3831 *suid = ret;
3832 return 0;
3833}
3834#define HAVE_GETRESUID
3835#endif
3836
3837#if !defined(HAVE_GETRESGID) && defined(HAVE_GETGIDX)
3838/* AIX 7.1 */
3839static int
3840getresgid(rb_gid_t *rgid, rb_gid_t *egid, rb_gid_t *sgid)
3841{
3842 rb_gid_t ret;
3843
3844 *rgid = getgid();
3845 *egid = getegid();
3846 ret = getgidx(ID_SAVED);
3847 if (ret == (rb_gid_t)-1)
3848 return -1;
3849 *sgid = ret;
3850 return 0;
3851}
3852#define HAVE_GETRESGID
3853#endif
3854
3855static int
3856has_privilege(void)
3857{
3858 /*
3859 * has_privilege() is used to choose vfork() or fork().
3860 *
3861 * If the process has privilege, the parent process or
3862 * the child process can change UID/GID.
3863 * If vfork() is used to create the child process and
3864 * the parent or child process change effective UID/GID,
3865 * different privileged processes shares memory.
3866 * It is a bad situation.
3867 * So, fork() should be used.
3868 */
3869
3870 rb_uid_t ruid, euid;
3871 rb_gid_t rgid, egid;
3872
3873#if defined HAVE_ISSETUGID
3874 if (issetugid())
3875 return 1;
3876#endif
3877
3878#ifdef HAVE_GETRESUID
3879 {
3880 int ret;
3881 rb_uid_t suid;
3882 ret = getresuid(&ruid, &euid, &suid);
3883 if (ret == -1)
3884 rb_sys_fail("getresuid(2)");
3885 if (euid != suid)
3886 return 1;
3887 }
3888#else
3889 ruid = getuid();
3890 euid = geteuid();
3891#endif
3892
3893 if (euid == 0 || euid != ruid)
3894 return 1;
3895
3896#ifdef HAVE_GETRESGID
3897 {
3898 int ret;
3899 rb_gid_t sgid;
3900 ret = getresgid(&rgid, &egid, &sgid);
3901 if (ret == -1)
3902 rb_sys_fail("getresgid(2)");
3903 if (egid != sgid)
3904 return 1;
3905 }
3906#else
3907 rgid = getgid();
3908 egid = getegid();
3909#endif
3910
3911 if (egid != rgid)
3912 return 1;
3913
3914 return 0;
3915}
3916#endif
3917
3918struct child_handler_disabler_state
3919{
3920 sigset_t sigmask;
3921};
3922
3923static void
3924disable_child_handler_before_fork(struct child_handler_disabler_state *old)
3925{
3926#ifdef HAVE_PTHREAD_SIGMASK
3927 int ret;
3928 sigset_t all;
3929
3930 ret = sigfillset(&all);
3931 if (ret == -1)
3932 rb_sys_fail("sigfillset");
3933
3934 ret = pthread_sigmask(SIG_SETMASK, &all, &old->sigmask); /* not async-signal-safe */
3935 if (ret != 0) {
3936 rb_syserr_fail(ret, "pthread_sigmask");
3937 }
3938#else
3939# pragma GCC warning "pthread_sigmask on fork is not available. potentially dangerous"
3940#endif
3941}
3942
3943static void
3944disable_child_handler_fork_parent(struct child_handler_disabler_state *old)
3945{
3946#ifdef HAVE_PTHREAD_SIGMASK
3947 int ret;
3948
3949 ret = pthread_sigmask(SIG_SETMASK, &old->sigmask, NULL); /* not async-signal-safe */
3950 if (ret != 0) {
3951 rb_syserr_fail(ret, "pthread_sigmask");
3952 }
3953#else
3954# pragma GCC warning "pthread_sigmask on fork is not available. potentially dangerous"
3955#endif
3956}
3957
3958/* This function should be async-signal-safe. Actually it is. */
3959static int
3960disable_child_handler_fork_child(struct child_handler_disabler_state *old, char *errmsg, size_t errmsg_buflen)
3961{
3962 int sig;
3963 int ret;
3964
3965 for (sig = 1; sig < NSIG; sig++) {
3966 sig_t handler = signal(sig, SIG_DFL);
3967
3968 if (handler == SIG_ERR && errno == EINVAL) {
3969 continue; /* Ignore invalid signal number */
3970 }
3971 if (handler == SIG_ERR) {
3972 ERRMSG("signal to obtain old action");
3973 return -1;
3974 }
3975#ifdef SIGPIPE
3976 if (sig == SIGPIPE) {
3977 continue;
3978 }
3979#endif
3980 /* it will be reset to SIG_DFL at execve time, instead */
3981 if (handler == SIG_IGN) {
3982 signal(sig, SIG_IGN);
3983 }
3984 }
3985
3986 /* non-Ruby child process, ensure cmake can see SIGCHLD */
3987 sigemptyset(&old->sigmask);
3988 ret = sigprocmask(SIG_SETMASK, &old->sigmask, NULL); /* async-signal-safe */
3989 if (ret != 0) {
3990 ERRMSG("sigprocmask");
3991 return -1;
3992 }
3993 return 0;
3994}
3995
3996static rb_pid_t
3997retry_fork_async_signal_safe(struct rb_process_status *status, int *ep,
3998 int (*chfunc)(void*, char *, size_t), void *charg,
3999 char *errmsg, size_t errmsg_buflen,
4000 struct waitpid_state *w)
4001{
4002 rb_pid_t pid;
4003 volatile int try_gc = 1;
4004 struct child_handler_disabler_state old;
4005 int err;
4006
4007 while (1) {
4008 prefork();
4009 disable_child_handler_before_fork(&old);
4010
4011 // Older versions of ASAN does not work with vfork
4012 // See https://github.com/google/sanitizers/issues/925
4013#if defined(HAVE_WORKING_VFORK) && !defined(RUBY_ASAN_ENABLED)
4014 if (!has_privilege())
4015 pid = vfork();
4016 else
4017 pid = rb_fork();
4018#else
4019 pid = rb_fork();
4020#endif
4021 if (pid == 0) {/* fork succeed, child process */
4022 int ret;
4023 close(ep[0]);
4024 ret = disable_child_handler_fork_child(&old, errmsg, errmsg_buflen); /* async-signal-safe */
4025 if (ret == 0) {
4026 ret = chfunc(charg, errmsg, errmsg_buflen);
4027 if (!ret) _exit(EXIT_SUCCESS);
4028 }
4029 send_child_error(ep[1], errmsg, errmsg_buflen);
4030#if EXIT_SUCCESS == 127
4031 _exit(EXIT_FAILURE);
4032#else
4033 _exit(127);
4034#endif
4035 }
4036 err = errno;
4037 disable_child_handler_fork_parent(&old);
4038 if (0 < pid) /* fork succeed, parent process */
4039 return pid;
4040 /* fork failed */
4041 if (handle_fork_error(err, status, ep, &try_gc))
4042 return -1;
4043 }
4044}
4045
4046static rb_pid_t
4047fork_check_err(struct rb_process_status *status, int (*chfunc)(void*, char *, size_t), void *charg,
4048 VALUE fds, char *errmsg, size_t errmsg_buflen,
4049 struct rb_execarg *eargp)
4050{
4051 rb_pid_t pid;
4052 int err;
4053 int ep[2];
4054 int error_occurred;
4055
4056 struct waitpid_state *w = eargp && eargp->waitpid_state ? eargp->waitpid_state : 0;
4057
4058 if (status) status->status = 0;
4059
4060 if (pipe_nocrash(ep, fds)) return -1;
4061
4062 pid = retry_fork_async_signal_safe(status, ep, chfunc, charg, errmsg, errmsg_buflen, w);
4063
4064 if (status) status->pid = pid;
4065
4066 if (pid < 0) {
4067 if (status) status->error = errno;
4068
4069 return pid;
4070 }
4071
4072 close(ep[1]);
4073
4074 error_occurred = recv_child_error(ep[0], &err, errmsg, errmsg_buflen);
4075
4076 if (error_occurred) {
4077 if (status) {
4078 int state = 0;
4079 status->error = err;
4080
4081 VM_ASSERT((w == 0) && "only used by extensions");
4082 rb_protect(proc_syswait, (VALUE)pid, &state);
4083
4084 status->status = state;
4085 }
4086 else if (!w) {
4087 rb_syswait(pid);
4088 }
4089
4090 errno = err;
4091 return -1;
4092 }
4093
4094 return pid;
4095}
4096
4097/*
4098 * The "async_signal_safe" name is a lie, but it is used by pty.c and
4099 * maybe other exts. fork() is not async-signal-safe due to pthread_atfork
4100 * and future POSIX revisions will remove it from a list of signal-safe
4101 * functions. rb_waitpid is not async-signal-safe.
4102 * For our purposes, we do not need async-signal-safety, here
4103 */
4104rb_pid_t
4105rb_fork_async_signal_safe(int *status,
4106 int (*chfunc)(void*, char *, size_t), void *charg,
4107 VALUE fds, char *errmsg, size_t errmsg_buflen)
4108{
4109 struct rb_process_status process_status;
4110
4111 rb_pid_t result = fork_check_err(&process_status, chfunc, charg, fds, errmsg, errmsg_buflen, 0);
4112
4113 if (status) {
4114 *status = process_status.status;
4115 }
4116
4117 return result;
4118}
4119
4120rb_pid_t
4121rb_fork_ruby(int *status)
4122{
4123 if (UNLIKELY(!rb_ractor_main_p())) {
4124 rb_raise(rb_eRactorIsolationError, "can not fork from non-main Ractors");
4125 }
4126
4127 struct rb_process_status child = {.status = 0};
4128 rb_pid_t pid;
4129 int try_gc = 1, err = 0;
4130 struct child_handler_disabler_state old;
4131
4132 do {
4133 prefork();
4134
4135 before_fork_ruby();
4136 rb_thread_acquire_fork_lock();
4137 disable_child_handler_before_fork(&old);
4138
4139 RB_VM_LOCKING() {
4140 child.pid = pid = rb_fork();
4141 child.error = err = errno;
4142 }
4143
4144 disable_child_handler_fork_parent(&old); /* yes, bad name */
4145 if (
4146#if defined(__FreeBSD__)
4147 pid != 0 &&
4148#endif
4149 true) {
4150 rb_thread_release_fork_lock();
4151 }
4152 if (pid == 0) {
4153 rb_thread_reset_fork_lock();
4154 }
4155 after_fork_ruby(pid);
4156
4157 /* repeat while fork failed but retryable */
4158 } while (pid < 0 && handle_fork_error(err, &child, NULL, &try_gc) == 0);
4159
4160 if (status) *status = child.status;
4161
4162 return pid;
4163}
4164
4165static rb_pid_t
4166proc_fork_pid(void)
4167{
4168 rb_pid_t pid = rb_fork_ruby(NULL);
4169
4170 if (pid == -1) {
4171 rb_sys_fail("fork(2)");
4172 }
4173
4174 return pid;
4175}
4176
4177rb_pid_t
4178rb_call_proc__fork(void)
4179{
4180 ID id__fork;
4181 CONST_ID(id__fork, "_fork");
4182 if (rb_method_basic_definition_p(CLASS_OF(rb_mProcess), id__fork)) {
4183 return proc_fork_pid();
4184 }
4185 else {
4186 VALUE pid = rb_funcall(rb_mProcess, id__fork, 0);
4187 return NUM2PIDT(pid);
4188 }
4189}
4190#endif
4191
4192#if defined(HAVE_WORKING_FORK) && !defined(CANNOT_FORK_WITH_PTHREAD)
4193/*
4194 * call-seq:
4195 * Process._fork -> integer
4196 *
4197 * An internal API for fork. Do not call this method directly.
4198 * Currently, this is called via Kernel#fork, Process.fork, and
4199 * IO.popen with <tt>"-"</tt>.
4200 *
4201 * This method is not for casual code but for application monitoring
4202 * libraries. You can add custom code before and after fork events
4203 * by overriding this method.
4204 *
4205 * Note: Process.daemon may be implemented using fork(2) BUT does not go
4206 * through this method.
4207 * Thus, depending on your reason to hook into this method, you
4208 * may also want to hook into that one.
4209 * See {this issue}[https://bugs.ruby-lang.org/issues/18911] for a
4210 * more detailed discussion of this.
4211 */
4212VALUE
4213rb_proc__fork(VALUE _obj)
4214{
4215 rb_pid_t pid = proc_fork_pid();
4216 return PIDT2NUM(pid);
4217}
4218
4219/*
4220 * call-seq:
4221 * Process.fork { ... } -> integer or nil
4222 * Process.fork -> integer or nil
4223 *
4224 * Creates a child process.
4225 *
4226 * With a block given, runs the block in the child process;
4227 * on block exit, the child terminates with a status of zero:
4228 *
4229 * puts "Before the fork: #{Process.pid}"
4230 * fork do
4231 * puts "In the child process: #{Process.pid}"
4232 * end # => 420520
4233 * puts "After the fork: #{Process.pid}"
4234 *
4235 * Output:
4236 *
4237 * Before the fork: 420496
4238 * After the fork: 420496
4239 * In the child process: 420520
4240 *
4241 * With no block given, the +fork+ call returns twice:
4242 *
4243 * - Once in the parent process, returning the pid of the child process.
4244 * - Once in the child process, returning +nil+.
4245 *
4246 * Example:
4247 *
4248 * puts "This is the first line before the fork (pid #{Process.pid})"
4249 * puts fork
4250 * puts "This is the second line after the fork (pid #{Process.pid})"
4251 *
4252 * Output:
4253 *
4254 * This is the first line before the fork (pid 420199)
4255 * 420223
4256 * This is the second line after the fork (pid 420199)
4257 *
4258 * This is the second line after the fork (pid 420223)
4259 *
4260 * In either case, the child process may exit using
4261 * Kernel.exit! to avoid the call to Kernel#at_exit.
4262 *
4263 * To avoid zombie processes, the parent process should call either:
4264 *
4265 * - Process.wait, to collect the termination statuses of its children.
4266 * - Process.detach, to register disinterest in their status.
4267 *
4268 * The thread calling +fork+ is the only thread in the created child process;
4269 * +fork+ doesn't copy other threads.
4270 *
4271 * Note that method +fork+ is available on some platforms,
4272 * but not on others:
4273 *
4274 * Process.respond_to?(:fork) # => true # Would be false on some.
4275 *
4276 * If not, you may use ::spawn instead of +fork+.
4277 */
4278
4279static VALUE
4280rb_f_fork(VALUE obj)
4281{
4282 rb_pid_t pid;
4283
4284 pid = rb_call_proc__fork();
4285
4286 if (pid == 0) {
4287 if (rb_block_given_p()) {
4288 int status;
4289 rb_protect(rb_yield, Qundef, &status);
4290 ruby_stop(status);
4291 }
4292 return Qnil;
4293 }
4294
4295 return PIDT2NUM(pid);
4296}
4297#else
4298#define rb_proc__fork rb_f_notimplement
4299#define rb_f_fork rb_f_notimplement
4300#endif
4301
4302static int
4303exit_status_code(VALUE status)
4304{
4305 int istatus;
4306
4307 switch (status) {
4308 case Qtrue:
4309 istatus = EXIT_SUCCESS;
4310 break;
4311 case Qfalse:
4312 istatus = EXIT_FAILURE;
4313 break;
4314 default:
4315 istatus = NUM2INT(status);
4316#if EXIT_SUCCESS != 0
4317 if (istatus == 0)
4318 istatus = EXIT_SUCCESS;
4319#endif
4320 break;
4321 }
4322 return istatus;
4323}
4324
4325NORETURN(static VALUE rb_f_exit_bang(int argc, VALUE *argv, VALUE obj));
4326/*
4327 * call-seq:
4328 * exit!(status = false)
4329 * Process.exit!(status = false)
4330 *
4331 * Exits the process immediately; no exit handlers are called.
4332 * Returns exit status +status+ to the underlying operating system.
4333 *
4334 * Process.exit!(true)
4335 *
4336 * Values +true+ and +false+ for argument +status+
4337 * indicate, respectively, success and failure;
4338 * The meanings of integer values are system-dependent.
4339 *
4340 */
4341
4342static VALUE
4343rb_f_exit_bang(int argc, VALUE *argv, VALUE obj)
4344{
4345 int istatus;
4346
4347 if (rb_check_arity(argc, 0, 1) == 1) {
4348 istatus = exit_status_code(argv[0]);
4349 }
4350 else {
4351 istatus = EXIT_FAILURE;
4352 }
4353 _exit(istatus);
4354
4356}
4357
4358void
4359rb_exit(int status)
4360{
4361 if (GET_EC()->tag) {
4362 VALUE args[2];
4363
4364 args[0] = INT2NUM(status);
4365 args[1] = rb_str_new2("exit");
4367 }
4368 ruby_stop(status);
4369}
4370
4371VALUE
4372rb_f_exit(int argc, const VALUE *argv)
4373{
4374 int istatus;
4375
4376 if (rb_check_arity(argc, 0, 1) == 1) {
4377 istatus = exit_status_code(argv[0]);
4378 }
4379 else {
4380 istatus = EXIT_SUCCESS;
4381 }
4382 rb_exit(istatus);
4383
4385}
4386
4387NORETURN(static VALUE f_exit(int c, const VALUE *a, VALUE _));
4388/*
4389 * call-seq:
4390 * exit(status = true)
4391 * Process.exit(status = true)
4392 *
4393 * Initiates termination of the Ruby script by raising SystemExit;
4394 * the exception may be caught.
4395 * Returns exit status +status+ to the underlying operating system.
4396 *
4397 * Values +true+ and +false+ for argument +status+
4398 * indicate, respectively, success and failure;
4399 * The meanings of integer values are system-dependent.
4400 *
4401 * Example:
4402 *
4403 * begin
4404 * exit
4405 * puts 'Never get here.'
4406 * rescue SystemExit
4407 * puts 'Rescued a SystemExit exception.'
4408 * end
4409 * puts 'After begin block.'
4410 *
4411 * Output:
4412 *
4413 * Rescued a SystemExit exception.
4414 * After begin block.
4415 *
4416 * Just prior to final termination,
4417 * Ruby executes any at-exit procedures (see Kernel::at_exit)
4418 * and any object finalizers (see ObjectSpace::define_finalizer).
4419 *
4420 * Example:
4421 *
4422 * at_exit { puts 'In at_exit function.' }
4423 * ObjectSpace.define_finalizer('string', proc { puts 'In finalizer.' })
4424 * exit
4425 *
4426 * Output:
4427 *
4428 * In at_exit function.
4429 * In finalizer.
4430 *
4431 */
4432
4433static VALUE
4434f_exit(int c, const VALUE *a, VALUE _)
4435{
4436 rb_f_exit(c, a);
4438}
4439
4440VALUE
4441rb_f_abort(int argc, const VALUE *argv)
4442{
4443 rb_check_arity(argc, 0, 1);
4444 if (argc == 0) {
4445 rb_execution_context_t *ec = GET_EC();
4446 VALUE errinfo = rb_ec_get_errinfo(ec);
4447 if (!NIL_P(errinfo)) {
4448 rb_ec_error_print(ec, errinfo);
4449 }
4450 rb_exit(EXIT_FAILURE);
4451 }
4452 else {
4453 VALUE args[2];
4454
4455 args[1] = args[0] = argv[0];
4456 StringValue(args[0]);
4457 rb_io_puts(1, args, rb_ractor_stderr());
4458 args[0] = INT2NUM(EXIT_FAILURE);
4460 }
4461
4463}
4464
4465NORETURN(static VALUE f_abort(int c, const VALUE *a, VALUE _));
4466
4467/*
4468 * call-seq:
4469 * abort
4470 * Process.abort(msg = nil)
4471 *
4472 * Terminates execution immediately, effectively by calling
4473 * <tt>Kernel.exit(false)</tt>.
4474 *
4475 * If string argument +msg+ is given,
4476 * it is written to STDERR prior to termination;
4477 * otherwise, if an exception was raised,
4478 * prints its message and backtrace.
4479 */
4480
4481static VALUE
4482f_abort(int c, const VALUE *a, VALUE _)
4483{
4484 rb_f_abort(c, a);
4486}
4487
4488void
4489rb_syswait(rb_pid_t pid)
4490{
4491 int status;
4492
4493 rb_waitpid(pid, &status, 0);
4494}
4495
4496#if !defined HAVE_WORKING_FORK && !defined HAVE_SPAWNV && !defined __EMSCRIPTEN__
4497char *
4498rb_execarg_commandline(const struct rb_execarg *eargp, VALUE *prog)
4499{
4500 VALUE cmd = *prog;
4501 if (eargp && !eargp->use_shell) {
4502 VALUE str = eargp->invoke.cmd.argv_str;
4503 VALUE buf = eargp->invoke.cmd.argv_buf;
4504 char *p, **argv = ARGVSTR2ARGV(str);
4505 long i, argc = ARGVSTR2ARGC(str);
4506 const char *start = RSTRING_PTR(buf);
4507 cmd = rb_str_new(start, RSTRING_LEN(buf));
4508 p = RSTRING_PTR(cmd);
4509 for (i = 1; i < argc; ++i) {
4510 p[argv[i] - start - 1] = ' ';
4511 }
4512 *prog = cmd;
4513 return p;
4514 }
4515 return StringValueCStr(*prog);
4516}
4517#endif
4518
4519static rb_pid_t
4520rb_spawn_process(struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
4521{
4522 rb_pid_t pid;
4523#if !defined HAVE_WORKING_FORK || USE_SPAWNV
4524 VALUE prog;
4525 struct rb_execarg sarg;
4526# if !defined HAVE_SPAWNV
4527 int status;
4528# endif
4529#endif
4530
4531#if defined HAVE_WORKING_FORK && !USE_SPAWNV
4532 pid = fork_check_err(eargp->status, rb_exec_atfork, eargp, eargp->redirect_fds, errmsg, errmsg_buflen, eargp);
4533#else
4534 prog = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
4535
4536 if (rb_execarg_run_options(eargp, &sarg, errmsg, errmsg_buflen) < 0) {
4537 return -1;
4538 }
4539
4540 if (prog && !eargp->use_shell) {
4541 char **argv = ARGVSTR2ARGV(eargp->invoke.cmd.argv_str);
4542 argv[0] = RSTRING_PTR(prog);
4543 }
4544# if defined HAVE_SPAWNV
4545 if (eargp->use_shell) {
4546 pid = proc_spawn_sh(RSTRING_PTR(prog));
4547 }
4548 else {
4549 char **argv = ARGVSTR2ARGV(eargp->invoke.cmd.argv_str);
4550 pid = proc_spawn_cmd(argv, prog, eargp);
4551 }
4552
4553 if (pid == -1) {
4554 rb_last_status_set(0x7f << 8, pid);
4555 }
4556# else
4557 status = system(rb_execarg_commandline(eargp, &prog));
4558 pid = 1; /* dummy */
4559 rb_last_status_set((status & 0xff) << 8, pid);
4560# endif
4561
4562 if (eargp->waitpid_state) {
4563 eargp->waitpid_state->pid = pid;
4564 }
4565
4566 rb_execarg_run_options(&sarg, NULL, errmsg, errmsg_buflen);
4567#endif
4568
4569 return pid;
4570}
4571
4573 VALUE execarg;
4574 struct {
4575 char *ptr;
4576 size_t buflen;
4577 } errmsg;
4578};
4579
4580static VALUE
4581do_spawn_process(VALUE arg)
4582{
4583 struct spawn_args *argp = (struct spawn_args *)arg;
4584
4585 rb_execarg_parent_start1(argp->execarg);
4586
4587 return (VALUE)rb_spawn_process(rb_execarg_get(argp->execarg),
4588 argp->errmsg.ptr, argp->errmsg.buflen);
4589}
4590
4591NOINLINE(static rb_pid_t
4592 rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen));
4593
4594static rb_pid_t
4595rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen)
4596{
4597 struct spawn_args args;
4598
4599 args.execarg = execarg_obj;
4600 args.errmsg.ptr = errmsg;
4601 args.errmsg.buflen = errmsg_buflen;
4602
4603 rb_pid_t r = (rb_pid_t)rb_ensure(do_spawn_process, (VALUE)&args,
4604 execarg_parent_end, execarg_obj);
4605 return r;
4606}
4607
4608static rb_pid_t
4609rb_spawn_internal(int argc, const VALUE *argv, char *errmsg, size_t errmsg_buflen)
4610{
4611 VALUE execarg_obj;
4612
4613 execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
4614 return rb_execarg_spawn(execarg_obj, errmsg, errmsg_buflen);
4615}
4616
4617rb_pid_t
4618rb_spawn_err(int argc, const VALUE *argv, char *errmsg, size_t errmsg_buflen)
4619{
4620 return rb_spawn_internal(argc, argv, errmsg, errmsg_buflen);
4621}
4622
4623rb_pid_t
4624rb_spawn(int argc, const VALUE *argv)
4625{
4626 return rb_spawn_internal(argc, argv, NULL, 0);
4627}
4628
4629/*
4630 * call-seq:
4631 * system([env, ] command_line, options = {}, exception: false) -> true, false, or nil
4632 * system([env, ] exe_path, *args, options = {}, exception: false) -> true, false, or nil
4633 *
4634 * Creates a new child process by doing one of the following
4635 * in that process:
4636 *
4637 * - Passing string +command_line+ to the shell.
4638 * - Invoking the executable at +exe_path+.
4639 *
4640 * This method has potential security vulnerabilities if called with untrusted input;
4641 * see {Command Injection}[rdoc-ref:security/command_injection.rdoc].
4642 *
4643 * Returns:
4644 *
4645 * - +true+ if the command exits with status zero.
4646 * - +false+ if the exit status is a non-zero integer.
4647 * - +nil+ if the command could not execute.
4648 *
4649 * Raises an exception (instead of returning +false+ or +nil+)
4650 * if keyword argument +exception+ is set to +true+.
4651 *
4652 * Assigns the command's error status to <tt>$?</tt>.
4653 *
4654 * The new process is created using the
4655 * {system system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/system.html];
4656 * it may inherit some of its environment from the calling program
4657 * (possibly including open file descriptors).
4658 *
4659 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
4660 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
4661 *
4662 * Argument +options+ is a hash of options for the new process;
4663 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
4664 *
4665 * The first required argument is one of the following:
4666 *
4667 * - +command_line+ if it is a string,
4668 * and if it begins with a shell reserved word or special built-in,
4669 * or if it contains one or more meta characters.
4670 * - +exe_path+ otherwise.
4671 *
4672 * <b>Argument +command_line+</b>
4673 *
4674 * \String argument +command_line+ is a command line to be passed to a shell;
4675 * it must begin with a shell reserved word, begin with a special built-in,
4676 * or contain meta characters:
4677 *
4678 * system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
4679 * system('exit') # => true # Built-in.
4680 * system('date > /tmp/date.tmp') # => true # Contains meta character.
4681 * system('date > /nop/date.tmp') # => false
4682 * system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
4683 *
4684 * Assigns the command's error status to <tt>$?</tt>:
4685 *
4686 * system('exit') # => true # Built-in.
4687 * $? # => #<Process::Status: pid 640610 exit 0>
4688 * system('date > /nop/date.tmp') # => false
4689 * $? # => #<Process::Status: pid 640742 exit 2>
4690 *
4691 * The command line may also contain arguments and options for the command:
4692 *
4693 * system('echo "Foo"') # => true
4694 *
4695 * Output:
4696 *
4697 * Foo
4698 *
4699 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
4700 *
4701 * Raises an exception if the new process could not execute.
4702 *
4703 * <b>Argument +exe_path+</b>
4704 *
4705 * Argument +exe_path+ is one of the following:
4706 *
4707 * - The string path to an executable to be called.
4708 * - A 2-element array containing the path to an executable
4709 * and the string to be used as the name of the executing process.
4710 *
4711 * Example:
4712 *
4713 * system('/usr/bin/date') # => true # Path to date on Unix-style system.
4714 * system('foo') # => nil # Command failed.
4715 *
4716 * Output:
4717 *
4718 * Mon Aug 28 11:43:10 AM CDT 2023
4719 *
4720 * Assigns the command's error status to <tt>$?</tt>:
4721 *
4722 * system('/usr/bin/date') # => true
4723 * $? # => #<Process::Status: pid 645605 exit 0>
4724 * system('foo') # => nil
4725 * $? # => #<Process::Status: pid 645608 exit 127>
4726 *
4727 * Ruby invokes the executable directly.
4728 * This form does not use the shell;
4729 * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
4730 *
4731 * system('doesnt_exist') # => nil
4732 *
4733 * If one or more +args+ is given, each is an argument or option
4734 * to be passed to the executable:
4735 *
4736 * system('echo', 'C*') # => true
4737 * system('echo', 'hello', 'world') # => true
4738 *
4739 * Output:
4740 *
4741 * C*
4742 * hello world
4743 *
4744 * Raises an exception if the new process could not execute.
4745 */
4746
4747static VALUE
4748rb_f_system(int argc, VALUE *argv, VALUE _)
4749{
4750 rb_thread_t *th = GET_THREAD();
4751 VALUE execarg_obj = rb_execarg_new(argc, argv, TRUE, TRUE);
4752 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
4753
4754 struct rb_process_status status = {0};
4755 eargp->status = &status;
4756
4757 last_status_clear(th);
4758
4759 // This function can set the thread's last status.
4760 // May be different from waitpid_state.pid on exec failure.
4761 rb_pid_t pid = rb_execarg_spawn(execarg_obj, 0, 0);
4762
4763 if (pid > 0) {
4764 VALUE status = rb_process_status_wait(pid, 0);
4765 struct rb_process_status *data = rb_check_typeddata(status, &rb_process_status_type);
4766 // Set the last status:
4767 rb_obj_freeze(status);
4768 th->last_status = status;
4769
4770 if (data->status == EXIT_SUCCESS) {
4771 return Qtrue;
4772 }
4773
4774 if (data->error != 0) {
4775 if (eargp->exception) {
4776 VALUE command = eargp->invoke.sh.shell_script;
4777 RB_GC_GUARD(execarg_obj);
4778 rb_syserr_fail_str(data->error, command);
4779 }
4780 else {
4781 return Qnil;
4782 }
4783 }
4784 else if (eargp->exception) {
4785 VALUE command = eargp->invoke.sh.shell_script;
4786 VALUE str = rb_str_new_cstr("Command failed with");
4787 rb_str_cat_cstr(pst_message_status(str, data->status), ": ");
4788 rb_str_append(str, command);
4789 RB_GC_GUARD(execarg_obj);
4791 }
4792 else {
4793 return Qfalse;
4794 }
4795
4796 RB_GC_GUARD(status);
4797 }
4798
4799 if (eargp->exception) {
4800 VALUE command = eargp->invoke.sh.shell_script;
4801 RB_GC_GUARD(execarg_obj);
4802 rb_syserr_fail_str(errno, command);
4803 }
4804 else {
4805 return Qnil;
4806 }
4807}
4808
4809/*
4810 * call-seq:
4811 * spawn([env, ] command_line, options = {}) -> pid
4812 * spawn([env, ] exe_path, *args, options = {}) -> pid
4813 *
4814 * Creates a new child process by doing one of the following
4815 * in that process:
4816 *
4817 * - Passing string +command_line+ to the shell.
4818 * - Invoking the executable at +exe_path+.
4819 *
4820 * This method has potential security vulnerabilities if called with untrusted input;
4821 * see {Command Injection}[rdoc-ref:security/command_injection.rdoc].
4822 *
4823 * Returns the process ID (pid) of the new process,
4824 * without waiting for it to complete.
4825 *
4826 * To avoid zombie processes, the parent process should call either:
4827 *
4828 * - Process.wait, to collect the termination statuses of its children.
4829 * - Process.detach, to register disinterest in their status.
4830 *
4831 * The new process is created using the
4832 * {exec system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/execve.html];
4833 * it may inherit some of its environment from the calling program
4834 * (possibly including open file descriptors).
4835 *
4836 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
4837 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
4838 *
4839 * Argument +options+ is a hash of options for the new process;
4840 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
4841 *
4842 * The first required argument is one of the following:
4843 *
4844 * - +command_line+ if it is a string,
4845 * and if it begins with a shell reserved word or special built-in,
4846 * or if it contains one or more meta characters.
4847 * - +exe_path+ otherwise.
4848 *
4849 * <b>Argument +command_line+</b>
4850 *
4851 * \String argument +command_line+ is a command line to be passed to a shell;
4852 * it must begin with a shell reserved word, begin with a special built-in,
4853 * or contain meta characters:
4854 *
4855 * spawn('if true; then echo "Foo"; fi') # => 798847 # Shell reserved word.
4856 * Process.wait # => 798847
4857 * spawn('exit') # => 798848 # Built-in.
4858 * Process.wait # => 798848
4859 * spawn('date > /tmp/date.tmp') # => 798879 # Contains meta character.
4860 * Process.wait # => 798849
4861 * spawn('date > /nop/date.tmp') # => 798882 # Issues error message.
4862 * Process.wait # => 798882
4863 *
4864 * The command line may also contain arguments and options for the command:
4865 *
4866 * spawn('echo "Foo"') # => 799031
4867 * Process.wait # => 799031
4868 *
4869 * Output:
4870 *
4871 * Foo
4872 *
4873 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
4874 *
4875 * Raises an exception if the new process could not execute.
4876 *
4877 * <b>Argument +exe_path+</b>
4878 *
4879 * Argument +exe_path+ is one of the following:
4880 *
4881 * - The string path to an executable to be called.
4882 * - A 2-element array containing the path to an executable to be called,
4883 * and the string to be used as the name of the executing process.
4884 *
4885 * spawn('/usr/bin/date') # Path to date on Unix-style system.
4886 * Process.wait
4887 *
4888 * Output:
4889 *
4890 * Mon Aug 28 11:43:10 AM CDT 2023
4891 *
4892 * Ruby invokes the executable directly.
4893 * This form does not use the shell;
4894 * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
4895 *
4896 * If one or more +args+ is given, each is an argument or option
4897 * to be passed to the executable:
4898 *
4899 * spawn('echo', 'C*') # => 799392
4900 * Process.wait # => 799392
4901 * spawn('echo', 'hello', 'world') # => 799393
4902 * Process.wait # => 799393
4903 *
4904 * Output:
4905 *
4906 * C*
4907 * hello world
4908 *
4909 * Raises an exception if the new process could not execute.
4910 */
4911
4912static VALUE
4913rb_f_spawn(int argc, VALUE *argv, VALUE _)
4914{
4915 rb_pid_t pid;
4916 char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' };
4917 VALUE execarg_obj, fail_str;
4918 struct rb_execarg *eargp;
4919
4920 execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
4921 eargp = rb_execarg_get(execarg_obj);
4922 fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
4923
4924 pid = rb_execarg_spawn(execarg_obj, errmsg, sizeof(errmsg));
4925
4926 if (pid == -1) {
4927 int err = errno;
4928 rb_exec_fail(eargp, err, errmsg);
4929 RB_GC_GUARD(execarg_obj);
4930 rb_syserr_fail_str(err, fail_str);
4931 }
4932#if defined(HAVE_WORKING_FORK) || defined(HAVE_SPAWNV)
4933 return PIDT2NUM(pid);
4934#else
4935 return Qnil;
4936#endif
4937}
4938
4939/*
4940 * call-seq:
4941 * sleep(secs = nil) -> slept_secs
4942 *
4943 * Suspends execution of the current thread for the number of seconds
4944 * specified by numeric argument +secs+, or forever if +secs+ is +nil+;
4945 * returns the integer number of seconds suspended (rounded).
4946 *
4947 * Time.new # => 2008-03-08 19:56:19 +0900
4948 * sleep 1.2 # => 1
4949 * Time.new # => 2008-03-08 19:56:20 +0900
4950 * sleep 1.9 # => 2
4951 * Time.new # => 2008-03-08 19:56:22 +0900
4952 *
4953 */
4954
4955static VALUE
4956rb_f_sleep(int argc, VALUE *argv, VALUE _)
4957{
4958 time_t beg = time(0);
4959 VALUE scheduler = rb_fiber_scheduler_current();
4960
4961 if (scheduler != Qnil) {
4962 rb_fiber_scheduler_kernel_sleepv(scheduler, argc, argv);
4963 }
4964 else {
4965 if (argc == 0 || (argc == 1 && NIL_P(argv[0]))) {
4967 }
4968 else {
4969 rb_check_arity(argc, 0, 1);
4971 }
4972 }
4973
4974 time_t end = time(0) - beg;
4975
4976 return TIMET2NUM(end);
4977}
4978
4979
4980#if (defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)) || defined(HAVE_GETPGID)
4981/*
4982 * call-seq:
4983 * Process.getpgrp -> integer
4984 *
4985 * Returns the process group ID for the current process:
4986 *
4987 * Process.getpgid(0) # => 25527
4988 * Process.getpgrp # => 25527
4989 *
4990 */
4991
4992static VALUE
4993proc_getpgrp(VALUE _)
4994{
4995 rb_pid_t pgrp;
4996
4997#if defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)
4998 pgrp = getpgrp();
4999 if (pgrp < 0) rb_sys_fail(0);
5000 return PIDT2NUM(pgrp);
5001#else /* defined(HAVE_GETPGID) */
5002 pgrp = getpgid(0);
5003 if (pgrp < 0) rb_sys_fail(0);
5004 return PIDT2NUM(pgrp);
5005#endif
5006}
5007#else
5008#define proc_getpgrp rb_f_notimplement
5009#endif
5010
5011
5012#if defined(HAVE_SETPGID) || (defined(HAVE_SETPGRP) && defined(SETPGRP_VOID))
5013/*
5014 * call-seq:
5015 * Process.setpgrp -> 0
5016 *
5017 * Equivalent to <tt>setpgid(0, 0)</tt>.
5018 *
5019 * Not available on all platforms.
5020 */
5021
5022static VALUE
5023proc_setpgrp(VALUE _)
5024{
5025 /* check for posix setpgid() first; this matches the posix */
5026 /* getpgrp() above. It appears that configure will set SETPGRP_VOID */
5027 /* even though setpgrp(0,0) would be preferred. The posix call avoids */
5028 /* this confusion. */
5029#ifdef HAVE_SETPGID
5030 if (setpgid(0,0) < 0) rb_sys_fail(0);
5031#elif defined(HAVE_SETPGRP) && defined(SETPGRP_VOID)
5032 if (setpgrp() < 0) rb_sys_fail(0);
5033#endif
5034 return INT2FIX(0);
5035}
5036#else
5037#define proc_setpgrp rb_f_notimplement
5038#endif
5039
5040
5041#if defined(HAVE_GETPGID)
5042/*
5043 * call-seq:
5044 * Process.getpgid(pid) -> integer
5045 *
5046 * Returns the process group ID for the given process ID +pid+:
5047 *
5048 * Process.getpgid(Process.ppid) # => 25527
5049 *
5050 * Not available on all platforms.
5051 */
5052
5053static VALUE
5054proc_getpgid(VALUE obj, VALUE pid)
5055{
5056 rb_pid_t i;
5057
5058 i = getpgid(NUM2PIDT(pid));
5059 if (i < 0) rb_sys_fail(0);
5060 return PIDT2NUM(i);
5061}
5062#else
5063#define proc_getpgid rb_f_notimplement
5064#endif
5065
5066
5067#ifdef HAVE_SETPGID
5068/*
5069 * call-seq:
5070 * Process.setpgid(pid, pgid) -> 0
5071 *
5072 * Sets the process group ID for the process given by process ID +pid+
5073 * to +pgid+.
5074 *
5075 * Not available on all platforms.
5076 */
5077
5078static VALUE
5079proc_setpgid(VALUE obj, VALUE pid, VALUE pgrp)
5080{
5081 rb_pid_t ipid, ipgrp;
5082
5083 ipid = NUM2PIDT(pid);
5084 ipgrp = NUM2PIDT(pgrp);
5085
5086 if (setpgid(ipid, ipgrp) < 0) rb_sys_fail(0);
5087 return INT2FIX(0);
5088}
5089#else
5090#define proc_setpgid rb_f_notimplement
5091#endif
5092
5093
5094#ifdef HAVE_GETSID
5095/*
5096 * call-seq:
5097 * Process.getsid(pid = nil) -> integer
5098 *
5099 * Returns the session ID of the given process ID +pid+,
5100 * or of the current process if not given:
5101 *
5102 * Process.getsid # => 27422
5103 * Process.getsid(0) # => 27422
5104 * Process.getsid(Process.pid()) # => 27422
5105 *
5106 * Not available on all platforms.
5107 */
5108static VALUE
5109proc_getsid(int argc, VALUE *argv, VALUE _)
5110{
5111 rb_pid_t sid;
5112 rb_pid_t pid = 0;
5113
5114 if (rb_check_arity(argc, 0, 1) == 1 && !NIL_P(argv[0]))
5115 pid = NUM2PIDT(argv[0]);
5116
5117 sid = getsid(pid);
5118 if (sid < 0) rb_sys_fail(0);
5119 return PIDT2NUM(sid);
5120}
5121#else
5122#define proc_getsid rb_f_notimplement
5123#endif
5124
5125
5126#if defined(HAVE_SETSID) || (defined(HAVE_SETPGRP) && defined(TIOCNOTTY))
5127#if !defined(HAVE_SETSID)
5128static rb_pid_t ruby_setsid(void);
5129#define setsid() ruby_setsid()
5130#endif
5131/*
5132 * call-seq:
5133 * Process.setsid -> integer
5134 *
5135 * Establishes the current process as a new session and process group leader,
5136 * with no controlling tty;
5137 * returns the session ID:
5138 *
5139 * Process.setsid # => 27422
5140 *
5141 * Not available on all platforms.
5142 */
5143
5144static VALUE
5145proc_setsid(VALUE _)
5146{
5147 rb_pid_t pid;
5148
5149 pid = setsid();
5150 if (pid < 0) rb_sys_fail(0);
5151 return PIDT2NUM(pid);
5152}
5153
5154#if !defined(HAVE_SETSID)
5155#define HAVE_SETSID 1
5156static rb_pid_t
5157ruby_setsid(void)
5158{
5159 rb_pid_t pid;
5160 int ret, fd;
5161
5162 pid = getpid();
5163#if defined(SETPGRP_VOID)
5164 ret = setpgrp();
5165 /* If `pid_t setpgrp(void)' is equivalent to setsid(),
5166 `ret' will be the same value as `pid', and following open() will fail.
5167 In Linux, `int setpgrp(void)' is equivalent to setpgid(0, 0). */
5168#else
5169 ret = setpgrp(0, pid);
5170#endif
5171 if (ret == -1) return -1;
5172
5173 if ((fd = rb_cloexec_open("/dev/tty", O_RDWR, 0)) >= 0) {
5174 rb_update_max_fd(fd);
5175 ioctl(fd, TIOCNOTTY, NULL);
5176 close(fd);
5177 }
5178 return pid;
5179}
5180#endif
5181#else
5182#define proc_setsid rb_f_notimplement
5183#endif
5184
5185
5186#ifdef HAVE_GETPRIORITY
5187/*
5188 * call-seq:
5189 * Process.getpriority(kind, id) -> integer
5190 *
5191 * Returns the scheduling priority for specified process, process group,
5192 * or user.
5193 *
5194 * Argument +kind+ is one of:
5195 *
5196 * - Process::PRIO_PROCESS: return priority for process.
5197 * - Process::PRIO_PGRP: return priority for process group.
5198 * - Process::PRIO_USER: return priority for user.
5199 *
5200 * Argument +id+ is the ID for the process, process group, or user;
5201 * zero specified the current ID for +kind+.
5202 *
5203 * Examples:
5204 *
5205 * Process.getpriority(Process::PRIO_USER, 0) # => 19
5206 * Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
5207 *
5208 * Not available on all platforms.
5209 */
5210
5211static VALUE
5212proc_getpriority(VALUE obj, VALUE which, VALUE who)
5213{
5214 int prio, iwhich, iwho;
5215
5216 iwhich = NUM2INT(which);
5217 iwho = NUM2INT(who);
5218
5219 errno = 0;
5220 prio = getpriority(iwhich, iwho);
5221 if (errno) rb_sys_fail(0);
5222 return INT2FIX(prio);
5223}
5224#else
5225#define proc_getpriority rb_f_notimplement
5226#endif
5227
5228
5229#ifdef HAVE_GETPRIORITY
5230/*
5231 * call-seq:
5232 * Process.setpriority(kind, integer, priority) -> 0
5233 *
5234 * See Process.getpriority.
5235 *
5236 * Examples:
5237 *
5238 * Process.setpriority(Process::PRIO_USER, 0, 19) # => 0
5239 * Process.setpriority(Process::PRIO_PROCESS, 0, 19) # => 0
5240 * Process.getpriority(Process::PRIO_USER, 0) # => 19
5241 * Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
5242 *
5243 * Not available on all platforms.
5244 */
5245
5246static VALUE
5247proc_setpriority(VALUE obj, VALUE which, VALUE who, VALUE prio)
5248{
5249 int iwhich, iwho, iprio;
5250
5251 iwhich = NUM2INT(which);
5252 iwho = NUM2INT(who);
5253 iprio = NUM2INT(prio);
5254
5255 if (setpriority(iwhich, iwho, iprio) < 0)
5256 rb_sys_fail(0);
5257 return INT2FIX(0);
5258}
5259#else
5260#define proc_setpriority rb_f_notimplement
5261#endif
5262
5263#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
5264static int
5265rlimit_resource_name2int(const char *name, long len, int casetype)
5266{
5267 int resource;
5268 const char *p;
5269#define RESCHECK(r) \
5270 do { \
5271 if (len == rb_strlen_lit(#r) && STRCASECMP(name, #r) == 0) { \
5272 resource = RLIMIT_##r; \
5273 goto found; \
5274 } \
5275 } while (0)
5276
5277 switch (TOUPPER(*name)) {
5278 case 'A':
5279#ifdef RLIMIT_AS
5280 RESCHECK(AS);
5281#endif
5282 break;
5283
5284 case 'C':
5285#ifdef RLIMIT_CORE
5286 RESCHECK(CORE);
5287#endif
5288#ifdef RLIMIT_CPU
5289 RESCHECK(CPU);
5290#endif
5291 break;
5292
5293 case 'D':
5294#ifdef RLIMIT_DATA
5295 RESCHECK(DATA);
5296#endif
5297 break;
5298
5299 case 'F':
5300#ifdef RLIMIT_FSIZE
5301 RESCHECK(FSIZE);
5302#endif
5303 break;
5304
5305 case 'M':
5306#ifdef RLIMIT_MEMLOCK
5307 RESCHECK(MEMLOCK);
5308#endif
5309#ifdef RLIMIT_MSGQUEUE
5310 RESCHECK(MSGQUEUE);
5311#endif
5312 break;
5313
5314 case 'N':
5315#ifdef RLIMIT_NOFILE
5316 RESCHECK(NOFILE);
5317#endif
5318#ifdef RLIMIT_NPROC
5319 RESCHECK(NPROC);
5320#endif
5321#ifdef RLIMIT_NPTS
5322 RESCHECK(NPTS);
5323#endif
5324#ifdef RLIMIT_NICE
5325 RESCHECK(NICE);
5326#endif
5327 break;
5328
5329 case 'R':
5330#ifdef RLIMIT_RSS
5331 RESCHECK(RSS);
5332#endif
5333#ifdef RLIMIT_RTPRIO
5334 RESCHECK(RTPRIO);
5335#endif
5336#ifdef RLIMIT_RTTIME
5337 RESCHECK(RTTIME);
5338#endif
5339 break;
5340
5341 case 'S':
5342#ifdef RLIMIT_STACK
5343 RESCHECK(STACK);
5344#endif
5345#ifdef RLIMIT_SBSIZE
5346 RESCHECK(SBSIZE);
5347#endif
5348#ifdef RLIMIT_SIGPENDING
5349 RESCHECK(SIGPENDING);
5350#endif
5351 break;
5352 }
5353 return -1;
5354
5355 found:
5356 switch (casetype) {
5357 case 0:
5358 for (p = name; *p; p++)
5359 if (!ISUPPER(*p))
5360 return -1;
5361 break;
5362
5363 case 1:
5364 for (p = name; *p; p++)
5365 if (!ISLOWER(*p))
5366 return -1;
5367 break;
5368
5369 default:
5370 rb_bug("unexpected casetype");
5371 }
5372 return resource;
5373#undef RESCHECK
5374}
5375
5376static int
5377rlimit_type_by_hname(const char *name, long len)
5378{
5379 return rlimit_resource_name2int(name, len, 0);
5380}
5381
5382static int
5383rlimit_type_by_lname(const char *name, long len)
5384{
5385 return rlimit_resource_name2int(name, len, 1);
5386}
5387
5388static int
5389rlimit_type_by_sym(VALUE key)
5390{
5391 VALUE name = rb_sym2str(key);
5392 const char *rname = RSTRING_PTR(name);
5393 long len = RSTRING_LEN(name);
5394 int rtype = -1;
5395 static const char prefix[] = "rlimit_";
5396 enum {prefix_len = sizeof(prefix)-1};
5397
5398 if (len > prefix_len && strncmp(prefix, rname, prefix_len) == 0) {
5399 rtype = rlimit_type_by_lname(rname + prefix_len, len - prefix_len);
5400 }
5401
5402 RB_GC_GUARD(key);
5403 return rtype;
5404}
5405
5406static int
5407rlimit_resource_type(VALUE rtype)
5408{
5409 const char *name;
5410 long len;
5411 VALUE v;
5412 int r;
5413
5414 switch (TYPE(rtype)) {
5415 case T_SYMBOL:
5416 v = rb_sym2str(rtype);
5417 name = RSTRING_PTR(v);
5418 len = RSTRING_LEN(v);
5419 break;
5420
5421 default:
5422 v = rb_check_string_type(rtype);
5423 if (!NIL_P(v)) {
5424 rtype = v;
5425 case T_STRING:
5426 name = StringValueCStr(rtype);
5427 len = RSTRING_LEN(rtype);
5428 break;
5429 }
5430 /* fall through */
5431
5432 case T_FIXNUM:
5433 case T_BIGNUM:
5434 return NUM2INT(rtype);
5435 }
5436
5437 r = rlimit_type_by_hname(name, len);
5438 if (r != -1)
5439 return r;
5440
5441 rb_raise(rb_eArgError, "invalid resource name: % "PRIsVALUE, rtype);
5442
5444}
5445
5446static rlim_t
5447rlimit_resource_value(VALUE rval)
5448{
5449 const char *name;
5450 VALUE v;
5451
5452 switch (TYPE(rval)) {
5453 case T_SYMBOL:
5454 v = rb_sym2str(rval);
5455 name = RSTRING_PTR(v);
5456 break;
5457
5458 default:
5459 v = rb_check_string_type(rval);
5460 if (!NIL_P(v)) {
5461 rval = v;
5462 case T_STRING:
5463 name = StringValueCStr(rval);
5464 break;
5465 }
5466 /* fall through */
5467
5468 case T_FIXNUM:
5469 case T_BIGNUM:
5470 return NUM2RLIM(rval);
5471 }
5472
5473#ifdef RLIM_INFINITY
5474 if (strcmp(name, "INFINITY") == 0) return RLIM_INFINITY;
5475#endif
5476#ifdef RLIM_SAVED_MAX
5477 if (strcmp(name, "SAVED_MAX") == 0) return RLIM_SAVED_MAX;
5478#endif
5479#ifdef RLIM_SAVED_CUR
5480 if (strcmp(name, "SAVED_CUR") == 0) return RLIM_SAVED_CUR;
5481#endif
5482 rb_raise(rb_eArgError, "invalid resource value: %"PRIsVALUE, rval);
5483
5484 UNREACHABLE_RETURN((rlim_t)-1);
5485}
5486#endif
5487
5488#if defined(HAVE_GETRLIMIT) && defined(RLIM2NUM)
5489/*
5490 * call-seq:
5491 * Process.getrlimit(resource) -> [cur_limit, max_limit]
5492 *
5493 * Returns a 2-element array of the current (soft) limit
5494 * and maximum (hard) limit for the given +resource+.
5495 *
5496 * Argument +resource+ specifies the resource whose limits are to be returned;
5497 * see Process.setrlimit.
5498 *
5499 * Each of the returned values +cur_limit+ and +max_limit+ is an integer;
5500 * see Process.setrlimit.
5501 *
5502 * Example:
5503 *
5504 * Process.getrlimit(:CORE) # => [0, 18446744073709551615]
5505 *
5506 * See Process.setrlimit.
5507 *
5508 * Not available on all platforms.
5509 */
5510
5511static VALUE
5512proc_getrlimit(VALUE obj, VALUE resource)
5513{
5514 struct rlimit rlim;
5515
5516 if (getrlimit(rlimit_resource_type(resource), &rlim) < 0) {
5517 rb_sys_fail("getrlimit");
5518 }
5519 return rb_assoc_new(RLIM2NUM(rlim.rlim_cur), RLIM2NUM(rlim.rlim_max));
5520}
5521#else
5522#define proc_getrlimit rb_f_notimplement
5523#endif
5524
5525#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
5526/*
5527 * call-seq:
5528 * Process.setrlimit(resource, cur_limit, max_limit = cur_limit) -> nil
5529 *
5530 * Sets limits for the current process for the given +resource+
5531 * to +cur_limit+ (soft limit) and +max_limit+ (hard limit);
5532 * returns +nil+.
5533 *
5534 * Argument +resource+ specifies the resource whose limits are to be set;
5535 * the argument may be given as a symbol, as a string, or as a constant
5536 * beginning with <tt>Process::RLIMIT_</tt>
5537 * (e.g., +:CORE+, <tt>'CORE'</tt>, or <tt>Process::RLIMIT_CORE</tt>.
5538 *
5539 * The resources available and supported are system-dependent,
5540 * and may include (here expressed as symbols):
5541 *
5542 * - +:AS+: Total available memory (bytes) (SUSv3, NetBSD, FreeBSD, OpenBSD except 4.4BSD-Lite).
5543 * - +:CORE+: Core size (bytes) (SUSv3).
5544 * - +:CPU+: CPU time (seconds) (SUSv3).
5545 * - +:DATA+: Data segment (bytes) (SUSv3).
5546 * - +:FSIZE+: File size (bytes) (SUSv3).
5547 * - +:MEMLOCK+: Total size for mlock(2) (bytes) (4.4BSD, GNU/Linux).
5548 * - +:MSGQUEUE+: Allocation for POSIX message queues (bytes) (GNU/Linux).
5549 * - +:NICE+: Ceiling on process's nice(2) value (number) (GNU/Linux).
5550 * - +:NOFILE+: File descriptors (number) (SUSv3).
5551 * - +:NPROC+: Number of processes for the user (number) (4.4BSD, GNU/Linux).
5552 * - +:NPTS+: Number of pseudo terminals (number) (FreeBSD).
5553 * - +:RSS+: Resident memory size (bytes) (4.2BSD, GNU/Linux).
5554 * - +:RTPRIO+: Ceiling on the process's real-time priority (number) (GNU/Linux).
5555 * - +:RTTIME+: CPU time for real-time process (us) (GNU/Linux).
5556 * - +:SBSIZE+: All socket buffers (bytes) (NetBSD, FreeBSD).
5557 * - +:SIGPENDING+: Number of queued signals allowed (signals) (GNU/Linux).
5558 * - +:STACK+: Stack size (bytes) (SUSv3).
5559 *
5560 * Arguments +cur_limit+ and +max_limit+ may be:
5561 *
5562 * - Integers (+max_limit+ should not be smaller than +cur_limit+).
5563 * - Symbol +:SAVED_MAX+, string <tt>'SAVED_MAX'</tt>,
5564 * or constant <tt>Process::RLIM_SAVED_MAX</tt>: saved maximum limit.
5565 * - Symbol +:SAVED_CUR+, string <tt>'SAVED_CUR'</tt>,
5566 * or constant <tt>Process::RLIM_SAVED_CUR</tt>: saved current limit.
5567 * - Symbol +:INFINITY+, string <tt>'INFINITY'</tt>,
5568 * or constant <tt>Process::RLIM_INFINITY</tt>: no limit on resource.
5569 *
5570 * This example raises the soft limit of core size to
5571 * the hard limit to try to make core dump possible:
5572 *
5573 * Process.setrlimit(:CORE, Process.getrlimit(:CORE)[1])
5574 *
5575 * Not available on all platforms.
5576 */
5577
5578static VALUE
5579proc_setrlimit(int argc, VALUE *argv, VALUE obj)
5580{
5581 VALUE resource, rlim_cur, rlim_max;
5582 struct rlimit rlim;
5583
5584 rb_check_arity(argc, 2, 3);
5585 resource = argv[0];
5586 rlim_cur = argv[1];
5587 if (argc < 3 || NIL_P(rlim_max = argv[2]))
5588 rlim_max = rlim_cur;
5589
5590 rlim.rlim_cur = rlimit_resource_value(rlim_cur);
5591 rlim.rlim_max = rlimit_resource_value(rlim_max);
5592
5593 if (setrlimit(rlimit_resource_type(resource), &rlim) < 0) {
5594 rb_sys_fail("setrlimit");
5595 }
5596 return Qnil;
5597}
5598#else
5599#define proc_setrlimit rb_f_notimplement
5600#endif
5601
5602static int under_uid_switch = 0;
5603static void
5604check_uid_switch(void)
5605{
5606 if (under_uid_switch) {
5607 rb_raise(rb_eRuntimeError, "can't handle UID while evaluating block given to Process::UID.switch method");
5608 }
5609}
5610
5611static int under_gid_switch = 0;
5612static void
5613check_gid_switch(void)
5614{
5615 if (under_gid_switch) {
5616 rb_raise(rb_eRuntimeError, "can't handle GID while evaluating block given to Process::UID.switch method");
5617 }
5618}
5619
5620
5621#if defined(HAVE_PWD_H)
5622static inline bool
5623login_not_found(int err)
5624{
5625 return (err == ENOTTY || err == ENXIO || err == ENOENT);
5626}
5627
5633VALUE
5634rb_getlogin(void)
5635{
5636# if !defined(USE_GETLOGIN_R) && !defined(USE_GETLOGIN)
5637 return Qnil;
5638# else
5639 char MAYBE_UNUSED(*login) = NULL;
5640
5641# ifdef USE_GETLOGIN_R
5642
5643# if defined(__FreeBSD__)
5644 typedef int getlogin_r_size_t;
5645# else
5646 typedef size_t getlogin_r_size_t;
5647# endif
5648
5649 long loginsize = GETLOGIN_R_SIZE_INIT; /* maybe -1 */
5650
5651 if (loginsize < 0)
5652 loginsize = GETLOGIN_R_SIZE_DEFAULT;
5653
5654 VALUE maybe_result = rb_str_buf_new(loginsize);
5655
5656 login = RSTRING_PTR(maybe_result);
5657 loginsize = rb_str_capacity(maybe_result);
5658 rb_str_set_len(maybe_result, loginsize);
5659
5660 int gle;
5661 while ((gle = getlogin_r(login, (getlogin_r_size_t)loginsize)) != 0) {
5662 if (login_not_found(gle)) {
5663 rb_str_resize(maybe_result, 0);
5664 return Qnil;
5665 }
5666
5667 if (gle != ERANGE || loginsize >= GETLOGIN_R_SIZE_LIMIT) {
5668 rb_str_resize(maybe_result, 0);
5669 rb_syserr_fail(gle, "getlogin_r");
5670 }
5671
5672 rb_str_modify_expand(maybe_result, loginsize);
5673 login = RSTRING_PTR(maybe_result);
5674 loginsize = rb_str_capacity(maybe_result);
5675 }
5676
5677 if (login == NULL) {
5678 rb_str_resize(maybe_result, 0);
5679 return Qnil;
5680 }
5681
5682 rb_str_set_len(maybe_result, strlen(login));
5683 return maybe_result;
5684
5685# elif defined(USE_GETLOGIN)
5686
5687 errno = 0;
5688 login = getlogin();
5689 int err = errno;
5690 if (err) {
5691 if (login_not_found(err)) {
5692 return Qnil;
5693 }
5694 rb_syserr_fail(err, "getlogin");
5695 }
5696
5697 return login ? rb_str_new_cstr(login) : Qnil;
5698# endif
5699
5700#endif
5701}
5702
5703/* avoid treating as errors errno values that indicate "not found" */
5704static inline bool
5705pwd_not_found(int err)
5706{
5707 switch (err) {
5708 case 0:
5709 case ENOENT:
5710 case ESRCH:
5711 case EBADF:
5712 case EPERM:
5713 return true;
5714 default:
5715 return false;
5716 }
5717}
5718
5719# if defined(USE_GETPWNAM_R)
5720struct getpwnam_r_args {
5721 const char *login;
5722 char *buf;
5723 size_t bufsize;
5724 struct passwd *result;
5725 struct passwd pwstore;
5726};
5727
5728# define GETPWNAM_R_ARGS(login_, buf_, bufsize_) (struct getpwnam_r_args) \
5729 {.login = login_, .buf = buf_, .bufsize = bufsize_, .result = NULL}
5730
5731static void *
5732nogvl_getpwnam_r(void *args)
5733{
5734 struct getpwnam_r_args *arg = args;
5735 return (void *)(VALUE)getpwnam_r(arg->login, &arg->pwstore, arg->buf, arg->bufsize, &arg->result);
5736}
5737# endif
5738
5739VALUE
5740rb_getpwdirnam_for_login(VALUE login_name)
5741{
5742#if !defined(USE_GETPWNAM_R) && !defined(USE_GETPWNAM)
5743 return Qnil;
5744#else
5745
5746 if (NIL_P(login_name)) {
5747 /* nothing to do; no name with which to query the password database */
5748 return Qnil;
5749 }
5750
5751 const char *login = RSTRING_PTR(login_name);
5752
5753
5754# ifdef USE_GETPWNAM_R
5755
5756 char *bufnm;
5757 long bufsizenm = GETPW_R_SIZE_INIT; /* maybe -1 */
5758
5759 if (bufsizenm < 0)
5760 bufsizenm = GETPW_R_SIZE_DEFAULT;
5761
5762 VALUE getpwnm_tmp = rb_str_tmp_new(bufsizenm);
5763
5764 bufnm = RSTRING_PTR(getpwnm_tmp);
5765 bufsizenm = rb_str_capacity(getpwnm_tmp);
5766 rb_str_set_len(getpwnm_tmp, bufsizenm);
5767 struct getpwnam_r_args args = GETPWNAM_R_ARGS(login, bufnm, (size_t)bufsizenm);
5768
5769 int enm;
5770 while ((enm = IO_WITHOUT_GVL_INT(nogvl_getpwnam_r, &args)) != 0) {
5771 if (pwd_not_found(enm)) {
5772 rb_str_resize(getpwnm_tmp, 0);
5773 return Qnil;
5774 }
5775
5776 if (enm != ERANGE || args.bufsize >= GETPW_R_SIZE_LIMIT) {
5777 rb_str_resize(getpwnm_tmp, 0);
5778 rb_syserr_fail(enm, "getpwnam_r");
5779 }
5780
5781 rb_str_modify_expand(getpwnm_tmp, (long)args.bufsize);
5782 args.buf = RSTRING_PTR(getpwnm_tmp);
5783 args.bufsize = (size_t)rb_str_capacity(getpwnm_tmp);
5784 }
5785
5786 if (args.result == NULL) {
5787 /* no record in the password database for the login name */
5788 rb_str_resize(getpwnm_tmp, 0);
5789 return Qnil;
5790 }
5791
5792 /* found it */
5793 VALUE result = rb_str_new_cstr(args.result->pw_dir);
5794 rb_str_resize(getpwnm_tmp, 0);
5795 return result;
5796
5797# elif defined(USE_GETPWNAM)
5798
5799 struct passwd *pwptr;
5800 errno = 0;
5801 if (!(pwptr = getpwnam(login))) {
5802 int err = errno;
5803
5804 if (pwd_not_found(err)) {
5805 return Qnil;
5806 }
5807
5808 rb_syserr_fail(err, "getpwnam");
5809 }
5810
5811 /* found it */
5812 return rb_str_new_cstr(pwptr->pw_dir);
5813# endif
5814
5815#endif
5816}
5817
5818# if defined(USE_GETPWUID_R)
5819struct getpwuid_r_args {
5820 uid_t uid;
5821 char *buf;
5822 size_t bufsize;
5823 struct passwd *result;
5824 struct passwd pwstore;
5825};
5826
5827# define GETPWUID_R_ARGS(uid_, buf_, bufsize_) (struct getpwuid_r_args) \
5828 {.uid = uid_, .buf = buf_, .bufsize = bufsize_, .result = NULL}
5829
5830static void *
5831nogvl_getpwuid_r(void *args)
5832{
5833 struct getpwuid_r_args *arg = args;
5834 return (void *)(VALUE)getpwuid_r(arg->uid, &arg->pwstore, arg->buf, arg->bufsize, &arg->result);
5835}
5836# endif
5837
5841VALUE
5842rb_getpwdiruid(void)
5843{
5844# if !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID)
5845 /* Should never happen... </famous-last-words> */
5846 return Qnil;
5847# else
5848 uid_t ruid = getuid();
5849
5850# ifdef USE_GETPWUID_R
5851
5852 char *bufid;
5853 long bufsizeid = GETPW_R_SIZE_INIT; /* maybe -1 */
5854
5855 if (bufsizeid < 0)
5856 bufsizeid = GETPW_R_SIZE_DEFAULT;
5857
5858 VALUE getpwid_tmp = rb_str_tmp_new(bufsizeid);
5859
5860 bufid = RSTRING_PTR(getpwid_tmp);
5861 bufsizeid = rb_str_capacity(getpwid_tmp);
5862 rb_str_set_len(getpwid_tmp, bufsizeid);
5863 struct getpwuid_r_args args = GETPWUID_R_ARGS(ruid, bufid, (size_t)bufsizeid);
5864
5865 int eid;
5866 while ((eid = IO_WITHOUT_GVL_INT(nogvl_getpwuid_r, &args)) != 0) {
5867 if (pwd_not_found(eid)) {
5868 rb_str_resize(getpwid_tmp, 0);
5869 return Qnil;
5870 }
5871
5872 if (eid != ERANGE || args.bufsize >= GETPW_R_SIZE_LIMIT) {
5873 rb_str_resize(getpwid_tmp, 0);
5874 rb_syserr_fail(eid, "getpwuid_r");
5875 }
5876
5877 rb_str_modify_expand(getpwid_tmp, (long)args.bufsize);
5878 args.buf = RSTRING_PTR(getpwid_tmp);
5879 args.bufsize = (size_t)rb_str_capacity(getpwid_tmp);
5880 }
5881
5882 if (args.result == NULL) {
5883 /* no record in the password database for the uid */
5884 rb_str_resize(getpwid_tmp, 0);
5885 return Qnil;
5886 }
5887
5888 /* found it */
5889 VALUE result = rb_str_new_cstr(args.result->pw_dir);
5890 rb_str_resize(getpwid_tmp, 0);
5891 return result;
5892
5893# elif defined(USE_GETPWUID)
5894
5895 struct passwd *pwptr;
5896 errno = 0;
5897 if (!(pwptr = getpwuid(ruid))) {
5898 int err = errno;
5899
5900 if (pwd_not_found(err)) {
5901 return Qnil;
5902 }
5903
5904 rb_syserr_fail(err, "getpwuid");
5905 }
5906
5907 /* found it */
5908 return rb_str_new_cstr(pwptr->pw_dir);
5909# endif
5910
5911#endif /* !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID) */
5912}
5913#endif /* HAVE_PWD_H */
5914
5915
5916/*********************************************************************
5917 * Document-class: Process::Sys
5918 *
5919 * The Process::Sys module contains UID and GID
5920 * functions which provide direct bindings to the system calls of the
5921 * same names instead of the more-portable versions of the same
5922 * functionality found in the +Process+,
5923 * Process::UID, and Process::GID modules.
5924 */
5925
5926#if defined(HAVE_PWD_H)
5927static rb_uid_t
5928obj2uid(VALUE id
5929# ifdef USE_GETPWNAM_R
5930 , VALUE *getpw_tmp
5931# endif
5932 )
5933{
5934 rb_uid_t uid;
5935 VALUE tmp;
5936
5937 if (FIXNUM_P(id) || NIL_P(tmp = rb_check_string_type(id))) {
5938 uid = NUM2UIDT(id);
5939 }
5940 else {
5941 const char *usrname = StringValueCStr(id);
5942 struct passwd *pwptr;
5943#ifdef USE_GETPWNAM_R
5944 char *getpw_buf;
5945 long getpw_buf_len;
5946 int e;
5947 if (!*getpw_tmp) {
5948 getpw_buf_len = GETPW_R_SIZE_INIT;
5949 if (getpw_buf_len < 0) getpw_buf_len = GETPW_R_SIZE_DEFAULT;
5950 *getpw_tmp = rb_str_tmp_new(getpw_buf_len);
5951 }
5952 getpw_buf = RSTRING_PTR(*getpw_tmp);
5953 getpw_buf_len = rb_str_capacity(*getpw_tmp);
5954 rb_str_set_len(*getpw_tmp, getpw_buf_len);
5955 errno = 0;
5956 struct getpwnam_r_args args = GETPWNAM_R_ARGS((char *)usrname, getpw_buf, (size_t)getpw_buf_len);
5957
5958 while ((e = IO_WITHOUT_GVL_INT(nogvl_getpwnam_r, &args)) != 0) {
5959 if (e != ERANGE || args.bufsize >= GETPW_R_SIZE_LIMIT) {
5960 rb_str_resize(*getpw_tmp, 0);
5961 rb_syserr_fail(e, "getpwnam_r");
5962 }
5963 rb_str_modify_expand(*getpw_tmp, (long)args.bufsize);
5964 args.buf = RSTRING_PTR(*getpw_tmp);
5965 args.bufsize = (size_t)rb_str_capacity(*getpw_tmp);
5966 }
5967 pwptr = args.result;
5968#else
5969 pwptr = getpwnam(usrname);
5970#endif
5971 if (!pwptr) {
5972#ifndef USE_GETPWNAM_R
5973 endpwent();
5974#endif
5975 rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, id);
5976 }
5977 uid = pwptr->pw_uid;
5978#ifndef USE_GETPWNAM_R
5979 endpwent();
5980#endif
5981 }
5982 return uid;
5983}
5984
5985# ifdef p_uid_from_name
5986/*
5987 * call-seq:
5988 * Process::UID.from_name(name) -> uid
5989 *
5990 * Get the user ID by the _name_.
5991 * If the user is not found, +ArgumentError+ will be raised.
5992 *
5993 * Process::UID.from_name("root") #=> 0
5994 * Process::UID.from_name("nosuchuser") #=> can't find user for nosuchuser (ArgumentError)
5995 */
5996
5997static VALUE
5998p_uid_from_name(VALUE self, VALUE id)
5999{
6000 return UIDT2NUM(OBJ2UID(id));
6001}
6002# endif
6003#endif
6004
6005#if defined(HAVE_GRP_H)
6006# if defined(USE_GETGRNAM_R)
6007struct getgrnam_r_args {
6008 const char *name;
6009 char *buf;
6010 size_t bufsize;
6011 struct group *result;
6012 struct group grp;
6013};
6014
6015# define GETGRNAM_R_ARGS(name_, buf_, bufsize_) (struct getgrnam_r_args) \
6016 {.name = name_, .buf = buf_, .bufsize = bufsize_, .result = NULL}
6017
6018static void *
6019nogvl_getgrnam_r(void *args)
6020{
6021 struct getgrnam_r_args *arg = args;
6022 return (void *)(VALUE)getgrnam_r(arg->name, &arg->grp, arg->buf, arg->bufsize, &arg->result);
6023}
6024# endif
6025
6026static rb_gid_t
6027obj2gid(VALUE id
6028# ifdef USE_GETGRNAM_R
6029 , VALUE *getgr_tmp
6030# endif
6031 )
6032{
6033 rb_gid_t gid;
6034 VALUE tmp;
6035
6036 if (FIXNUM_P(id) || NIL_P(tmp = rb_check_string_type(id))) {
6037 gid = NUM2GIDT(id);
6038 }
6039 else {
6040 const char *grpname = StringValueCStr(id);
6041 struct group *grptr;
6042#ifdef USE_GETGRNAM_R
6043 char *getgr_buf;
6044 long getgr_buf_len;
6045 int e;
6046 if (!*getgr_tmp) {
6047 getgr_buf_len = GETGR_R_SIZE_INIT;
6048 if (getgr_buf_len < 0) getgr_buf_len = GETGR_R_SIZE_DEFAULT;
6049 *getgr_tmp = rb_str_tmp_new(getgr_buf_len);
6050 }
6051 getgr_buf = RSTRING_PTR(*getgr_tmp);
6052 getgr_buf_len = rb_str_capacity(*getgr_tmp);
6053 rb_str_set_len(*getgr_tmp, getgr_buf_len);
6054 errno = 0;
6055 struct getgrnam_r_args args = GETGRNAM_R_ARGS(grpname, getgr_buf, (size_t)getgr_buf_len);
6056
6057 while ((e = IO_WITHOUT_GVL_INT(nogvl_getgrnam_r, &args)) != 0) {
6058 if (e != ERANGE || args.bufsize >= GETGR_R_SIZE_LIMIT) {
6059 rb_str_resize(*getgr_tmp, 0);
6060 rb_syserr_fail(e, "getgrnam_r");
6061 }
6062 rb_str_modify_expand(*getgr_tmp, (long)args.bufsize);
6063 args.buf = RSTRING_PTR(*getgr_tmp);
6064 args.bufsize = (size_t)rb_str_capacity(*getgr_tmp);
6065 }
6066 grptr = args.result;
6067#elif defined(HAVE_GETGRNAM)
6068 grptr = getgrnam(grpname);
6069#else
6070 grptr = NULL;
6071#endif
6072 if (!grptr) {
6073#if !defined(USE_GETGRNAM_R) && defined(HAVE_ENDGRENT)
6074 endgrent();
6075#endif
6076 rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, id);
6077 }
6078 gid = grptr->gr_gid;
6079#if !defined(USE_GETGRNAM_R) && defined(HAVE_ENDGRENT)
6080 endgrent();
6081#endif
6082 }
6083 return gid;
6084}
6085
6086# ifdef p_gid_from_name
6087/*
6088 * call-seq:
6089 * Process::GID.from_name(name) -> gid
6090 *
6091 * Get the group ID by the _name_.
6092 * If the group is not found, +ArgumentError+ will be raised.
6093 *
6094 * Process::GID.from_name("wheel") #=> 0
6095 * Process::GID.from_name("nosuchgroup") #=> can't find group for nosuchgroup (ArgumentError)
6096 */
6097
6098static VALUE
6099p_gid_from_name(VALUE self, VALUE id)
6100{
6101 return GIDT2NUM(OBJ2GID(id));
6102}
6103# endif
6104#endif
6105
6106#if defined HAVE_SETUID
6107/*
6108 * call-seq:
6109 * Process::Sys.setuid(user) -> nil
6110 *
6111 * Set the user ID of the current process to _user_. Not
6112 * available on all platforms.
6113 *
6114 */
6115
6116static VALUE
6117p_sys_setuid(VALUE obj, VALUE id)
6118{
6119 check_uid_switch();
6120 if (setuid(OBJ2UID(id)) != 0) rb_sys_fail(0);
6121 return Qnil;
6122}
6123#else
6124#define p_sys_setuid rb_f_notimplement
6125#endif
6126
6127
6128#if defined HAVE_SETRUID
6129/*
6130 * call-seq:
6131 * Process::Sys.setruid(user) -> nil
6132 *
6133 * Set the real user ID of the calling process to _user_.
6134 * Not available on all platforms.
6135 *
6136 */
6137
6138static VALUE
6139p_sys_setruid(VALUE obj, VALUE id)
6140{
6141 check_uid_switch();
6142 if (setruid(OBJ2UID(id)) != 0) rb_sys_fail(0);
6143 return Qnil;
6144}
6145#else
6146#define p_sys_setruid rb_f_notimplement
6147#endif
6148
6149
6150#if defined HAVE_SETEUID
6151/*
6152 * call-seq:
6153 * Process::Sys.seteuid(user) -> nil
6154 *
6155 * Set the effective user ID of the calling process to
6156 * _user_. Not available on all platforms.
6157 *
6158 */
6159
6160static VALUE
6161p_sys_seteuid(VALUE obj, VALUE id)
6162{
6163 check_uid_switch();
6164 if (seteuid(OBJ2UID(id)) != 0) rb_sys_fail(0);
6165 return Qnil;
6166}
6167#else
6168#define p_sys_seteuid rb_f_notimplement
6169#endif
6170
6171
6172#if defined HAVE_SETREUID
6173/*
6174 * call-seq:
6175 * Process::Sys.setreuid(rid, eid) -> nil
6176 *
6177 * Sets the (user) real and/or effective user IDs of the current
6178 * process to _rid_ and _eid_, respectively. A value of
6179 * <code>-1</code> for either means to leave that ID unchanged. Not
6180 * available on all platforms.
6181 *
6182 */
6183
6184static VALUE
6185p_sys_setreuid(VALUE obj, VALUE rid, VALUE eid)
6186{
6187 rb_uid_t ruid, euid;
6188 PREPARE_GETPWNAM;
6189 check_uid_switch();
6190 ruid = OBJ2UID1(rid);
6191 euid = OBJ2UID1(eid);
6192 FINISH_GETPWNAM;
6193 if (setreuid(ruid, euid) != 0) rb_sys_fail(0);
6194 return Qnil;
6195}
6196#else
6197#define p_sys_setreuid rb_f_notimplement
6198#endif
6199
6200
6201#if defined HAVE_SETRESUID
6202/*
6203 * call-seq:
6204 * Process::Sys.setresuid(rid, eid, sid) -> nil
6205 *
6206 * Sets the (user) real, effective, and saved user IDs of the
6207 * current process to _rid_, _eid_, and _sid_ respectively. A
6208 * value of <code>-1</code> for any value means to
6209 * leave that ID unchanged. Not available on all platforms.
6210 *
6211 */
6212
6213static VALUE
6214p_sys_setresuid(VALUE obj, VALUE rid, VALUE eid, VALUE sid)
6215{
6216 rb_uid_t ruid, euid, suid;
6217 PREPARE_GETPWNAM;
6218 check_uid_switch();
6219 ruid = OBJ2UID1(rid);
6220 euid = OBJ2UID1(eid);
6221 suid = OBJ2UID1(sid);
6222 FINISH_GETPWNAM;
6223 if (setresuid(ruid, euid, suid) != 0) rb_sys_fail(0);
6224 return Qnil;
6225}
6226#else
6227#define p_sys_setresuid rb_f_notimplement
6228#endif
6229
6230
6231/*
6232 * call-seq:
6233 * Process.uid -> integer
6234 * Process::UID.rid -> integer
6235 * Process::Sys.getuid -> integer
6236 *
6237 * Returns the (real) user ID of the current process.
6238 *
6239 * Process.uid # => 1000
6240 *
6241 */
6242
6243static VALUE
6244proc_getuid(VALUE obj)
6245{
6246 rb_uid_t uid = getuid();
6247 return UIDT2NUM(uid);
6248}
6249
6250
6251#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRUID) || defined(HAVE_SETUID)
6252/*
6253 * call-seq:
6254 * Process.uid = new_uid -> new_uid
6255 *
6256 * Sets the (user) user ID for the current process to +new_uid+:
6257 *
6258 * Process.uid = 1000 # => 1000
6259 *
6260 * Not available on all platforms.
6261 */
6262
6263static VALUE
6264proc_setuid(VALUE obj, VALUE id)
6265{
6266 rb_uid_t uid;
6267
6268 check_uid_switch();
6269
6270 uid = OBJ2UID(id);
6271#if defined(HAVE_SETRESUID)
6272 if (setresuid(uid, -1, -1) < 0) rb_sys_fail(0);
6273#elif defined HAVE_SETREUID
6274 if (setreuid(uid, -1) < 0) rb_sys_fail(0);
6275#elif defined HAVE_SETRUID
6276 if (setruid(uid) < 0) rb_sys_fail(0);
6277#elif defined HAVE_SETUID
6278 {
6279 if (geteuid() == uid) {
6280 if (setuid(uid) < 0) rb_sys_fail(0);
6281 }
6282 else {
6284 }
6285 }
6286#endif
6287 return id;
6288}
6289#else
6290#define proc_setuid rb_f_notimplement
6291#endif
6292
6293
6294/********************************************************************
6295 *
6296 * Document-class: Process::UID
6297 *
6298 * The Process::UID module contains a collection of
6299 * module functions which can be used to portably get, set, and
6300 * switch the current process's real, effective, and saved user IDs.
6301 *
6302 */
6303
6304static rb_uid_t SAVED_USER_ID = -1;
6305
6306#ifdef BROKEN_SETREUID
6307int
6308setreuid(rb_uid_t ruid, rb_uid_t euid)
6309{
6310 if (ruid != (rb_uid_t)-1 && ruid != getuid()) {
6311 if (euid == (rb_uid_t)-1) euid = geteuid();
6312 if (setuid(ruid) < 0) return -1;
6313 }
6314 if (euid != (rb_uid_t)-1 && euid != geteuid()) {
6315 if (seteuid(euid) < 0) return -1;
6316 }
6317 return 0;
6318}
6319#endif
6320
6321/*
6322 * call-seq:
6323 * Process::UID.change_privilege(user) -> integer
6324 *
6325 * Change the current process's real and effective user ID to that
6326 * specified by _user_. Returns the new user ID. Not
6327 * available on all platforms.
6328 *
6329 * [Process.uid, Process.euid] #=> [0, 0]
6330 * Process::UID.change_privilege(31) #=> 31
6331 * [Process.uid, Process.euid] #=> [31, 31]
6332 */
6333
6334static VALUE
6335p_uid_change_privilege(VALUE obj, VALUE id)
6336{
6337 rb_uid_t uid;
6338
6339 check_uid_switch();
6340
6341 uid = OBJ2UID(id);
6342
6343 if (geteuid() == 0) { /* root-user */
6344#if defined(HAVE_SETRESUID)
6345 if (setresuid(uid, uid, uid) < 0) rb_sys_fail(0);
6346 SAVED_USER_ID = uid;
6347#elif defined(HAVE_SETUID)
6348 if (setuid(uid) < 0) rb_sys_fail(0);
6349 SAVED_USER_ID = uid;
6350#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
6351 if (getuid() == uid) {
6352 if (SAVED_USER_ID == uid) {
6353 if (setreuid(-1, uid) < 0) rb_sys_fail(0);
6354 }
6355 else {
6356 if (uid == 0) { /* (r,e,s) == (root, root, x) */
6357 if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
6358 if (setreuid(SAVED_USER_ID, 0) < 0) rb_sys_fail(0);
6359 SAVED_USER_ID = 0; /* (r,e,s) == (x, root, root) */
6360 if (setreuid(uid, uid) < 0) rb_sys_fail(0);
6361 SAVED_USER_ID = uid;
6362 }
6363 else {
6364 if (setreuid(0, -1) < 0) rb_sys_fail(0);
6365 SAVED_USER_ID = 0;
6366 if (setreuid(uid, uid) < 0) rb_sys_fail(0);
6367 SAVED_USER_ID = uid;
6368 }
6369 }
6370 }
6371 else {
6372 if (setreuid(uid, uid) < 0) rb_sys_fail(0);
6373 SAVED_USER_ID = uid;
6374 }
6375#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
6376 if (getuid() == uid) {
6377 if (SAVED_USER_ID == uid) {
6378 if (seteuid(uid) < 0) rb_sys_fail(0);
6379 }
6380 else {
6381 if (uid == 0) {
6382 if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
6383 SAVED_USER_ID = 0;
6384 if (setruid(0) < 0) rb_sys_fail(0);
6385 }
6386 else {
6387 if (setruid(0) < 0) rb_sys_fail(0);
6388 SAVED_USER_ID = 0;
6389 if (seteuid(uid) < 0) rb_sys_fail(0);
6390 if (setruid(uid) < 0) rb_sys_fail(0);
6391 SAVED_USER_ID = uid;
6392 }
6393 }
6394 }
6395 else {
6396 if (seteuid(uid) < 0) rb_sys_fail(0);
6397 if (setruid(uid) < 0) rb_sys_fail(0);
6398 SAVED_USER_ID = uid;
6399 }
6400#else
6401 (void)uid;
6403#endif
6404 }
6405 else { /* unprivileged user */
6406#if defined(HAVE_SETRESUID)
6407 if (setresuid((getuid() == uid)? (rb_uid_t)-1: uid,
6408 (geteuid() == uid)? (rb_uid_t)-1: uid,
6409 (SAVED_USER_ID == uid)? (rb_uid_t)-1: uid) < 0) rb_sys_fail(0);
6410 SAVED_USER_ID = uid;
6411#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
6412 if (SAVED_USER_ID == uid) {
6413 if (setreuid((getuid() == uid)? (rb_uid_t)-1: uid,
6414 (geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
6415 rb_sys_fail(0);
6416 }
6417 else if (getuid() != uid) {
6418 if (setreuid(uid, (geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
6419 rb_sys_fail(0);
6420 SAVED_USER_ID = uid;
6421 }
6422 else if (/* getuid() == uid && */ geteuid() != uid) {
6423 if (setreuid(geteuid(), uid) < 0) rb_sys_fail(0);
6424 SAVED_USER_ID = uid;
6425 if (setreuid(uid, -1) < 0) rb_sys_fail(0);
6426 }
6427 else { /* getuid() == uid && geteuid() == uid */
6428 if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
6429 if (setreuid(SAVED_USER_ID, uid) < 0) rb_sys_fail(0);
6430 SAVED_USER_ID = uid;
6431 if (setreuid(uid, -1) < 0) rb_sys_fail(0);
6432 }
6433#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
6434 if (SAVED_USER_ID == uid) {
6435 if (geteuid() != uid && seteuid(uid) < 0) rb_sys_fail(0);
6436 if (getuid() != uid && setruid(uid) < 0) rb_sys_fail(0);
6437 }
6438 else if (/* SAVED_USER_ID != uid && */ geteuid() == uid) {
6439 if (getuid() != uid) {
6440 if (setruid(uid) < 0) rb_sys_fail(0);
6441 SAVED_USER_ID = uid;
6442 }
6443 else {
6444 if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
6445 SAVED_USER_ID = uid;
6446 if (setruid(uid) < 0) rb_sys_fail(0);
6447 }
6448 }
6449 else if (/* geteuid() != uid && */ getuid() == uid) {
6450 if (seteuid(uid) < 0) rb_sys_fail(0);
6451 if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
6452 SAVED_USER_ID = uid;
6453 if (setruid(uid) < 0) rb_sys_fail(0);
6454 }
6455 else {
6456 rb_syserr_fail(EPERM, 0);
6457 }
6458#elif defined HAVE_44BSD_SETUID
6459 if (getuid() == uid) {
6460 /* (r,e,s)==(uid,?,?) ==> (uid,uid,uid) */
6461 if (setuid(uid) < 0) rb_sys_fail(0);
6462 SAVED_USER_ID = uid;
6463 }
6464 else {
6465 rb_syserr_fail(EPERM, 0);
6466 }
6467#elif defined HAVE_SETEUID
6468 if (getuid() == uid && SAVED_USER_ID == uid) {
6469 if (seteuid(uid) < 0) rb_sys_fail(0);
6470 }
6471 else {
6472 rb_syserr_fail(EPERM, 0);
6473 }
6474#elif defined HAVE_SETUID
6475 if (getuid() == uid && SAVED_USER_ID == uid) {
6476 if (setuid(uid) < 0) rb_sys_fail(0);
6477 }
6478 else {
6479 rb_syserr_fail(EPERM, 0);
6480 }
6481#else
6483#endif
6484 }
6485 return id;
6486}
6487
6488
6489
6490#if defined HAVE_SETGID
6491/*
6492 * call-seq:
6493 * Process::Sys.setgid(group) -> nil
6494 *
6495 * Set the group ID of the current process to _group_. Not
6496 * available on all platforms.
6497 *
6498 */
6499
6500static VALUE
6501p_sys_setgid(VALUE obj, VALUE id)
6502{
6503 check_gid_switch();
6504 if (setgid(OBJ2GID(id)) != 0) rb_sys_fail(0);
6505 return Qnil;
6506}
6507#else
6508#define p_sys_setgid rb_f_notimplement
6509#endif
6510
6511
6512#if defined HAVE_SETRGID
6513/*
6514 * call-seq:
6515 * Process::Sys.setrgid(group) -> nil
6516 *
6517 * Set the real group ID of the calling process to _group_.
6518 * Not available on all platforms.
6519 *
6520 */
6521
6522static VALUE
6523p_sys_setrgid(VALUE obj, VALUE id)
6524{
6525 check_gid_switch();
6526 if (setrgid(OBJ2GID(id)) != 0) rb_sys_fail(0);
6527 return Qnil;
6528}
6529#else
6530#define p_sys_setrgid rb_f_notimplement
6531#endif
6532
6533
6534#if defined HAVE_SETEGID
6535/*
6536 * call-seq:
6537 * Process::Sys.setegid(group) -> nil
6538 *
6539 * Set the effective group ID of the calling process to
6540 * _group_. Not available on all platforms.
6541 *
6542 */
6543
6544static VALUE
6545p_sys_setegid(VALUE obj, VALUE id)
6546{
6547 check_gid_switch();
6548 if (setegid(OBJ2GID(id)) != 0) rb_sys_fail(0);
6549 return Qnil;
6550}
6551#else
6552#define p_sys_setegid rb_f_notimplement
6553#endif
6554
6555
6556#if defined HAVE_SETREGID
6557/*
6558 * call-seq:
6559 * Process::Sys.setregid(rid, eid) -> nil
6560 *
6561 * Sets the (group) real and/or effective group IDs of the current
6562 * process to <em>rid</em> and <em>eid</em>, respectively. A value of
6563 * <code>-1</code> for either means to leave that ID unchanged. Not
6564 * available on all platforms.
6565 *
6566 */
6567
6568static VALUE
6569p_sys_setregid(VALUE obj, VALUE rid, VALUE eid)
6570{
6571 rb_gid_t rgid, egid;
6572 check_gid_switch();
6573 rgid = OBJ2GID(rid);
6574 egid = OBJ2GID(eid);
6575 if (setregid(rgid, egid) != 0) rb_sys_fail(0);
6576 return Qnil;
6577}
6578#else
6579#define p_sys_setregid rb_f_notimplement
6580#endif
6581
6582#if defined HAVE_SETRESGID
6583/*
6584 * call-seq:
6585 * Process::Sys.setresgid(rid, eid, sid) -> nil
6586 *
6587 * Sets the (group) real, effective, and saved user IDs of the
6588 * current process to <em>rid</em>, <em>eid</em>, and <em>sid</em>
6589 * respectively. A value of <code>-1</code> for any value means to
6590 * leave that ID unchanged. Not available on all platforms.
6591 *
6592 */
6593
6594static VALUE
6595p_sys_setresgid(VALUE obj, VALUE rid, VALUE eid, VALUE sid)
6596{
6597 rb_gid_t rgid, egid, sgid;
6598 check_gid_switch();
6599 rgid = OBJ2GID(rid);
6600 egid = OBJ2GID(eid);
6601 sgid = OBJ2GID(sid);
6602 if (setresgid(rgid, egid, sgid) != 0) rb_sys_fail(0);
6603 return Qnil;
6604}
6605#else
6606#define p_sys_setresgid rb_f_notimplement
6607#endif
6608
6609
6610#if defined HAVE_ISSETUGID
6611/*
6612 * call-seq:
6613 * Process::Sys.issetugid -> true or false
6614 *
6615 * Returns +true+ if the process was created as a result
6616 * of an execve(2) system call which had either of the setuid or
6617 * setgid bits set (and extra privileges were given as a result) or
6618 * if it has changed any of its real, effective or saved user or
6619 * group IDs since it began execution.
6620 *
6621 */
6622
6623static VALUE
6624p_sys_issetugid(VALUE obj)
6625{
6626 return RBOOL(issetugid());
6627}
6628#else
6629#define p_sys_issetugid rb_f_notimplement
6630#endif
6631
6632
6633/*
6634 * call-seq:
6635 * Process.gid -> integer
6636 * Process::GID.rid -> integer
6637 * Process::Sys.getgid -> integer
6638 *
6639 * Returns the (real) group ID for the current process:
6640 *
6641 * Process.gid # => 1000
6642 *
6643 */
6644
6645static VALUE
6646proc_getgid(VALUE obj)
6647{
6648 rb_gid_t gid = getgid();
6649 return GIDT2NUM(gid);
6650}
6651
6652
6653#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETRGID) || defined(HAVE_SETGID)
6654/*
6655 * call-seq:
6656 * Process.gid = new_gid -> new_gid
6657 *
6658 * Sets the group ID for the current process to +new_gid+:
6659 *
6660 * Process.gid = 1000 # => 1000
6661 *
6662 */
6663
6664static VALUE
6665proc_setgid(VALUE obj, VALUE id)
6666{
6667 rb_gid_t gid;
6668
6669 check_gid_switch();
6670
6671 gid = OBJ2GID(id);
6672#if defined(HAVE_SETRESGID)
6673 if (setresgid(gid, -1, -1) < 0) rb_sys_fail(0);
6674#elif defined HAVE_SETREGID
6675 if (setregid(gid, -1) < 0) rb_sys_fail(0);
6676#elif defined HAVE_SETRGID
6677 if (setrgid(gid) < 0) rb_sys_fail(0);
6678#elif defined HAVE_SETGID
6679 {
6680 if (getegid() == gid) {
6681 if (setgid(gid) < 0) rb_sys_fail(0);
6682 }
6683 else {
6685 }
6686 }
6687#endif
6688 return GIDT2NUM(gid);
6689}
6690#else
6691#define proc_setgid rb_f_notimplement
6692#endif
6693
6694
6695#if defined(_SC_NGROUPS_MAX) || defined(NGROUPS_MAX)
6696/*
6697 * Maximum supplementary groups are platform dependent.
6698 * FWIW, 65536 is enough big for our supported OSs.
6699 *
6700 * OS Name max groups
6701 * -----------------------------------------------
6702 * Linux Kernel >= 2.6.3 65536
6703 * Linux Kernel < 2.6.3 32
6704 * IBM AIX 5.2 64
6705 * IBM AIX 5.3 ... 6.1 128
6706 * IBM AIX 7.1 128 (can be configured to be up to 2048)
6707 * OpenBSD, NetBSD 16
6708 * FreeBSD < 8.0 16
6709 * FreeBSD >=8.0 1023
6710 * Darwin (Mac OS X) 16
6711 * Sun Solaris 7,8,9,10 16
6712 * Sun Solaris 11 / OpenSolaris 1024
6713 * Windows 1015
6714 */
6715static int _maxgroups = -1;
6716static int
6717get_sc_ngroups_max(void)
6718{
6719#ifdef _SC_NGROUPS_MAX
6720 return (int)sysconf(_SC_NGROUPS_MAX);
6721#elif defined(NGROUPS_MAX)
6722 return (int)NGROUPS_MAX;
6723#else
6724 return -1;
6725#endif
6726}
6727static int
6728maxgroups(void)
6729{
6730 if (_maxgroups < 0) {
6731 _maxgroups = get_sc_ngroups_max();
6732 if (_maxgroups < 0)
6733 _maxgroups = RB_MAX_GROUPS;
6734 }
6735
6736 return _maxgroups;
6737}
6738#endif
6739
6740
6741
6742#ifdef HAVE_GETGROUPS
6743/*
6744 * call-seq:
6745 * Process.groups -> array
6746 *
6747 * Returns an array of the group IDs
6748 * in the supplemental group access list for the current process:
6749 *
6750 * Process.groups # => [4, 24, 27, 30, 46, 122, 135, 136, 1000]
6751 *
6752 * These properties of the returned array are system-dependent:
6753 *
6754 * - Whether (and how) the array is sorted.
6755 * - Whether the array includes effective group IDs.
6756 * - Whether the array includes duplicate group IDs.
6757 * - Whether the array size exceeds the value of Process.maxgroups.
6758 *
6759 * Use this call to get a sorted and unique array:
6760 *
6761 * Process.groups.uniq.sort
6762 *
6763 */
6764
6765static VALUE
6766proc_getgroups(VALUE obj)
6767{
6768 VALUE ary, tmp;
6769 int i, ngroups;
6770 rb_gid_t *groups;
6771
6772 ngroups = getgroups(0, NULL);
6773 if (ngroups == -1)
6774 rb_sys_fail(0);
6775
6776 groups = ALLOCV_N(rb_gid_t, tmp, ngroups);
6777
6778 ngroups = getgroups(ngroups, groups);
6779 if (ngroups == -1)
6780 rb_sys_fail(0);
6781
6782 ary = rb_ary_new();
6783 for (i = 0; i < ngroups; i++)
6784 rb_ary_push(ary, GIDT2NUM(groups[i]));
6785
6786 ALLOCV_END(tmp);
6787
6788 return ary;
6789}
6790#else
6791#define proc_getgroups rb_f_notimplement
6792#endif
6793
6794
6795#ifdef HAVE_SETGROUPS
6796/*
6797 * call-seq:
6798 * Process.groups = new_groups -> new_groups
6799 *
6800 * Sets the supplemental group access list to the given
6801 * array of group IDs.
6802 *
6803 * Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
6804 * Process.groups = [27, 6, 10, 11] # => [27, 6, 10, 11]
6805 * Process.groups # => [27, 6, 10, 11]
6806 *
6807 */
6808
6809static VALUE
6810proc_setgroups(VALUE obj, VALUE ary)
6811{
6812 int ngroups, i;
6813 rb_gid_t *groups;
6814 VALUE tmp;
6815 PREPARE_GETGRNAM;
6816
6817 Check_Type(ary, T_ARRAY);
6818
6819 ngroups = RARRAY_LENINT(ary);
6820 if (ngroups > maxgroups())
6821 rb_raise(rb_eArgError, "too many groups, %d max", maxgroups());
6822
6823 groups = ALLOCV_N(rb_gid_t, tmp, ngroups);
6824
6825 for (i = 0; i < ngroups; i++) {
6826 VALUE g = RARRAY_AREF(ary, i);
6827
6828 groups[i] = OBJ2GID1(g);
6829 }
6830 FINISH_GETGRNAM;
6831
6832 if (setgroups(ngroups, groups) == -1) /* ngroups <= maxgroups */
6833 rb_sys_fail(0);
6834
6835 ALLOCV_END(tmp);
6836
6837 return proc_getgroups(obj);
6838}
6839#else
6840#define proc_setgroups rb_f_notimplement
6841#endif
6842
6843
6844#ifdef HAVE_INITGROUPS
6845/*
6846 * call-seq:
6847 * Process.initgroups(username, gid) -> array
6848 *
6849 * Sets the supplemental group access list;
6850 * the new list includes:
6851 *
6852 * - The group IDs of those groups to which the user given by +username+ belongs.
6853 * - The group ID +gid+.
6854 *
6855 * Example:
6856 *
6857 * Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
6858 * Process.initgroups('me', 30) # => [30, 6, 10, 11]
6859 * Process.groups # => [30, 6, 10, 11]
6860 *
6861 * Not available on all platforms.
6862 */
6863
6864static VALUE
6865proc_initgroups(VALUE obj, VALUE uname, VALUE base_grp)
6866{
6867 if (initgroups(StringValueCStr(uname), OBJ2GID(base_grp)) != 0) {
6868 rb_sys_fail(0);
6869 }
6870 return proc_getgroups(obj);
6871}
6872#else
6873#define proc_initgroups rb_f_notimplement
6874#endif
6875
6876#if defined(_SC_NGROUPS_MAX) || defined(NGROUPS_MAX)
6877/*
6878 * call-seq:
6879 * Process.maxgroups -> integer
6880 *
6881 * Returns the maximum number of group IDs allowed
6882 * in the supplemental group access list:
6883 *
6884 * Process.maxgroups # => 32
6885 *
6886 */
6887
6888static VALUE
6889proc_getmaxgroups(VALUE obj)
6890{
6891 return INT2FIX(maxgroups());
6892}
6893#else
6894#define proc_getmaxgroups rb_f_notimplement
6895#endif
6896
6897#ifdef HAVE_SETGROUPS
6898/*
6899 * call-seq:
6900 * Process.maxgroups = new_max -> new_max
6901 *
6902 * Sets the maximum number of group IDs allowed
6903 * in the supplemental group access list.
6904 */
6905
6906static VALUE
6907proc_setmaxgroups(VALUE obj, VALUE val)
6908{
6909 int ngroups = FIX2INT(val);
6910 int ngroups_max = get_sc_ngroups_max();
6911
6912 if (ngroups <= 0)
6913 rb_raise(rb_eArgError, "maxgroups %d should be positive", ngroups);
6914
6915 if (ngroups > RB_MAX_GROUPS)
6916 ngroups = RB_MAX_GROUPS;
6917
6918 if (ngroups_max > 0 && ngroups > ngroups_max)
6919 ngroups = ngroups_max;
6920
6921 _maxgroups = ngroups;
6922
6923 return INT2FIX(_maxgroups);
6924}
6925#else
6926#define proc_setmaxgroups rb_f_notimplement
6927#endif
6928
6929#if defined(HAVE_DAEMON) || (defined(HAVE_WORKING_FORK) && defined(HAVE_SETSID))
6930static int rb_daemon(int nochdir, int noclose);
6931
6932/*
6933 * call-seq:
6934 * Process.daemon(nochdir = nil, noclose = nil) -> 0
6935 *
6936 * Detaches the current process from its controlling terminal
6937 * and runs it in the background as system daemon;
6938 * returns zero.
6939 *
6940 * By default:
6941 *
6942 * - Changes the current working directory to the root directory.
6943 * - Redirects $stdin, $stdout, and $stderr to the null device.
6944 *
6945 * If optional argument +nochdir+ is +true+,
6946 * does not change the current working directory.
6947 *
6948 * If optional argument +noclose+ is +true+,
6949 * does not redirect $stdin, $stdout, or $stderr.
6950 */
6951
6952static VALUE
6953proc_daemon(int argc, VALUE *argv, VALUE _)
6954{
6955 int n, nochdir = FALSE, noclose = FALSE;
6956
6957 switch (rb_check_arity(argc, 0, 2)) {
6958 case 2: noclose = TO_BOOL(argv[1], "noclose");
6959 case 1: nochdir = TO_BOOL(argv[0], "nochdir");
6960 }
6961
6962 prefork();
6963 n = rb_daemon(nochdir, noclose);
6964 if (n < 0) rb_sys_fail("daemon");
6965 return INT2FIX(n);
6966}
6967
6968extern const char ruby_null_device[];
6969
6970static int
6971rb_daemon(int nochdir, int noclose)
6972{
6973 int err = 0;
6974#ifdef HAVE_DAEMON
6975 before_fork_ruby();
6976 err = daemon(nochdir, noclose);
6977 after_fork_ruby(0);
6978#else
6979 int n;
6980
6981 switch (rb_fork_ruby(NULL)) {
6982 case -1: return -1;
6983 case 0: break;
6984 default: _exit(EXIT_SUCCESS);
6985 }
6986
6987 /* ignore EPERM which means already being process-leader */
6988 if (setsid() < 0) (void)0;
6989
6990 if (!nochdir)
6991 err = chdir("/");
6992
6993 if (!noclose && (n = rb_cloexec_open(ruby_null_device, O_RDWR, 0)) != -1) {
6995 (void)dup2(n, 0);
6996 (void)dup2(n, 1);
6997 (void)dup2(n, 2);
6998 if (n > 2)
6999 (void)close (n);
7000 }
7001#endif
7002 return err;
7003}
7004#else
7005#define proc_daemon rb_f_notimplement
7006#endif
7007
7008/********************************************************************
7009 *
7010 * Document-class: Process::GID
7011 *
7012 * The Process::GID module contains a collection of
7013 * module functions which can be used to portably get, set, and
7014 * switch the current process's real, effective, and saved group IDs.
7015 *
7016 */
7017
7018static rb_gid_t SAVED_GROUP_ID = -1;
7019
7020#ifdef BROKEN_SETREGID
7021int
7022setregid(rb_gid_t rgid, rb_gid_t egid)
7023{
7024 if (rgid != (rb_gid_t)-1 && rgid != getgid()) {
7025 if (egid == (rb_gid_t)-1) egid = getegid();
7026 if (setgid(rgid) < 0) return -1;
7027 }
7028 if (egid != (rb_gid_t)-1 && egid != getegid()) {
7029 if (setegid(egid) < 0) return -1;
7030 }
7031 return 0;
7032}
7033#endif
7034
7035/*
7036 * call-seq:
7037 * Process::GID.change_privilege(group) -> integer
7038 *
7039 * Change the current process's real and effective group ID to that
7040 * specified by _group_. Returns the new group ID. Not
7041 * available on all platforms.
7042 *
7043 * [Process.gid, Process.egid] #=> [0, 0]
7044 * Process::GID.change_privilege(33) #=> 33
7045 * [Process.gid, Process.egid] #=> [33, 33]
7046 */
7047
7048static VALUE
7049p_gid_change_privilege(VALUE obj, VALUE id)
7050{
7051 rb_gid_t gid;
7052
7053 check_gid_switch();
7054
7055 gid = OBJ2GID(id);
7056
7057 if (geteuid() == 0) { /* root-user */
7058#if defined(HAVE_SETRESGID)
7059 if (setresgid(gid, gid, gid) < 0) rb_sys_fail(0);
7060 SAVED_GROUP_ID = gid;
7061#elif defined HAVE_SETGID
7062 if (setgid(gid) < 0) rb_sys_fail(0);
7063 SAVED_GROUP_ID = gid;
7064#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7065 if (getgid() == gid) {
7066 if (SAVED_GROUP_ID == gid) {
7067 if (setregid(-1, gid) < 0) rb_sys_fail(0);
7068 }
7069 else {
7070 if (gid == 0) { /* (r,e,s) == (root, y, x) */
7071 if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7072 if (setregid(SAVED_GROUP_ID, 0) < 0) rb_sys_fail(0);
7073 SAVED_GROUP_ID = 0; /* (r,e,s) == (x, root, root) */
7074 if (setregid(gid, gid) < 0) rb_sys_fail(0);
7075 SAVED_GROUP_ID = gid;
7076 }
7077 else { /* (r,e,s) == (z, y, x) */
7078 if (setregid(0, 0) < 0) rb_sys_fail(0);
7079 SAVED_GROUP_ID = 0;
7080 if (setregid(gid, gid) < 0) rb_sys_fail(0);
7081 SAVED_GROUP_ID = gid;
7082 }
7083 }
7084 }
7085 else {
7086 if (setregid(gid, gid) < 0) rb_sys_fail(0);
7087 SAVED_GROUP_ID = gid;
7088 }
7089#elif defined(HAVE_SETRGID) && defined (HAVE_SETEGID)
7090 if (getgid() == gid) {
7091 if (SAVED_GROUP_ID == gid) {
7092 if (setegid(gid) < 0) rb_sys_fail(0);
7093 }
7094 else {
7095 if (gid == 0) {
7096 if (setegid(gid) < 0) rb_sys_fail(0);
7097 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7098 SAVED_GROUP_ID = 0;
7099 if (setrgid(0) < 0) rb_sys_fail(0);
7100 }
7101 else {
7102 if (setrgid(0) < 0) rb_sys_fail(0);
7103 SAVED_GROUP_ID = 0;
7104 if (setegid(gid) < 0) rb_sys_fail(0);
7105 if (setrgid(gid) < 0) rb_sys_fail(0);
7106 SAVED_GROUP_ID = gid;
7107 }
7108 }
7109 }
7110 else {
7111 if (setegid(gid) < 0) rb_sys_fail(0);
7112 if (setrgid(gid) < 0) rb_sys_fail(0);
7113 SAVED_GROUP_ID = gid;
7114 }
7115#else
7117#endif
7118 }
7119 else { /* unprivileged user */
7120#if defined(HAVE_SETRESGID)
7121 if (setresgid((getgid() == gid)? (rb_gid_t)-1: gid,
7122 (getegid() == gid)? (rb_gid_t)-1: gid,
7123 (SAVED_GROUP_ID == gid)? (rb_gid_t)-1: gid) < 0) rb_sys_fail(0);
7124 SAVED_GROUP_ID = gid;
7125#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7126 if (SAVED_GROUP_ID == gid) {
7127 if (setregid((getgid() == gid)? (rb_uid_t)-1: gid,
7128 (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
7129 rb_sys_fail(0);
7130 }
7131 else if (getgid() != gid) {
7132 if (setregid(gid, (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
7133 rb_sys_fail(0);
7134 SAVED_GROUP_ID = gid;
7135 }
7136 else if (/* getgid() == gid && */ getegid() != gid) {
7137 if (setregid(getegid(), gid) < 0) rb_sys_fail(0);
7138 SAVED_GROUP_ID = gid;
7139 if (setregid(gid, -1) < 0) rb_sys_fail(0);
7140 }
7141 else { /* getgid() == gid && getegid() == gid */
7142 if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7143 if (setregid(SAVED_GROUP_ID, gid) < 0) rb_sys_fail(0);
7144 SAVED_GROUP_ID = gid;
7145 if (setregid(gid, -1) < 0) rb_sys_fail(0);
7146 }
7147#elif defined(HAVE_SETRGID) && defined(HAVE_SETEGID)
7148 if (SAVED_GROUP_ID == gid) {
7149 if (getegid() != gid && setegid(gid) < 0) rb_sys_fail(0);
7150 if (getgid() != gid && setrgid(gid) < 0) rb_sys_fail(0);
7151 }
7152 else if (/* SAVED_GROUP_ID != gid && */ getegid() == gid) {
7153 if (getgid() != gid) {
7154 if (setrgid(gid) < 0) rb_sys_fail(0);
7155 SAVED_GROUP_ID = gid;
7156 }
7157 else {
7158 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7159 SAVED_GROUP_ID = gid;
7160 if (setrgid(gid) < 0) rb_sys_fail(0);
7161 }
7162 }
7163 else if (/* getegid() != gid && */ getgid() == gid) {
7164 if (setegid(gid) < 0) rb_sys_fail(0);
7165 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7166 SAVED_GROUP_ID = gid;
7167 if (setrgid(gid) < 0) rb_sys_fail(0);
7168 }
7169 else {
7170 rb_syserr_fail(EPERM, 0);
7171 }
7172#elif defined HAVE_44BSD_SETGID
7173 if (getgid() == gid) {
7174 /* (r,e,s)==(gid,?,?) ==> (gid,gid,gid) */
7175 if (setgid(gid) < 0) rb_sys_fail(0);
7176 SAVED_GROUP_ID = gid;
7177 }
7178 else {
7179 rb_syserr_fail(EPERM, 0);
7180 }
7181#elif defined HAVE_SETEGID
7182 if (getgid() == gid && SAVED_GROUP_ID == gid) {
7183 if (setegid(gid) < 0) rb_sys_fail(0);
7184 }
7185 else {
7186 rb_syserr_fail(EPERM, 0);
7187 }
7188#elif defined HAVE_SETGID
7189 if (getgid() == gid && SAVED_GROUP_ID == gid) {
7190 if (setgid(gid) < 0) rb_sys_fail(0);
7191 }
7192 else {
7193 rb_syserr_fail(EPERM, 0);
7194 }
7195#else
7196 (void)gid;
7198#endif
7199 }
7200 return id;
7201}
7202
7203
7204/*
7205 * call-seq:
7206 * Process.euid -> integer
7207 * Process::UID.eid -> integer
7208 * Process::Sys.geteuid -> integer
7209 *
7210 * Returns the effective user ID for the current process.
7211 *
7212 * Process.euid # => 501
7213 *
7214 */
7215
7216static VALUE
7217proc_geteuid(VALUE obj)
7218{
7219 rb_uid_t euid = geteuid();
7220 return UIDT2NUM(euid);
7221}
7222
7223#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID) || defined(_POSIX_SAVED_IDS)
7224static void
7225proc_seteuid(rb_uid_t uid)
7226{
7227#if defined(HAVE_SETRESUID)
7228 if (setresuid(-1, uid, -1) < 0) rb_sys_fail(0);
7229#elif defined HAVE_SETREUID
7230 if (setreuid(-1, uid) < 0) rb_sys_fail(0);
7231#elif defined HAVE_SETEUID
7232 if (seteuid(uid) < 0) rb_sys_fail(0);
7233#elif defined HAVE_SETUID
7234 if (uid == getuid()) {
7235 if (setuid(uid) < 0) rb_sys_fail(0);
7236 }
7237 else {
7239 }
7240#else
7242#endif
7243}
7244#endif
7245
7246#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID)
7247/*
7248 * call-seq:
7249 * Process.euid = new_euid -> new_euid
7250 *
7251 * Sets the effective user ID for the current process.
7252 *
7253 * Not available on all platforms.
7254 */
7255
7256static VALUE
7257proc_seteuid_m(VALUE mod, VALUE euid)
7258{
7259 check_uid_switch();
7260 proc_seteuid(OBJ2UID(euid));
7261 return euid;
7262}
7263#else
7264#define proc_seteuid_m rb_f_notimplement
7265#endif
7266
7267static rb_uid_t
7268rb_seteuid_core(rb_uid_t euid)
7269{
7270#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7271 rb_uid_t uid;
7272#endif
7273
7274 check_uid_switch();
7275
7276#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7277 uid = getuid();
7278#endif
7279
7280#if defined(HAVE_SETRESUID)
7281 if (uid != euid) {
7282 if (setresuid(-1,euid,euid) < 0) rb_sys_fail(0);
7283 SAVED_USER_ID = euid;
7284 }
7285 else {
7286 if (setresuid(-1,euid,-1) < 0) rb_sys_fail(0);
7287 }
7288#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7289 if (setreuid(-1, euid) < 0) rb_sys_fail(0);
7290 if (uid != euid) {
7291 if (setreuid(euid,uid) < 0) rb_sys_fail(0);
7292 if (setreuid(uid,euid) < 0) rb_sys_fail(0);
7293 SAVED_USER_ID = euid;
7294 }
7295#elif defined HAVE_SETEUID
7296 if (seteuid(euid) < 0) rb_sys_fail(0);
7297#elif defined HAVE_SETUID
7298 if (geteuid() == 0) rb_sys_fail(0);
7299 if (setuid(euid) < 0) rb_sys_fail(0);
7300#else
7302#endif
7303 return euid;
7304}
7305
7306
7307/*
7308 * call-seq:
7309 * Process::UID.grant_privilege(user) -> integer
7310 * Process::UID.eid= user -> integer
7311 *
7312 * Set the effective user ID, and if possible, the saved user ID of
7313 * the process to the given _user_. Returns the new
7314 * effective user ID. Not available on all platforms.
7315 *
7316 * [Process.uid, Process.euid] #=> [0, 0]
7317 * Process::UID.grant_privilege(31) #=> 31
7318 * [Process.uid, Process.euid] #=> [0, 31]
7319 */
7320
7321static VALUE
7322p_uid_grant_privilege(VALUE obj, VALUE id)
7323{
7324 rb_seteuid_core(OBJ2UID(id));
7325 return id;
7326}
7327
7328
7329/*
7330 * call-seq:
7331 * Process.egid -> integer
7332 * Process::GID.eid -> integer
7333 * Process::Sys.geteid -> integer
7334 *
7335 * Returns the effective group ID for the current process:
7336 *
7337 * Process.egid # => 500
7338 *
7339 * Not available on all platforms.
7340 */
7341
7342static VALUE
7343proc_getegid(VALUE obj)
7344{
7345 rb_gid_t egid = getegid();
7346
7347 return GIDT2NUM(egid);
7348}
7349
7350#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID) || defined(_POSIX_SAVED_IDS)
7351/*
7352 * call-seq:
7353 * Process.egid = new_egid -> new_egid
7354 *
7355 * Sets the effective group ID for the current process.
7356 *
7357 * Not available on all platforms.
7358 */
7359
7360static VALUE
7361proc_setegid(VALUE obj, VALUE egid)
7362{
7363#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7364 rb_gid_t gid;
7365#endif
7366
7367 check_gid_switch();
7368
7369#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7370 gid = OBJ2GID(egid);
7371#endif
7372
7373#if defined(HAVE_SETRESGID)
7374 if (setresgid(-1, gid, -1) < 0) rb_sys_fail(0);
7375#elif defined HAVE_SETREGID
7376 if (setregid(-1, gid) < 0) rb_sys_fail(0);
7377#elif defined HAVE_SETEGID
7378 if (setegid(gid) < 0) rb_sys_fail(0);
7379#elif defined HAVE_SETGID
7380 if (gid == getgid()) {
7381 if (setgid(gid) < 0) rb_sys_fail(0);
7382 }
7383 else {
7385 }
7386#else
7388#endif
7389 return egid;
7390}
7391#endif
7392
7393#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7394#define proc_setegid_m proc_setegid
7395#else
7396#define proc_setegid_m rb_f_notimplement
7397#endif
7398
7399static rb_gid_t
7400rb_setegid_core(rb_gid_t egid)
7401{
7402#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7403 rb_gid_t gid;
7404#endif
7405
7406 check_gid_switch();
7407
7408#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7409 gid = getgid();
7410#endif
7411
7412#if defined(HAVE_SETRESGID)
7413 if (gid != egid) {
7414 if (setresgid(-1,egid,egid) < 0) rb_sys_fail(0);
7415 SAVED_GROUP_ID = egid;
7416 }
7417 else {
7418 if (setresgid(-1,egid,-1) < 0) rb_sys_fail(0);
7419 }
7420#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7421 if (setregid(-1, egid) < 0) rb_sys_fail(0);
7422 if (gid != egid) {
7423 if (setregid(egid,gid) < 0) rb_sys_fail(0);
7424 if (setregid(gid,egid) < 0) rb_sys_fail(0);
7425 SAVED_GROUP_ID = egid;
7426 }
7427#elif defined HAVE_SETEGID
7428 if (setegid(egid) < 0) rb_sys_fail(0);
7429#elif defined HAVE_SETGID
7430 if (geteuid() == 0 /* root user */) rb_sys_fail(0);
7431 if (setgid(egid) < 0) rb_sys_fail(0);
7432#else
7434#endif
7435 return egid;
7436}
7437
7438
7439/*
7440 * call-seq:
7441 * Process::GID.grant_privilege(group) -> integer
7442 * Process::GID.eid = group -> integer
7443 *
7444 * Set the effective group ID, and if possible, the saved group ID of
7445 * the process to the given _group_. Returns the new
7446 * effective group ID. Not available on all platforms.
7447 *
7448 * [Process.gid, Process.egid] #=> [0, 0]
7449 * Process::GID.grant_privilege(31) #=> 33
7450 * [Process.gid, Process.egid] #=> [0, 33]
7451 */
7452
7453static VALUE
7454p_gid_grant_privilege(VALUE obj, VALUE id)
7455{
7456 rb_setegid_core(OBJ2GID(id));
7457 return id;
7458}
7459
7460
7461/*
7462 * call-seq:
7463 * Process::UID.re_exchangeable? -> true or false
7464 *
7465 * Returns +true+ if the real and effective user IDs of a
7466 * process may be exchanged on the current platform.
7467 *
7468 */
7469
7470static VALUE
7471p_uid_exchangeable(VALUE _)
7472{
7473#if defined(HAVE_SETRESUID)
7474 return Qtrue;
7475#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7476 return Qtrue;
7477#else
7478 return Qfalse;
7479#endif
7480}
7481
7482
7483/*
7484 * call-seq:
7485 * Process::UID.re_exchange -> integer
7486 *
7487 * Exchange real and effective user IDs and return the new effective
7488 * user ID. Not available on all platforms.
7489 *
7490 * [Process.uid, Process.euid] #=> [0, 31]
7491 * Process::UID.re_exchange #=> 0
7492 * [Process.uid, Process.euid] #=> [31, 0]
7493 */
7494
7495static VALUE
7496p_uid_exchange(VALUE obj)
7497{
7498 rb_uid_t uid;
7499#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7500 rb_uid_t euid;
7501#endif
7502
7503 check_uid_switch();
7504
7505 uid = getuid();
7506#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7507 euid = geteuid();
7508#endif
7509
7510#if defined(HAVE_SETRESUID)
7511 if (setresuid(euid, uid, uid) < 0) rb_sys_fail(0);
7512 SAVED_USER_ID = uid;
7513#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7514 if (setreuid(euid,uid) < 0) rb_sys_fail(0);
7515 SAVED_USER_ID = uid;
7516#else
7518#endif
7519 return UIDT2NUM(uid);
7520}
7521
7522
7523/*
7524 * call-seq:
7525 * Process::GID.re_exchangeable? -> true or false
7526 *
7527 * Returns +true+ if the real and effective group IDs of a
7528 * process may be exchanged on the current platform.
7529 *
7530 */
7531
7532static VALUE
7533p_gid_exchangeable(VALUE _)
7534{
7535#if defined(HAVE_SETRESGID)
7536 return Qtrue;
7537#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7538 return Qtrue;
7539#else
7540 return Qfalse;
7541#endif
7542}
7543
7544
7545/*
7546 * call-seq:
7547 * Process::GID.re_exchange -> integer
7548 *
7549 * Exchange real and effective group IDs and return the new effective
7550 * group ID. Not available on all platforms.
7551 *
7552 * [Process.gid, Process.egid] #=> [0, 33]
7553 * Process::GID.re_exchange #=> 0
7554 * [Process.gid, Process.egid] #=> [33, 0]
7555 */
7556
7557static VALUE
7558p_gid_exchange(VALUE obj)
7559{
7560 rb_gid_t gid;
7561#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7562 rb_gid_t egid;
7563#endif
7564
7565 check_gid_switch();
7566
7567 gid = getgid();
7568#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7569 egid = getegid();
7570#endif
7571
7572#if defined(HAVE_SETRESGID)
7573 if (setresgid(egid, gid, gid) < 0) rb_sys_fail(0);
7574 SAVED_GROUP_ID = gid;
7575#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7576 if (setregid(egid,gid) < 0) rb_sys_fail(0);
7577 SAVED_GROUP_ID = gid;
7578#else
7580#endif
7581 return GIDT2NUM(gid);
7582}
7583
7584/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
7585
7586/*
7587 * call-seq:
7588 * Process::UID.sid_available? -> true or false
7589 *
7590 * Returns +true+ if the current platform has saved user
7591 * ID functionality.
7592 *
7593 */
7594
7595static VALUE
7596p_uid_have_saved_id(VALUE _)
7597{
7598#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
7599 return Qtrue;
7600#else
7601 return Qfalse;
7602#endif
7603}
7604
7605
7606#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
7607static VALUE
7608p_uid_sw_ensure(VALUE i)
7609{
7610 rb_uid_t id = (rb_uid_t/* narrowing */)i;
7611 under_uid_switch = 0;
7612 id = rb_seteuid_core(id);
7613 return UIDT2NUM(id);
7614}
7615
7616
7617/*
7618 * call-seq:
7619 * Process::UID.switch -> integer
7620 * Process::UID.switch {|| block} -> object
7621 *
7622 * Switch the effective and real user IDs of the current process. If
7623 * a <em>block</em> is given, the user IDs will be switched back
7624 * after the block is executed. Returns the new effective user ID if
7625 * called without a block, and the return value of the block if one
7626 * is given.
7627 *
7628 */
7629
7630static VALUE
7631p_uid_switch(VALUE obj)
7632{
7633 rb_uid_t uid, euid;
7634
7635 check_uid_switch();
7636
7637 uid = getuid();
7638 euid = geteuid();
7639
7640 if (uid != euid) {
7641 proc_seteuid(uid);
7642 if (rb_block_given_p()) {
7643 under_uid_switch = 1;
7644 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, SAVED_USER_ID);
7645 }
7646 else {
7647 return UIDT2NUM(euid);
7648 }
7649 }
7650 else if (euid != SAVED_USER_ID) {
7651 proc_seteuid(SAVED_USER_ID);
7652 if (rb_block_given_p()) {
7653 under_uid_switch = 1;
7654 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, euid);
7655 }
7656 else {
7657 return UIDT2NUM(uid);
7658 }
7659 }
7660 else {
7661 rb_syserr_fail(EPERM, 0);
7662 }
7663
7665}
7666#else
7667static VALUE
7668p_uid_sw_ensure(VALUE obj)
7669{
7670 under_uid_switch = 0;
7671 return p_uid_exchange(obj);
7672}
7673
7674static VALUE
7675p_uid_switch(VALUE obj)
7676{
7677 rb_uid_t uid, euid;
7678
7679 check_uid_switch();
7680
7681 uid = getuid();
7682 euid = geteuid();
7683
7684 if (uid == euid) {
7685 rb_syserr_fail(EPERM, 0);
7686 }
7687 p_uid_exchange(obj);
7688 if (rb_block_given_p()) {
7689 under_uid_switch = 1;
7690 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, obj);
7691 }
7692 else {
7693 return UIDT2NUM(euid);
7694 }
7695}
7696#endif
7697
7698
7699/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
7700
7701/*
7702 * call-seq:
7703 * Process::GID.sid_available? -> true or false
7704 *
7705 * Returns +true+ if the current platform has saved group
7706 * ID functionality.
7707 *
7708 */
7709
7710static VALUE
7711p_gid_have_saved_id(VALUE _)
7712{
7713#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
7714 return Qtrue;
7715#else
7716 return Qfalse;
7717#endif
7718}
7719
7720#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
7721static VALUE
7722p_gid_sw_ensure(VALUE i)
7723{
7724 rb_gid_t id = (rb_gid_t/* narrowing */)i;
7725 under_gid_switch = 0;
7726 id = rb_setegid_core(id);
7727 return GIDT2NUM(id);
7728}
7729
7730
7731/*
7732 * call-seq:
7733 * Process::GID.switch -> integer
7734 * Process::GID.switch {|| block} -> object
7735 *
7736 * Switch the effective and real group IDs of the current process. If
7737 * a <em>block</em> is given, the group IDs will be switched back
7738 * after the block is executed. Returns the new effective group ID if
7739 * called without a block, and the return value of the block if one
7740 * is given.
7741 *
7742 */
7743
7744static VALUE
7745p_gid_switch(VALUE obj)
7746{
7747 rb_gid_t gid, egid;
7748
7749 check_gid_switch();
7750
7751 gid = getgid();
7752 egid = getegid();
7753
7754 if (gid != egid) {
7755 proc_setegid(obj, GIDT2NUM(gid));
7756 if (rb_block_given_p()) {
7757 under_gid_switch = 1;
7758 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, SAVED_GROUP_ID);
7759 }
7760 else {
7761 return GIDT2NUM(egid);
7762 }
7763 }
7764 else if (egid != SAVED_GROUP_ID) {
7765 proc_setegid(obj, GIDT2NUM(SAVED_GROUP_ID));
7766 if (rb_block_given_p()) {
7767 under_gid_switch = 1;
7768 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, egid);
7769 }
7770 else {
7771 return GIDT2NUM(gid);
7772 }
7773 }
7774 else {
7775 rb_syserr_fail(EPERM, 0);
7776 }
7777
7779}
7780#else
7781static VALUE
7782p_gid_sw_ensure(VALUE obj)
7783{
7784 under_gid_switch = 0;
7785 return p_gid_exchange(obj);
7786}
7787
7788static VALUE
7789p_gid_switch(VALUE obj)
7790{
7791 rb_gid_t gid, egid;
7792
7793 check_gid_switch();
7794
7795 gid = getgid();
7796 egid = getegid();
7797
7798 if (gid == egid) {
7799 rb_syserr_fail(EPERM, 0);
7800 }
7801 p_gid_exchange(obj);
7802 if (rb_block_given_p()) {
7803 under_gid_switch = 1;
7804 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, obj);
7805 }
7806 else {
7807 return GIDT2NUM(egid);
7808 }
7809}
7810#endif
7811
7812
7813#if defined(HAVE_TIMES)
7814static long
7815get_clk_tck(void)
7816{
7817#ifdef HAVE__SC_CLK_TCK
7818 return sysconf(_SC_CLK_TCK);
7819#elif defined CLK_TCK
7820 return CLK_TCK;
7821#elif defined HZ
7822 return HZ;
7823#else
7824 return 60;
7825#endif
7826}
7827
7828/*
7829 * call-seq:
7830 * Process.times -> process_tms
7831 *
7832 * Returns a Process::Tms structure that contains user and system CPU times
7833 * for the current process, and for its children processes:
7834 *
7835 * Process.times
7836 * # => #<struct Process::Tms utime=55.122118, stime=35.533068, cutime=0.0, cstime=0.002846>
7837 *
7838 * The precision is platform-defined.
7839 */
7840
7841VALUE
7842rb_proc_times(VALUE obj)
7843{
7844 VALUE utime, stime, cutime, cstime, ret;
7845#if defined(RUSAGE_SELF) && defined(RUSAGE_CHILDREN)
7846 struct rusage usage_s, usage_c;
7847
7848 if (getrusage(RUSAGE_SELF, &usage_s) != 0 || getrusage(RUSAGE_CHILDREN, &usage_c) != 0)
7849 rb_sys_fail("getrusage");
7850 utime = DBL2NUM((double)usage_s.ru_utime.tv_sec + (double)usage_s.ru_utime.tv_usec/1e6);
7851 stime = DBL2NUM((double)usage_s.ru_stime.tv_sec + (double)usage_s.ru_stime.tv_usec/1e6);
7852 cutime = DBL2NUM((double)usage_c.ru_utime.tv_sec + (double)usage_c.ru_utime.tv_usec/1e6);
7853 cstime = DBL2NUM((double)usage_c.ru_stime.tv_sec + (double)usage_c.ru_stime.tv_usec/1e6);
7854#else
7855 const double hertz = (double)get_clk_tck();
7856 struct tms buf;
7857
7858 times(&buf);
7859 utime = DBL2NUM(buf.tms_utime / hertz);
7860 stime = DBL2NUM(buf.tms_stime / hertz);
7861 cutime = DBL2NUM(buf.tms_cutime / hertz);
7862 cstime = DBL2NUM(buf.tms_cstime / hertz);
7863#endif
7864 ret = rb_struct_new(rb_cProcessTms, utime, stime, cutime, cstime);
7865 RB_GC_GUARD(utime);
7866 RB_GC_GUARD(stime);
7867 RB_GC_GUARD(cutime);
7868 RB_GC_GUARD(cstime);
7869 return ret;
7870}
7871#else
7872#define rb_proc_times rb_f_notimplement
7873#endif
7874
7875#ifdef HAVE_LONG_LONG
7876typedef LONG_LONG timetick_int_t;
7877#define TIMETICK_INT_MIN LLONG_MIN
7878#define TIMETICK_INT_MAX LLONG_MAX
7879#define TIMETICK_INT2NUM(v) LL2NUM(v)
7880#define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_LONG_P(a, b)
7881#else
7882typedef long timetick_int_t;
7883#define TIMETICK_INT_MIN LONG_MIN
7884#define TIMETICK_INT_MAX LONG_MAX
7885#define TIMETICK_INT2NUM(v) LONG2NUM(v)
7886#define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_P(a, b)
7887#endif
7888
7889CONSTFUNC(static timetick_int_t gcd_timetick_int(timetick_int_t, timetick_int_t));
7890static timetick_int_t
7891gcd_timetick_int(timetick_int_t a, timetick_int_t b)
7892{
7893 timetick_int_t t;
7894
7895 if (a < b) {
7896 t = a;
7897 a = b;
7898 b = t;
7899 }
7900
7901 while (1) {
7902 t = a % b;
7903 if (t == 0)
7904 return b;
7905 a = b;
7906 b = t;
7907 }
7908}
7909
7910static void
7911reduce_fraction(timetick_int_t *np, timetick_int_t *dp)
7912{
7913 timetick_int_t gcd = gcd_timetick_int(*np, *dp);
7914 if (gcd != 1) {
7915 *np /= gcd;
7916 *dp /= gcd;
7917 }
7918}
7919
7920static void
7921reduce_factors(timetick_int_t *numerators, int num_numerators,
7922 timetick_int_t *denominators, int num_denominators)
7923{
7924 int i, j;
7925 for (i = 0; i < num_numerators; i++) {
7926 if (numerators[i] == 1)
7927 continue;
7928 for (j = 0; j < num_denominators; j++) {
7929 if (denominators[j] == 1)
7930 continue;
7931 reduce_fraction(&numerators[i], &denominators[j]);
7932 }
7933 }
7934}
7935
7936struct timetick {
7937 timetick_int_t giga_count;
7938 int32_t count; /* 0 .. 999999999 */
7939};
7940
7941static VALUE
7942timetick2dblnum(struct timetick *ttp,
7943 timetick_int_t *numerators, int num_numerators,
7944 timetick_int_t *denominators, int num_denominators)
7945{
7946 double d;
7947 int i;
7948
7949 reduce_factors(numerators, num_numerators,
7950 denominators, num_denominators);
7951
7952 d = ttp->giga_count * 1e9 + ttp->count;
7953
7954 for (i = 0; i < num_numerators; i++)
7955 d *= numerators[i];
7956 for (i = 0; i < num_denominators; i++)
7957 d /= denominators[i];
7958
7959 return DBL2NUM(d);
7960}
7961
7962static VALUE
7963timetick2dblnum_reciprocal(struct timetick *ttp,
7964 timetick_int_t *numerators, int num_numerators,
7965 timetick_int_t *denominators, int num_denominators)
7966{
7967 double d;
7968 int i;
7969
7970 reduce_factors(numerators, num_numerators,
7971 denominators, num_denominators);
7972
7973 d = 1.0;
7974 for (i = 0; i < num_denominators; i++)
7975 d *= denominators[i];
7976 for (i = 0; i < num_numerators; i++)
7977 d /= numerators[i];
7978 d /= ttp->giga_count * 1e9 + ttp->count;
7979
7980 return DBL2NUM(d);
7981}
7982
7983#define NDIV(x,y) (-(-((x)+1)/(y))-1)
7984#define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d))
7985
7986static VALUE
7987timetick2integer(struct timetick *ttp,
7988 timetick_int_t *numerators, int num_numerators,
7989 timetick_int_t *denominators, int num_denominators)
7990{
7991 VALUE v;
7992 int i;
7993
7994 reduce_factors(numerators, num_numerators,
7995 denominators, num_denominators);
7996
7997 if (!MUL_OVERFLOW_SIGNED_INTEGER_P(1000000000, ttp->giga_count,
7998 TIMETICK_INT_MIN, TIMETICK_INT_MAX-ttp->count)) {
7999 timetick_int_t t = ttp->giga_count * 1000000000 + ttp->count;
8000 for (i = 0; i < num_numerators; i++) {
8001 timetick_int_t factor = numerators[i];
8002 if (MUL_OVERFLOW_TIMETICK_P(factor, t))
8003 goto generic;
8004 t *= factor;
8005 }
8006 for (i = 0; i < num_denominators; i++) {
8007 t = DIV(t, denominators[i]);
8008 }
8009 return TIMETICK_INT2NUM(t);
8010 }
8011
8012 generic:
8013 v = TIMETICK_INT2NUM(ttp->giga_count);
8014 v = rb_funcall(v, '*', 1, LONG2FIX(1000000000));
8015 v = rb_funcall(v, '+', 1, LONG2FIX(ttp->count));
8016 for (i = 0; i < num_numerators; i++) {
8017 timetick_int_t factor = numerators[i];
8018 if (factor == 1)
8019 continue;
8020 v = rb_funcall(v, '*', 1, TIMETICK_INT2NUM(factor));
8021 }
8022 for (i = 0; i < num_denominators; i++) {
8023 v = rb_funcall(v, '/', 1, TIMETICK_INT2NUM(denominators[i])); /* Ruby's '/' is div. */
8024 }
8025 return v;
8026}
8027
8028static VALUE
8029make_clock_result(struct timetick *ttp,
8030 timetick_int_t *numerators, int num_numerators,
8031 timetick_int_t *denominators, int num_denominators,
8032 VALUE unit)
8033{
8034 if (unit == ID2SYM(id_nanosecond)) {
8035 numerators[num_numerators++] = 1000000000;
8036 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8037 }
8038 else if (unit == ID2SYM(id_microsecond)) {
8039 numerators[num_numerators++] = 1000000;
8040 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8041 }
8042 else if (unit == ID2SYM(id_millisecond)) {
8043 numerators[num_numerators++] = 1000;
8044 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8045 }
8046 else if (unit == ID2SYM(id_second)) {
8047 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8048 }
8049 else if (unit == ID2SYM(id_float_microsecond)) {
8050 numerators[num_numerators++] = 1000000;
8051 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
8052 }
8053 else if (unit == ID2SYM(id_float_millisecond)) {
8054 numerators[num_numerators++] = 1000;
8055 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
8056 }
8057 else if (NIL_P(unit) || unit == ID2SYM(id_float_second)) {
8058 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
8059 }
8060 else
8061 rb_raise(rb_eArgError, "unexpected unit: %"PRIsVALUE, unit);
8062}
8063
8064#ifdef __APPLE__
8065static const mach_timebase_info_data_t *
8066get_mach_timebase_info(void)
8067{
8068 static mach_timebase_info_data_t sTimebaseInfo;
8069
8070 if ( sTimebaseInfo.denom == 0 ) {
8071 (void) mach_timebase_info(&sTimebaseInfo);
8072 }
8073
8074 return &sTimebaseInfo;
8075}
8076
8077double
8078ruby_real_ms_time(void)
8079{
8080 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8081 uint64_t t = mach_absolute_time();
8082 return (double)t * info->numer / info->denom / 1e6;
8083}
8084#endif
8085
8086#if defined(NUM2CLOCKID)
8087# define NUMERIC_CLOCKID 1
8088#else
8089# define NUMERIC_CLOCKID 0
8090# define NUM2CLOCKID(x) 0
8091#endif
8092
8093#define clock_failed(name, err, arg) do { \
8094 int clock_error = (err); \
8095 rb_syserr_fail_str(clock_error, rb_sprintf("clock_" name "(%+"PRIsVALUE")", (arg))); \
8096 } while (0)
8097
8098/*
8099 * call-seq:
8100 * Process.clock_gettime(clock_id, unit = :float_second) -> number
8101 *
8102 * Returns a clock time as determined by POSIX function
8103 * {clock_gettime()}[https://man7.org/linux/man-pages/man3/clock_gettime.3.html]:
8104 *
8105 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID) # => 198.650379677
8106 *
8107 * Argument +clock_id+ should be a symbol or a constant that specifies
8108 * the clock whose time is to be returned;
8109 * see below.
8110 *
8111 * Optional argument +unit+ should be a symbol that specifies
8112 * the unit to be used in the returned clock time;
8113 * see below.
8114 *
8115 * <b>Argument +clock_id+</b>
8116 *
8117 * Argument +clock_id+ specifies the clock whose time is to be returned;
8118 * it may be a constant such as <tt>Process::CLOCK_REALTIME</tt>,
8119 * or a symbol shorthand such as +:CLOCK_REALTIME+.
8120 *
8121 * The supported clocks depend on the underlying operating system;
8122 * this method supports the following clocks on the indicated platforms
8123 * (raises Errno::EINVAL if called with an unsupported clock):
8124 *
8125 * - +:CLOCK_BOOTTIME+: Linux 2.6.39.
8126 * - +:CLOCK_BOOTTIME_ALARM+: Linux 3.0.
8127 * - +:CLOCK_MONOTONIC+: SUSv3 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4, macOS 10.12, Windows-2000.
8128 * - +:CLOCK_MONOTONIC_COARSE+: Linux 2.6.32.
8129 * - +:CLOCK_MONOTONIC_FAST+: FreeBSD 8.1.
8130 * - +:CLOCK_MONOTONIC_PRECISE+: FreeBSD 8.1.
8131 * - +:CLOCK_MONOTONIC_RAW+: Linux 2.6.28, macOS 10.12.
8132 * - +:CLOCK_MONOTONIC_RAW_APPROX+: macOS 10.12.
8133 * - +:CLOCK_PROCESS_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 9.3, OpenBSD 5.4, macOS 10.12.
8134 * - +:CLOCK_PROF+: FreeBSD 3.0, OpenBSD 2.1.
8135 * - +: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.
8136 * Time.now is recommended over +:CLOCK_REALTIME:.
8137 * - +:CLOCK_REALTIME_ALARM+: Linux 3.0.
8138 * - +:CLOCK_REALTIME_COARSE+: Linux 2.6.32.
8139 * - +:CLOCK_REALTIME_FAST+: FreeBSD 8.1.
8140 * - +:CLOCK_REALTIME_PRECISE+: FreeBSD 8.1.
8141 * - +:CLOCK_SECOND+: FreeBSD 8.1.
8142 * - +:CLOCK_TAI+: Linux 3.10.
8143 * - +:CLOCK_THREAD_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 7.1, OpenBSD 5.4, macOS 10.12.
8144 * - +:CLOCK_UPTIME+: FreeBSD 7.0, OpenBSD 5.5.
8145 * - +:CLOCK_UPTIME_FAST+: FreeBSD 8.1.
8146 * - +:CLOCK_UPTIME_PRECISE+: FreeBSD 8.1.
8147 * - +:CLOCK_UPTIME_RAW+: macOS 10.12.
8148 * - +:CLOCK_UPTIME_RAW_APPROX+: macOS 10.12.
8149 * - +:CLOCK_VIRTUAL+: FreeBSD 3.0, OpenBSD 2.1.
8150 *
8151 * Note that SUS stands for Single Unix Specification.
8152 * SUS contains POSIX and clock_gettime is defined in the POSIX part.
8153 * SUS defines +:CLOCK_REALTIME+ as mandatory but
8154 * +:CLOCK_MONOTONIC+, +:CLOCK_PROCESS_CPUTIME_ID+,
8155 * and +:CLOCK_THREAD_CPUTIME_ID+ are optional.
8156 *
8157 * Certain emulations are used when the given +clock_id+
8158 * is not supported directly:
8159 *
8160 * - Emulations for +:CLOCK_REALTIME+:
8161 *
8162 * - +:GETTIMEOFDAY_BASED_CLOCK_REALTIME+:
8163 * Use gettimeofday() defined by SUS (deprecated in SUSv4).
8164 * The resolution is 1 microsecond.
8165 * - +:TIME_BASED_CLOCK_REALTIME+:
8166 * Use time() defined by ISO C.
8167 * The resolution is 1 second.
8168 *
8169 * - Emulations for +:CLOCK_MONOTONIC+:
8170 *
8171 * - +:MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC+:
8172 * Use mach_absolute_time(), available on Darwin.
8173 * The resolution is CPU dependent.
8174 * - +:TIMES_BASED_CLOCK_MONOTONIC+:
8175 * Use the result value of times() defined by POSIX, thus:
8176 * >>>
8177 * Upon successful completion, times() shall return the elapsed real time,
8178 * in clock ticks, since an arbitrary point in the past
8179 * (for example, system start-up time).
8180 *
8181 * For example, GNU/Linux returns a value based on jiffies and it is monotonic.
8182 * However, 4.4BSD uses gettimeofday() and it is not monotonic.
8183 * (FreeBSD uses +:CLOCK_MONOTONIC+ instead, though.)
8184 *
8185 * The resolution is the clock tick.
8186 * "getconf CLK_TCK" command shows the clock ticks per second.
8187 * (The clock ticks-per-second is defined by HZ macro in older systems.)
8188 * If it is 100 and clock_t is 32 bits integer type,
8189 * the resolution is 10 millisecond and cannot represent over 497 days.
8190 *
8191 * - Emulations for +:CLOCK_PROCESS_CPUTIME_ID+:
8192 *
8193 * - +:GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8194 * Use getrusage() defined by SUS.
8195 * getrusage() is used with RUSAGE_SELF to obtain the time only for
8196 * the calling process (excluding the time for child processes).
8197 * The result is addition of user time (ru_utime) and system time (ru_stime).
8198 * The resolution is 1 microsecond.
8199 * - +:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8200 * Use times() defined by POSIX.
8201 * The result is addition of user time (tms_utime) and system time (tms_stime).
8202 * tms_cutime and tms_cstime are ignored to exclude the time for child processes.
8203 * The resolution is the clock tick.
8204 * "getconf CLK_TCK" command shows the clock ticks per second.
8205 * (The clock ticks per second is defined by HZ macro in older systems.)
8206 * If it is 100, the resolution is 10 millisecond.
8207 * - +:CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8208 * Use clock() defined by ISO C.
8209 * The resolution is <tt>1/CLOCKS_PER_SEC</tt>.
8210 * +CLOCKS_PER_SEC+ is the C-level macro defined by time.h.
8211 * SUS defines +CLOCKS_PER_SEC+ as 1000000;
8212 * other systems may define it differently.
8213 * If +CLOCKS_PER_SEC+ is 1000000 (as in SUS),
8214 * the resolution is 1 microsecond.
8215 * If +CLOCKS_PER_SEC+ is 1000000 and clock_t is a 32-bit integer type,
8216 * it cannot represent over 72 minutes.
8217 *
8218 * <b>Argument +unit+</b>
8219 *
8220 * Optional argument +unit+ (default +:float_second+)
8221 * specifies the unit for the returned value.
8222 *
8223 * - +:float_microsecond+: Number of microseconds as a float.
8224 * - +:float_millisecond+: Number of milliseconds as a float.
8225 * - +:float_second+: Number of seconds as a float.
8226 * - +:microsecond+: Number of microseconds as an integer.
8227 * - +:millisecond+: Number of milliseconds as an integer.
8228 * - +:nanosecond+: Number of nanoseconds as an integer.
8229 * - +:second+: Number of seconds as an integer.
8230 *
8231 * Examples:
8232 *
8233 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond)
8234 * # => 203605054.825
8235 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond)
8236 * # => 203643.696848
8237 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_second)
8238 * # => 203.762181929
8239 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :microsecond)
8240 * # => 204123212
8241 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :millisecond)
8242 * # => 204298
8243 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond)
8244 * # => 204602286036
8245 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :second)
8246 * # => 204
8247 *
8248 * The underlying function, clock_gettime(), returns a number of nanoseconds.
8249 * Float object (IEEE 754 double) is not enough to represent
8250 * the return value for +:CLOCK_REALTIME+.
8251 * If the exact nanoseconds value is required, use +:nanosecond+ as the +unit+.
8252 *
8253 * The origin (time zero) of the returned value is system-dependent,
8254 * and may be, for example, system start up time,
8255 * process start up time, the Epoch, etc.
8256 *
8257 * The origin in +:CLOCK_REALTIME+ is defined as the Epoch:
8258 * <tt>1970-01-01 00:00:00 UTC</tt>;
8259 * some systems count leap seconds and others don't,
8260 * so the result may vary across systems.
8261 */
8262static VALUE
8263rb_clock_gettime(int argc, VALUE *argv, VALUE _)
8264{
8265 int ret;
8266
8267 struct timetick tt;
8268 timetick_int_t numerators[2];
8269 timetick_int_t denominators[2];
8270 int num_numerators = 0;
8271 int num_denominators = 0;
8272
8273 VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
8274 VALUE clk_id = argv[0];
8275#ifdef HAVE_CLOCK_GETTIME
8276 clockid_t c;
8277#endif
8278
8279 if (SYMBOL_P(clk_id)) {
8280#ifdef CLOCK_REALTIME
8281 if (clk_id == RUBY_CLOCK_REALTIME) {
8282 c = CLOCK_REALTIME;
8283 goto gettime;
8284 }
8285#endif
8286
8287#ifdef CLOCK_MONOTONIC
8288 if (clk_id == RUBY_CLOCK_MONOTONIC) {
8289 c = CLOCK_MONOTONIC;
8290 goto gettime;
8291 }
8292#endif
8293
8294#ifdef CLOCK_PROCESS_CPUTIME_ID
8295 if (clk_id == RUBY_CLOCK_PROCESS_CPUTIME_ID) {
8296 c = CLOCK_PROCESS_CPUTIME_ID;
8297 goto gettime;
8298 }
8299#endif
8300
8301#ifdef CLOCK_THREAD_CPUTIME_ID
8302 if (clk_id == RUBY_CLOCK_THREAD_CPUTIME_ID) {
8303 c = CLOCK_THREAD_CPUTIME_ID;
8304 goto gettime;
8305 }
8306#endif
8307
8308 /*
8309 * Non-clock_gettime clocks are provided by symbol clk_id.
8310 */
8311#ifdef HAVE_GETTIMEOFDAY
8312 /*
8313 * GETTIMEOFDAY_BASED_CLOCK_REALTIME is used for
8314 * CLOCK_REALTIME if clock_gettime is not available.
8315 */
8316#define RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME ID2SYM(id_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
8317 if (clk_id == RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME) {
8318 struct timeval tv;
8319 ret = gettimeofday(&tv, 0);
8320 if (ret != 0)
8321 rb_sys_fail("gettimeofday");
8322 tt.giga_count = tv.tv_sec;
8323 tt.count = (int32_t)tv.tv_usec * 1000;
8324 denominators[num_denominators++] = 1000000000;
8325 goto success;
8326 }
8327#endif
8328
8329#define RUBY_TIME_BASED_CLOCK_REALTIME ID2SYM(id_TIME_BASED_CLOCK_REALTIME)
8330 if (clk_id == RUBY_TIME_BASED_CLOCK_REALTIME) {
8331 time_t t;
8332 t = time(NULL);
8333 if (t == (time_t)-1)
8334 rb_sys_fail("time");
8335 tt.giga_count = t;
8336 tt.count = 0;
8337 denominators[num_denominators++] = 1000000000;
8338 goto success;
8339 }
8340
8341#ifdef HAVE_TIMES
8342#define RUBY_TIMES_BASED_CLOCK_MONOTONIC \
8343 ID2SYM(id_TIMES_BASED_CLOCK_MONOTONIC)
8344 if (clk_id == RUBY_TIMES_BASED_CLOCK_MONOTONIC) {
8345 struct tms buf;
8346 clock_t c;
8347 unsigned_clock_t uc;
8348 c = times(&buf);
8349 if (c == (clock_t)-1)
8350 rb_sys_fail("times");
8351 uc = (unsigned_clock_t)c;
8352 tt.count = (int32_t)(uc % 1000000000);
8353 tt.giga_count = (uc / 1000000000);
8354 denominators[num_denominators++] = get_clk_tck();
8355 goto success;
8356 }
8357#endif
8358
8359#ifdef RUSAGE_SELF
8360#define RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID \
8361 ID2SYM(id_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
8362 if (clk_id == RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8363 struct rusage usage;
8364 int32_t usec;
8365 ret = getrusage(RUSAGE_SELF, &usage);
8366 if (ret != 0)
8367 rb_sys_fail("getrusage");
8368 tt.giga_count = usage.ru_utime.tv_sec + usage.ru_stime.tv_sec;
8369 usec = (int32_t)(usage.ru_utime.tv_usec + usage.ru_stime.tv_usec);
8370 if (1000000 <= usec) {
8371 tt.giga_count++;
8372 usec -= 1000000;
8373 }
8374 tt.count = usec * 1000;
8375 denominators[num_denominators++] = 1000000000;
8376 goto success;
8377 }
8378#endif
8379
8380#ifdef HAVE_TIMES
8381#define RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID \
8382 ID2SYM(id_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID)
8383 if (clk_id == RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8384 struct tms buf;
8385 unsigned_clock_t utime, stime;
8386 if (times(&buf) == (clock_t)-1)
8387 rb_sys_fail("times");
8388 utime = (unsigned_clock_t)buf.tms_utime;
8389 stime = (unsigned_clock_t)buf.tms_stime;
8390 tt.count = (int32_t)((utime % 1000000000) + (stime % 1000000000));
8391 tt.giga_count = (utime / 1000000000) + (stime / 1000000000);
8392 if (1000000000 <= tt.count) {
8393 tt.count -= 1000000000;
8394 tt.giga_count++;
8395 }
8396 denominators[num_denominators++] = get_clk_tck();
8397 goto success;
8398 }
8399#endif
8400
8401#define RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID \
8402 ID2SYM(id_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID)
8403 if (clk_id == RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8404 clock_t c;
8405 unsigned_clock_t uc;
8406 errno = 0;
8407 c = clock();
8408 if (c == (clock_t)-1)
8409 rb_sys_fail("clock");
8410 uc = (unsigned_clock_t)c;
8411 tt.count = (int32_t)(uc % 1000000000);
8412 tt.giga_count = uc / 1000000000;
8413 denominators[num_denominators++] = CLOCKS_PER_SEC;
8414 goto success;
8415 }
8416
8417#ifdef __APPLE__
8418 if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
8419 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8420 uint64_t t = mach_absolute_time();
8421 tt.count = (int32_t)(t % 1000000000);
8422 tt.giga_count = t / 1000000000;
8423 numerators[num_numerators++] = info->numer;
8424 denominators[num_denominators++] = info->denom;
8425 denominators[num_denominators++] = 1000000000;
8426 goto success;
8427 }
8428#endif
8429 }
8430 else if (NUMERIC_CLOCKID) {
8431#if defined(HAVE_CLOCK_GETTIME)
8432 struct timespec ts;
8433 c = NUM2CLOCKID(clk_id);
8434 gettime:
8435 ret = clock_gettime(c, &ts);
8436 if (ret == -1)
8437 clock_failed("gettime", errno, clk_id);
8438 tt.count = (int32_t)ts.tv_nsec;
8439 tt.giga_count = ts.tv_sec;
8440 denominators[num_denominators++] = 1000000000;
8441 goto success;
8442#endif
8443 }
8444 else {
8446 }
8447 clock_failed("gettime", EINVAL, clk_id);
8448
8449 success:
8450 return make_clock_result(&tt, numerators, num_numerators, denominators, num_denominators, unit);
8451}
8452
8453/*
8454 * call-seq:
8455 * Process.clock_getres(clock_id, unit = :float_second) -> number
8456 *
8457 * Returns a clock resolution as determined by POSIX function
8458 * {clock_getres()}[https://man7.org/linux/man-pages/man3/clock_getres.3.html]:
8459 *
8460 * Process.clock_getres(:CLOCK_REALTIME) # => 1.0e-09
8461 *
8462 * See Process.clock_gettime for the values of +clock_id+ and +unit+.
8463 *
8464 * Examples:
8465 *
8466 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond) # => 0.001
8467 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond) # => 1.0e-06
8468 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 1.0e-09
8469 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :microsecond) # => 0
8470 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :millisecond) # => 0
8471 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond) # => 1
8472 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :second) # => 0
8473 *
8474 * In addition to the values for +unit+ supported in Process.clock_gettime,
8475 * this method supports +:hertz+, the integer number of clock ticks per second
8476 * (which is the reciprocal of +:float_second+):
8477 *
8478 * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz) # => 100.0
8479 * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 0.01
8480 *
8481 * <b>Accuracy</b>:
8482 * Note that the returned resolution may be inaccurate on some platforms
8483 * due to underlying bugs.
8484 * Inaccurate resolutions have been reported for various clocks including
8485 * +:CLOCK_MONOTONIC+ and +:CLOCK_MONOTONIC_RAW+
8486 * on Linux, macOS, BSD or AIX platforms, when using ARM processors,
8487 * or when using virtualization.
8488 */
8489static VALUE
8490rb_clock_getres(int argc, VALUE *argv, VALUE _)
8491{
8492 int ret;
8493
8494 struct timetick tt;
8495 timetick_int_t numerators[2];
8496 timetick_int_t denominators[2];
8497 int num_numerators = 0;
8498 int num_denominators = 0;
8499#ifdef HAVE_CLOCK_GETRES
8500 clockid_t c;
8501#endif
8502
8503 VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
8504 VALUE clk_id = argv[0];
8505
8506 if (SYMBOL_P(clk_id)) {
8507#ifdef CLOCK_REALTIME
8508 if (clk_id == RUBY_CLOCK_REALTIME) {
8509 c = CLOCK_REALTIME;
8510 goto getres;
8511 }
8512#endif
8513
8514#ifdef CLOCK_MONOTONIC
8515 if (clk_id == RUBY_CLOCK_MONOTONIC) {
8516 c = CLOCK_MONOTONIC;
8517 goto getres;
8518 }
8519#endif
8520
8521#ifdef CLOCK_PROCESS_CPUTIME_ID
8522 if (clk_id == RUBY_CLOCK_PROCESS_CPUTIME_ID) {
8523 c = CLOCK_PROCESS_CPUTIME_ID;
8524 goto getres;
8525 }
8526#endif
8527
8528#ifdef CLOCK_THREAD_CPUTIME_ID
8529 if (clk_id == RUBY_CLOCK_THREAD_CPUTIME_ID) {
8530 c = CLOCK_THREAD_CPUTIME_ID;
8531 goto getres;
8532 }
8533#endif
8534
8535#ifdef RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
8536 if (clk_id == RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME) {
8537 tt.giga_count = 0;
8538 tt.count = 1000;
8539 denominators[num_denominators++] = 1000000000;
8540 goto success;
8541 }
8542#endif
8543
8544#ifdef RUBY_TIME_BASED_CLOCK_REALTIME
8545 if (clk_id == RUBY_TIME_BASED_CLOCK_REALTIME) {
8546 tt.giga_count = 1;
8547 tt.count = 0;
8548 denominators[num_denominators++] = 1000000000;
8549 goto success;
8550 }
8551#endif
8552
8553#ifdef RUBY_TIMES_BASED_CLOCK_MONOTONIC
8554 if (clk_id == RUBY_TIMES_BASED_CLOCK_MONOTONIC) {
8555 tt.count = 1;
8556 tt.giga_count = 0;
8557 denominators[num_denominators++] = get_clk_tck();
8558 goto success;
8559 }
8560#endif
8561
8562#ifdef RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
8563 if (clk_id == RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8564 tt.giga_count = 0;
8565 tt.count = 1000;
8566 denominators[num_denominators++] = 1000000000;
8567 goto success;
8568 }
8569#endif
8570
8571#ifdef RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
8572 if (clk_id == RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8573 tt.count = 1;
8574 tt.giga_count = 0;
8575 denominators[num_denominators++] = get_clk_tck();
8576 goto success;
8577 }
8578#endif
8579
8580#ifdef RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
8581 if (clk_id == RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8582 tt.count = 1;
8583 tt.giga_count = 0;
8584 denominators[num_denominators++] = CLOCKS_PER_SEC;
8585 goto success;
8586 }
8587#endif
8588
8589#ifdef RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
8590 if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
8591 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8592 tt.count = 1;
8593 tt.giga_count = 0;
8594 numerators[num_numerators++] = info->numer;
8595 denominators[num_denominators++] = info->denom;
8596 denominators[num_denominators++] = 1000000000;
8597 goto success;
8598 }
8599#endif
8600 }
8601 else if (NUMERIC_CLOCKID) {
8602#if defined(HAVE_CLOCK_GETRES)
8603 struct timespec ts;
8604 c = NUM2CLOCKID(clk_id);
8605 getres:
8606 ret = clock_getres(c, &ts);
8607 if (ret == -1)
8608 clock_failed("getres", errno, clk_id);
8609 tt.count = (int32_t)ts.tv_nsec;
8610 tt.giga_count = ts.tv_sec;
8611 denominators[num_denominators++] = 1000000000;
8612 goto success;
8613#endif
8614 }
8615 else {
8617 }
8618 clock_failed("getres", EINVAL, clk_id);
8619
8620 success:
8621 if (unit == ID2SYM(id_hertz)) {
8622 return timetick2dblnum_reciprocal(&tt, numerators, num_numerators, denominators, num_denominators);
8623 }
8624 else {
8625 return make_clock_result(&tt, numerators, num_numerators, denominators, num_denominators, unit);
8626 }
8627}
8628
8629static VALUE
8630get_CHILD_STATUS(ID _x, VALUE *_y)
8631{
8632 return rb_last_status_get();
8633}
8634
8635static VALUE
8636get_PROCESS_ID(ID _x, VALUE *_y)
8637{
8638 return get_pid();
8639}
8640
8641/*
8642 * call-seq:
8643 * Process.kill(signal, *ids) -> count
8644 *
8645 * Sends a signal to each process specified by +ids+
8646 * (which must specify at least one ID);
8647 * returns the count of signals sent.
8648 *
8649 * For each given +id+, if +id+ is:
8650 *
8651 * - Positive, sends the signal to the process whose process ID is +id+.
8652 * - Zero, send the signal to all processes in the current process group.
8653 * - Negative, sends the signal to a system-dependent collection of processes.
8654 *
8655 * Argument +signal+ specifies the signal to be sent;
8656 * the argument may be:
8657 *
8658 * - An integer signal number: e.g., +-29+, +0+, +29+.
8659 * - A signal name (string), with or without leading <tt>'SIG'</tt>,
8660 * and with or without a further prefixed minus sign (<tt>'-'</tt>):
8661 * e.g.:
8662 *
8663 * - <tt>'SIGPOLL'</tt>.
8664 * - <tt>'POLL'</tt>,
8665 * - <tt>'-SIGPOLL'</tt>.
8666 * - <tt>'-POLL'</tt>.
8667 *
8668 * - A signal symbol, with or without leading <tt>'SIG'</tt>,
8669 * and with or without a further prefixed minus sign (<tt>'-'</tt>):
8670 * e.g.:
8671 *
8672 * - +:SIGPOLL+.
8673 * - +:POLL+.
8674 * - <tt>:'-SIGPOLL'</tt>.
8675 * - <tt>:'-POLL'</tt>.
8676 *
8677 * If +signal+ is:
8678 *
8679 * - A non-negative integer, or a signal name or symbol
8680 * without prefixed <tt>'-'</tt>,
8681 * each process with process ID +id+ is signalled.
8682 * - A negative integer, or a signal name or symbol
8683 * with prefixed <tt>'-'</tt>,
8684 * each process group with group ID +id+ is signalled.
8685 *
8686 * Use method Signal.list to see which signals are supported
8687 * by Ruby on the underlying platform;
8688 * the method returns a hash of the string names
8689 * and non-negative integer values of the supported signals.
8690 * The size and content of the returned hash varies widely
8691 * among platforms.
8692 *
8693 * Additionally, signal +0+ is useful to determine if the process exists.
8694 *
8695 * Example:
8696 *
8697 * pid = fork do
8698 * Signal.trap('HUP') { puts 'Ouch!'; exit }
8699 * # ... do some work ...
8700 * end
8701 * # ...
8702 * Process.kill('HUP', pid)
8703 * Process.wait
8704 *
8705 * Output:
8706 *
8707 * Ouch!
8708 *
8709 * Exceptions:
8710 *
8711 * - Raises Errno::EINVAL or RangeError if +signal+ is an integer
8712 * but invalid.
8713 * - Raises ArgumentError if +signal+ is a string or symbol
8714 * but invalid.
8715 * - Raises Errno::ESRCH or RangeError if one of +ids+ is invalid.
8716 * - Raises Errno::EPERM if needed permissions are not in force.
8717 *
8718 * In the last two cases, signals may have been sent to some processes.
8719 */
8720
8721static VALUE
8722proc_rb_f_kill(int c, const VALUE *v, VALUE _)
8723{
8724 return rb_f_kill(c, v);
8725}
8726
8728static VALUE rb_mProcUID;
8729static VALUE rb_mProcGID;
8730static VALUE rb_mProcID_Syscall;
8731
8732/*
8733 * call-seq:
8734 * Process.warmup -> true
8735 *
8736 * Notify the Ruby virtual machine that the boot sequence is finished,
8737 * and that now is a good time to optimize the application. This is useful
8738 * for long running applications.
8739 *
8740 * This method is expected to be called at the end of the application boot.
8741 * If the application is deployed using a pre-forking model, +Process.warmup+
8742 * should be called in the original process before the first fork.
8743 *
8744 * The actual optimizations performed are entirely implementation specific
8745 * and may change in the future without notice.
8746 *
8747 * On CRuby, +Process.warmup+:
8748 *
8749 * * Performs a major GC.
8750 * * Compacts the heap.
8751 * * Promotes all surviving objects to the old generation.
8752 * * Precomputes the coderange of all strings.
8753 * * Frees all empty heap pages and increments the allocatable pages counter
8754 * by the number of pages freed.
8755 * * Invoke +malloc_trim+ if available to free empty malloc pages.
8756 */
8757
8758static VALUE
8759proc_warmup(VALUE _)
8760{
8761 RB_VM_LOCKING() {
8762 rb_gc_prepare_heap();
8763 }
8764 return Qtrue;
8765}
8766
8767/*
8768 * Document-module: Process
8769 *
8770 * Module +Process+ represents a process in the underlying operating system.
8771 * Its methods support management of the current process and its child processes.
8772 *
8773 * == Process Creation
8774 *
8775 * Each of the following methods executes a given command in a new process or subshell,
8776 * or multiple commands in new processes and/or subshells.
8777 * The choice of process or subshell depends on the form of the command;
8778 * see {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
8779 *
8780 * - Process.spawn, Kernel#spawn: Executes the command;
8781 * returns the new pid without waiting for completion.
8782 * - Process.exec: Replaces the current process by executing the command.
8783 *
8784 * In addition:
8785 *
8786 * - Method Kernel#system executes a given command-line (string) in a subshell;
8787 * returns +true+, +false+, or +nil+.
8788 * - Method Kernel#` executes a given command-line (string) in a subshell;
8789 * returns its $stdout string.
8790 * - Module Open3 supports creating child processes
8791 * with access to their $stdin, $stdout, and $stderr streams.
8792 *
8793 * === Execution Environment
8794 *
8795 * Optional leading argument +env+ is a hash of name/value pairs,
8796 * where each name is a string and each value is a string or +nil+;
8797 * each name/value pair is added to ENV in the new process.
8798 *
8799 * Process.spawn( 'ruby -e "p ENV[\"Foo\"]"')
8800 * Process.spawn({'Foo' => '0'}, 'ruby -e "p ENV[\"Foo\"]"')
8801 *
8802 * Output:
8803 *
8804 * "0"
8805 *
8806 * The effect is usually similar to that of calling ENV#update with argument +env+,
8807 * where each named environment variable is created or updated
8808 * (if the value is non-+nil+),
8809 * or deleted (if the value is +nil+).
8810 *
8811 * However, some modifications to the calling process may remain
8812 * if the new process fails.
8813 * For example, hard resource limits are not restored.
8814 *
8815 * === Argument +command_line+ or +exe_path+
8816 *
8817 * The required string argument is one of the following:
8818 *
8819 * - +command_line+ if it begins with a shell reserved word or special built-in,
8820 * or if it contains one or more meta characters.
8821 * - +exe_path+ otherwise.
8822 *
8823 * ==== Argument +command_line+
8824 *
8825 * \String argument +command_line+ is a command line to be passed to a shell;
8826 * it must begin with a shell reserved word, begin with a special built-in,
8827 * or contain meta characters:
8828 *
8829 * system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
8830 * system('exit') # => true # Built-in.
8831 * system('date > /tmp/date.tmp') # => true # Contains meta character.
8832 * system('date > /nop/date.tmp') # => false
8833 * system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
8834 *
8835 * The command line may also contain arguments and options for the command:
8836 *
8837 * system('echo "Foo"') # => true
8838 *
8839 * Output:
8840 *
8841 * Foo
8842 *
8843 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
8844 *
8845 * ==== Argument +exe_path+
8846 *
8847 * Argument +exe_path+ is one of the following:
8848 *
8849 * - The string path to an executable file to be called:
8850 *
8851 * Example:
8852 *
8853 * system('/usr/bin/date') # => true # Path to date on Unix-style system.
8854 * system('foo') # => nil # Command execlution failed.
8855 *
8856 * Output:
8857 *
8858 * Thu Aug 31 10:06:48 AM CDT 2023
8859 *
8860 * A path or command name containing spaces without arguments cannot
8861 * be distinguished from +command_line+ above, so you must quote or
8862 * escape the entire command name using a shell in platform
8863 * dependent manner, or use the array form below.
8864 *
8865 * If +exe_path+ does not contain any path separator, an executable
8866 * file is searched from directories specified with the +PATH+
8867 * environment variable. What the word "executable" means here is
8868 * depending on platforms.
8869 *
8870 * Even if the file considered "executable", its content may not be
8871 * in proper executable format. In that case, Ruby tries to run it
8872 * by using <tt>/bin/sh</tt> on a Unix-like system, like system(3)
8873 * does.
8874 *
8875 * File.write('shell_command', 'echo $SHELL', perm: 0o755)
8876 * system('./shell_command') # prints "/bin/sh" or something.
8877 *
8878 * - A 2-element array containing the path to an executable
8879 * and the string to be used as the name of the executing process:
8880 *
8881 * Example:
8882 *
8883 * pid = spawn(['sleep', 'Hello!'], '1') # 2-element array.
8884 * p `ps -p #{pid} -o command=`
8885 *
8886 * Output:
8887 *
8888 * "Hello! 1\n"
8889 *
8890 * === Arguments +args+
8891 *
8892 * If +command_line+ does not contain shell meta characters except for
8893 * spaces and tabs, or +exe_path+ is given, Ruby invokes the
8894 * executable directly. This form does not use the shell:
8895 *
8896 * spawn("doesnt_exist") # Raises Errno::ENOENT
8897 * spawn("doesnt_exist", "\n") # Raises Errno::ENOENT
8898 *
8899 * spawn("doesnt_exist\n") # => false
8900 * # sh: 1: doesnot_exist: not found
8901 *
8902 * The error message is from a shell and would vary depending on your
8903 * system.
8904 *
8905 * If one or more +args+ is given after +exe_path+, each is an
8906 * argument or option to be passed to the executable:
8907 *
8908 * Example:
8909 *
8910 * system('echo', '<', 'C*', '|', '$SHELL', '>') # => true
8911 *
8912 * Output:
8913 *
8914 * < C* | $SHELL >
8915 *
8916 * However, there are exceptions on Windows. See {Execution Shell on
8917 * Windows}[rdoc-ref:Process@Execution+Shell+on+Windows].
8918 *
8919 * If you want to invoke a path containing spaces with no arguments
8920 * without shell, you will need to use a 2-element array +exe_path+.
8921 *
8922 * Example:
8923 *
8924 * path = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
8925 * spawn(path) # Raises Errno::ENOENT; No such file or directory - /Applications/Google
8926 * spawn([path] * 2)
8927 *
8928 * === Execution Options
8929 *
8930 * Optional trailing argument +options+ is a hash of execution options.
8931 *
8932 * ==== Working Directory (+:chdir+)
8933 *
8934 * By default, the working directory for the new process is the same as
8935 * that of the current process:
8936 *
8937 * Dir.chdir('/var')
8938 * Process.spawn('ruby -e "puts Dir.pwd"')
8939 *
8940 * Output:
8941 *
8942 * /var
8943 *
8944 * Use option +:chdir+ to set the working directory for the new process:
8945 *
8946 * Process.spawn('ruby -e "puts Dir.pwd"', {chdir: '/tmp'})
8947 *
8948 * Output:
8949 *
8950 * /tmp
8951 *
8952 * The working directory of the current process is not changed:
8953 *
8954 * Dir.pwd # => "/var"
8955 *
8956 * ==== \File Redirection (\File Descriptor)
8957 *
8958 * Use execution options for file redirection in the new process.
8959 *
8960 * The key for such an option may be an integer file descriptor (fd),
8961 * specifying a source,
8962 * or an array of fds, specifying multiple sources.
8963 *
8964 * An integer source fd may be specified as:
8965 *
8966 * - _n_: Specifies file descriptor _n_.
8967 *
8968 * There are these shorthand symbols for fds:
8969 *
8970 * - +:in+: Specifies file descriptor 0 (STDIN).
8971 * - +:out+: Specifies file descriptor 1 (STDOUT).
8972 * - +:err+: Specifies file descriptor 2 (STDERR).
8973 *
8974 * The value given with a source is one of:
8975 *
8976 * - _n_:
8977 * Redirects to fd _n_ in the parent process.
8978 * - +filepath+:
8979 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>,
8980 * where +mode+ is <tt>'r'</tt> for source +:in+,
8981 * or <tt>'w'</tt> for source +:out+ or +:err+.
8982 * - <tt>[filepath]</tt>:
8983 * Redirects from the file at +filepath+ via <tt>open(filepath, 'r', 0644)</tt>.
8984 * - <tt>[filepath, mode]</tt>:
8985 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>.
8986 * - <tt>[filepath, mode, perm]</tt>:
8987 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, perm)</tt>.
8988 * - <tt>[:child, fd]</tt>:
8989 * Redirects to the redirected +fd+.
8990 * - +:close+: Closes the file descriptor in child process.
8991 *
8992 * See {Access Modes}[rdoc-ref:File@Access+Modes]
8993 * and {File Permissions}[rdoc-ref:File@File+Permissions].
8994 *
8995 * ==== Environment Variables (+:unsetenv_others+)
8996 *
8997 * By default, the new process inherits environment variables
8998 * from the parent process;
8999 * use execution option key +:unsetenv_others+ with value +true+
9000 * to clear environment variables in the new process.
9001 *
9002 * Any changes specified by execution option +env+ are made after the new process
9003 * inherits or clears its environment variables;
9004 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
9005 *
9006 * ==== \File-Creation Access (+:umask+)
9007 *
9008 * Use execution option +:umask+ to set the file-creation access
9009 * for the new process;
9010 * see {Access Modes}[rdoc-ref:File@Access+Modes]:
9011 *
9012 * command = 'ruby -e "puts sprintf(\"0%o\", File.umask)"'
9013 * options = {:umask => 0644}
9014 * Process.spawn(command, options)
9015 *
9016 * Output:
9017 *
9018 * 0644
9019 *
9020 * ==== Process Groups (+:pgroup+ and +:new_pgroup+)
9021 *
9022 * By default, the new process belongs to the same
9023 * {process group}[https://en.wikipedia.org/wiki/Process_group]
9024 * as the parent process.
9025 *
9026 * To specify a different process group.
9027 * use execution option +:pgroup+ with one of the following values:
9028 *
9029 * - +true+: Create a new process group for the new process.
9030 * - _pgid_: Create the new process in the process group
9031 * whose id is _pgid_.
9032 *
9033 * On Windows only, use execution option +:new_pgroup+ with value +true+
9034 * to create a new process group for the new process.
9035 *
9036 * ==== Resource Limits
9037 *
9038 * Use execution options to set resource limits.
9039 *
9040 * The keys for these options are symbols of the form
9041 * <tt>:rlimit_<i>resource_name</i></tt>,
9042 * where _resource_name_ is the downcased form of one of the string
9043 * resource names described at method Process.setrlimit.
9044 * For example, key +:rlimit_cpu+ corresponds to resource limit <tt>'CPU'</tt>.
9045 *
9046 * The value for such as key is one of:
9047 *
9048 * - An integer, specifying both the current and maximum limits.
9049 * - A 2-element array of integers, specifying the current and maximum limits.
9050 *
9051 * ==== \File Descriptor Inheritance
9052 *
9053 * By default, the new process inherits file descriptors from the parent process.
9054 *
9055 * Use execution option <tt>:close_others => true</tt> to modify that inheritance
9056 * by closing non-standard fds (3 and greater) that are not otherwise redirected.
9057 *
9058 * === Execution Shell
9059 *
9060 * On a Unix-like system, the shell invoked is <tt>/bin/sh</tt>;
9061 * the entire string +command_line+ is passed as an argument
9062 * to {shell option -c}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/sh.html].
9063 *
9064 * The shell performs normal shell expansion on the command line:
9065 *
9066 * Example:
9067 *
9068 * system('echo $SHELL: C*') # => true
9069 *
9070 * Output:
9071 *
9072 * /bin/bash: CONTRIBUTING.md COPYING COPYING.ja
9073 *
9074 * ==== Execution Shell on Windows
9075 *
9076 * On Windows, the shell invoked is determined by environment variable
9077 * +RUBYSHELL+, if defined, or +COMSPEC+ otherwise; the entire string
9078 * +command_line+ is passed as an argument to <tt>-c</tt> option for
9079 * +RUBYSHELL+, as well as <tt>/bin/sh</tt>, and {/c
9080 * option}[https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/cmd]
9081 * for +COMSPEC+. The shell is invoked automatically in the following
9082 * cases:
9083 *
9084 * - The command is a built-in of +cmd.exe+, such as +echo+.
9085 * - The executable file is a batch file; its name ends with +.bat+ or
9086 * +.cmd+.
9087 *
9088 * Note that the command will still be invoked as +command_line+ form
9089 * even when called in +exe_path+ form, because +cmd.exe+ does not
9090 * accept a script name like <tt>/bin/sh</tt> does but only works with
9091 * <tt>/c</tt> option.
9092 *
9093 * The standard shell +cmd.exe+ performs environment variable
9094 * expansion but does not have globbing functionality:
9095 *
9096 * Example:
9097 *
9098 * system("echo %COMSPEC%: C*")' # => true
9099 *
9100 * Output:
9101 *
9102 * C:\WINDOWS\system32\cmd.exe: C*
9103 *
9104 * == What's Here
9105 *
9106 * === Current-Process Getters
9107 *
9108 * - ::argv0: Returns the process name as a frozen string.
9109 * - ::egid: Returns the effective group ID.
9110 * - ::euid: Returns the effective user ID.
9111 * - ::getpgrp: Return the process group ID.
9112 * - ::getrlimit: Returns the resource limit.
9113 * - ::gid: Returns the (real) group ID.
9114 * - ::pid: Returns the process ID.
9115 * - ::ppid: Returns the process ID of the parent process.
9116 * - ::uid: Returns the (real) user ID.
9117 *
9118 * === Current-Process Setters
9119 *
9120 * - ::egid=: Sets the effective group ID.
9121 * - ::euid=: Sets the effective user ID.
9122 * - ::gid=: Sets the (real) group ID.
9123 * - ::setproctitle: Sets the process title.
9124 * - ::setpgrp: Sets the process group ID of the process to zero.
9125 * - ::setrlimit: Sets a resource limit.
9126 * - ::setsid: Establishes the process as a new session and process group leader,
9127 * with no controlling tty.
9128 * - ::uid=: Sets the user ID.
9129 *
9130 * === Current-Process Execution
9131 *
9132 * - ::abort: Immediately terminates the process.
9133 * - ::daemon: Detaches the process from its controlling terminal
9134 * and continues running it in the background as system daemon.
9135 * - ::exec: Replaces the process by running a given external command.
9136 * - ::exit: Initiates process termination by raising exception SystemExit
9137 * (which may be caught).
9138 * - ::exit!: Immediately exits the process.
9139 * - ::warmup: Notifies the Ruby virtual machine that the boot sequence
9140 * for the application is completed,
9141 * and that the VM may begin optimizing the application.
9142 *
9143 * === Child Processes
9144 *
9145 * - ::detach: Guards against a child process becoming a zombie.
9146 * - ::fork: Creates a child process.
9147 * - ::kill: Sends a given signal to processes.
9148 * - ::spawn: Creates a child process.
9149 * - ::wait, ::waitpid: Waits for a child process to exit; returns its process ID.
9150 * - ::wait2, ::waitpid2: Waits for a child process to exit; returns its process ID and status.
9151 * - ::waitall: Waits for all child processes to exit;
9152 * returns their process IDs and statuses.
9153 *
9154 * === Process Groups
9155 *
9156 * - ::getpgid: Returns the process group ID for a process.
9157 * - ::getpriority: Returns the scheduling priority
9158 * for a process, process group, or user.
9159 * - ::getsid: Returns the session ID for a process.
9160 * - ::groups: Returns an array of the group IDs
9161 * in the supplemental group access list for this process.
9162 * - ::groups=: Sets the supplemental group access list
9163 * to the given array of group IDs.
9164 * - ::initgroups: Initializes the supplemental group access list.
9165 * - ::last_status: Returns the status of the last executed child process
9166 * in the current thread.
9167 * - ::maxgroups: Returns the maximum number of group IDs allowed
9168 * in the supplemental group access list.
9169 * - ::maxgroups=: Sets the maximum number of group IDs allowed
9170 * in the supplemental group access list.
9171 * - ::setpgid: Sets the process group ID of a process.
9172 * - ::setpriority: Sets the scheduling priority
9173 * for a process, process group, or user.
9174 *
9175 * === Timing
9176 *
9177 * - ::clock_getres: Returns the resolution of a system clock.
9178 * - ::clock_gettime: Returns the time from a system clock.
9179 * - ::times: Returns a Process::Tms object containing times
9180 * for the current process and its child processes.
9181 *
9182 */
9183
9184void
9185InitVM_process(void)
9186{
9187 rb_define_virtual_variable("$?", get_CHILD_STATUS, 0);
9188 rb_define_virtual_variable("$$", get_PROCESS_ID, 0);
9189
9190 rb_gvar_ractor_local("$$");
9191 rb_gvar_ractor_local("$?");
9192
9193 rb_define_global_function("exec", f_exec, -1);
9194 rb_define_global_function("fork", rb_f_fork, 0);
9195 rb_define_global_function("exit!", rb_f_exit_bang, -1);
9196 rb_define_global_function("system", rb_f_system, -1);
9197 rb_define_global_function("spawn", rb_f_spawn, -1);
9198 rb_define_global_function("sleep", rb_f_sleep, -1);
9199 rb_define_global_function("exit", f_exit, -1);
9200 rb_define_global_function("abort", f_abort, -1);
9201
9202 rb_mProcess = rb_define_module("Process");
9203
9204#ifdef WNOHANG
9205 /* see Process.wait */
9206 rb_define_const(rb_mProcess, "WNOHANG", INT2FIX(WNOHANG));
9207#else
9208 /* see Process.wait */
9209 rb_define_const(rb_mProcess, "WNOHANG", INT2FIX(0));
9210#endif
9211#ifdef WUNTRACED
9212 /* see Process.wait */
9213 rb_define_const(rb_mProcess, "WUNTRACED", INT2FIX(WUNTRACED));
9214#else
9215 /* see Process.wait */
9216 rb_define_const(rb_mProcess, "WUNTRACED", INT2FIX(0));
9217#endif
9218
9219 rb_define_singleton_method(rb_mProcess, "exec", f_exec, -1);
9220 rb_define_singleton_method(rb_mProcess, "fork", rb_f_fork, 0);
9221 rb_define_singleton_method(rb_mProcess, "spawn", rb_f_spawn, -1);
9222 rb_define_singleton_method(rb_mProcess, "exit!", rb_f_exit_bang, -1);
9223 rb_define_singleton_method(rb_mProcess, "exit", f_exit, -1);
9224 rb_define_singleton_method(rb_mProcess, "abort", f_abort, -1);
9225 rb_define_singleton_method(rb_mProcess, "last_status", proc_s_last_status, 0);
9226 rb_define_singleton_method(rb_mProcess, "_fork", rb_proc__fork, 0);
9227
9228 rb_define_module_function(rb_mProcess, "kill", proc_rb_f_kill, -1);
9229 rb_define_module_function(rb_mProcess, "wait", proc_m_wait, -1);
9230 rb_define_module_function(rb_mProcess, "wait2", proc_wait2, -1);
9231 rb_define_module_function(rb_mProcess, "waitpid", proc_m_wait, -1);
9232 rb_define_module_function(rb_mProcess, "waitpid2", proc_wait2, -1);
9233 rb_define_module_function(rb_mProcess, "waitall", proc_waitall, 0);
9234 rb_define_module_function(rb_mProcess, "detach", proc_detach, 1);
9235
9236 /* :nodoc: */
9237 rb_cWaiter = rb_define_class_under(rb_mProcess, "Waiter", rb_cThread);
9238 rb_undef_alloc_func(rb_cWaiter);
9239 rb_undef_method(CLASS_OF(rb_cWaiter), "new");
9240 rb_define_method(rb_cWaiter, "pid", detach_process_pid, 0);
9241
9242 rb_cProcessStatus = rb_define_class_under(rb_mProcess, "Status", rb_cObject);
9243 rb_define_alloc_func(rb_cProcessStatus, rb_process_status_allocate);
9244 rb_undef_method(CLASS_OF(rb_cProcessStatus), "new");
9245 rb_marshal_define_compat(rb_cProcessStatus, rb_cObject,
9246 process_status_dump, process_status_load);
9247
9248 rb_define_singleton_method(rb_cProcessStatus, "wait", rb_process_status_waitv, -1);
9249
9250 rb_define_method(rb_cProcessStatus, "==", pst_equal, 1);
9251 rb_define_method(rb_cProcessStatus, "to_i", pst_to_i, 0);
9252 rb_define_method(rb_cProcessStatus, "to_s", pst_to_s, 0);
9253 rb_define_method(rb_cProcessStatus, "inspect", pst_inspect, 0);
9254
9255 rb_define_method(rb_cProcessStatus, "pid", pst_pid_m, 0);
9256
9257 rb_define_method(rb_cProcessStatus, "stopped?", pst_wifstopped, 0);
9258 rb_define_method(rb_cProcessStatus, "stopsig", pst_wstopsig, 0);
9259 rb_define_method(rb_cProcessStatus, "signaled?", pst_wifsignaled, 0);
9260 rb_define_method(rb_cProcessStatus, "termsig", pst_wtermsig, 0);
9261 rb_define_method(rb_cProcessStatus, "exited?", pst_wifexited, 0);
9262 rb_define_method(rb_cProcessStatus, "exitstatus", pst_wexitstatus, 0);
9263 rb_define_method(rb_cProcessStatus, "success?", pst_success_p, 0);
9264 rb_define_method(rb_cProcessStatus, "coredump?", pst_wcoredump, 0);
9265
9266 rb_define_module_function(rb_mProcess, "pid", proc_get_pid, 0);
9267 rb_define_module_function(rb_mProcess, "ppid", proc_get_ppid, 0);
9268
9269 rb_define_module_function(rb_mProcess, "getpgrp", proc_getpgrp, 0);
9270 rb_define_module_function(rb_mProcess, "setpgrp", proc_setpgrp, 0);
9271 rb_define_module_function(rb_mProcess, "getpgid", proc_getpgid, 1);
9272 rb_define_module_function(rb_mProcess, "setpgid", proc_setpgid, 2);
9273
9274 rb_define_module_function(rb_mProcess, "getsid", proc_getsid, -1);
9275 rb_define_module_function(rb_mProcess, "setsid", proc_setsid, 0);
9276
9277 rb_define_module_function(rb_mProcess, "getpriority", proc_getpriority, 2);
9278 rb_define_module_function(rb_mProcess, "setpriority", proc_setpriority, 3);
9279
9280 rb_define_module_function(rb_mProcess, "warmup", proc_warmup, 0);
9281
9282#ifdef HAVE_GETPRIORITY
9283 /* see Process.setpriority */
9284 rb_define_const(rb_mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS));
9285 /* see Process.setpriority */
9286 rb_define_const(rb_mProcess, "PRIO_PGRP", INT2FIX(PRIO_PGRP));
9287 /* see Process.setpriority */
9288 rb_define_const(rb_mProcess, "PRIO_USER", INT2FIX(PRIO_USER));
9289#endif
9290
9291 rb_define_module_function(rb_mProcess, "getrlimit", proc_getrlimit, 1);
9292 rb_define_module_function(rb_mProcess, "setrlimit", proc_setrlimit, -1);
9293#if defined(RLIM2NUM) && defined(RLIM_INFINITY)
9294 {
9295 VALUE inf = RLIM2NUM(RLIM_INFINITY);
9296#ifdef RLIM_SAVED_MAX
9297 {
9298 VALUE v = RLIM_INFINITY == RLIM_SAVED_MAX ? inf : RLIM2NUM(RLIM_SAVED_MAX);
9299 /* see Process.setrlimit */
9300 rb_define_const(rb_mProcess, "RLIM_SAVED_MAX", v);
9301 }
9302#endif
9303 /* see Process.setrlimit */
9304 rb_define_const(rb_mProcess, "RLIM_INFINITY", inf);
9305#ifdef RLIM_SAVED_CUR
9306 {
9307 VALUE v = RLIM_INFINITY == RLIM_SAVED_CUR ? inf : RLIM2NUM(RLIM_SAVED_CUR);
9308 /* see Process.setrlimit */
9309 rb_define_const(rb_mProcess, "RLIM_SAVED_CUR", v);
9310 }
9311#endif
9312 }
9313#ifdef RLIMIT_AS
9314 /* Maximum size of the process's virtual memory (address space) in bytes.
9315 *
9316 * see the system getrlimit(2) manual for details.
9317 */
9318 rb_define_const(rb_mProcess, "RLIMIT_AS", INT2FIX(RLIMIT_AS));
9319#endif
9320#ifdef RLIMIT_CORE
9321 /* Maximum size of the core file.
9322 *
9323 * see the system getrlimit(2) manual for details.
9324 */
9325 rb_define_const(rb_mProcess, "RLIMIT_CORE", INT2FIX(RLIMIT_CORE));
9326#endif
9327#ifdef RLIMIT_CPU
9328 /* CPU time limit in seconds.
9329 *
9330 * see the system getrlimit(2) manual for details.
9331 */
9332 rb_define_const(rb_mProcess, "RLIMIT_CPU", INT2FIX(RLIMIT_CPU));
9333#endif
9334#ifdef RLIMIT_DATA
9335 /* Maximum size of the process's data segment.
9336 *
9337 * see the system getrlimit(2) manual for details.
9338 */
9339 rb_define_const(rb_mProcess, "RLIMIT_DATA", INT2FIX(RLIMIT_DATA));
9340#endif
9341#ifdef RLIMIT_FSIZE
9342 /* Maximum size of files that the process may create.
9343 *
9344 * see the system getrlimit(2) manual for details.
9345 */
9346 rb_define_const(rb_mProcess, "RLIMIT_FSIZE", INT2FIX(RLIMIT_FSIZE));
9347#endif
9348#ifdef RLIMIT_MEMLOCK
9349 /* Maximum number of bytes of memory that may be locked into RAM.
9350 *
9351 * see the system getrlimit(2) manual for details.
9352 */
9353 rb_define_const(rb_mProcess, "RLIMIT_MEMLOCK", INT2FIX(RLIMIT_MEMLOCK));
9354#endif
9355#ifdef RLIMIT_MSGQUEUE
9356 /* Specifies the limit on the number of bytes that can be allocated
9357 * for POSIX message queues for the real user ID of the calling process.
9358 *
9359 * see the system getrlimit(2) manual for details.
9360 */
9361 rb_define_const(rb_mProcess, "RLIMIT_MSGQUEUE", INT2FIX(RLIMIT_MSGQUEUE));
9362#endif
9363#ifdef RLIMIT_NICE
9364 /* Specifies a ceiling to which the process's nice value can be raised.
9365 *
9366 * see the system getrlimit(2) manual for details.
9367 */
9368 rb_define_const(rb_mProcess, "RLIMIT_NICE", INT2FIX(RLIMIT_NICE));
9369#endif
9370#ifdef RLIMIT_NOFILE
9371 /* Specifies a value one greater than the maximum file descriptor
9372 * number that can be opened by this process.
9373 *
9374 * see the system getrlimit(2) manual for details.
9375 */
9376 rb_define_const(rb_mProcess, "RLIMIT_NOFILE", INT2FIX(RLIMIT_NOFILE));
9377#endif
9378#ifdef RLIMIT_NPROC
9379 /* The maximum number of processes that can be created for the
9380 * real user ID of the calling process.
9381 *
9382 * see the system getrlimit(2) manual for details.
9383 */
9384 rb_define_const(rb_mProcess, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC));
9385#endif
9386#ifdef RLIMIT_NPTS
9387 /* The maximum number of pseudo-terminals that can be created for the
9388 * real user ID of the calling process.
9389 *
9390 * see the system getrlimit(2) manual for details.
9391 */
9392 rb_define_const(rb_mProcess, "RLIMIT_NPTS", INT2FIX(RLIMIT_NPTS));
9393#endif
9394#ifdef RLIMIT_RSS
9395 /* Specifies the limit (in pages) of the process's resident set.
9396 *
9397 * see the system getrlimit(2) manual for details.
9398 */
9399 rb_define_const(rb_mProcess, "RLIMIT_RSS", INT2FIX(RLIMIT_RSS));
9400#endif
9401#ifdef RLIMIT_RTPRIO
9402 /* Specifies a ceiling on the real-time priority that may be set for this process.
9403 *
9404 * see the system getrlimit(2) manual for details.
9405 */
9406 rb_define_const(rb_mProcess, "RLIMIT_RTPRIO", INT2FIX(RLIMIT_RTPRIO));
9407#endif
9408#ifdef RLIMIT_RTTIME
9409 /* Specifies limit on CPU time this process scheduled under a real-time
9410 * scheduling policy can consume.
9411 *
9412 * see the system getrlimit(2) manual for details.
9413 */
9414 rb_define_const(rb_mProcess, "RLIMIT_RTTIME", INT2FIX(RLIMIT_RTTIME));
9415#endif
9416#ifdef RLIMIT_SBSIZE
9417 /* Maximum size of the socket buffer.
9418 */
9419 rb_define_const(rb_mProcess, "RLIMIT_SBSIZE", INT2FIX(RLIMIT_SBSIZE));
9420#endif
9421#ifdef RLIMIT_SIGPENDING
9422 /* Specifies a limit on the number of signals that may be queued for
9423 * the real user ID of the calling process.
9424 *
9425 * see the system getrlimit(2) manual for details.
9426 */
9427 rb_define_const(rb_mProcess, "RLIMIT_SIGPENDING", INT2FIX(RLIMIT_SIGPENDING));
9428#endif
9429#ifdef RLIMIT_STACK
9430 /* Maximum size of the stack, in bytes.
9431 *
9432 * see the system getrlimit(2) manual for details.
9433 */
9434 rb_define_const(rb_mProcess, "RLIMIT_STACK", INT2FIX(RLIMIT_STACK));
9435#endif
9436#endif
9437
9438 rb_define_module_function(rb_mProcess, "uid", proc_getuid, 0);
9439 rb_define_module_function(rb_mProcess, "uid=", proc_setuid, 1);
9440 rb_define_module_function(rb_mProcess, "gid", proc_getgid, 0);
9441 rb_define_module_function(rb_mProcess, "gid=", proc_setgid, 1);
9442 rb_define_module_function(rb_mProcess, "euid", proc_geteuid, 0);
9443 rb_define_module_function(rb_mProcess, "euid=", proc_seteuid_m, 1);
9444 rb_define_module_function(rb_mProcess, "egid", proc_getegid, 0);
9445 rb_define_module_function(rb_mProcess, "egid=", proc_setegid_m, 1);
9446 rb_define_module_function(rb_mProcess, "initgroups", proc_initgroups, 2);
9447 rb_define_module_function(rb_mProcess, "groups", proc_getgroups, 0);
9448 rb_define_module_function(rb_mProcess, "groups=", proc_setgroups, 1);
9449 rb_define_module_function(rb_mProcess, "maxgroups", proc_getmaxgroups, 0);
9450 rb_define_module_function(rb_mProcess, "maxgroups=", proc_setmaxgroups, 1);
9451
9452 rb_define_module_function(rb_mProcess, "daemon", proc_daemon, -1);
9453
9454 rb_define_module_function(rb_mProcess, "times", rb_proc_times, 0);
9455
9456#if defined(RUBY_CLOCK_REALTIME)
9457#elif defined(RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
9458# define RUBY_CLOCK_REALTIME RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
9459#elif defined(RUBY_TIME_BASED_CLOCK_REALTIME)
9460# define RUBY_CLOCK_REALTIME RUBY_TIME_BASED_CLOCK_REALTIME
9461#endif
9462#if defined(CLOCK_REALTIME) && defined(CLOCKID2NUM)
9463 /* see Process.clock_gettime */
9464 rb_define_const(rb_mProcess, "CLOCK_REALTIME", CLOCKID2NUM(CLOCK_REALTIME));
9465#elif defined(RUBY_CLOCK_REALTIME)
9466 rb_define_const(rb_mProcess, "CLOCK_REALTIME", RUBY_CLOCK_REALTIME);
9467#endif
9468
9469#if defined(RUBY_CLOCK_MONOTONIC)
9470#elif defined(RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC)
9471# define RUBY_CLOCK_MONOTONIC RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
9472#endif
9473#if defined(CLOCK_MONOTONIC) && defined(CLOCKID2NUM)
9474 /* see Process.clock_gettime */
9475 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC", CLOCKID2NUM(CLOCK_MONOTONIC));
9476#elif defined(RUBY_CLOCK_MONOTONIC)
9477 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC", RUBY_CLOCK_MONOTONIC);
9478#endif
9479
9480#if defined(RUBY_CLOCK_PROCESS_CPUTIME_ID)
9481#elif defined(RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
9482# define RUBY_CLOCK_PROCESS_CPUTIME_ID RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
9483#endif
9484#if defined(CLOCK_PROCESS_CPUTIME_ID) && defined(CLOCKID2NUM)
9485 /* see Process.clock_gettime */
9486 rb_define_const(rb_mProcess, "CLOCK_PROCESS_CPUTIME_ID", CLOCKID2NUM(CLOCK_PROCESS_CPUTIME_ID));
9487#elif defined(RUBY_CLOCK_PROCESS_CPUTIME_ID)
9488 rb_define_const(rb_mProcess, "CLOCK_PROCESS_CPUTIME_ID", RUBY_CLOCK_PROCESS_CPUTIME_ID);
9489#endif
9490
9491#if defined(CLOCK_THREAD_CPUTIME_ID) && defined(CLOCKID2NUM)
9492 /* see Process.clock_gettime */
9493 rb_define_const(rb_mProcess, "CLOCK_THREAD_CPUTIME_ID", CLOCKID2NUM(CLOCK_THREAD_CPUTIME_ID));
9494#elif defined(RUBY_CLOCK_THREAD_CPUTIME_ID)
9495 rb_define_const(rb_mProcess, "CLOCK_THREAD_CPUTIME_ID", RUBY_CLOCK_THREAD_CPUTIME_ID);
9496#endif
9497
9498#ifdef CLOCKID2NUM
9499#ifdef CLOCK_VIRTUAL
9500 /* see Process.clock_gettime */
9501 rb_define_const(rb_mProcess, "CLOCK_VIRTUAL", CLOCKID2NUM(CLOCK_VIRTUAL));
9502#endif
9503#ifdef CLOCK_PROF
9504 /* see Process.clock_gettime */
9505 rb_define_const(rb_mProcess, "CLOCK_PROF", CLOCKID2NUM(CLOCK_PROF));
9506#endif
9507#ifdef CLOCK_REALTIME_FAST
9508 /* see Process.clock_gettime */
9509 rb_define_const(rb_mProcess, "CLOCK_REALTIME_FAST", CLOCKID2NUM(CLOCK_REALTIME_FAST));
9510#endif
9511#ifdef CLOCK_REALTIME_PRECISE
9512 /* see Process.clock_gettime */
9513 rb_define_const(rb_mProcess, "CLOCK_REALTIME_PRECISE", CLOCKID2NUM(CLOCK_REALTIME_PRECISE));
9514#endif
9515#ifdef CLOCK_REALTIME_COARSE
9516 /* see Process.clock_gettime */
9517 rb_define_const(rb_mProcess, "CLOCK_REALTIME_COARSE", CLOCKID2NUM(CLOCK_REALTIME_COARSE));
9518#endif
9519#ifdef CLOCK_REALTIME_ALARM
9520 /* see Process.clock_gettime */
9521 rb_define_const(rb_mProcess, "CLOCK_REALTIME_ALARM", CLOCKID2NUM(CLOCK_REALTIME_ALARM));
9522#endif
9523#ifdef CLOCK_MONOTONIC_FAST
9524 /* see Process.clock_gettime */
9525 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_FAST", CLOCKID2NUM(CLOCK_MONOTONIC_FAST));
9526#endif
9527#ifdef CLOCK_MONOTONIC_PRECISE
9528 /* see Process.clock_gettime */
9529 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_PRECISE", CLOCKID2NUM(CLOCK_MONOTONIC_PRECISE));
9530#endif
9531#ifdef CLOCK_MONOTONIC_RAW
9532 /* see Process.clock_gettime */
9533 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_RAW", CLOCKID2NUM(CLOCK_MONOTONIC_RAW));
9534#endif
9535#ifdef CLOCK_MONOTONIC_RAW_APPROX
9536 /* see Process.clock_gettime */
9537 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_RAW_APPROX", CLOCKID2NUM(CLOCK_MONOTONIC_RAW_APPROX));
9538#endif
9539#ifdef CLOCK_MONOTONIC_COARSE
9540 /* see Process.clock_gettime */
9541 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_COARSE", CLOCKID2NUM(CLOCK_MONOTONIC_COARSE));
9542#endif
9543#ifdef CLOCK_BOOTTIME
9544 /* see Process.clock_gettime */
9545 rb_define_const(rb_mProcess, "CLOCK_BOOTTIME", CLOCKID2NUM(CLOCK_BOOTTIME));
9546#endif
9547#ifdef CLOCK_BOOTTIME_ALARM
9548 /* see Process.clock_gettime */
9549 rb_define_const(rb_mProcess, "CLOCK_BOOTTIME_ALARM", CLOCKID2NUM(CLOCK_BOOTTIME_ALARM));
9550#endif
9551#ifdef CLOCK_UPTIME
9552 /* see Process.clock_gettime */
9553 rb_define_const(rb_mProcess, "CLOCK_UPTIME", CLOCKID2NUM(CLOCK_UPTIME));
9554#endif
9555#ifdef CLOCK_UPTIME_FAST
9556 /* see Process.clock_gettime */
9557 rb_define_const(rb_mProcess, "CLOCK_UPTIME_FAST", CLOCKID2NUM(CLOCK_UPTIME_FAST));
9558#endif
9559#ifdef CLOCK_UPTIME_PRECISE
9560 /* see Process.clock_gettime */
9561 rb_define_const(rb_mProcess, "CLOCK_UPTIME_PRECISE", CLOCKID2NUM(CLOCK_UPTIME_PRECISE));
9562#endif
9563#ifdef CLOCK_UPTIME_RAW
9564 /* see Process.clock_gettime */
9565 rb_define_const(rb_mProcess, "CLOCK_UPTIME_RAW", CLOCKID2NUM(CLOCK_UPTIME_RAW));
9566#endif
9567#ifdef CLOCK_UPTIME_RAW_APPROX
9568 /* see Process.clock_gettime */
9569 rb_define_const(rb_mProcess, "CLOCK_UPTIME_RAW_APPROX", CLOCKID2NUM(CLOCK_UPTIME_RAW_APPROX));
9570#endif
9571#ifdef CLOCK_SECOND
9572 /* see Process.clock_gettime */
9573 rb_define_const(rb_mProcess, "CLOCK_SECOND", CLOCKID2NUM(CLOCK_SECOND));
9574#endif
9575#ifdef CLOCK_TAI
9576 /* see Process.clock_gettime */
9577 rb_define_const(rb_mProcess, "CLOCK_TAI", CLOCKID2NUM(CLOCK_TAI));
9578#endif
9579#endif
9580 rb_define_module_function(rb_mProcess, "clock_gettime", rb_clock_gettime, -1);
9581 rb_define_module_function(rb_mProcess, "clock_getres", rb_clock_getres, -1);
9582
9583#if defined(HAVE_TIMES) || defined(_WIN32)
9584 rb_cProcessTms = rb_struct_define_under(rb_mProcess, "Tms", "utime", "stime", "cutime", "cstime", NULL);
9585#if 0 /* for RDoc */
9586 /* user time used in this process */
9587 rb_define_attr(rb_cProcessTms, "utime", TRUE, TRUE);
9588 /* system time used in this process */
9589 rb_define_attr(rb_cProcessTms, "stime", TRUE, TRUE);
9590 /* user time used in the child processes */
9591 rb_define_attr(rb_cProcessTms, "cutime", TRUE, TRUE);
9592 /* system time used in the child processes */
9593 rb_define_attr(rb_cProcessTms, "cstime", TRUE, TRUE);
9594#endif
9595#endif
9596
9597 SAVED_USER_ID = geteuid();
9598 SAVED_GROUP_ID = getegid();
9599
9600 rb_mProcUID = rb_define_module_under(rb_mProcess, "UID");
9601 rb_mProcGID = rb_define_module_under(rb_mProcess, "GID");
9602
9603 rb_define_module_function(rb_mProcUID, "rid", proc_getuid, 0);
9604 rb_define_module_function(rb_mProcGID, "rid", proc_getgid, 0);
9605 rb_define_module_function(rb_mProcUID, "eid", proc_geteuid, 0);
9606 rb_define_module_function(rb_mProcGID, "eid", proc_getegid, 0);
9607 rb_define_module_function(rb_mProcUID, "change_privilege", p_uid_change_privilege, 1);
9608 rb_define_module_function(rb_mProcGID, "change_privilege", p_gid_change_privilege, 1);
9609 rb_define_module_function(rb_mProcUID, "grant_privilege", p_uid_grant_privilege, 1);
9610 rb_define_module_function(rb_mProcGID, "grant_privilege", p_gid_grant_privilege, 1);
9611 rb_define_alias(rb_singleton_class(rb_mProcUID), "eid=", "grant_privilege");
9612 rb_define_alias(rb_singleton_class(rb_mProcGID), "eid=", "grant_privilege");
9613 rb_define_module_function(rb_mProcUID, "re_exchange", p_uid_exchange, 0);
9614 rb_define_module_function(rb_mProcGID, "re_exchange", p_gid_exchange, 0);
9615 rb_define_module_function(rb_mProcUID, "re_exchangeable?", p_uid_exchangeable, 0);
9616 rb_define_module_function(rb_mProcGID, "re_exchangeable?", p_gid_exchangeable, 0);
9617 rb_define_module_function(rb_mProcUID, "sid_available?", p_uid_have_saved_id, 0);
9618 rb_define_module_function(rb_mProcGID, "sid_available?", p_gid_have_saved_id, 0);
9619 rb_define_module_function(rb_mProcUID, "switch", p_uid_switch, 0);
9620 rb_define_module_function(rb_mProcGID, "switch", p_gid_switch, 0);
9621#ifdef p_uid_from_name
9622 rb_define_module_function(rb_mProcUID, "from_name", p_uid_from_name, 1);
9623#endif
9624#ifdef p_gid_from_name
9625 rb_define_module_function(rb_mProcGID, "from_name", p_gid_from_name, 1);
9626#endif
9627
9628 rb_mProcID_Syscall = rb_define_module_under(rb_mProcess, "Sys");
9629
9630 rb_define_module_function(rb_mProcID_Syscall, "getuid", proc_getuid, 0);
9631 rb_define_module_function(rb_mProcID_Syscall, "geteuid", proc_geteuid, 0);
9632 rb_define_module_function(rb_mProcID_Syscall, "getgid", proc_getgid, 0);
9633 rb_define_module_function(rb_mProcID_Syscall, "getegid", proc_getegid, 0);
9634
9635 rb_define_module_function(rb_mProcID_Syscall, "setuid", p_sys_setuid, 1);
9636 rb_define_module_function(rb_mProcID_Syscall, "setgid", p_sys_setgid, 1);
9637
9638 rb_define_module_function(rb_mProcID_Syscall, "setruid", p_sys_setruid, 1);
9639 rb_define_module_function(rb_mProcID_Syscall, "setrgid", p_sys_setrgid, 1);
9640
9641 rb_define_module_function(rb_mProcID_Syscall, "seteuid", p_sys_seteuid, 1);
9642 rb_define_module_function(rb_mProcID_Syscall, "setegid", p_sys_setegid, 1);
9643
9644 rb_define_module_function(rb_mProcID_Syscall, "setreuid", p_sys_setreuid, 2);
9645 rb_define_module_function(rb_mProcID_Syscall, "setregid", p_sys_setregid, 2);
9646
9647 rb_define_module_function(rb_mProcID_Syscall, "setresuid", p_sys_setresuid, 3);
9648 rb_define_module_function(rb_mProcID_Syscall, "setresgid", p_sys_setresgid, 3);
9649 rb_define_module_function(rb_mProcID_Syscall, "issetugid", p_sys_issetugid, 0);
9650}
9651
9652void
9653Init_process(void)
9654{
9655#define define_id(name) id_##name = rb_intern_const(#name)
9656 define_id(in);
9657 define_id(out);
9658 define_id(err);
9659 define_id(pid);
9660 define_id(uid);
9661 define_id(gid);
9662 define_id(close);
9663 define_id(child);
9664#ifdef HAVE_SETPGID
9665 define_id(pgroup);
9666#endif
9667#ifdef _WIN32
9668 define_id(new_pgroup);
9669#endif
9670 define_id(unsetenv_others);
9671 define_id(chdir);
9672 define_id(umask);
9673 define_id(close_others);
9674 define_id(nanosecond);
9675 define_id(microsecond);
9676 define_id(millisecond);
9677 define_id(second);
9678 define_id(float_microsecond);
9679 define_id(float_millisecond);
9680 define_id(float_second);
9681 define_id(GETTIMEOFDAY_BASED_CLOCK_REALTIME);
9682 define_id(TIME_BASED_CLOCK_REALTIME);
9683#ifdef CLOCK_REALTIME
9684 define_id(CLOCK_REALTIME);
9685#endif
9686#ifdef CLOCK_MONOTONIC
9687 define_id(CLOCK_MONOTONIC);
9688#endif
9689#ifdef CLOCK_PROCESS_CPUTIME_ID
9690 define_id(CLOCK_PROCESS_CPUTIME_ID);
9691#endif
9692#ifdef CLOCK_THREAD_CPUTIME_ID
9693 define_id(CLOCK_THREAD_CPUTIME_ID);
9694#endif
9695#ifdef HAVE_TIMES
9696 define_id(TIMES_BASED_CLOCK_MONOTONIC);
9697 define_id(TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID);
9698#endif
9699#ifdef RUSAGE_SELF
9700 define_id(GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID);
9701#endif
9702 define_id(CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID);
9703#ifdef __APPLE__
9704 define_id(MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC);
9705#endif
9706 define_id(hertz);
9707
9708 InitVM(process);
9709}
#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:2736
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1435
VALUE rb_define_module(const char *name)
Defines a top-level module.
Definition class.c:1517
VALUE rb_define_module_under(VALUE outer, const char *name)
Defines a module under the namespace of outer.
Definition class.c:1540
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition class.c:2779
void rb_define_attr(VALUE klass, const char *name, int read, int write)
Defines public accessor method(s) for an attribute.
Definition class.c:2785
void rb_undef_method(VALUE klass, const char *name)
Defines an undef of a method.
Definition class.c:2589
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:1018
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1676
#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:1683
#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:1684
#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:291
void rb_notimplement(void)
Definition error.c:3901
VALUE rb_eNotImpError
NotImplementedError exception.
Definition error.c:1440
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:661
void rb_syserr_fail(int e, const char *mesg)
Raises appropriate exception that represents a C errno.
Definition error.c:3970
VALUE rb_eSystemExit
SystemExit exception.
Definition error.c:1423
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:3976
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1428
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:1416
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:467
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:1481
void rb_unexpected_type(VALUE x, int t)
Fails with the given object's type incompatibility to the type.
Definition error.c:1385
void rb_exit(int status)
Terminates the current execution context.
Definition process.c:4359
VALUE rb_cObject
Object class.
Definition object.c:61
VALUE rb_mProcess
Process module.
Definition process.c:8727
VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
Allocates, then initialises an instance of the given class.
Definition object.c:2280
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:235
VALUE rb_cThread
Thread class.
Definition vm.c:668
VALUE rb_equal(VALUE lhs, VALUE rhs)
This function is an optimised version of calling #==.
Definition object.c:141
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1297
VALUE rb_to_int(VALUE val)
Identical to rb_check_to_int(), except it raises in case of conversion mismatch.
Definition object.c:3319
#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:468
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1121
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:4441
VALUE rb_f_exit(int argc, const VALUE *argv)
Identical to rb_exit(), except how arguments are passed.
Definition process.c:4372
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:7425
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:4618
void rb_syswait(rb_pid_t pid)
This is a shorthand of rb_waitpid without status and flags.
Definition process.c:4489
VALUE rb_f_exec(int argc, const VALUE *argv)
Replaces the current process by running the given external command.
Definition process.c:2914
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:4624
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:3834
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1752
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:3187
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1499
#define rb_str_buf_cat
Just another name of rb_str_cat.
Definition string.h:1682
size_t rb_str_capacity(VALUE str)
Queries the capacity of the given string.
Definition string.c:1009
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition string.c:1524
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:2000
void rb_str_set_len(VALUE str, long len)
Overwrites the length of the string.
Definition string.c:3421
VALUE rb_check_string_type(VALUE obj)
Try converting an object to its stringised representation using its to_str method,...
Definition string.c:2976
#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:1657
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:2745
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition string.c:1724
#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:1515
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:878
VALUE rb_thread_local_aref(VALUE thread, ID key)
This badly named function reads from a Fiber local storage.
Definition thread.c:3783
#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:1412
void rb_thread_wait_for(struct timeval time)
Identical to rb_thread_sleep(), except it takes struct timeval instead.
Definition thread.c:1445
void rb_thread_check_ints(void)
Checks for interrupts.
Definition thread.c:1466
void rb_thread_atfork(void)
A pthread_atfork(3posix)-like API.
Definition thread.c:5062
VALUE rb_thread_local_aset(VALUE thread, ID key, VALUE val)
This badly named function writes to a Fiber local storage.
Definition thread.c:3931
#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:1489
struct timeval rb_time_interval(VALUE num)
Creates a "time interval".
Definition time.c:2948
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:2057
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1789
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:1164
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:1024
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:6642
#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:818
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:1731
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:1376
void rb_marshal_define_compat(VALUE newclass, VALUE oldclass, VALUE(*dumper)(VALUE), VALUE(*loader)(VALUE, VALUE))
Marshal format compatibility layer.
Definition marshal.c:138
#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:56
#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:81
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
Definition rtypeddata.h:122
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:759
#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:590
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:458
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:536
VALUE rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags)
Non-blocking waitpid.
Definition scheduler.c:623
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:224
const char * wrap_struct_name
Name of structs of this kind.
Definition rtypeddata.h:231
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:710
#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:425
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