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