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