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