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