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