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