Ruby 3.5.0dev (2025-06-06 revision dde9fca63bf4bf56050c734adca3eaae70506179)
process.c (dde9fca63bf4bf56050c734adca3eaae70506179)
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 = 0;
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_LOCKING() {
4136 child.pid = pid = rb_fork();
4137 child.error = err = errno;
4138 }
4139
4140 disable_child_handler_fork_parent(&old); /* yes, bad name */
4141 if (
4142#if defined(__FreeBSD__)
4143 pid != 0 &&
4144#endif
4145 true) {
4146 rb_thread_release_fork_lock();
4147 }
4148 if (pid == 0) {
4149 rb_thread_reset_fork_lock();
4150 }
4151 after_fork_ruby(pid);
4152
4153 /* repeat while fork failed but retryable */
4154 } while (pid < 0 && handle_fork_error(err, &child, NULL, &try_gc) == 0);
4155
4156 if (status) *status = child.status;
4157
4158 return pid;
4159}
4160
4161static rb_pid_t
4162proc_fork_pid(void)
4163{
4164 rb_pid_t pid = rb_fork_ruby(NULL);
4165
4166 if (pid == -1) {
4167 rb_sys_fail("fork(2)");
4168 }
4169
4170 return pid;
4171}
4172
4173rb_pid_t
4174rb_call_proc__fork(void)
4175{
4176 ID id__fork;
4177 CONST_ID(id__fork, "_fork");
4178 if (rb_method_basic_definition_p(CLASS_OF(rb_mProcess), id__fork)) {
4179 return proc_fork_pid();
4180 }
4181 else {
4182 VALUE pid = rb_funcall(rb_mProcess, id__fork, 0);
4183 return NUM2PIDT(pid);
4184 }
4185}
4186#endif
4187
4188#if defined(HAVE_WORKING_FORK) && !defined(CANNOT_FORK_WITH_PTHREAD)
4189/*
4190 * call-seq:
4191 * Process._fork -> integer
4192 *
4193 * An internal API for fork. Do not call this method directly.
4194 * Currently, this is called via Kernel#fork, Process.fork, and
4195 * IO.popen with <tt>"-"</tt>.
4196 *
4197 * This method is not for casual code but for application monitoring
4198 * libraries. You can add custom code before and after fork events
4199 * by overriding this method.
4200 *
4201 * Note: Process.daemon may be implemented using fork(2) BUT does not go
4202 * through this method.
4203 * Thus, depending on your reason to hook into this method, you
4204 * may also want to hook into that one.
4205 * See {this issue}[https://bugs.ruby-lang.org/issues/18911] for a
4206 * more detailed discussion of this.
4207 */
4208VALUE
4209rb_proc__fork(VALUE _obj)
4210{
4211 rb_pid_t pid = proc_fork_pid();
4212 return PIDT2NUM(pid);
4213}
4214
4215/*
4216 * call-seq:
4217 * Process.fork { ... } -> integer or nil
4218 * Process.fork -> integer or nil
4219 *
4220 * Creates a child process.
4221 *
4222 * With a block given, runs the block in the child process;
4223 * on block exit, the child terminates with a status of zero:
4224 *
4225 * puts "Before the fork: #{Process.pid}"
4226 * fork do
4227 * puts "In the child process: #{Process.pid}"
4228 * end # => 382141
4229 * puts "After the fork: #{Process.pid}"
4230 *
4231 * Output:
4232 *
4233 * Before the fork: 420496
4234 * After the fork: 420496
4235 * In the child process: 420520
4236 *
4237 * With no block given, the +fork+ call returns twice:
4238 *
4239 * - Once in the parent process, returning the pid of the child process.
4240 * - Once in the child process, returning +nil+.
4241 *
4242 * Example:
4243 *
4244 * puts "This is the first line before the fork (pid #{Process.pid})"
4245 * puts fork
4246 * puts "This is the second line after the fork (pid #{Process.pid})"
4247 *
4248 * Output:
4249 *
4250 * This is the first line before the fork (pid 420199)
4251 * 420223
4252 * This is the second line after the fork (pid 420199)
4253 *
4254 * This is the second line after the fork (pid 420223)
4255 *
4256 * In either case, the child process may exit using
4257 * Kernel.exit! to avoid the call to Kernel#at_exit.
4258 *
4259 * To avoid zombie processes, the parent process should call either:
4260 *
4261 * - Process.wait, to collect the termination statuses of its children.
4262 * - Process.detach, to register disinterest in their status.
4263 *
4264 * The thread calling +fork+ is the only thread in the created child process;
4265 * +fork+ doesn't copy other threads.
4266 *
4267 * Note that method +fork+ is available on some platforms,
4268 * but not on others:
4269 *
4270 * Process.respond_to?(:fork) # => true # Would be false on some.
4271 *
4272 * If not, you may use ::spawn instead of +fork+.
4273 */
4274
4275static VALUE
4276rb_f_fork(VALUE obj)
4277{
4278 rb_pid_t pid;
4279
4280 pid = rb_call_proc__fork();
4281
4282 if (pid == 0) {
4283 if (rb_block_given_p()) {
4284 int status;
4285 rb_protect(rb_yield, Qundef, &status);
4286 ruby_stop(status);
4287 }
4288 return Qnil;
4289 }
4290
4291 return PIDT2NUM(pid);
4292}
4293#else
4294#define rb_proc__fork rb_f_notimplement
4295#define rb_f_fork rb_f_notimplement
4296#endif
4297
4298static int
4299exit_status_code(VALUE status)
4300{
4301 int istatus;
4302
4303 switch (status) {
4304 case Qtrue:
4305 istatus = EXIT_SUCCESS;
4306 break;
4307 case Qfalse:
4308 istatus = EXIT_FAILURE;
4309 break;
4310 default:
4311 istatus = NUM2INT(status);
4312#if EXIT_SUCCESS != 0
4313 if (istatus == 0)
4314 istatus = EXIT_SUCCESS;
4315#endif
4316 break;
4317 }
4318 return istatus;
4319}
4320
4321NORETURN(static VALUE rb_f_exit_bang(int argc, VALUE *argv, VALUE obj));
4322/*
4323 * call-seq:
4324 * exit!(status = false)
4325 * Process.exit!(status = false)
4326 *
4327 * Exits the process immediately; no exit handlers are called.
4328 * Returns exit status +status+ to the underlying operating system.
4329 *
4330 * Process.exit!(true)
4331 *
4332 * Values +true+ and +false+ for argument +status+
4333 * indicate, respectively, success and failure;
4334 * The meanings of integer values are system-dependent.
4335 *
4336 */
4337
4338static VALUE
4339rb_f_exit_bang(int argc, VALUE *argv, VALUE obj)
4340{
4341 int istatus;
4342
4343 if (rb_check_arity(argc, 0, 1) == 1) {
4344 istatus = exit_status_code(argv[0]);
4345 }
4346 else {
4347 istatus = EXIT_FAILURE;
4348 }
4349 _exit(istatus);
4350
4352}
4353
4354void
4355rb_exit(int status)
4356{
4357 if (GET_EC()->tag) {
4358 VALUE args[2];
4359
4360 args[0] = INT2NUM(status);
4361 args[1] = rb_str_new2("exit");
4363 }
4364 ruby_stop(status);
4365}
4366
4367VALUE
4368rb_f_exit(int argc, const VALUE *argv)
4369{
4370 int istatus;
4371
4372 if (rb_check_arity(argc, 0, 1) == 1) {
4373 istatus = exit_status_code(argv[0]);
4374 }
4375 else {
4376 istatus = EXIT_SUCCESS;
4377 }
4378 rb_exit(istatus);
4379
4381}
4382
4383NORETURN(static VALUE f_exit(int c, const VALUE *a, VALUE _));
4384/*
4385 * call-seq:
4386 * exit(status = true)
4387 * Process.exit(status = true)
4388 *
4389 * Initiates termination of the Ruby script by raising SystemExit;
4390 * the exception may be caught.
4391 * Returns exit status +status+ to the underlying operating system.
4392 *
4393 * Values +true+ and +false+ for argument +status+
4394 * indicate, respectively, success and failure;
4395 * The meanings of integer values are system-dependent.
4396 *
4397 * Example:
4398 *
4399 * begin
4400 * exit
4401 * puts 'Never get here.'
4402 * rescue SystemExit
4403 * puts 'Rescued a SystemExit exception.'
4404 * end
4405 * puts 'After begin block.'
4406 *
4407 * Output:
4408 *
4409 * Rescued a SystemExit exception.
4410 * After begin block.
4411 *
4412 * Just prior to final termination,
4413 * Ruby executes any at-exit procedures (see Kernel::at_exit)
4414 * and any object finalizers (see ObjectSpace::define_finalizer).
4415 *
4416 * Example:
4417 *
4418 * at_exit { puts 'In at_exit function.' }
4419 * ObjectSpace.define_finalizer('string', proc { puts 'In finalizer.' })
4420 * exit
4421 *
4422 * Output:
4423 *
4424 * In at_exit function.
4425 * In finalizer.
4426 *
4427 */
4428
4429static VALUE
4430f_exit(int c, const VALUE *a, VALUE _)
4431{
4432 rb_f_exit(c, a);
4434}
4435
4436VALUE
4437rb_f_abort(int argc, const VALUE *argv)
4438{
4439 rb_check_arity(argc, 0, 1);
4440 if (argc == 0) {
4441 rb_execution_context_t *ec = GET_EC();
4442 VALUE errinfo = rb_ec_get_errinfo(ec);
4443 if (!NIL_P(errinfo)) {
4444 rb_ec_error_print(ec, errinfo);
4445 }
4446 rb_exit(EXIT_FAILURE);
4447 }
4448 else {
4449 VALUE args[2];
4450
4451 args[1] = args[0] = argv[0];
4452 StringValue(args[0]);
4453 rb_io_puts(1, args, rb_ractor_stderr());
4454 args[0] = INT2NUM(EXIT_FAILURE);
4456 }
4457
4459}
4460
4461NORETURN(static VALUE f_abort(int c, const VALUE *a, VALUE _));
4462
4463/*
4464 * call-seq:
4465 * abort
4466 * Process.abort(msg = nil)
4467 *
4468 * Terminates execution immediately, effectively by calling
4469 * <tt>Kernel.exit(false)</tt>.
4470 *
4471 * If string argument +msg+ is given,
4472 * it is written to STDERR prior to termination;
4473 * otherwise, if an exception was raised,
4474 * prints its message and backtrace.
4475 */
4476
4477static VALUE
4478f_abort(int c, const VALUE *a, VALUE _)
4479{
4480 rb_f_abort(c, a);
4482}
4483
4484void
4485rb_syswait(rb_pid_t pid)
4486{
4487 int status;
4488
4489 rb_waitpid(pid, &status, 0);
4490}
4491
4492#if !defined HAVE_WORKING_FORK && !defined HAVE_SPAWNV && !defined __EMSCRIPTEN__
4493char *
4494rb_execarg_commandline(const struct rb_execarg *eargp, VALUE *prog)
4495{
4496 VALUE cmd = *prog;
4497 if (eargp && !eargp->use_shell) {
4498 VALUE str = eargp->invoke.cmd.argv_str;
4499 VALUE buf = eargp->invoke.cmd.argv_buf;
4500 char *p, **argv = ARGVSTR2ARGV(str);
4501 long i, argc = ARGVSTR2ARGC(str);
4502 const char *start = RSTRING_PTR(buf);
4503 cmd = rb_str_new(start, RSTRING_LEN(buf));
4504 p = RSTRING_PTR(cmd);
4505 for (i = 1; i < argc; ++i) {
4506 p[argv[i] - start - 1] = ' ';
4507 }
4508 *prog = cmd;
4509 return p;
4510 }
4511 return StringValueCStr(*prog);
4512}
4513#endif
4514
4515static rb_pid_t
4516rb_spawn_process(struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
4517{
4518 rb_pid_t pid;
4519#if !defined HAVE_WORKING_FORK || USE_SPAWNV
4520 VALUE prog;
4521 struct rb_execarg sarg;
4522# if !defined HAVE_SPAWNV
4523 int status;
4524# endif
4525#endif
4526
4527#if defined HAVE_WORKING_FORK && !USE_SPAWNV
4528 pid = fork_check_err(eargp->status, rb_exec_atfork, eargp, eargp->redirect_fds, errmsg, errmsg_buflen, eargp);
4529#else
4530 prog = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
4531
4532 if (rb_execarg_run_options(eargp, &sarg, errmsg, errmsg_buflen) < 0) {
4533 return -1;
4534 }
4535
4536 if (prog && !eargp->use_shell) {
4537 char **argv = ARGVSTR2ARGV(eargp->invoke.cmd.argv_str);
4538 argv[0] = RSTRING_PTR(prog);
4539 }
4540# if defined HAVE_SPAWNV
4541 if (eargp->use_shell) {
4542 pid = proc_spawn_sh(RSTRING_PTR(prog));
4543 }
4544 else {
4545 char **argv = ARGVSTR2ARGV(eargp->invoke.cmd.argv_str);
4546 pid = proc_spawn_cmd(argv, prog, eargp);
4547 }
4548
4549 if (pid == -1) {
4550 rb_last_status_set(0x7f << 8, pid);
4551 }
4552# else
4553 status = system(rb_execarg_commandline(eargp, &prog));
4554 pid = 1; /* dummy */
4555 rb_last_status_set((status & 0xff) << 8, pid);
4556# endif
4557
4558 if (eargp->waitpid_state) {
4559 eargp->waitpid_state->pid = pid;
4560 }
4561
4562 rb_execarg_run_options(&sarg, NULL, errmsg, errmsg_buflen);
4563#endif
4564
4565 return pid;
4566}
4567
4569 VALUE execarg;
4570 struct {
4571 char *ptr;
4572 size_t buflen;
4573 } errmsg;
4574};
4575
4576static VALUE
4577do_spawn_process(VALUE arg)
4578{
4579 struct spawn_args *argp = (struct spawn_args *)arg;
4580
4581 rb_execarg_parent_start1(argp->execarg);
4582
4583 return (VALUE)rb_spawn_process(rb_execarg_get(argp->execarg),
4584 argp->errmsg.ptr, argp->errmsg.buflen);
4585}
4586
4587NOINLINE(static rb_pid_t
4588 rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen));
4589
4590static rb_pid_t
4591rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen)
4592{
4593 struct spawn_args args;
4594
4595 args.execarg = execarg_obj;
4596 args.errmsg.ptr = errmsg;
4597 args.errmsg.buflen = errmsg_buflen;
4598
4599 rb_pid_t r = (rb_pid_t)rb_ensure(do_spawn_process, (VALUE)&args,
4600 execarg_parent_end, execarg_obj);
4601 return r;
4602}
4603
4604static rb_pid_t
4605rb_spawn_internal(int argc, const VALUE *argv, char *errmsg, size_t errmsg_buflen)
4606{
4607 VALUE execarg_obj;
4608
4609 execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
4610 return rb_execarg_spawn(execarg_obj, errmsg, errmsg_buflen);
4611}
4612
4613rb_pid_t
4614rb_spawn_err(int argc, const VALUE *argv, char *errmsg, size_t errmsg_buflen)
4615{
4616 return rb_spawn_internal(argc, argv, errmsg, errmsg_buflen);
4617}
4618
4619rb_pid_t
4620rb_spawn(int argc, const VALUE *argv)
4621{
4622 return rb_spawn_internal(argc, argv, NULL, 0);
4623}
4624
4625/*
4626 * call-seq:
4627 * system([env, ] command_line, options = {}, exception: false) -> true, false, or nil
4628 * system([env, ] exe_path, *args, options = {}, exception: false) -> true, false, or nil
4629 *
4630 * Creates a new child process by doing one of the following
4631 * in that process:
4632 *
4633 * - Passing string +command_line+ to the shell.
4634 * - Invoking the executable at +exe_path+.
4635 *
4636 * This method has potential security vulnerabilities if called with untrusted input;
4637 * see {Command Injection}[rdoc-ref:command_injection.rdoc].
4638 *
4639 * Returns:
4640 *
4641 * - +true+ if the command exits with status zero.
4642 * - +false+ if the exit status is a non-zero integer.
4643 * - +nil+ if the command could not execute.
4644 *
4645 * Raises an exception (instead of returning +false+ or +nil+)
4646 * if keyword argument +exception+ is set to +true+.
4647 *
4648 * Assigns the command's error status to <tt>$?</tt>.
4649 *
4650 * The new process is created using the
4651 * {system system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/system.html];
4652 * it may inherit some of its environment from the calling program
4653 * (possibly including open file descriptors).
4654 *
4655 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
4656 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
4657 *
4658 * Argument +options+ is a hash of options for the new process;
4659 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
4660 *
4661 * The first required argument is one of the following:
4662 *
4663 * - +command_line+ if it is a string,
4664 * and if it begins with a shell reserved word or special built-in,
4665 * or if it contains one or more meta characters.
4666 * - +exe_path+ otherwise.
4667 *
4668 * <b>Argument +command_line+</b>
4669 *
4670 * \String argument +command_line+ is a command line to be passed to a shell;
4671 * it must begin with a shell reserved word, begin with a special built-in,
4672 * or contain meta characters:
4673 *
4674 * system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
4675 * system('exit') # => true # Built-in.
4676 * system('date > /tmp/date.tmp') # => true # Contains meta character.
4677 * system('date > /nop/date.tmp') # => false
4678 * system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
4679 *
4680 * Assigns the command's error status to <tt>$?</tt>:
4681 *
4682 * system('exit') # => true # Built-in.
4683 * $? # => #<Process::Status: pid 640610 exit 0>
4684 * system('date > /nop/date.tmp') # => false
4685 * $? # => #<Process::Status: pid 640742 exit 2>
4686 *
4687 * The command line may also contain arguments and options for the command:
4688 *
4689 * system('echo "Foo"') # => true
4690 *
4691 * Output:
4692 *
4693 * Foo
4694 *
4695 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
4696 *
4697 * Raises an exception if the new process could not execute.
4698 *
4699 * <b>Argument +exe_path+</b>
4700 *
4701 * Argument +exe_path+ is one of the following:
4702 *
4703 * - The string path to an executable to be called.
4704 * - A 2-element array containing the path to an executable
4705 * and the string to be used as the name of the executing process.
4706 *
4707 * Example:
4708 *
4709 * system('/usr/bin/date') # => true # Path to date on Unix-style system.
4710 * system('foo') # => nil # Command failed.
4711 *
4712 * Output:
4713 *
4714 * Mon Aug 28 11:43:10 AM CDT 2023
4715 *
4716 * Assigns the command's error status to <tt>$?</tt>:
4717 *
4718 * system('/usr/bin/date') # => true
4719 * $? # => #<Process::Status: pid 645605 exit 0>
4720 * system('foo') # => nil
4721 * $? # => #<Process::Status: pid 645608 exit 127>
4722 *
4723 * Ruby invokes the executable directly.
4724 * This form does not use the shell;
4725 * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
4726 *
4727 * system('doesnt_exist') # => nil
4728 *
4729 * If one or more +args+ is given, each is an argument or option
4730 * to be passed to the executable:
4731 *
4732 * system('echo', 'C*') # => true
4733 * system('echo', 'hello', 'world') # => true
4734 *
4735 * Output:
4736 *
4737 * C*
4738 * hello world
4739 *
4740 * Raises an exception if the new process could not execute.
4741 */
4742
4743static VALUE
4744rb_f_system(int argc, VALUE *argv, VALUE _)
4745{
4746 rb_thread_t *th = GET_THREAD();
4747 VALUE execarg_obj = rb_execarg_new(argc, argv, TRUE, TRUE);
4748 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
4749
4750 struct rb_process_status status = {0};
4751 eargp->status = &status;
4752
4753 last_status_clear(th);
4754
4755 // This function can set the thread's last status.
4756 // May be different from waitpid_state.pid on exec failure.
4757 rb_pid_t pid = rb_execarg_spawn(execarg_obj, 0, 0);
4758
4759 if (pid > 0) {
4760 VALUE status = rb_process_status_wait(pid, 0);
4761 struct rb_process_status *data = rb_check_typeddata(status, &rb_process_status_type);
4762 // Set the last status:
4763 rb_obj_freeze(status);
4764 th->last_status = status;
4765
4766 if (data->status == EXIT_SUCCESS) {
4767 return Qtrue;
4768 }
4769
4770 if (data->error != 0) {
4771 if (eargp->exception) {
4772 VALUE command = eargp->invoke.sh.shell_script;
4773 RB_GC_GUARD(execarg_obj);
4774 rb_syserr_fail_str(data->error, command);
4775 }
4776 else {
4777 return Qnil;
4778 }
4779 }
4780 else if (eargp->exception) {
4781 VALUE command = eargp->invoke.sh.shell_script;
4782 VALUE str = rb_str_new_cstr("Command failed with");
4783 rb_str_cat_cstr(pst_message_status(str, data->status), ": ");
4784 rb_str_append(str, command);
4785 RB_GC_GUARD(execarg_obj);
4787 }
4788 else {
4789 return Qfalse;
4790 }
4791
4792 RB_GC_GUARD(status);
4793 }
4794
4795 if (eargp->exception) {
4796 VALUE command = eargp->invoke.sh.shell_script;
4797 RB_GC_GUARD(execarg_obj);
4798 rb_syserr_fail_str(errno, command);
4799 }
4800 else {
4801 return Qnil;
4802 }
4803}
4804
4805/*
4806 * call-seq:
4807 * spawn([env, ] command_line, options = {}) -> pid
4808 * spawn([env, ] exe_path, *args, options = {}) -> pid
4809 *
4810 * Creates a new child process by doing one of the following
4811 * in that process:
4812 *
4813 * - Passing string +command_line+ to the shell.
4814 * - Invoking the executable at +exe_path+.
4815 *
4816 * This method has potential security vulnerabilities if called with untrusted input;
4817 * see {Command Injection}[rdoc-ref:command_injection.rdoc].
4818 *
4819 * Returns the process ID (pid) of the new process,
4820 * without waiting for it to complete.
4821 *
4822 * To avoid zombie processes, the parent process should call either:
4823 *
4824 * - Process.wait, to collect the termination statuses of its children.
4825 * - Process.detach, to register disinterest in their status.
4826 *
4827 * The new process is created using the
4828 * {exec system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/execve.html];
4829 * it may inherit some of its environment from the calling program
4830 * (possibly including open file descriptors).
4831 *
4832 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
4833 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
4834 *
4835 * Argument +options+ is a hash of options for the new process;
4836 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
4837 *
4838 * The first required argument is one of the following:
4839 *
4840 * - +command_line+ if it is a string,
4841 * and if it begins with a shell reserved word or special built-in,
4842 * or if it contains one or more meta characters.
4843 * - +exe_path+ otherwise.
4844 *
4845 * <b>Argument +command_line+</b>
4846 *
4847 * \String argument +command_line+ is a command line to be passed to a shell;
4848 * it must begin with a shell reserved word, begin with a special built-in,
4849 * or contain meta characters:
4850 *
4851 * spawn('if true; then echo "Foo"; fi') # => 798847 # Shell reserved word.
4852 * Process.wait # => 798847
4853 * spawn('exit') # => 798848 # Built-in.
4854 * Process.wait # => 798848
4855 * spawn('date > /tmp/date.tmp') # => 798879 # Contains meta character.
4856 * Process.wait # => 798849
4857 * spawn('date > /nop/date.tmp') # => 798882 # Issues error message.
4858 * Process.wait # => 798882
4859 *
4860 * The command line may also contain arguments and options for the command:
4861 *
4862 * spawn('echo "Foo"') # => 799031
4863 * Process.wait # => 799031
4864 *
4865 * Output:
4866 *
4867 * Foo
4868 *
4869 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
4870 *
4871 * Raises an exception if the new process could not execute.
4872 *
4873 * <b>Argument +exe_path+</b>
4874 *
4875 * Argument +exe_path+ is one of the following:
4876 *
4877 * - The string path to an executable to be called.
4878 * - A 2-element array containing the path to an executable to be called,
4879 * and the string to be used as the name of the executing process.
4880 *
4881 * spawn('/usr/bin/date') # Path to date on Unix-style system.
4882 * Process.wait
4883 *
4884 * Output:
4885 *
4886 * Mon Aug 28 11:43:10 AM CDT 2023
4887 *
4888 * Ruby invokes the executable directly.
4889 * This form does not use the shell;
4890 * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
4891 *
4892 * If one or more +args+ is given, each is an argument or option
4893 * to be passed to the executable:
4894 *
4895 * spawn('echo', 'C*') # => 799392
4896 * Process.wait # => 799392
4897 * spawn('echo', 'hello', 'world') # => 799393
4898 * Process.wait # => 799393
4899 *
4900 * Output:
4901 *
4902 * C*
4903 * hello world
4904 *
4905 * Raises an exception if the new process could not execute.
4906 */
4907
4908static VALUE
4909rb_f_spawn(int argc, VALUE *argv, VALUE _)
4910{
4911 rb_pid_t pid;
4912 char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' };
4913 VALUE execarg_obj, fail_str;
4914 struct rb_execarg *eargp;
4915
4916 execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
4917 eargp = rb_execarg_get(execarg_obj);
4918 fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
4919
4920 pid = rb_execarg_spawn(execarg_obj, errmsg, sizeof(errmsg));
4921
4922 if (pid == -1) {
4923 int err = errno;
4924 rb_exec_fail(eargp, err, errmsg);
4925 RB_GC_GUARD(execarg_obj);
4926 rb_syserr_fail_str(err, fail_str);
4927 }
4928#if defined(HAVE_WORKING_FORK) || defined(HAVE_SPAWNV)
4929 return PIDT2NUM(pid);
4930#else
4931 return Qnil;
4932#endif
4933}
4934
4935/*
4936 * call-seq:
4937 * sleep(secs = nil) -> slept_secs
4938 *
4939 * Suspends execution of the current thread for the number of seconds
4940 * specified by numeric argument +secs+, or forever if +secs+ is +nil+;
4941 * returns the integer number of seconds suspended (rounded).
4942 *
4943 * Time.new # => 2008-03-08 19:56:19 +0900
4944 * sleep 1.2 # => 1
4945 * Time.new # => 2008-03-08 19:56:20 +0900
4946 * sleep 1.9 # => 2
4947 * Time.new # => 2008-03-08 19:56:22 +0900
4948 *
4949 */
4950
4951static VALUE
4952rb_f_sleep(int argc, VALUE *argv, VALUE _)
4953{
4954 time_t beg = time(0);
4955 VALUE scheduler = rb_fiber_scheduler_current();
4956
4957 if (scheduler != Qnil) {
4958 rb_fiber_scheduler_kernel_sleepv(scheduler, argc, argv);
4959 }
4960 else {
4961 if (argc == 0 || (argc == 1 && NIL_P(argv[0]))) {
4963 }
4964 else {
4965 rb_check_arity(argc, 0, 1);
4967 }
4968 }
4969
4970 time_t end = time(0) - beg;
4971
4972 return TIMET2NUM(end);
4973}
4974
4975
4976#if (defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)) || defined(HAVE_GETPGID)
4977/*
4978 * call-seq:
4979 * Process.getpgrp -> integer
4980 *
4981 * Returns the process group ID for the current process:
4982 *
4983 * Process.getpgid(0) # => 25527
4984 * Process.getpgrp # => 25527
4985 *
4986 */
4987
4988static VALUE
4989proc_getpgrp(VALUE _)
4990{
4991 rb_pid_t pgrp;
4992
4993#if defined(HAVE_GETPGRP) && defined(GETPGRP_VOID)
4994 pgrp = getpgrp();
4995 if (pgrp < 0) rb_sys_fail(0);
4996 return PIDT2NUM(pgrp);
4997#else /* defined(HAVE_GETPGID) */
4998 pgrp = getpgid(0);
4999 if (pgrp < 0) rb_sys_fail(0);
5000 return PIDT2NUM(pgrp);
5001#endif
5002}
5003#else
5004#define proc_getpgrp rb_f_notimplement
5005#endif
5006
5007
5008#if defined(HAVE_SETPGID) || (defined(HAVE_SETPGRP) && defined(SETPGRP_VOID))
5009/*
5010 * call-seq:
5011 * Process.setpgrp -> 0
5012 *
5013 * Equivalent to <tt>setpgid(0, 0)</tt>.
5014 *
5015 * Not available on all platforms.
5016 */
5017
5018static VALUE
5019proc_setpgrp(VALUE _)
5020{
5021 /* check for posix setpgid() first; this matches the posix */
5022 /* getpgrp() above. It appears that configure will set SETPGRP_VOID */
5023 /* even though setpgrp(0,0) would be preferred. The posix call avoids */
5024 /* this confusion. */
5025#ifdef HAVE_SETPGID
5026 if (setpgid(0,0) < 0) rb_sys_fail(0);
5027#elif defined(HAVE_SETPGRP) && defined(SETPGRP_VOID)
5028 if (setpgrp() < 0) rb_sys_fail(0);
5029#endif
5030 return INT2FIX(0);
5031}
5032#else
5033#define proc_setpgrp rb_f_notimplement
5034#endif
5035
5036
5037#if defined(HAVE_GETPGID)
5038/*
5039 * call-seq:
5040 * Process.getpgid(pid) -> integer
5041 *
5042 * Returns the process group ID for the given process ID +pid+:
5043 *
5044 * Process.getpgid(Process.ppid) # => 25527
5045 *
5046 * Not available on all platforms.
5047 */
5048
5049static VALUE
5050proc_getpgid(VALUE obj, VALUE pid)
5051{
5052 rb_pid_t i;
5053
5054 i = getpgid(NUM2PIDT(pid));
5055 if (i < 0) rb_sys_fail(0);
5056 return PIDT2NUM(i);
5057}
5058#else
5059#define proc_getpgid rb_f_notimplement
5060#endif
5061
5062
5063#ifdef HAVE_SETPGID
5064/*
5065 * call-seq:
5066 * Process.setpgid(pid, pgid) -> 0
5067 *
5068 * Sets the process group ID for the process given by process ID +pid+
5069 * to +pgid+.
5070 *
5071 * Not available on all platforms.
5072 */
5073
5074static VALUE
5075proc_setpgid(VALUE obj, VALUE pid, VALUE pgrp)
5076{
5077 rb_pid_t ipid, ipgrp;
5078
5079 ipid = NUM2PIDT(pid);
5080 ipgrp = NUM2PIDT(pgrp);
5081
5082 if (setpgid(ipid, ipgrp) < 0) rb_sys_fail(0);
5083 return INT2FIX(0);
5084}
5085#else
5086#define proc_setpgid rb_f_notimplement
5087#endif
5088
5089
5090#ifdef HAVE_GETSID
5091/*
5092 * call-seq:
5093 * Process.getsid(pid = nil) -> integer
5094 *
5095 * Returns the session ID of the given process ID +pid+,
5096 * or of the current process if not given:
5097 *
5098 * Process.getsid # => 27422
5099 * Process.getsid(0) # => 27422
5100 * Process.getsid(Process.pid()) # => 27422
5101 *
5102 * Not available on all platforms.
5103 */
5104static VALUE
5105proc_getsid(int argc, VALUE *argv, VALUE _)
5106{
5107 rb_pid_t sid;
5108 rb_pid_t pid = 0;
5109
5110 if (rb_check_arity(argc, 0, 1) == 1 && !NIL_P(argv[0]))
5111 pid = NUM2PIDT(argv[0]);
5112
5113 sid = getsid(pid);
5114 if (sid < 0) rb_sys_fail(0);
5115 return PIDT2NUM(sid);
5116}
5117#else
5118#define proc_getsid rb_f_notimplement
5119#endif
5120
5121
5122#if defined(HAVE_SETSID) || (defined(HAVE_SETPGRP) && defined(TIOCNOTTY))
5123#if !defined(HAVE_SETSID)
5124static rb_pid_t ruby_setsid(void);
5125#define setsid() ruby_setsid()
5126#endif
5127/*
5128 * call-seq:
5129 * Process.setsid -> integer
5130 *
5131 * Establishes the current process as a new session and process group leader,
5132 * with no controlling tty;
5133 * returns the session ID:
5134 *
5135 * Process.setsid # => 27422
5136 *
5137 * Not available on all platforms.
5138 */
5139
5140static VALUE
5141proc_setsid(VALUE _)
5142{
5143 rb_pid_t pid;
5144
5145 pid = setsid();
5146 if (pid < 0) rb_sys_fail(0);
5147 return PIDT2NUM(pid);
5148}
5149
5150#if !defined(HAVE_SETSID)
5151#define HAVE_SETSID 1
5152static rb_pid_t
5153ruby_setsid(void)
5154{
5155 rb_pid_t pid;
5156 int ret, fd;
5157
5158 pid = getpid();
5159#if defined(SETPGRP_VOID)
5160 ret = setpgrp();
5161 /* If `pid_t setpgrp(void)' is equivalent to setsid(),
5162 `ret' will be the same value as `pid', and following open() will fail.
5163 In Linux, `int setpgrp(void)' is equivalent to setpgid(0, 0). */
5164#else
5165 ret = setpgrp(0, pid);
5166#endif
5167 if (ret == -1) return -1;
5168
5169 if ((fd = rb_cloexec_open("/dev/tty", O_RDWR, 0)) >= 0) {
5170 rb_update_max_fd(fd);
5171 ioctl(fd, TIOCNOTTY, NULL);
5172 close(fd);
5173 }
5174 return pid;
5175}
5176#endif
5177#else
5178#define proc_setsid rb_f_notimplement
5179#endif
5180
5181
5182#ifdef HAVE_GETPRIORITY
5183/*
5184 * call-seq:
5185 * Process.getpriority(kind, id) -> integer
5186 *
5187 * Returns the scheduling priority for specified process, process group,
5188 * or user.
5189 *
5190 * Argument +kind+ is one of:
5191 *
5192 * - Process::PRIO_PROCESS: return priority for process.
5193 * - Process::PRIO_PGRP: return priority for process group.
5194 * - Process::PRIO_USER: return priority for user.
5195 *
5196 * Argument +id+ is the ID for the process, process group, or user;
5197 * zero specified the current ID for +kind+.
5198 *
5199 * Examples:
5200 *
5201 * Process.getpriority(Process::PRIO_USER, 0) # => 19
5202 * Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
5203 *
5204 * Not available on all platforms.
5205 */
5206
5207static VALUE
5208proc_getpriority(VALUE obj, VALUE which, VALUE who)
5209{
5210 int prio, iwhich, iwho;
5211
5212 iwhich = NUM2INT(which);
5213 iwho = NUM2INT(who);
5214
5215 errno = 0;
5216 prio = getpriority(iwhich, iwho);
5217 if (errno) rb_sys_fail(0);
5218 return INT2FIX(prio);
5219}
5220#else
5221#define proc_getpriority rb_f_notimplement
5222#endif
5223
5224
5225#ifdef HAVE_GETPRIORITY
5226/*
5227 * call-seq:
5228 * Process.setpriority(kind, integer, priority) -> 0
5229 *
5230 * See Process.getpriority.
5231 *
5232 * Examples:
5233 *
5234 * Process.setpriority(Process::PRIO_USER, 0, 19) # => 0
5235 * Process.setpriority(Process::PRIO_PROCESS, 0, 19) # => 0
5236 * Process.getpriority(Process::PRIO_USER, 0) # => 19
5237 * Process.getpriority(Process::PRIO_PROCESS, 0) # => 19
5238 *
5239 * Not available on all platforms.
5240 */
5241
5242static VALUE
5243proc_setpriority(VALUE obj, VALUE which, VALUE who, VALUE prio)
5244{
5245 int iwhich, iwho, iprio;
5246
5247 iwhich = NUM2INT(which);
5248 iwho = NUM2INT(who);
5249 iprio = NUM2INT(prio);
5250
5251 if (setpriority(iwhich, iwho, iprio) < 0)
5252 rb_sys_fail(0);
5253 return INT2FIX(0);
5254}
5255#else
5256#define proc_setpriority rb_f_notimplement
5257#endif
5258
5259#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
5260static int
5261rlimit_resource_name2int(const char *name, long len, int casetype)
5262{
5263 int resource;
5264 const char *p;
5265#define RESCHECK(r) \
5266 do { \
5267 if (len == rb_strlen_lit(#r) && STRCASECMP(name, #r) == 0) { \
5268 resource = RLIMIT_##r; \
5269 goto found; \
5270 } \
5271 } while (0)
5272
5273 switch (TOUPPER(*name)) {
5274 case 'A':
5275#ifdef RLIMIT_AS
5276 RESCHECK(AS);
5277#endif
5278 break;
5279
5280 case 'C':
5281#ifdef RLIMIT_CORE
5282 RESCHECK(CORE);
5283#endif
5284#ifdef RLIMIT_CPU
5285 RESCHECK(CPU);
5286#endif
5287 break;
5288
5289 case 'D':
5290#ifdef RLIMIT_DATA
5291 RESCHECK(DATA);
5292#endif
5293 break;
5294
5295 case 'F':
5296#ifdef RLIMIT_FSIZE
5297 RESCHECK(FSIZE);
5298#endif
5299 break;
5300
5301 case 'M':
5302#ifdef RLIMIT_MEMLOCK
5303 RESCHECK(MEMLOCK);
5304#endif
5305#ifdef RLIMIT_MSGQUEUE
5306 RESCHECK(MSGQUEUE);
5307#endif
5308 break;
5309
5310 case 'N':
5311#ifdef RLIMIT_NOFILE
5312 RESCHECK(NOFILE);
5313#endif
5314#ifdef RLIMIT_NPROC
5315 RESCHECK(NPROC);
5316#endif
5317#ifdef RLIMIT_NPTS
5318 RESCHECK(NPTS);
5319#endif
5320#ifdef RLIMIT_NICE
5321 RESCHECK(NICE);
5322#endif
5323 break;
5324
5325 case 'R':
5326#ifdef RLIMIT_RSS
5327 RESCHECK(RSS);
5328#endif
5329#ifdef RLIMIT_RTPRIO
5330 RESCHECK(RTPRIO);
5331#endif
5332#ifdef RLIMIT_RTTIME
5333 RESCHECK(RTTIME);
5334#endif
5335 break;
5336
5337 case 'S':
5338#ifdef RLIMIT_STACK
5339 RESCHECK(STACK);
5340#endif
5341#ifdef RLIMIT_SBSIZE
5342 RESCHECK(SBSIZE);
5343#endif
5344#ifdef RLIMIT_SIGPENDING
5345 RESCHECK(SIGPENDING);
5346#endif
5347 break;
5348 }
5349 return -1;
5350
5351 found:
5352 switch (casetype) {
5353 case 0:
5354 for (p = name; *p; p++)
5355 if (!ISUPPER(*p))
5356 return -1;
5357 break;
5358
5359 case 1:
5360 for (p = name; *p; p++)
5361 if (!ISLOWER(*p))
5362 return -1;
5363 break;
5364
5365 default:
5366 rb_bug("unexpected casetype");
5367 }
5368 return resource;
5369#undef RESCHECK
5370}
5371
5372static int
5373rlimit_type_by_hname(const char *name, long len)
5374{
5375 return rlimit_resource_name2int(name, len, 0);
5376}
5377
5378static int
5379rlimit_type_by_lname(const char *name, long len)
5380{
5381 return rlimit_resource_name2int(name, len, 1);
5382}
5383
5384static int
5385rlimit_type_by_sym(VALUE key)
5386{
5387 VALUE name = rb_sym2str(key);
5388 const char *rname = RSTRING_PTR(name);
5389 long len = RSTRING_LEN(name);
5390 int rtype = -1;
5391 static const char prefix[] = "rlimit_";
5392 enum {prefix_len = sizeof(prefix)-1};
5393
5394 if (len > prefix_len && strncmp(prefix, rname, prefix_len) == 0) {
5395 rtype = rlimit_type_by_lname(rname + prefix_len, len - prefix_len);
5396 }
5397
5398 RB_GC_GUARD(key);
5399 return rtype;
5400}
5401
5402static int
5403rlimit_resource_type(VALUE rtype)
5404{
5405 const char *name;
5406 long len;
5407 VALUE v;
5408 int r;
5409
5410 switch (TYPE(rtype)) {
5411 case T_SYMBOL:
5412 v = rb_sym2str(rtype);
5413 name = RSTRING_PTR(v);
5414 len = RSTRING_LEN(v);
5415 break;
5416
5417 default:
5418 v = rb_check_string_type(rtype);
5419 if (!NIL_P(v)) {
5420 rtype = v;
5421 case T_STRING:
5422 name = StringValueCStr(rtype);
5423 len = RSTRING_LEN(rtype);
5424 break;
5425 }
5426 /* fall through */
5427
5428 case T_FIXNUM:
5429 case T_BIGNUM:
5430 return NUM2INT(rtype);
5431 }
5432
5433 r = rlimit_type_by_hname(name, len);
5434 if (r != -1)
5435 return r;
5436
5437 rb_raise(rb_eArgError, "invalid resource name: % "PRIsVALUE, rtype);
5438
5440}
5441
5442static rlim_t
5443rlimit_resource_value(VALUE rval)
5444{
5445 const char *name;
5446 VALUE v;
5447
5448 switch (TYPE(rval)) {
5449 case T_SYMBOL:
5450 v = rb_sym2str(rval);
5451 name = RSTRING_PTR(v);
5452 break;
5453
5454 default:
5455 v = rb_check_string_type(rval);
5456 if (!NIL_P(v)) {
5457 rval = v;
5458 case T_STRING:
5459 name = StringValueCStr(rval);
5460 break;
5461 }
5462 /* fall through */
5463
5464 case T_FIXNUM:
5465 case T_BIGNUM:
5466 return NUM2RLIM(rval);
5467 }
5468
5469#ifdef RLIM_INFINITY
5470 if (strcmp(name, "INFINITY") == 0) return RLIM_INFINITY;
5471#endif
5472#ifdef RLIM_SAVED_MAX
5473 if (strcmp(name, "SAVED_MAX") == 0) return RLIM_SAVED_MAX;
5474#endif
5475#ifdef RLIM_SAVED_CUR
5476 if (strcmp(name, "SAVED_CUR") == 0) return RLIM_SAVED_CUR;
5477#endif
5478 rb_raise(rb_eArgError, "invalid resource value: %"PRIsVALUE, rval);
5479
5480 UNREACHABLE_RETURN((rlim_t)-1);
5481}
5482#endif
5483
5484#if defined(HAVE_GETRLIMIT) && defined(RLIM2NUM)
5485/*
5486 * call-seq:
5487 * Process.getrlimit(resource) -> [cur_limit, max_limit]
5488 *
5489 * Returns a 2-element array of the current (soft) limit
5490 * and maximum (hard) limit for the given +resource+.
5491 *
5492 * Argument +resource+ specifies the resource whose limits are to be returned;
5493 * see Process.setrlimit.
5494 *
5495 * Each of the returned values +cur_limit+ and +max_limit+ is an integer;
5496 * see Process.setrlimit.
5497 *
5498 * Example:
5499 *
5500 * Process.getrlimit(:CORE) # => [0, 18446744073709551615]
5501 *
5502 * See Process.setrlimit.
5503 *
5504 * Not available on all platforms.
5505 */
5506
5507static VALUE
5508proc_getrlimit(VALUE obj, VALUE resource)
5509{
5510 struct rlimit rlim;
5511
5512 if (getrlimit(rlimit_resource_type(resource), &rlim) < 0) {
5513 rb_sys_fail("getrlimit");
5514 }
5515 return rb_assoc_new(RLIM2NUM(rlim.rlim_cur), RLIM2NUM(rlim.rlim_max));
5516}
5517#else
5518#define proc_getrlimit rb_f_notimplement
5519#endif
5520
5521#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
5522/*
5523 * call-seq:
5524 * Process.setrlimit(resource, cur_limit, max_limit = cur_limit) -> nil
5525 *
5526 * Sets limits for the current process for the given +resource+
5527 * to +cur_limit+ (soft limit) and +max_limit+ (hard limit);
5528 * returns +nil+.
5529 *
5530 * Argument +resource+ specifies the resource whose limits are to be set;
5531 * the argument may be given as a symbol, as a string, or as a constant
5532 * beginning with <tt>Process::RLIMIT_</tt>
5533 * (e.g., +:CORE+, <tt>'CORE'</tt>, or <tt>Process::RLIMIT_CORE</tt>.
5534 *
5535 * The resources available and supported are system-dependent,
5536 * and may include (here expressed as symbols):
5537 *
5538 * - +:AS+: Total available memory (bytes) (SUSv3, NetBSD, FreeBSD, OpenBSD except 4.4BSD-Lite).
5539 * - +:CORE+: Core size (bytes) (SUSv3).
5540 * - +:CPU+: CPU time (seconds) (SUSv3).
5541 * - +:DATA+: Data segment (bytes) (SUSv3).
5542 * - +:FSIZE+: File size (bytes) (SUSv3).
5543 * - +:MEMLOCK+: Total size for mlock(2) (bytes) (4.4BSD, GNU/Linux).
5544 * - +:MSGQUEUE+: Allocation for POSIX message queues (bytes) (GNU/Linux).
5545 * - +:NICE+: Ceiling on process's nice(2) value (number) (GNU/Linux).
5546 * - +:NOFILE+: File descriptors (number) (SUSv3).
5547 * - +:NPROC+: Number of processes for the user (number) (4.4BSD, GNU/Linux).
5548 * - +:NPTS+: Number of pseudo terminals (number) (FreeBSD).
5549 * - +:RSS+: Resident memory size (bytes) (4.2BSD, GNU/Linux).
5550 * - +:RTPRIO+: Ceiling on the process's real-time priority (number) (GNU/Linux).
5551 * - +:RTTIME+: CPU time for real-time process (us) (GNU/Linux).
5552 * - +:SBSIZE+: All socket buffers (bytes) (NetBSD, FreeBSD).
5553 * - +:SIGPENDING+: Number of queued signals allowed (signals) (GNU/Linux).
5554 * - +:STACK+: Stack size (bytes) (SUSv3).
5555 *
5556 * Arguments +cur_limit+ and +max_limit+ may be:
5557 *
5558 * - Integers (+max_limit+ should not be smaller than +cur_limit+).
5559 * - Symbol +:SAVED_MAX+, string <tt>'SAVED_MAX'</tt>,
5560 * or constant <tt>Process::RLIM_SAVED_MAX</tt>: saved maximum limit.
5561 * - Symbol +:SAVED_CUR+, string <tt>'SAVED_CUR'</tt>,
5562 * or constant <tt>Process::RLIM_SAVED_CUR</tt>: saved current limit.
5563 * - Symbol +:INFINITY+, string <tt>'INFINITY'</tt>,
5564 * or constant <tt>Process::RLIM_INFINITY</tt>: no limit on resource.
5565 *
5566 * This example raises the soft limit of core size to
5567 * the hard limit to try to make core dump possible:
5568 *
5569 * Process.setrlimit(:CORE, Process.getrlimit(:CORE)[1])
5570 *
5571 * Not available on all platforms.
5572 */
5573
5574static VALUE
5575proc_setrlimit(int argc, VALUE *argv, VALUE obj)
5576{
5577 VALUE resource, rlim_cur, rlim_max;
5578 struct rlimit rlim;
5579
5580 rb_check_arity(argc, 2, 3);
5581 resource = argv[0];
5582 rlim_cur = argv[1];
5583 if (argc < 3 || NIL_P(rlim_max = argv[2]))
5584 rlim_max = rlim_cur;
5585
5586 rlim.rlim_cur = rlimit_resource_value(rlim_cur);
5587 rlim.rlim_max = rlimit_resource_value(rlim_max);
5588
5589 if (setrlimit(rlimit_resource_type(resource), &rlim) < 0) {
5590 rb_sys_fail("setrlimit");
5591 }
5592 return Qnil;
5593}
5594#else
5595#define proc_setrlimit rb_f_notimplement
5596#endif
5597
5598static int under_uid_switch = 0;
5599static void
5600check_uid_switch(void)
5601{
5602 if (under_uid_switch) {
5603 rb_raise(rb_eRuntimeError, "can't handle UID while evaluating block given to Process::UID.switch method");
5604 }
5605}
5606
5607static int under_gid_switch = 0;
5608static void
5609check_gid_switch(void)
5610{
5611 if (under_gid_switch) {
5612 rb_raise(rb_eRuntimeError, "can't handle GID while evaluating block given to Process::UID.switch method");
5613 }
5614}
5615
5616
5617#if defined(HAVE_PWD_H)
5618static inline bool
5619login_not_found(int err)
5620{
5621 return (err == ENOTTY || err == ENXIO || err == ENOENT);
5622}
5623
5629VALUE
5630rb_getlogin(void)
5631{
5632# if !defined(USE_GETLOGIN_R) && !defined(USE_GETLOGIN)
5633 return Qnil;
5634# else
5635 char MAYBE_UNUSED(*login) = NULL;
5636
5637# ifdef USE_GETLOGIN_R
5638
5639# if defined(__FreeBSD__)
5640 typedef int getlogin_r_size_t;
5641# else
5642 typedef size_t getlogin_r_size_t;
5643# endif
5644
5645 long loginsize = GETLOGIN_R_SIZE_INIT; /* maybe -1 */
5646
5647 if (loginsize < 0)
5648 loginsize = GETLOGIN_R_SIZE_DEFAULT;
5649
5650 VALUE maybe_result = rb_str_buf_new(loginsize);
5651
5652 login = RSTRING_PTR(maybe_result);
5653 loginsize = rb_str_capacity(maybe_result);
5654 rb_str_set_len(maybe_result, loginsize);
5655
5656 int gle;
5657 while ((gle = getlogin_r(login, (getlogin_r_size_t)loginsize)) != 0) {
5658 if (login_not_found(gle)) {
5659 rb_str_resize(maybe_result, 0);
5660 return Qnil;
5661 }
5662
5663 if (gle != ERANGE || loginsize >= GETLOGIN_R_SIZE_LIMIT) {
5664 rb_str_resize(maybe_result, 0);
5665 rb_syserr_fail(gle, "getlogin_r");
5666 }
5667
5668 rb_str_modify_expand(maybe_result, loginsize);
5669 login = RSTRING_PTR(maybe_result);
5670 loginsize = rb_str_capacity(maybe_result);
5671 }
5672
5673 if (login == NULL) {
5674 rb_str_resize(maybe_result, 0);
5675 return Qnil;
5676 }
5677
5678 rb_str_set_len(maybe_result, strlen(login));
5679 return maybe_result;
5680
5681# elif defined(USE_GETLOGIN)
5682
5683 errno = 0;
5684 login = getlogin();
5685 int err = errno;
5686 if (err) {
5687 if (login_not_found(err)) {
5688 return Qnil;
5689 }
5690 rb_syserr_fail(err, "getlogin");
5691 }
5692
5693 return login ? rb_str_new_cstr(login) : Qnil;
5694# endif
5695
5696#endif
5697}
5698
5699/* avoid treating as errors errno values that indicate "not found" */
5700static inline bool
5701pwd_not_found(int err)
5702{
5703 switch (err) {
5704 case 0:
5705 case ENOENT:
5706 case ESRCH:
5707 case EBADF:
5708 case EPERM:
5709 return true;
5710 default:
5711 return false;
5712 }
5713}
5714
5715# if defined(USE_GETPWNAM_R)
5716struct getpwnam_r_args {
5717 const char *login;
5718 char *buf;
5719 size_t bufsize;
5720 struct passwd *result;
5721 struct passwd pwstore;
5722};
5723
5724# define GETPWNAM_R_ARGS(login_, buf_, bufsize_) (struct getpwnam_r_args) \
5725 {.login = login_, .buf = buf_, .bufsize = bufsize_, .result = NULL}
5726
5727static void *
5728nogvl_getpwnam_r(void *args)
5729{
5730 struct getpwnam_r_args *arg = args;
5731 return (void *)(VALUE)getpwnam_r(arg->login, &arg->pwstore, arg->buf, arg->bufsize, &arg->result);
5732}
5733# endif
5734
5735VALUE
5736rb_getpwdirnam_for_login(VALUE login_name)
5737{
5738#if !defined(USE_GETPWNAM_R) && !defined(USE_GETPWNAM)
5739 return Qnil;
5740#else
5741
5742 if (NIL_P(login_name)) {
5743 /* nothing to do; no name with which to query the password database */
5744 return Qnil;
5745 }
5746
5747 const char *login = RSTRING_PTR(login_name);
5748
5749
5750# ifdef USE_GETPWNAM_R
5751
5752 char *bufnm;
5753 long bufsizenm = GETPW_R_SIZE_INIT; /* maybe -1 */
5754
5755 if (bufsizenm < 0)
5756 bufsizenm = GETPW_R_SIZE_DEFAULT;
5757
5758 VALUE getpwnm_tmp = rb_str_tmp_new(bufsizenm);
5759
5760 bufnm = RSTRING_PTR(getpwnm_tmp);
5761 bufsizenm = rb_str_capacity(getpwnm_tmp);
5762 rb_str_set_len(getpwnm_tmp, bufsizenm);
5763 struct getpwnam_r_args args = GETPWNAM_R_ARGS(login, bufnm, (size_t)bufsizenm);
5764
5765 int enm;
5766 while ((enm = IO_WITHOUT_GVL_INT(nogvl_getpwnam_r, &args)) != 0) {
5767 if (pwd_not_found(enm)) {
5768 rb_str_resize(getpwnm_tmp, 0);
5769 return Qnil;
5770 }
5771
5772 if (enm != ERANGE || args.bufsize >= GETPW_R_SIZE_LIMIT) {
5773 rb_str_resize(getpwnm_tmp, 0);
5774 rb_syserr_fail(enm, "getpwnam_r");
5775 }
5776
5777 rb_str_modify_expand(getpwnm_tmp, (long)args.bufsize);
5778 args.buf = RSTRING_PTR(getpwnm_tmp);
5779 args.bufsize = (size_t)rb_str_capacity(getpwnm_tmp);
5780 }
5781
5782 if (args.result == NULL) {
5783 /* no record in the password database for the login name */
5784 rb_str_resize(getpwnm_tmp, 0);
5785 return Qnil;
5786 }
5787
5788 /* found it */
5789 VALUE result = rb_str_new_cstr(args.result->pw_dir);
5790 rb_str_resize(getpwnm_tmp, 0);
5791 return result;
5792
5793# elif defined(USE_GETPWNAM)
5794
5795 struct passwd *pwptr;
5796 errno = 0;
5797 if (!(pwptr = getpwnam(login))) {
5798 int err = errno;
5799
5800 if (pwd_not_found(err)) {
5801 return Qnil;
5802 }
5803
5804 rb_syserr_fail(err, "getpwnam");
5805 }
5806
5807 /* found it */
5808 return rb_str_new_cstr(pwptr->pw_dir);
5809# endif
5810
5811#endif
5812}
5813
5814# if defined(USE_GETPWUID_R)
5815struct getpwuid_r_args {
5816 uid_t uid;
5817 char *buf;
5818 size_t bufsize;
5819 struct passwd *result;
5820 struct passwd pwstore;
5821};
5822
5823# define GETPWUID_R_ARGS(uid_, buf_, bufsize_) (struct getpwuid_r_args) \
5824 {.uid = uid_, .buf = buf_, .bufsize = bufsize_, .result = NULL}
5825
5826static void *
5827nogvl_getpwuid_r(void *args)
5828{
5829 struct getpwuid_r_args *arg = args;
5830 return (void *)(VALUE)getpwuid_r(arg->uid, &arg->pwstore, arg->buf, arg->bufsize, &arg->result);
5831}
5832# endif
5833
5837VALUE
5838rb_getpwdiruid(void)
5839{
5840# if !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID)
5841 /* Should never happen... </famous-last-words> */
5842 return Qnil;
5843# else
5844 uid_t ruid = getuid();
5845
5846# ifdef USE_GETPWUID_R
5847
5848 char *bufid;
5849 long bufsizeid = GETPW_R_SIZE_INIT; /* maybe -1 */
5850
5851 if (bufsizeid < 0)
5852 bufsizeid = GETPW_R_SIZE_DEFAULT;
5853
5854 VALUE getpwid_tmp = rb_str_tmp_new(bufsizeid);
5855
5856 bufid = RSTRING_PTR(getpwid_tmp);
5857 bufsizeid = rb_str_capacity(getpwid_tmp);
5858 rb_str_set_len(getpwid_tmp, bufsizeid);
5859 struct getpwuid_r_args args = GETPWUID_R_ARGS(ruid, bufid, (size_t)bufsizeid);
5860
5861 int eid;
5862 while ((eid = IO_WITHOUT_GVL_INT(nogvl_getpwuid_r, &args)) != 0) {
5863 if (pwd_not_found(eid)) {
5864 rb_str_resize(getpwid_tmp, 0);
5865 return Qnil;
5866 }
5867
5868 if (eid != ERANGE || args.bufsize >= GETPW_R_SIZE_LIMIT) {
5869 rb_str_resize(getpwid_tmp, 0);
5870 rb_syserr_fail(eid, "getpwuid_r");
5871 }
5872
5873 rb_str_modify_expand(getpwid_tmp, (long)args.bufsize);
5874 args.buf = RSTRING_PTR(getpwid_tmp);
5875 args.bufsize = (size_t)rb_str_capacity(getpwid_tmp);
5876 }
5877
5878 if (args.result == NULL) {
5879 /* no record in the password database for the uid */
5880 rb_str_resize(getpwid_tmp, 0);
5881 return Qnil;
5882 }
5883
5884 /* found it */
5885 VALUE result = rb_str_new_cstr(args.result->pw_dir);
5886 rb_str_resize(getpwid_tmp, 0);
5887 return result;
5888
5889# elif defined(USE_GETPWUID)
5890
5891 struct passwd *pwptr;
5892 errno = 0;
5893 if (!(pwptr = getpwuid(ruid))) {
5894 int err = errno;
5895
5896 if (pwd_not_found(err)) {
5897 return Qnil;
5898 }
5899
5900 rb_syserr_fail(err, "getpwuid");
5901 }
5902
5903 /* found it */
5904 return rb_str_new_cstr(pwptr->pw_dir);
5905# endif
5906
5907#endif /* !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID) */
5908}
5909#endif /* HAVE_PWD_H */
5910
5911
5912/*********************************************************************
5913 * Document-class: Process::Sys
5914 *
5915 * The Process::Sys module contains UID and GID
5916 * functions which provide direct bindings to the system calls of the
5917 * same names instead of the more-portable versions of the same
5918 * functionality found in the +Process+,
5919 * Process::UID, and Process::GID modules.
5920 */
5921
5922#if defined(HAVE_PWD_H)
5923static rb_uid_t
5924obj2uid(VALUE id
5925# ifdef USE_GETPWNAM_R
5926 , VALUE *getpw_tmp
5927# endif
5928 )
5929{
5930 rb_uid_t uid;
5931 VALUE tmp;
5932
5933 if (FIXNUM_P(id) || NIL_P(tmp = rb_check_string_type(id))) {
5934 uid = NUM2UIDT(id);
5935 }
5936 else {
5937 const char *usrname = StringValueCStr(id);
5938 struct passwd *pwptr;
5939#ifdef USE_GETPWNAM_R
5940 char *getpw_buf;
5941 long getpw_buf_len;
5942 int e;
5943 if (!*getpw_tmp) {
5944 getpw_buf_len = GETPW_R_SIZE_INIT;
5945 if (getpw_buf_len < 0) getpw_buf_len = GETPW_R_SIZE_DEFAULT;
5946 *getpw_tmp = rb_str_tmp_new(getpw_buf_len);
5947 }
5948 getpw_buf = RSTRING_PTR(*getpw_tmp);
5949 getpw_buf_len = rb_str_capacity(*getpw_tmp);
5950 rb_str_set_len(*getpw_tmp, getpw_buf_len);
5951 errno = 0;
5952 struct getpwnam_r_args args = GETPWNAM_R_ARGS((char *)usrname, getpw_buf, (size_t)getpw_buf_len);
5953
5954 while ((e = IO_WITHOUT_GVL_INT(nogvl_getpwnam_r, &args)) != 0) {
5955 if (e != ERANGE || args.bufsize >= GETPW_R_SIZE_LIMIT) {
5956 rb_str_resize(*getpw_tmp, 0);
5957 rb_syserr_fail(e, "getpwnam_r");
5958 }
5959 rb_str_modify_expand(*getpw_tmp, (long)args.bufsize);
5960 args.buf = RSTRING_PTR(*getpw_tmp);
5961 args.bufsize = (size_t)rb_str_capacity(*getpw_tmp);
5962 }
5963 pwptr = args.result;
5964#else
5965 pwptr = getpwnam(usrname);
5966#endif
5967 if (!pwptr) {
5968#ifndef USE_GETPWNAM_R
5969 endpwent();
5970#endif
5971 rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, id);
5972 }
5973 uid = pwptr->pw_uid;
5974#ifndef USE_GETPWNAM_R
5975 endpwent();
5976#endif
5977 }
5978 return uid;
5979}
5980
5981# ifdef p_uid_from_name
5982/*
5983 * call-seq:
5984 * Process::UID.from_name(name) -> uid
5985 *
5986 * Get the user ID by the _name_.
5987 * If the user is not found, +ArgumentError+ will be raised.
5988 *
5989 * Process::UID.from_name("root") #=> 0
5990 * Process::UID.from_name("nosuchuser") #=> can't find user for nosuchuser (ArgumentError)
5991 */
5992
5993static VALUE
5994p_uid_from_name(VALUE self, VALUE id)
5995{
5996 return UIDT2NUM(OBJ2UID(id));
5997}
5998# endif
5999#endif
6000
6001#if defined(HAVE_GRP_H)
6002# if defined(USE_GETGRNAM_R)
6003struct getgrnam_r_args {
6004 const char *name;
6005 char *buf;
6006 size_t bufsize;
6007 struct group *result;
6008 struct group grp;
6009};
6010
6011# define GETGRNAM_R_ARGS(name_, buf_, bufsize_) (struct getgrnam_r_args) \
6012 {.name = name_, .buf = buf_, .bufsize = bufsize_, .result = NULL}
6013
6014static void *
6015nogvl_getgrnam_r(void *args)
6016{
6017 struct getgrnam_r_args *arg = args;
6018 return (void *)(VALUE)getgrnam_r(arg->name, &arg->grp, arg->buf, arg->bufsize, &arg->result);
6019}
6020# endif
6021
6022static rb_gid_t
6023obj2gid(VALUE id
6024# ifdef USE_GETGRNAM_R
6025 , VALUE *getgr_tmp
6026# endif
6027 )
6028{
6029 rb_gid_t gid;
6030 VALUE tmp;
6031
6032 if (FIXNUM_P(id) || NIL_P(tmp = rb_check_string_type(id))) {
6033 gid = NUM2GIDT(id);
6034 }
6035 else {
6036 const char *grpname = StringValueCStr(id);
6037 struct group *grptr;
6038#ifdef USE_GETGRNAM_R
6039 char *getgr_buf;
6040 long getgr_buf_len;
6041 int e;
6042 if (!*getgr_tmp) {
6043 getgr_buf_len = GETGR_R_SIZE_INIT;
6044 if (getgr_buf_len < 0) getgr_buf_len = GETGR_R_SIZE_DEFAULT;
6045 *getgr_tmp = rb_str_tmp_new(getgr_buf_len);
6046 }
6047 getgr_buf = RSTRING_PTR(*getgr_tmp);
6048 getgr_buf_len = rb_str_capacity(*getgr_tmp);
6049 rb_str_set_len(*getgr_tmp, getgr_buf_len);
6050 errno = 0;
6051 struct getgrnam_r_args args = GETGRNAM_R_ARGS(grpname, getgr_buf, (size_t)getgr_buf_len);
6052
6053 while ((e = IO_WITHOUT_GVL_INT(nogvl_getgrnam_r, &args)) != 0) {
6054 if (e != ERANGE || args.bufsize >= GETGR_R_SIZE_LIMIT) {
6055 rb_str_resize(*getgr_tmp, 0);
6056 rb_syserr_fail(e, "getgrnam_r");
6057 }
6058 rb_str_modify_expand(*getgr_tmp, (long)args.bufsize);
6059 args.buf = RSTRING_PTR(*getgr_tmp);
6060 args.bufsize = (size_t)rb_str_capacity(*getgr_tmp);
6061 }
6062 grptr = args.result;
6063#elif defined(HAVE_GETGRNAM)
6064 grptr = getgrnam(grpname);
6065#else
6066 grptr = NULL;
6067#endif
6068 if (!grptr) {
6069#if !defined(USE_GETGRNAM_R) && defined(HAVE_ENDGRENT)
6070 endgrent();
6071#endif
6072 rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, id);
6073 }
6074 gid = grptr->gr_gid;
6075#if !defined(USE_GETGRNAM_R) && defined(HAVE_ENDGRENT)
6076 endgrent();
6077#endif
6078 }
6079 return gid;
6080}
6081
6082# ifdef p_gid_from_name
6083/*
6084 * call-seq:
6085 * Process::GID.from_name(name) -> gid
6086 *
6087 * Get the group ID by the _name_.
6088 * If the group is not found, +ArgumentError+ will be raised.
6089 *
6090 * Process::GID.from_name("wheel") #=> 0
6091 * Process::GID.from_name("nosuchgroup") #=> can't find group for nosuchgroup (ArgumentError)
6092 */
6093
6094static VALUE
6095p_gid_from_name(VALUE self, VALUE id)
6096{
6097 return GIDT2NUM(OBJ2GID(id));
6098}
6099# endif
6100#endif
6101
6102#if defined HAVE_SETUID
6103/*
6104 * call-seq:
6105 * Process::Sys.setuid(user) -> nil
6106 *
6107 * Set the user ID of the current process to _user_. Not
6108 * available on all platforms.
6109 *
6110 */
6111
6112static VALUE
6113p_sys_setuid(VALUE obj, VALUE id)
6114{
6115 check_uid_switch();
6116 if (setuid(OBJ2UID(id)) != 0) rb_sys_fail(0);
6117 return Qnil;
6118}
6119#else
6120#define p_sys_setuid rb_f_notimplement
6121#endif
6122
6123
6124#if defined HAVE_SETRUID
6125/*
6126 * call-seq:
6127 * Process::Sys.setruid(user) -> nil
6128 *
6129 * Set the real user ID of the calling process to _user_.
6130 * Not available on all platforms.
6131 *
6132 */
6133
6134static VALUE
6135p_sys_setruid(VALUE obj, VALUE id)
6136{
6137 check_uid_switch();
6138 if (setruid(OBJ2UID(id)) != 0) rb_sys_fail(0);
6139 return Qnil;
6140}
6141#else
6142#define p_sys_setruid rb_f_notimplement
6143#endif
6144
6145
6146#if defined HAVE_SETEUID
6147/*
6148 * call-seq:
6149 * Process::Sys.seteuid(user) -> nil
6150 *
6151 * Set the effective user ID of the calling process to
6152 * _user_. Not available on all platforms.
6153 *
6154 */
6155
6156static VALUE
6157p_sys_seteuid(VALUE obj, VALUE id)
6158{
6159 check_uid_switch();
6160 if (seteuid(OBJ2UID(id)) != 0) rb_sys_fail(0);
6161 return Qnil;
6162}
6163#else
6164#define p_sys_seteuid rb_f_notimplement
6165#endif
6166
6167
6168#if defined HAVE_SETREUID
6169/*
6170 * call-seq:
6171 * Process::Sys.setreuid(rid, eid) -> nil
6172 *
6173 * Sets the (user) real and/or effective user IDs of the current
6174 * process to _rid_ and _eid_, respectively. A value of
6175 * <code>-1</code> for either means to leave that ID unchanged. Not
6176 * available on all platforms.
6177 *
6178 */
6179
6180static VALUE
6181p_sys_setreuid(VALUE obj, VALUE rid, VALUE eid)
6182{
6183 rb_uid_t ruid, euid;
6184 PREPARE_GETPWNAM;
6185 check_uid_switch();
6186 ruid = OBJ2UID1(rid);
6187 euid = OBJ2UID1(eid);
6188 FINISH_GETPWNAM;
6189 if (setreuid(ruid, euid) != 0) rb_sys_fail(0);
6190 return Qnil;
6191}
6192#else
6193#define p_sys_setreuid rb_f_notimplement
6194#endif
6195
6196
6197#if defined HAVE_SETRESUID
6198/*
6199 * call-seq:
6200 * Process::Sys.setresuid(rid, eid, sid) -> nil
6201 *
6202 * Sets the (user) real, effective, and saved user IDs of the
6203 * current process to _rid_, _eid_, and _sid_ respectively. A
6204 * value of <code>-1</code> for any value means to
6205 * leave that ID unchanged. Not available on all platforms.
6206 *
6207 */
6208
6209static VALUE
6210p_sys_setresuid(VALUE obj, VALUE rid, VALUE eid, VALUE sid)
6211{
6212 rb_uid_t ruid, euid, suid;
6213 PREPARE_GETPWNAM;
6214 check_uid_switch();
6215 ruid = OBJ2UID1(rid);
6216 euid = OBJ2UID1(eid);
6217 suid = OBJ2UID1(sid);
6218 FINISH_GETPWNAM;
6219 if (setresuid(ruid, euid, suid) != 0) rb_sys_fail(0);
6220 return Qnil;
6221}
6222#else
6223#define p_sys_setresuid rb_f_notimplement
6224#endif
6225
6226
6227/*
6228 * call-seq:
6229 * Process.uid -> integer
6230 * Process::UID.rid -> integer
6231 * Process::Sys.getuid -> integer
6232 *
6233 * Returns the (real) user ID of the current process.
6234 *
6235 * Process.uid # => 1000
6236 *
6237 */
6238
6239static VALUE
6240proc_getuid(VALUE obj)
6241{
6242 rb_uid_t uid = getuid();
6243 return UIDT2NUM(uid);
6244}
6245
6246
6247#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETRUID) || defined(HAVE_SETUID)
6248/*
6249 * call-seq:
6250 * Process.uid = new_uid -> new_uid
6251 *
6252 * Sets the (user) user ID for the current process to +new_uid+:
6253 *
6254 * Process.uid = 1000 # => 1000
6255 *
6256 * Not available on all platforms.
6257 */
6258
6259static VALUE
6260proc_setuid(VALUE obj, VALUE id)
6261{
6262 rb_uid_t uid;
6263
6264 check_uid_switch();
6265
6266 uid = OBJ2UID(id);
6267#if defined(HAVE_SETRESUID)
6268 if (setresuid(uid, -1, -1) < 0) rb_sys_fail(0);
6269#elif defined HAVE_SETREUID
6270 if (setreuid(uid, -1) < 0) rb_sys_fail(0);
6271#elif defined HAVE_SETRUID
6272 if (setruid(uid) < 0) rb_sys_fail(0);
6273#elif defined HAVE_SETUID
6274 {
6275 if (geteuid() == uid) {
6276 if (setuid(uid) < 0) rb_sys_fail(0);
6277 }
6278 else {
6280 }
6281 }
6282#endif
6283 return id;
6284}
6285#else
6286#define proc_setuid rb_f_notimplement
6287#endif
6288
6289
6290/********************************************************************
6291 *
6292 * Document-class: Process::UID
6293 *
6294 * The Process::UID module contains a collection of
6295 * module functions which can be used to portably get, set, and
6296 * switch the current process's real, effective, and saved user IDs.
6297 *
6298 */
6299
6300static rb_uid_t SAVED_USER_ID = -1;
6301
6302#ifdef BROKEN_SETREUID
6303int
6304setreuid(rb_uid_t ruid, rb_uid_t euid)
6305{
6306 if (ruid != (rb_uid_t)-1 && ruid != getuid()) {
6307 if (euid == (rb_uid_t)-1) euid = geteuid();
6308 if (setuid(ruid) < 0) return -1;
6309 }
6310 if (euid != (rb_uid_t)-1 && euid != geteuid()) {
6311 if (seteuid(euid) < 0) return -1;
6312 }
6313 return 0;
6314}
6315#endif
6316
6317/*
6318 * call-seq:
6319 * Process::UID.change_privilege(user) -> integer
6320 *
6321 * Change the current process's real and effective user ID to that
6322 * specified by _user_. Returns the new user ID. Not
6323 * available on all platforms.
6324 *
6325 * [Process.uid, Process.euid] #=> [0, 0]
6326 * Process::UID.change_privilege(31) #=> 31
6327 * [Process.uid, Process.euid] #=> [31, 31]
6328 */
6329
6330static VALUE
6331p_uid_change_privilege(VALUE obj, VALUE id)
6332{
6333 rb_uid_t uid;
6334
6335 check_uid_switch();
6336
6337 uid = OBJ2UID(id);
6338
6339 if (geteuid() == 0) { /* root-user */
6340#if defined(HAVE_SETRESUID)
6341 if (setresuid(uid, uid, uid) < 0) rb_sys_fail(0);
6342 SAVED_USER_ID = uid;
6343#elif defined(HAVE_SETUID)
6344 if (setuid(uid) < 0) rb_sys_fail(0);
6345 SAVED_USER_ID = uid;
6346#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
6347 if (getuid() == uid) {
6348 if (SAVED_USER_ID == uid) {
6349 if (setreuid(-1, uid) < 0) rb_sys_fail(0);
6350 }
6351 else {
6352 if (uid == 0) { /* (r,e,s) == (root, root, x) */
6353 if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
6354 if (setreuid(SAVED_USER_ID, 0) < 0) rb_sys_fail(0);
6355 SAVED_USER_ID = 0; /* (r,e,s) == (x, root, root) */
6356 if (setreuid(uid, uid) < 0) rb_sys_fail(0);
6357 SAVED_USER_ID = uid;
6358 }
6359 else {
6360 if (setreuid(0, -1) < 0) rb_sys_fail(0);
6361 SAVED_USER_ID = 0;
6362 if (setreuid(uid, uid) < 0) rb_sys_fail(0);
6363 SAVED_USER_ID = uid;
6364 }
6365 }
6366 }
6367 else {
6368 if (setreuid(uid, uid) < 0) rb_sys_fail(0);
6369 SAVED_USER_ID = uid;
6370 }
6371#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
6372 if (getuid() == uid) {
6373 if (SAVED_USER_ID == uid) {
6374 if (seteuid(uid) < 0) rb_sys_fail(0);
6375 }
6376 else {
6377 if (uid == 0) {
6378 if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
6379 SAVED_USER_ID = 0;
6380 if (setruid(0) < 0) rb_sys_fail(0);
6381 }
6382 else {
6383 if (setruid(0) < 0) rb_sys_fail(0);
6384 SAVED_USER_ID = 0;
6385 if (seteuid(uid) < 0) rb_sys_fail(0);
6386 if (setruid(uid) < 0) rb_sys_fail(0);
6387 SAVED_USER_ID = uid;
6388 }
6389 }
6390 }
6391 else {
6392 if (seteuid(uid) < 0) rb_sys_fail(0);
6393 if (setruid(uid) < 0) rb_sys_fail(0);
6394 SAVED_USER_ID = uid;
6395 }
6396#else
6397 (void)uid;
6399#endif
6400 }
6401 else { /* unprivileged user */
6402#if defined(HAVE_SETRESUID)
6403 if (setresuid((getuid() == uid)? (rb_uid_t)-1: uid,
6404 (geteuid() == uid)? (rb_uid_t)-1: uid,
6405 (SAVED_USER_ID == uid)? (rb_uid_t)-1: uid) < 0) rb_sys_fail(0);
6406 SAVED_USER_ID = uid;
6407#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
6408 if (SAVED_USER_ID == uid) {
6409 if (setreuid((getuid() == uid)? (rb_uid_t)-1: uid,
6410 (geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
6411 rb_sys_fail(0);
6412 }
6413 else if (getuid() != uid) {
6414 if (setreuid(uid, (geteuid() == uid)? (rb_uid_t)-1: uid) < 0)
6415 rb_sys_fail(0);
6416 SAVED_USER_ID = uid;
6417 }
6418 else if (/* getuid() == uid && */ geteuid() != uid) {
6419 if (setreuid(geteuid(), uid) < 0) rb_sys_fail(0);
6420 SAVED_USER_ID = uid;
6421 if (setreuid(uid, -1) < 0) rb_sys_fail(0);
6422 }
6423 else { /* getuid() == uid && geteuid() == uid */
6424 if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
6425 if (setreuid(SAVED_USER_ID, uid) < 0) rb_sys_fail(0);
6426 SAVED_USER_ID = uid;
6427 if (setreuid(uid, -1) < 0) rb_sys_fail(0);
6428 }
6429#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
6430 if (SAVED_USER_ID == uid) {
6431 if (geteuid() != uid && seteuid(uid) < 0) rb_sys_fail(0);
6432 if (getuid() != uid && setruid(uid) < 0) rb_sys_fail(0);
6433 }
6434 else if (/* SAVED_USER_ID != uid && */ geteuid() == uid) {
6435 if (getuid() != uid) {
6436 if (setruid(uid) < 0) rb_sys_fail(0);
6437 SAVED_USER_ID = uid;
6438 }
6439 else {
6440 if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
6441 SAVED_USER_ID = uid;
6442 if (setruid(uid) < 0) rb_sys_fail(0);
6443 }
6444 }
6445 else if (/* geteuid() != uid && */ getuid() == uid) {
6446 if (seteuid(uid) < 0) rb_sys_fail(0);
6447 if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
6448 SAVED_USER_ID = uid;
6449 if (setruid(uid) < 0) rb_sys_fail(0);
6450 }
6451 else {
6452 rb_syserr_fail(EPERM, 0);
6453 }
6454#elif defined HAVE_44BSD_SETUID
6455 if (getuid() == uid) {
6456 /* (r,e,s)==(uid,?,?) ==> (uid,uid,uid) */
6457 if (setuid(uid) < 0) rb_sys_fail(0);
6458 SAVED_USER_ID = uid;
6459 }
6460 else {
6461 rb_syserr_fail(EPERM, 0);
6462 }
6463#elif defined HAVE_SETEUID
6464 if (getuid() == uid && SAVED_USER_ID == uid) {
6465 if (seteuid(uid) < 0) rb_sys_fail(0);
6466 }
6467 else {
6468 rb_syserr_fail(EPERM, 0);
6469 }
6470#elif defined HAVE_SETUID
6471 if (getuid() == uid && SAVED_USER_ID == uid) {
6472 if (setuid(uid) < 0) rb_sys_fail(0);
6473 }
6474 else {
6475 rb_syserr_fail(EPERM, 0);
6476 }
6477#else
6479#endif
6480 }
6481 return id;
6482}
6483
6484
6485
6486#if defined HAVE_SETGID
6487/*
6488 * call-seq:
6489 * Process::Sys.setgid(group) -> nil
6490 *
6491 * Set the group ID of the current process to _group_. Not
6492 * available on all platforms.
6493 *
6494 */
6495
6496static VALUE
6497p_sys_setgid(VALUE obj, VALUE id)
6498{
6499 check_gid_switch();
6500 if (setgid(OBJ2GID(id)) != 0) rb_sys_fail(0);
6501 return Qnil;
6502}
6503#else
6504#define p_sys_setgid rb_f_notimplement
6505#endif
6506
6507
6508#if defined HAVE_SETRGID
6509/*
6510 * call-seq:
6511 * Process::Sys.setrgid(group) -> nil
6512 *
6513 * Set the real group ID of the calling process to _group_.
6514 * Not available on all platforms.
6515 *
6516 */
6517
6518static VALUE
6519p_sys_setrgid(VALUE obj, VALUE id)
6520{
6521 check_gid_switch();
6522 if (setrgid(OBJ2GID(id)) != 0) rb_sys_fail(0);
6523 return Qnil;
6524}
6525#else
6526#define p_sys_setrgid rb_f_notimplement
6527#endif
6528
6529
6530#if defined HAVE_SETEGID
6531/*
6532 * call-seq:
6533 * Process::Sys.setegid(group) -> nil
6534 *
6535 * Set the effective group ID of the calling process to
6536 * _group_. Not available on all platforms.
6537 *
6538 */
6539
6540static VALUE
6541p_sys_setegid(VALUE obj, VALUE id)
6542{
6543 check_gid_switch();
6544 if (setegid(OBJ2GID(id)) != 0) rb_sys_fail(0);
6545 return Qnil;
6546}
6547#else
6548#define p_sys_setegid rb_f_notimplement
6549#endif
6550
6551
6552#if defined HAVE_SETREGID
6553/*
6554 * call-seq:
6555 * Process::Sys.setregid(rid, eid) -> nil
6556 *
6557 * Sets the (group) real and/or effective group IDs of the current
6558 * process to <em>rid</em> and <em>eid</em>, respectively. A value of
6559 * <code>-1</code> for either means to leave that ID unchanged. Not
6560 * available on all platforms.
6561 *
6562 */
6563
6564static VALUE
6565p_sys_setregid(VALUE obj, VALUE rid, VALUE eid)
6566{
6567 rb_gid_t rgid, egid;
6568 check_gid_switch();
6569 rgid = OBJ2GID(rid);
6570 egid = OBJ2GID(eid);
6571 if (setregid(rgid, egid) != 0) rb_sys_fail(0);
6572 return Qnil;
6573}
6574#else
6575#define p_sys_setregid rb_f_notimplement
6576#endif
6577
6578#if defined HAVE_SETRESGID
6579/*
6580 * call-seq:
6581 * Process::Sys.setresgid(rid, eid, sid) -> nil
6582 *
6583 * Sets the (group) real, effective, and saved user IDs of the
6584 * current process to <em>rid</em>, <em>eid</em>, and <em>sid</em>
6585 * respectively. A value of <code>-1</code> for any value means to
6586 * leave that ID unchanged. Not available on all platforms.
6587 *
6588 */
6589
6590static VALUE
6591p_sys_setresgid(VALUE obj, VALUE rid, VALUE eid, VALUE sid)
6592{
6593 rb_gid_t rgid, egid, sgid;
6594 check_gid_switch();
6595 rgid = OBJ2GID(rid);
6596 egid = OBJ2GID(eid);
6597 sgid = OBJ2GID(sid);
6598 if (setresgid(rgid, egid, sgid) != 0) rb_sys_fail(0);
6599 return Qnil;
6600}
6601#else
6602#define p_sys_setresgid rb_f_notimplement
6603#endif
6604
6605
6606#if defined HAVE_ISSETUGID
6607/*
6608 * call-seq:
6609 * Process::Sys.issetugid -> true or false
6610 *
6611 * Returns +true+ if the process was created as a result
6612 * of an execve(2) system call which had either of the setuid or
6613 * setgid bits set (and extra privileges were given as a result) or
6614 * if it has changed any of its real, effective or saved user or
6615 * group IDs since it began execution.
6616 *
6617 */
6618
6619static VALUE
6620p_sys_issetugid(VALUE obj)
6621{
6622 return RBOOL(issetugid());
6623}
6624#else
6625#define p_sys_issetugid rb_f_notimplement
6626#endif
6627
6628
6629/*
6630 * call-seq:
6631 * Process.gid -> integer
6632 * Process::GID.rid -> integer
6633 * Process::Sys.getgid -> integer
6634 *
6635 * Returns the (real) group ID for the current process:
6636 *
6637 * Process.gid # => 1000
6638 *
6639 */
6640
6641static VALUE
6642proc_getgid(VALUE obj)
6643{
6644 rb_gid_t gid = getgid();
6645 return GIDT2NUM(gid);
6646}
6647
6648
6649#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETRGID) || defined(HAVE_SETGID)
6650/*
6651 * call-seq:
6652 * Process.gid = new_gid -> new_gid
6653 *
6654 * Sets the group ID for the current process to +new_gid+:
6655 *
6656 * Process.gid = 1000 # => 1000
6657 *
6658 */
6659
6660static VALUE
6661proc_setgid(VALUE obj, VALUE id)
6662{
6663 rb_gid_t gid;
6664
6665 check_gid_switch();
6666
6667 gid = OBJ2GID(id);
6668#if defined(HAVE_SETRESGID)
6669 if (setresgid(gid, -1, -1) < 0) rb_sys_fail(0);
6670#elif defined HAVE_SETREGID
6671 if (setregid(gid, -1) < 0) rb_sys_fail(0);
6672#elif defined HAVE_SETRGID
6673 if (setrgid(gid) < 0) rb_sys_fail(0);
6674#elif defined HAVE_SETGID
6675 {
6676 if (getegid() == gid) {
6677 if (setgid(gid) < 0) rb_sys_fail(0);
6678 }
6679 else {
6681 }
6682 }
6683#endif
6684 return GIDT2NUM(gid);
6685}
6686#else
6687#define proc_setgid rb_f_notimplement
6688#endif
6689
6690
6691#if defined(_SC_NGROUPS_MAX) || defined(NGROUPS_MAX)
6692/*
6693 * Maximum supplementary groups are platform dependent.
6694 * FWIW, 65536 is enough big for our supported OSs.
6695 *
6696 * OS Name max groups
6697 * -----------------------------------------------
6698 * Linux Kernel >= 2.6.3 65536
6699 * Linux Kernel < 2.6.3 32
6700 * IBM AIX 5.2 64
6701 * IBM AIX 5.3 ... 6.1 128
6702 * IBM AIX 7.1 128 (can be configured to be up to 2048)
6703 * OpenBSD, NetBSD 16
6704 * FreeBSD < 8.0 16
6705 * FreeBSD >=8.0 1023
6706 * Darwin (Mac OS X) 16
6707 * Sun Solaris 7,8,9,10 16
6708 * Sun Solaris 11 / OpenSolaris 1024
6709 * Windows 1015
6710 */
6711static int _maxgroups = -1;
6712static int
6713get_sc_ngroups_max(void)
6714{
6715#ifdef _SC_NGROUPS_MAX
6716 return (int)sysconf(_SC_NGROUPS_MAX);
6717#elif defined(NGROUPS_MAX)
6718 return (int)NGROUPS_MAX;
6719#else
6720 return -1;
6721#endif
6722}
6723static int
6724maxgroups(void)
6725{
6726 if (_maxgroups < 0) {
6727 _maxgroups = get_sc_ngroups_max();
6728 if (_maxgroups < 0)
6729 _maxgroups = RB_MAX_GROUPS;
6730 }
6731
6732 return _maxgroups;
6733}
6734#endif
6735
6736
6737
6738#ifdef HAVE_GETGROUPS
6739/*
6740 * call-seq:
6741 * Process.groups -> array
6742 *
6743 * Returns an array of the group IDs
6744 * in the supplemental group access list for the current process:
6745 *
6746 * Process.groups # => [4, 24, 27, 30, 46, 122, 135, 136, 1000]
6747 *
6748 * These properties of the returned array are system-dependent:
6749 *
6750 * - Whether (and how) the array is sorted.
6751 * - Whether the array includes effective group IDs.
6752 * - Whether the array includes duplicate group IDs.
6753 * - Whether the array size exceeds the value of Process.maxgroups.
6754 *
6755 * Use this call to get a sorted and unique array:
6756 *
6757 * Process.groups.uniq.sort
6758 *
6759 */
6760
6761static VALUE
6762proc_getgroups(VALUE obj)
6763{
6764 VALUE ary, tmp;
6765 int i, ngroups;
6766 rb_gid_t *groups;
6767
6768 ngroups = getgroups(0, NULL);
6769 if (ngroups == -1)
6770 rb_sys_fail(0);
6771
6772 groups = ALLOCV_N(rb_gid_t, tmp, ngroups);
6773
6774 ngroups = getgroups(ngroups, groups);
6775 if (ngroups == -1)
6776 rb_sys_fail(0);
6777
6778 ary = rb_ary_new();
6779 for (i = 0; i < ngroups; i++)
6780 rb_ary_push(ary, GIDT2NUM(groups[i]));
6781
6782 ALLOCV_END(tmp);
6783
6784 return ary;
6785}
6786#else
6787#define proc_getgroups rb_f_notimplement
6788#endif
6789
6790
6791#ifdef HAVE_SETGROUPS
6792/*
6793 * call-seq:
6794 * Process.groups = new_groups -> new_groups
6795 *
6796 * Sets the supplemental group access list to the given
6797 * array of group IDs.
6798 *
6799 * Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
6800 * Process.groups = [27, 6, 10, 11] # => [27, 6, 10, 11]
6801 * Process.groups # => [27, 6, 10, 11]
6802 *
6803 */
6804
6805static VALUE
6806proc_setgroups(VALUE obj, VALUE ary)
6807{
6808 int ngroups, i;
6809 rb_gid_t *groups;
6810 VALUE tmp;
6811 PREPARE_GETGRNAM;
6812
6813 Check_Type(ary, T_ARRAY);
6814
6815 ngroups = RARRAY_LENINT(ary);
6816 if (ngroups > maxgroups())
6817 rb_raise(rb_eArgError, "too many groups, %d max", maxgroups());
6818
6819 groups = ALLOCV_N(rb_gid_t, tmp, ngroups);
6820
6821 for (i = 0; i < ngroups; i++) {
6822 VALUE g = RARRAY_AREF(ary, i);
6823
6824 groups[i] = OBJ2GID1(g);
6825 }
6826 FINISH_GETGRNAM;
6827
6828 if (setgroups(ngroups, groups) == -1) /* ngroups <= maxgroups */
6829 rb_sys_fail(0);
6830
6831 ALLOCV_END(tmp);
6832
6833 return proc_getgroups(obj);
6834}
6835#else
6836#define proc_setgroups rb_f_notimplement
6837#endif
6838
6839
6840#ifdef HAVE_INITGROUPS
6841/*
6842 * call-seq:
6843 * Process.initgroups(username, gid) -> array
6844 *
6845 * Sets the supplemental group access list;
6846 * the new list includes:
6847 *
6848 * - The group IDs of those groups to which the user given by +username+ belongs.
6849 * - The group ID +gid+.
6850 *
6851 * Example:
6852 *
6853 * Process.groups # => [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
6854 * Process.initgroups('me', 30) # => [30, 6, 10, 11]
6855 * Process.groups # => [30, 6, 10, 11]
6856 *
6857 * Not available on all platforms.
6858 */
6859
6860static VALUE
6861proc_initgroups(VALUE obj, VALUE uname, VALUE base_grp)
6862{
6863 if (initgroups(StringValueCStr(uname), OBJ2GID(base_grp)) != 0) {
6864 rb_sys_fail(0);
6865 }
6866 return proc_getgroups(obj);
6867}
6868#else
6869#define proc_initgroups rb_f_notimplement
6870#endif
6871
6872#if defined(_SC_NGROUPS_MAX) || defined(NGROUPS_MAX)
6873/*
6874 * call-seq:
6875 * Process.maxgroups -> integer
6876 *
6877 * Returns the maximum number of group IDs allowed
6878 * in the supplemental group access list:
6879 *
6880 * Process.maxgroups # => 32
6881 *
6882 */
6883
6884static VALUE
6885proc_getmaxgroups(VALUE obj)
6886{
6887 return INT2FIX(maxgroups());
6888}
6889#else
6890#define proc_getmaxgroups rb_f_notimplement
6891#endif
6892
6893#ifdef HAVE_SETGROUPS
6894/*
6895 * call-seq:
6896 * Process.maxgroups = new_max -> new_max
6897 *
6898 * Sets the maximum number of group IDs allowed
6899 * in the supplemental group access list.
6900 */
6901
6902static VALUE
6903proc_setmaxgroups(VALUE obj, VALUE val)
6904{
6905 int ngroups = FIX2INT(val);
6906 int ngroups_max = get_sc_ngroups_max();
6907
6908 if (ngroups <= 0)
6909 rb_raise(rb_eArgError, "maxgroups %d should be positive", ngroups);
6910
6911 if (ngroups > RB_MAX_GROUPS)
6912 ngroups = RB_MAX_GROUPS;
6913
6914 if (ngroups_max > 0 && ngroups > ngroups_max)
6915 ngroups = ngroups_max;
6916
6917 _maxgroups = ngroups;
6918
6919 return INT2FIX(_maxgroups);
6920}
6921#else
6922#define proc_setmaxgroups rb_f_notimplement
6923#endif
6924
6925#if defined(HAVE_DAEMON) || (defined(HAVE_WORKING_FORK) && defined(HAVE_SETSID))
6926static int rb_daemon(int nochdir, int noclose);
6927
6928/*
6929 * call-seq:
6930 * Process.daemon(nochdir = nil, noclose = nil) -> 0
6931 *
6932 * Detaches the current process from its controlling terminal
6933 * and runs it in the background as system daemon;
6934 * returns zero.
6935 *
6936 * By default:
6937 *
6938 * - Changes the current working directory to the root directory.
6939 * - Redirects $stdin, $stdout, and $stderr to the null device.
6940 *
6941 * If optional argument +nochdir+ is +true+,
6942 * does not change the current working directory.
6943 *
6944 * If optional argument +noclose+ is +true+,
6945 * does not redirect $stdin, $stdout, or $stderr.
6946 */
6947
6948static VALUE
6949proc_daemon(int argc, VALUE *argv, VALUE _)
6950{
6951 int n, nochdir = FALSE, noclose = FALSE;
6952
6953 switch (rb_check_arity(argc, 0, 2)) {
6954 case 2: noclose = TO_BOOL(argv[1], "noclose");
6955 case 1: nochdir = TO_BOOL(argv[0], "nochdir");
6956 }
6957
6958 prefork();
6959 n = rb_daemon(nochdir, noclose);
6960 if (n < 0) rb_sys_fail("daemon");
6961 return INT2FIX(n);
6962}
6963
6964extern const char ruby_null_device[];
6965
6966static int
6967rb_daemon(int nochdir, int noclose)
6968{
6969 int err = 0;
6970#ifdef HAVE_DAEMON
6971 before_fork_ruby();
6972 err = daemon(nochdir, noclose);
6973 after_fork_ruby(0);
6974#else
6975 int n;
6976
6977 switch (rb_fork_ruby(NULL)) {
6978 case -1: return -1;
6979 case 0: break;
6980 default: _exit(EXIT_SUCCESS);
6981 }
6982
6983 /* ignore EPERM which means already being process-leader */
6984 if (setsid() < 0) (void)0;
6985
6986 if (!nochdir)
6987 err = chdir("/");
6988
6989 if (!noclose && (n = rb_cloexec_open(ruby_null_device, O_RDWR, 0)) != -1) {
6991 (void)dup2(n, 0);
6992 (void)dup2(n, 1);
6993 (void)dup2(n, 2);
6994 if (n > 2)
6995 (void)close (n);
6996 }
6997#endif
6998 return err;
6999}
7000#else
7001#define proc_daemon rb_f_notimplement
7002#endif
7003
7004/********************************************************************
7005 *
7006 * Document-class: Process::GID
7007 *
7008 * The Process::GID module contains a collection of
7009 * module functions which can be used to portably get, set, and
7010 * switch the current process's real, effective, and saved group IDs.
7011 *
7012 */
7013
7014static rb_gid_t SAVED_GROUP_ID = -1;
7015
7016#ifdef BROKEN_SETREGID
7017int
7018setregid(rb_gid_t rgid, rb_gid_t egid)
7019{
7020 if (rgid != (rb_gid_t)-1 && rgid != getgid()) {
7021 if (egid == (rb_gid_t)-1) egid = getegid();
7022 if (setgid(rgid) < 0) return -1;
7023 }
7024 if (egid != (rb_gid_t)-1 && egid != getegid()) {
7025 if (setegid(egid) < 0) return -1;
7026 }
7027 return 0;
7028}
7029#endif
7030
7031/*
7032 * call-seq:
7033 * Process::GID.change_privilege(group) -> integer
7034 *
7035 * Change the current process's real and effective group ID to that
7036 * specified by _group_. Returns the new group ID. Not
7037 * available on all platforms.
7038 *
7039 * [Process.gid, Process.egid] #=> [0, 0]
7040 * Process::GID.change_privilege(33) #=> 33
7041 * [Process.gid, Process.egid] #=> [33, 33]
7042 */
7043
7044static VALUE
7045p_gid_change_privilege(VALUE obj, VALUE id)
7046{
7047 rb_gid_t gid;
7048
7049 check_gid_switch();
7050
7051 gid = OBJ2GID(id);
7052
7053 if (geteuid() == 0) { /* root-user */
7054#if defined(HAVE_SETRESGID)
7055 if (setresgid(gid, gid, gid) < 0) rb_sys_fail(0);
7056 SAVED_GROUP_ID = gid;
7057#elif defined HAVE_SETGID
7058 if (setgid(gid) < 0) rb_sys_fail(0);
7059 SAVED_GROUP_ID = gid;
7060#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7061 if (getgid() == gid) {
7062 if (SAVED_GROUP_ID == gid) {
7063 if (setregid(-1, gid) < 0) rb_sys_fail(0);
7064 }
7065 else {
7066 if (gid == 0) { /* (r,e,s) == (root, y, x) */
7067 if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7068 if (setregid(SAVED_GROUP_ID, 0) < 0) rb_sys_fail(0);
7069 SAVED_GROUP_ID = 0; /* (r,e,s) == (x, root, root) */
7070 if (setregid(gid, gid) < 0) rb_sys_fail(0);
7071 SAVED_GROUP_ID = gid;
7072 }
7073 else { /* (r,e,s) == (z, y, x) */
7074 if (setregid(0, 0) < 0) rb_sys_fail(0);
7075 SAVED_GROUP_ID = 0;
7076 if (setregid(gid, gid) < 0) rb_sys_fail(0);
7077 SAVED_GROUP_ID = gid;
7078 }
7079 }
7080 }
7081 else {
7082 if (setregid(gid, gid) < 0) rb_sys_fail(0);
7083 SAVED_GROUP_ID = gid;
7084 }
7085#elif defined(HAVE_SETRGID) && defined (HAVE_SETEGID)
7086 if (getgid() == gid) {
7087 if (SAVED_GROUP_ID == gid) {
7088 if (setegid(gid) < 0) rb_sys_fail(0);
7089 }
7090 else {
7091 if (gid == 0) {
7092 if (setegid(gid) < 0) rb_sys_fail(0);
7093 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7094 SAVED_GROUP_ID = 0;
7095 if (setrgid(0) < 0) rb_sys_fail(0);
7096 }
7097 else {
7098 if (setrgid(0) < 0) rb_sys_fail(0);
7099 SAVED_GROUP_ID = 0;
7100 if (setegid(gid) < 0) rb_sys_fail(0);
7101 if (setrgid(gid) < 0) rb_sys_fail(0);
7102 SAVED_GROUP_ID = gid;
7103 }
7104 }
7105 }
7106 else {
7107 if (setegid(gid) < 0) rb_sys_fail(0);
7108 if (setrgid(gid) < 0) rb_sys_fail(0);
7109 SAVED_GROUP_ID = gid;
7110 }
7111#else
7113#endif
7114 }
7115 else { /* unprivileged user */
7116#if defined(HAVE_SETRESGID)
7117 if (setresgid((getgid() == gid)? (rb_gid_t)-1: gid,
7118 (getegid() == gid)? (rb_gid_t)-1: gid,
7119 (SAVED_GROUP_ID == gid)? (rb_gid_t)-1: gid) < 0) rb_sys_fail(0);
7120 SAVED_GROUP_ID = gid;
7121#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7122 if (SAVED_GROUP_ID == gid) {
7123 if (setregid((getgid() == gid)? (rb_uid_t)-1: gid,
7124 (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
7125 rb_sys_fail(0);
7126 }
7127 else if (getgid() != gid) {
7128 if (setregid(gid, (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
7129 rb_sys_fail(0);
7130 SAVED_GROUP_ID = gid;
7131 }
7132 else if (/* getgid() == gid && */ getegid() != gid) {
7133 if (setregid(getegid(), gid) < 0) rb_sys_fail(0);
7134 SAVED_GROUP_ID = gid;
7135 if (setregid(gid, -1) < 0) rb_sys_fail(0);
7136 }
7137 else { /* getgid() == gid && getegid() == gid */
7138 if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7139 if (setregid(SAVED_GROUP_ID, gid) < 0) rb_sys_fail(0);
7140 SAVED_GROUP_ID = gid;
7141 if (setregid(gid, -1) < 0) rb_sys_fail(0);
7142 }
7143#elif defined(HAVE_SETRGID) && defined(HAVE_SETEGID)
7144 if (SAVED_GROUP_ID == gid) {
7145 if (getegid() != gid && setegid(gid) < 0) rb_sys_fail(0);
7146 if (getgid() != gid && setrgid(gid) < 0) rb_sys_fail(0);
7147 }
7148 else if (/* SAVED_GROUP_ID != gid && */ getegid() == gid) {
7149 if (getgid() != gid) {
7150 if (setrgid(gid) < 0) rb_sys_fail(0);
7151 SAVED_GROUP_ID = gid;
7152 }
7153 else {
7154 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7155 SAVED_GROUP_ID = gid;
7156 if (setrgid(gid) < 0) rb_sys_fail(0);
7157 }
7158 }
7159 else if (/* getegid() != gid && */ getgid() == gid) {
7160 if (setegid(gid) < 0) rb_sys_fail(0);
7161 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7162 SAVED_GROUP_ID = gid;
7163 if (setrgid(gid) < 0) rb_sys_fail(0);
7164 }
7165 else {
7166 rb_syserr_fail(EPERM, 0);
7167 }
7168#elif defined HAVE_44BSD_SETGID
7169 if (getgid() == gid) {
7170 /* (r,e,s)==(gid,?,?) ==> (gid,gid,gid) */
7171 if (setgid(gid) < 0) rb_sys_fail(0);
7172 SAVED_GROUP_ID = gid;
7173 }
7174 else {
7175 rb_syserr_fail(EPERM, 0);
7176 }
7177#elif defined HAVE_SETEGID
7178 if (getgid() == gid && SAVED_GROUP_ID == gid) {
7179 if (setegid(gid) < 0) rb_sys_fail(0);
7180 }
7181 else {
7182 rb_syserr_fail(EPERM, 0);
7183 }
7184#elif defined HAVE_SETGID
7185 if (getgid() == gid && SAVED_GROUP_ID == gid) {
7186 if (setgid(gid) < 0) rb_sys_fail(0);
7187 }
7188 else {
7189 rb_syserr_fail(EPERM, 0);
7190 }
7191#else
7192 (void)gid;
7194#endif
7195 }
7196 return id;
7197}
7198
7199
7200/*
7201 * call-seq:
7202 * Process.euid -> integer
7203 * Process::UID.eid -> integer
7204 * Process::Sys.geteuid -> integer
7205 *
7206 * Returns the effective user ID for the current process.
7207 *
7208 * Process.euid # => 501
7209 *
7210 */
7211
7212static VALUE
7213proc_geteuid(VALUE obj)
7214{
7215 rb_uid_t euid = geteuid();
7216 return UIDT2NUM(euid);
7217}
7218
7219#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID) || defined(_POSIX_SAVED_IDS)
7220static void
7221proc_seteuid(rb_uid_t uid)
7222{
7223#if defined(HAVE_SETRESUID)
7224 if (setresuid(-1, uid, -1) < 0) rb_sys_fail(0);
7225#elif defined HAVE_SETREUID
7226 if (setreuid(-1, uid) < 0) rb_sys_fail(0);
7227#elif defined HAVE_SETEUID
7228 if (seteuid(uid) < 0) rb_sys_fail(0);
7229#elif defined HAVE_SETUID
7230 if (uid == getuid()) {
7231 if (setuid(uid) < 0) rb_sys_fail(0);
7232 }
7233 else {
7235 }
7236#else
7238#endif
7239}
7240#endif
7241
7242#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID)
7243/*
7244 * call-seq:
7245 * Process.euid = new_euid -> new_euid
7246 *
7247 * Sets the effective user ID for the current process.
7248 *
7249 * Not available on all platforms.
7250 */
7251
7252static VALUE
7253proc_seteuid_m(VALUE mod, VALUE euid)
7254{
7255 check_uid_switch();
7256 proc_seteuid(OBJ2UID(euid));
7257 return euid;
7258}
7259#else
7260#define proc_seteuid_m rb_f_notimplement
7261#endif
7262
7263static rb_uid_t
7264rb_seteuid_core(rb_uid_t euid)
7265{
7266#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7267 rb_uid_t uid;
7268#endif
7269
7270 check_uid_switch();
7271
7272#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7273 uid = getuid();
7274#endif
7275
7276#if defined(HAVE_SETRESUID)
7277 if (uid != euid) {
7278 if (setresuid(-1,euid,euid) < 0) rb_sys_fail(0);
7279 SAVED_USER_ID = euid;
7280 }
7281 else {
7282 if (setresuid(-1,euid,-1) < 0) rb_sys_fail(0);
7283 }
7284#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7285 if (setreuid(-1, euid) < 0) rb_sys_fail(0);
7286 if (uid != euid) {
7287 if (setreuid(euid,uid) < 0) rb_sys_fail(0);
7288 if (setreuid(uid,euid) < 0) rb_sys_fail(0);
7289 SAVED_USER_ID = euid;
7290 }
7291#elif defined HAVE_SETEUID
7292 if (seteuid(euid) < 0) rb_sys_fail(0);
7293#elif defined HAVE_SETUID
7294 if (geteuid() == 0) rb_sys_fail(0);
7295 if (setuid(euid) < 0) rb_sys_fail(0);
7296#else
7298#endif
7299 return euid;
7300}
7301
7302
7303/*
7304 * call-seq:
7305 * Process::UID.grant_privilege(user) -> integer
7306 * Process::UID.eid= user -> integer
7307 *
7308 * Set the effective user ID, and if possible, the saved user ID of
7309 * the process to the given _user_. Returns the new
7310 * effective user ID. Not available on all platforms.
7311 *
7312 * [Process.uid, Process.euid] #=> [0, 0]
7313 * Process::UID.grant_privilege(31) #=> 31
7314 * [Process.uid, Process.euid] #=> [0, 31]
7315 */
7316
7317static VALUE
7318p_uid_grant_privilege(VALUE obj, VALUE id)
7319{
7320 rb_seteuid_core(OBJ2UID(id));
7321 return id;
7322}
7323
7324
7325/*
7326 * call-seq:
7327 * Process.egid -> integer
7328 * Process::GID.eid -> integer
7329 * Process::Sys.geteid -> integer
7330 *
7331 * Returns the effective group ID for the current process:
7332 *
7333 * Process.egid # => 500
7334 *
7335 * Not available on all platforms.
7336 */
7337
7338static VALUE
7339proc_getegid(VALUE obj)
7340{
7341 rb_gid_t egid = getegid();
7342
7343 return GIDT2NUM(egid);
7344}
7345
7346#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID) || defined(_POSIX_SAVED_IDS)
7347/*
7348 * call-seq:
7349 * Process.egid = new_egid -> new_egid
7350 *
7351 * Sets the effective group ID for the current process.
7352 *
7353 * Not available on all platforms.
7354 */
7355
7356static VALUE
7357proc_setegid(VALUE obj, VALUE egid)
7358{
7359#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7360 rb_gid_t gid;
7361#endif
7362
7363 check_gid_switch();
7364
7365#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7366 gid = OBJ2GID(egid);
7367#endif
7368
7369#if defined(HAVE_SETRESGID)
7370 if (setresgid(-1, gid, -1) < 0) rb_sys_fail(0);
7371#elif defined HAVE_SETREGID
7372 if (setregid(-1, gid) < 0) rb_sys_fail(0);
7373#elif defined HAVE_SETEGID
7374 if (setegid(gid) < 0) rb_sys_fail(0);
7375#elif defined HAVE_SETGID
7376 if (gid == getgid()) {
7377 if (setgid(gid) < 0) rb_sys_fail(0);
7378 }
7379 else {
7381 }
7382#else
7384#endif
7385 return egid;
7386}
7387#endif
7388
7389#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7390#define proc_setegid_m proc_setegid
7391#else
7392#define proc_setegid_m rb_f_notimplement
7393#endif
7394
7395static rb_gid_t
7396rb_setegid_core(rb_gid_t egid)
7397{
7398#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7399 rb_gid_t gid;
7400#endif
7401
7402 check_gid_switch();
7403
7404#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7405 gid = getgid();
7406#endif
7407
7408#if defined(HAVE_SETRESGID)
7409 if (gid != egid) {
7410 if (setresgid(-1,egid,egid) < 0) rb_sys_fail(0);
7411 SAVED_GROUP_ID = egid;
7412 }
7413 else {
7414 if (setresgid(-1,egid,-1) < 0) rb_sys_fail(0);
7415 }
7416#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7417 if (setregid(-1, egid) < 0) rb_sys_fail(0);
7418 if (gid != egid) {
7419 if (setregid(egid,gid) < 0) rb_sys_fail(0);
7420 if (setregid(gid,egid) < 0) rb_sys_fail(0);
7421 SAVED_GROUP_ID = egid;
7422 }
7423#elif defined HAVE_SETEGID
7424 if (setegid(egid) < 0) rb_sys_fail(0);
7425#elif defined HAVE_SETGID
7426 if (geteuid() == 0 /* root user */) rb_sys_fail(0);
7427 if (setgid(egid) < 0) rb_sys_fail(0);
7428#else
7430#endif
7431 return egid;
7432}
7433
7434
7435/*
7436 * call-seq:
7437 * Process::GID.grant_privilege(group) -> integer
7438 * Process::GID.eid = group -> integer
7439 *
7440 * Set the effective group ID, and if possible, the saved group ID of
7441 * the process to the given _group_. Returns the new
7442 * effective group ID. Not available on all platforms.
7443 *
7444 * [Process.gid, Process.egid] #=> [0, 0]
7445 * Process::GID.grant_privilege(31) #=> 33
7446 * [Process.gid, Process.egid] #=> [0, 33]
7447 */
7448
7449static VALUE
7450p_gid_grant_privilege(VALUE obj, VALUE id)
7451{
7452 rb_setegid_core(OBJ2GID(id));
7453 return id;
7454}
7455
7456
7457/*
7458 * call-seq:
7459 * Process::UID.re_exchangeable? -> true or false
7460 *
7461 * Returns +true+ if the real and effective user IDs of a
7462 * process may be exchanged on the current platform.
7463 *
7464 */
7465
7466static VALUE
7467p_uid_exchangeable(VALUE _)
7468{
7469#if defined(HAVE_SETRESUID)
7470 return Qtrue;
7471#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7472 return Qtrue;
7473#else
7474 return Qfalse;
7475#endif
7476}
7477
7478
7479/*
7480 * call-seq:
7481 * Process::UID.re_exchange -> integer
7482 *
7483 * Exchange real and effective user IDs and return the new effective
7484 * user ID. Not available on all platforms.
7485 *
7486 * [Process.uid, Process.euid] #=> [0, 31]
7487 * Process::UID.re_exchange #=> 0
7488 * [Process.uid, Process.euid] #=> [31, 0]
7489 */
7490
7491static VALUE
7492p_uid_exchange(VALUE obj)
7493{
7494 rb_uid_t uid;
7495#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7496 rb_uid_t euid;
7497#endif
7498
7499 check_uid_switch();
7500
7501 uid = getuid();
7502#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7503 euid = geteuid();
7504#endif
7505
7506#if defined(HAVE_SETRESUID)
7507 if (setresuid(euid, uid, uid) < 0) rb_sys_fail(0);
7508 SAVED_USER_ID = uid;
7509#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7510 if (setreuid(euid,uid) < 0) rb_sys_fail(0);
7511 SAVED_USER_ID = uid;
7512#else
7514#endif
7515 return UIDT2NUM(uid);
7516}
7517
7518
7519/*
7520 * call-seq:
7521 * Process::GID.re_exchangeable? -> true or false
7522 *
7523 * Returns +true+ if the real and effective group IDs of a
7524 * process may be exchanged on the current platform.
7525 *
7526 */
7527
7528static VALUE
7529p_gid_exchangeable(VALUE _)
7530{
7531#if defined(HAVE_SETRESGID)
7532 return Qtrue;
7533#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7534 return Qtrue;
7535#else
7536 return Qfalse;
7537#endif
7538}
7539
7540
7541/*
7542 * call-seq:
7543 * Process::GID.re_exchange -> integer
7544 *
7545 * Exchange real and effective group IDs and return the new effective
7546 * group ID. Not available on all platforms.
7547 *
7548 * [Process.gid, Process.egid] #=> [0, 33]
7549 * Process::GID.re_exchange #=> 0
7550 * [Process.gid, Process.egid] #=> [33, 0]
7551 */
7552
7553static VALUE
7554p_gid_exchange(VALUE obj)
7555{
7556 rb_gid_t gid;
7557#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7558 rb_gid_t egid;
7559#endif
7560
7561 check_gid_switch();
7562
7563 gid = getgid();
7564#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7565 egid = getegid();
7566#endif
7567
7568#if defined(HAVE_SETRESGID)
7569 if (setresgid(egid, gid, gid) < 0) rb_sys_fail(0);
7570 SAVED_GROUP_ID = gid;
7571#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7572 if (setregid(egid,gid) < 0) rb_sys_fail(0);
7573 SAVED_GROUP_ID = gid;
7574#else
7576#endif
7577 return GIDT2NUM(gid);
7578}
7579
7580/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
7581
7582/*
7583 * call-seq:
7584 * Process::UID.sid_available? -> true or false
7585 *
7586 * Returns +true+ if the current platform has saved user
7587 * ID functionality.
7588 *
7589 */
7590
7591static VALUE
7592p_uid_have_saved_id(VALUE _)
7593{
7594#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
7595 return Qtrue;
7596#else
7597 return Qfalse;
7598#endif
7599}
7600
7601
7602#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
7603static VALUE
7604p_uid_sw_ensure(VALUE i)
7605{
7606 rb_uid_t id = (rb_uid_t/* narrowing */)i;
7607 under_uid_switch = 0;
7608 id = rb_seteuid_core(id);
7609 return UIDT2NUM(id);
7610}
7611
7612
7613/*
7614 * call-seq:
7615 * Process::UID.switch -> integer
7616 * Process::UID.switch {|| block} -> object
7617 *
7618 * Switch the effective and real user IDs of the current process. If
7619 * a <em>block</em> is given, the user IDs will be switched back
7620 * after the block is executed. Returns the new effective user ID if
7621 * called without a block, and the return value of the block if one
7622 * is given.
7623 *
7624 */
7625
7626static VALUE
7627p_uid_switch(VALUE obj)
7628{
7629 rb_uid_t uid, euid;
7630
7631 check_uid_switch();
7632
7633 uid = getuid();
7634 euid = geteuid();
7635
7636 if (uid != euid) {
7637 proc_seteuid(uid);
7638 if (rb_block_given_p()) {
7639 under_uid_switch = 1;
7640 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, SAVED_USER_ID);
7641 }
7642 else {
7643 return UIDT2NUM(euid);
7644 }
7645 }
7646 else if (euid != SAVED_USER_ID) {
7647 proc_seteuid(SAVED_USER_ID);
7648 if (rb_block_given_p()) {
7649 under_uid_switch = 1;
7650 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, euid);
7651 }
7652 else {
7653 return UIDT2NUM(uid);
7654 }
7655 }
7656 else {
7657 rb_syserr_fail(EPERM, 0);
7658 }
7659
7661}
7662#else
7663static VALUE
7664p_uid_sw_ensure(VALUE obj)
7665{
7666 under_uid_switch = 0;
7667 return p_uid_exchange(obj);
7668}
7669
7670static VALUE
7671p_uid_switch(VALUE obj)
7672{
7673 rb_uid_t uid, euid;
7674
7675 check_uid_switch();
7676
7677 uid = getuid();
7678 euid = geteuid();
7679
7680 if (uid == euid) {
7681 rb_syserr_fail(EPERM, 0);
7682 }
7683 p_uid_exchange(obj);
7684 if (rb_block_given_p()) {
7685 under_uid_switch = 1;
7686 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, obj);
7687 }
7688 else {
7689 return UIDT2NUM(euid);
7690 }
7691}
7692#endif
7693
7694
7695/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
7696
7697/*
7698 * call-seq:
7699 * Process::GID.sid_available? -> true or false
7700 *
7701 * Returns +true+ if the current platform has saved group
7702 * ID functionality.
7703 *
7704 */
7705
7706static VALUE
7707p_gid_have_saved_id(VALUE _)
7708{
7709#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
7710 return Qtrue;
7711#else
7712 return Qfalse;
7713#endif
7714}
7715
7716#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
7717static VALUE
7718p_gid_sw_ensure(VALUE i)
7719{
7720 rb_gid_t id = (rb_gid_t/* narrowing */)i;
7721 under_gid_switch = 0;
7722 id = rb_setegid_core(id);
7723 return GIDT2NUM(id);
7724}
7725
7726
7727/*
7728 * call-seq:
7729 * Process::GID.switch -> integer
7730 * Process::GID.switch {|| block} -> object
7731 *
7732 * Switch the effective and real group IDs of the current process. If
7733 * a <em>block</em> is given, the group IDs will be switched back
7734 * after the block is executed. Returns the new effective group ID if
7735 * called without a block, and the return value of the block if one
7736 * is given.
7737 *
7738 */
7739
7740static VALUE
7741p_gid_switch(VALUE obj)
7742{
7743 rb_gid_t gid, egid;
7744
7745 check_gid_switch();
7746
7747 gid = getgid();
7748 egid = getegid();
7749
7750 if (gid != egid) {
7751 proc_setegid(obj, GIDT2NUM(gid));
7752 if (rb_block_given_p()) {
7753 under_gid_switch = 1;
7754 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, SAVED_GROUP_ID);
7755 }
7756 else {
7757 return GIDT2NUM(egid);
7758 }
7759 }
7760 else if (egid != SAVED_GROUP_ID) {
7761 proc_setegid(obj, GIDT2NUM(SAVED_GROUP_ID));
7762 if (rb_block_given_p()) {
7763 under_gid_switch = 1;
7764 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, egid);
7765 }
7766 else {
7767 return GIDT2NUM(gid);
7768 }
7769 }
7770 else {
7771 rb_syserr_fail(EPERM, 0);
7772 }
7773
7775}
7776#else
7777static VALUE
7778p_gid_sw_ensure(VALUE obj)
7779{
7780 under_gid_switch = 0;
7781 return p_gid_exchange(obj);
7782}
7783
7784static VALUE
7785p_gid_switch(VALUE obj)
7786{
7787 rb_gid_t gid, egid;
7788
7789 check_gid_switch();
7790
7791 gid = getgid();
7792 egid = getegid();
7793
7794 if (gid == egid) {
7795 rb_syserr_fail(EPERM, 0);
7796 }
7797 p_gid_exchange(obj);
7798 if (rb_block_given_p()) {
7799 under_gid_switch = 1;
7800 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, obj);
7801 }
7802 else {
7803 return GIDT2NUM(egid);
7804 }
7805}
7806#endif
7807
7808
7809#if defined(HAVE_TIMES)
7810static long
7811get_clk_tck(void)
7812{
7813#ifdef HAVE__SC_CLK_TCK
7814 return sysconf(_SC_CLK_TCK);
7815#elif defined CLK_TCK
7816 return CLK_TCK;
7817#elif defined HZ
7818 return HZ;
7819#else
7820 return 60;
7821#endif
7822}
7823
7824/*
7825 * call-seq:
7826 * Process.times -> process_tms
7827 *
7828 * Returns a Process::Tms structure that contains user and system CPU times
7829 * for the current process, and for its children processes:
7830 *
7831 * Process.times
7832 * # => #<struct Process::Tms utime=55.122118, stime=35.533068, cutime=0.0, cstime=0.002846>
7833 *
7834 * The precision is platform-defined.
7835 */
7836
7837VALUE
7838rb_proc_times(VALUE obj)
7839{
7840 VALUE utime, stime, cutime, cstime, ret;
7841#if defined(RUSAGE_SELF) && defined(RUSAGE_CHILDREN)
7842 struct rusage usage_s, usage_c;
7843
7844 if (getrusage(RUSAGE_SELF, &usage_s) != 0 || getrusage(RUSAGE_CHILDREN, &usage_c) != 0)
7845 rb_sys_fail("getrusage");
7846 utime = DBL2NUM((double)usage_s.ru_utime.tv_sec + (double)usage_s.ru_utime.tv_usec/1e6);
7847 stime = DBL2NUM((double)usage_s.ru_stime.tv_sec + (double)usage_s.ru_stime.tv_usec/1e6);
7848 cutime = DBL2NUM((double)usage_c.ru_utime.tv_sec + (double)usage_c.ru_utime.tv_usec/1e6);
7849 cstime = DBL2NUM((double)usage_c.ru_stime.tv_sec + (double)usage_c.ru_stime.tv_usec/1e6);
7850#else
7851 const double hertz = (double)get_clk_tck();
7852 struct tms buf;
7853
7854 times(&buf);
7855 utime = DBL2NUM(buf.tms_utime / hertz);
7856 stime = DBL2NUM(buf.tms_stime / hertz);
7857 cutime = DBL2NUM(buf.tms_cutime / hertz);
7858 cstime = DBL2NUM(buf.tms_cstime / hertz);
7859#endif
7860 ret = rb_struct_new(rb_cProcessTms, utime, stime, cutime, cstime);
7861 RB_GC_GUARD(utime);
7862 RB_GC_GUARD(stime);
7863 RB_GC_GUARD(cutime);
7864 RB_GC_GUARD(cstime);
7865 return ret;
7866}
7867#else
7868#define rb_proc_times rb_f_notimplement
7869#endif
7870
7871#ifdef HAVE_LONG_LONG
7872typedef LONG_LONG timetick_int_t;
7873#define TIMETICK_INT_MIN LLONG_MIN
7874#define TIMETICK_INT_MAX LLONG_MAX
7875#define TIMETICK_INT2NUM(v) LL2NUM(v)
7876#define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_LONG_P(a, b)
7877#else
7878typedef long timetick_int_t;
7879#define TIMETICK_INT_MIN LONG_MIN
7880#define TIMETICK_INT_MAX LONG_MAX
7881#define TIMETICK_INT2NUM(v) LONG2NUM(v)
7882#define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_P(a, b)
7883#endif
7884
7885CONSTFUNC(static timetick_int_t gcd_timetick_int(timetick_int_t, timetick_int_t));
7886static timetick_int_t
7887gcd_timetick_int(timetick_int_t a, timetick_int_t b)
7888{
7889 timetick_int_t t;
7890
7891 if (a < b) {
7892 t = a;
7893 a = b;
7894 b = t;
7895 }
7896
7897 while (1) {
7898 t = a % b;
7899 if (t == 0)
7900 return b;
7901 a = b;
7902 b = t;
7903 }
7904}
7905
7906static void
7907reduce_fraction(timetick_int_t *np, timetick_int_t *dp)
7908{
7909 timetick_int_t gcd = gcd_timetick_int(*np, *dp);
7910 if (gcd != 1) {
7911 *np /= gcd;
7912 *dp /= gcd;
7913 }
7914}
7915
7916static void
7917reduce_factors(timetick_int_t *numerators, int num_numerators,
7918 timetick_int_t *denominators, int num_denominators)
7919{
7920 int i, j;
7921 for (i = 0; i < num_numerators; i++) {
7922 if (numerators[i] == 1)
7923 continue;
7924 for (j = 0; j < num_denominators; j++) {
7925 if (denominators[j] == 1)
7926 continue;
7927 reduce_fraction(&numerators[i], &denominators[j]);
7928 }
7929 }
7930}
7931
7932struct timetick {
7933 timetick_int_t giga_count;
7934 int32_t count; /* 0 .. 999999999 */
7935};
7936
7937static VALUE
7938timetick2dblnum(struct timetick *ttp,
7939 timetick_int_t *numerators, int num_numerators,
7940 timetick_int_t *denominators, int num_denominators)
7941{
7942 double d;
7943 int i;
7944
7945 reduce_factors(numerators, num_numerators,
7946 denominators, num_denominators);
7947
7948 d = ttp->giga_count * 1e9 + ttp->count;
7949
7950 for (i = 0; i < num_numerators; i++)
7951 d *= numerators[i];
7952 for (i = 0; i < num_denominators; i++)
7953 d /= denominators[i];
7954
7955 return DBL2NUM(d);
7956}
7957
7958static VALUE
7959timetick2dblnum_reciprocal(struct timetick *ttp,
7960 timetick_int_t *numerators, int num_numerators,
7961 timetick_int_t *denominators, int num_denominators)
7962{
7963 double d;
7964 int i;
7965
7966 reduce_factors(numerators, num_numerators,
7967 denominators, num_denominators);
7968
7969 d = 1.0;
7970 for (i = 0; i < num_denominators; i++)
7971 d *= denominators[i];
7972 for (i = 0; i < num_numerators; i++)
7973 d /= numerators[i];
7974 d /= ttp->giga_count * 1e9 + ttp->count;
7975
7976 return DBL2NUM(d);
7977}
7978
7979#define NDIV(x,y) (-(-((x)+1)/(y))-1)
7980#define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d))
7981
7982static VALUE
7983timetick2integer(struct timetick *ttp,
7984 timetick_int_t *numerators, int num_numerators,
7985 timetick_int_t *denominators, int num_denominators)
7986{
7987 VALUE v;
7988 int i;
7989
7990 reduce_factors(numerators, num_numerators,
7991 denominators, num_denominators);
7992
7993 if (!MUL_OVERFLOW_SIGNED_INTEGER_P(1000000000, ttp->giga_count,
7994 TIMETICK_INT_MIN, TIMETICK_INT_MAX-ttp->count)) {
7995 timetick_int_t t = ttp->giga_count * 1000000000 + ttp->count;
7996 for (i = 0; i < num_numerators; i++) {
7997 timetick_int_t factor = numerators[i];
7998 if (MUL_OVERFLOW_TIMETICK_P(factor, t))
7999 goto generic;
8000 t *= factor;
8001 }
8002 for (i = 0; i < num_denominators; i++) {
8003 t = DIV(t, denominators[i]);
8004 }
8005 return TIMETICK_INT2NUM(t);
8006 }
8007
8008 generic:
8009 v = TIMETICK_INT2NUM(ttp->giga_count);
8010 v = rb_funcall(v, '*', 1, LONG2FIX(1000000000));
8011 v = rb_funcall(v, '+', 1, LONG2FIX(ttp->count));
8012 for (i = 0; i < num_numerators; i++) {
8013 timetick_int_t factor = numerators[i];
8014 if (factor == 1)
8015 continue;
8016 v = rb_funcall(v, '*', 1, TIMETICK_INT2NUM(factor));
8017 }
8018 for (i = 0; i < num_denominators; i++) {
8019 v = rb_funcall(v, '/', 1, TIMETICK_INT2NUM(denominators[i])); /* Ruby's '/' is div. */
8020 }
8021 return v;
8022}
8023
8024static VALUE
8025make_clock_result(struct timetick *ttp,
8026 timetick_int_t *numerators, int num_numerators,
8027 timetick_int_t *denominators, int num_denominators,
8028 VALUE unit)
8029{
8030 if (unit == ID2SYM(id_nanosecond)) {
8031 numerators[num_numerators++] = 1000000000;
8032 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8033 }
8034 else if (unit == ID2SYM(id_microsecond)) {
8035 numerators[num_numerators++] = 1000000;
8036 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8037 }
8038 else if (unit == ID2SYM(id_millisecond)) {
8039 numerators[num_numerators++] = 1000;
8040 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8041 }
8042 else if (unit == ID2SYM(id_second)) {
8043 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
8044 }
8045 else if (unit == ID2SYM(id_float_microsecond)) {
8046 numerators[num_numerators++] = 1000000;
8047 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
8048 }
8049 else if (unit == ID2SYM(id_float_millisecond)) {
8050 numerators[num_numerators++] = 1000;
8051 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
8052 }
8053 else if (NIL_P(unit) || unit == ID2SYM(id_float_second)) {
8054 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
8055 }
8056 else
8057 rb_raise(rb_eArgError, "unexpected unit: %"PRIsVALUE, unit);
8058}
8059
8060#ifdef __APPLE__
8061static const mach_timebase_info_data_t *
8062get_mach_timebase_info(void)
8063{
8064 static mach_timebase_info_data_t sTimebaseInfo;
8065
8066 if ( sTimebaseInfo.denom == 0 ) {
8067 (void) mach_timebase_info(&sTimebaseInfo);
8068 }
8069
8070 return &sTimebaseInfo;
8071}
8072
8073double
8074ruby_real_ms_time(void)
8075{
8076 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8077 uint64_t t = mach_absolute_time();
8078 return (double)t * info->numer / info->denom / 1e6;
8079}
8080#endif
8081
8082#if defined(NUM2CLOCKID)
8083# define NUMERIC_CLOCKID 1
8084#else
8085# define NUMERIC_CLOCKID 0
8086# define NUM2CLOCKID(x) 0
8087#endif
8088
8089#define clock_failed(name, err, arg) do { \
8090 int clock_error = (err); \
8091 rb_syserr_fail_str(clock_error, rb_sprintf("clock_" name "(%+"PRIsVALUE")", (arg))); \
8092 } while (0)
8093
8094/*
8095 * call-seq:
8096 * Process.clock_gettime(clock_id, unit = :float_second) -> number
8097 *
8098 * Returns a clock time as determined by POSIX function
8099 * {clock_gettime()}[https://man7.org/linux/man-pages/man3/clock_gettime.3.html]:
8100 *
8101 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID) # => 198.650379677
8102 *
8103 * Argument +clock_id+ should be a symbol or a constant that specifies
8104 * the clock whose time is to be returned;
8105 * see below.
8106 *
8107 * Optional argument +unit+ should be a symbol that specifies
8108 * the unit to be used in the returned clock time;
8109 * see below.
8110 *
8111 * <b>Argument +clock_id+</b>
8112 *
8113 * Argument +clock_id+ specifies the clock whose time is to be returned;
8114 * it may be a constant such as <tt>Process::CLOCK_REALTIME</tt>,
8115 * or a symbol shorthand such as +:CLOCK_REALTIME+.
8116 *
8117 * The supported clocks depend on the underlying operating system;
8118 * this method supports the following clocks on the indicated platforms
8119 * (raises Errno::EINVAL if called with an unsupported clock):
8120 *
8121 * - +:CLOCK_BOOTTIME+: Linux 2.6.39.
8122 * - +:CLOCK_BOOTTIME_ALARM+: Linux 3.0.
8123 * - +:CLOCK_MONOTONIC+: SUSv3 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4, macOS 10.12, Windows-2000.
8124 * - +:CLOCK_MONOTONIC_COARSE+: Linux 2.6.32.
8125 * - +:CLOCK_MONOTONIC_FAST+: FreeBSD 8.1.
8126 * - +:CLOCK_MONOTONIC_PRECISE+: FreeBSD 8.1.
8127 * - +:CLOCK_MONOTONIC_RAW+: Linux 2.6.28, macOS 10.12.
8128 * - +:CLOCK_MONOTONIC_RAW_APPROX+: macOS 10.12.
8129 * - +:CLOCK_PROCESS_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 9.3, OpenBSD 5.4, macOS 10.12.
8130 * - +:CLOCK_PROF+: FreeBSD 3.0, OpenBSD 2.1.
8131 * - +: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.
8132 * Time.now is recommended over +:CLOCK_REALTIME:.
8133 * - +:CLOCK_REALTIME_ALARM+: Linux 3.0.
8134 * - +:CLOCK_REALTIME_COARSE+: Linux 2.6.32.
8135 * - +:CLOCK_REALTIME_FAST+: FreeBSD 8.1.
8136 * - +:CLOCK_REALTIME_PRECISE+: FreeBSD 8.1.
8137 * - +:CLOCK_SECOND+: FreeBSD 8.1.
8138 * - +:CLOCK_TAI+: Linux 3.10.
8139 * - +:CLOCK_THREAD_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 7.1, OpenBSD 5.4, macOS 10.12.
8140 * - +:CLOCK_UPTIME+: FreeBSD 7.0, OpenBSD 5.5.
8141 * - +:CLOCK_UPTIME_FAST+: FreeBSD 8.1.
8142 * - +:CLOCK_UPTIME_PRECISE+: FreeBSD 8.1.
8143 * - +:CLOCK_UPTIME_RAW+: macOS 10.12.
8144 * - +:CLOCK_UPTIME_RAW_APPROX+: macOS 10.12.
8145 * - +:CLOCK_VIRTUAL+: FreeBSD 3.0, OpenBSD 2.1.
8146 *
8147 * Note that SUS stands for Single Unix Specification.
8148 * SUS contains POSIX and clock_gettime is defined in the POSIX part.
8149 * SUS defines +:CLOCK_REALTIME+ as mandatory but
8150 * +:CLOCK_MONOTONIC+, +:CLOCK_PROCESS_CPUTIME_ID+,
8151 * and +:CLOCK_THREAD_CPUTIME_ID+ are optional.
8152 *
8153 * Certain emulations are used when the given +clock_id+
8154 * is not supported directly:
8155 *
8156 * - Emulations for +:CLOCK_REALTIME+:
8157 *
8158 * - +:GETTIMEOFDAY_BASED_CLOCK_REALTIME+:
8159 * Use gettimeofday() defined by SUS (deprecated in SUSv4).
8160 * The resolution is 1 microsecond.
8161 * - +:TIME_BASED_CLOCK_REALTIME+:
8162 * Use time() defined by ISO C.
8163 * The resolution is 1 second.
8164 *
8165 * - Emulations for +:CLOCK_MONOTONIC+:
8166 *
8167 * - +:MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC+:
8168 * Use mach_absolute_time(), available on Darwin.
8169 * The resolution is CPU dependent.
8170 * - +:TIMES_BASED_CLOCK_MONOTONIC+:
8171 * Use the result value of times() defined by POSIX, thus:
8172 * >>>
8173 * Upon successful completion, times() shall return the elapsed real time,
8174 * in clock ticks, since an arbitrary point in the past
8175 * (for example, system start-up time).
8176 *
8177 * For example, GNU/Linux returns a value based on jiffies and it is monotonic.
8178 * However, 4.4BSD uses gettimeofday() and it is not monotonic.
8179 * (FreeBSD uses +:CLOCK_MONOTONIC+ instead, though.)
8180 *
8181 * The resolution is the clock tick.
8182 * "getconf CLK_TCK" command shows the clock ticks per second.
8183 * (The clock ticks-per-second is defined by HZ macro in older systems.)
8184 * If it is 100 and clock_t is 32 bits integer type,
8185 * the resolution is 10 millisecond and cannot represent over 497 days.
8186 *
8187 * - Emulations for +:CLOCK_PROCESS_CPUTIME_ID+:
8188 *
8189 * - +:GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8190 * Use getrusage() defined by SUS.
8191 * getrusage() is used with RUSAGE_SELF to obtain the time only for
8192 * the calling process (excluding the time for child processes).
8193 * The result is addition of user time (ru_utime) and system time (ru_stime).
8194 * The resolution is 1 microsecond.
8195 * - +:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8196 * Use times() defined by POSIX.
8197 * The result is addition of user time (tms_utime) and system time (tms_stime).
8198 * tms_cutime and tms_cstime are ignored to exclude the time for child processes.
8199 * The resolution is the clock tick.
8200 * "getconf CLK_TCK" command shows the clock ticks per second.
8201 * (The clock ticks per second is defined by HZ macro in older systems.)
8202 * If it is 100, the resolution is 10 millisecond.
8203 * - +:CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8204 * Use clock() defined by ISO C.
8205 * The resolution is <tt>1/CLOCKS_PER_SEC</tt>.
8206 * +CLOCKS_PER_SEC+ is the C-level macro defined by time.h.
8207 * SUS defines +CLOCKS_PER_SEC+ as 1000000;
8208 * other systems may define it differently.
8209 * If +CLOCKS_PER_SEC+ is 1000000 (as in SUS),
8210 * the resolution is 1 microsecond.
8211 * If +CLOCKS_PER_SEC+ is 1000000 and clock_t is a 32-bit integer type,
8212 * it cannot represent over 72 minutes.
8213 *
8214 * <b>Argument +unit+</b>
8215 *
8216 * Optional argument +unit+ (default +:float_second+)
8217 * specifies the unit for the returned value.
8218 *
8219 * - +:float_microsecond+: Number of microseconds as a float.
8220 * - +:float_millisecond+: Number of milliseconds as a float.
8221 * - +:float_second+: Number of seconds as a float.
8222 * - +:microsecond+: Number of microseconds as an integer.
8223 * - +:millisecond+: Number of milliseconds as an integer.
8224 * - +:nanosecond+: Number of nanoseconds as an integer.
8225 * - +::second+: Number of seconds as an integer.
8226 *
8227 * Examples:
8228 *
8229 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond)
8230 * # => 203605054.825
8231 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond)
8232 * # => 203643.696848
8233 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_second)
8234 * # => 203.762181929
8235 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :microsecond)
8236 * # => 204123212
8237 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :millisecond)
8238 * # => 204298
8239 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond)
8240 * # => 204602286036
8241 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :second)
8242 * # => 204
8243 *
8244 * The underlying function, clock_gettime(), returns a number of nanoseconds.
8245 * Float object (IEEE 754 double) is not enough to represent
8246 * the return value for +:CLOCK_REALTIME+.
8247 * If the exact nanoseconds value is required, use +:nanosecond+ as the +unit+.
8248 *
8249 * The origin (time zero) of the returned value is system-dependent,
8250 * and may be, for example, system start up time,
8251 * process start up time, the Epoch, etc.
8252 *
8253 * The origin in +:CLOCK_REALTIME+ is defined as the Epoch:
8254 * <tt>1970-01-01 00:00:00 UTC</tt>;
8255 * some systems count leap seconds and others don't,
8256 * so the result may vary across systems.
8257 */
8258static VALUE
8259rb_clock_gettime(int argc, VALUE *argv, VALUE _)
8260{
8261 int ret;
8262
8263 struct timetick tt;
8264 timetick_int_t numerators[2];
8265 timetick_int_t denominators[2];
8266 int num_numerators = 0;
8267 int num_denominators = 0;
8268
8269 VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
8270 VALUE clk_id = argv[0];
8271#ifdef HAVE_CLOCK_GETTIME
8272 clockid_t c;
8273#endif
8274
8275 if (SYMBOL_P(clk_id)) {
8276#ifdef CLOCK_REALTIME
8277 if (clk_id == RUBY_CLOCK_REALTIME) {
8278 c = CLOCK_REALTIME;
8279 goto gettime;
8280 }
8281#endif
8282
8283#ifdef CLOCK_MONOTONIC
8284 if (clk_id == RUBY_CLOCK_MONOTONIC) {
8285 c = CLOCK_MONOTONIC;
8286 goto gettime;
8287 }
8288#endif
8289
8290#ifdef CLOCK_PROCESS_CPUTIME_ID
8291 if (clk_id == RUBY_CLOCK_PROCESS_CPUTIME_ID) {
8292 c = CLOCK_PROCESS_CPUTIME_ID;
8293 goto gettime;
8294 }
8295#endif
8296
8297#ifdef CLOCK_THREAD_CPUTIME_ID
8298 if (clk_id == RUBY_CLOCK_THREAD_CPUTIME_ID) {
8299 c = CLOCK_THREAD_CPUTIME_ID;
8300 goto gettime;
8301 }
8302#endif
8303
8304 /*
8305 * Non-clock_gettime clocks are provided by symbol clk_id.
8306 */
8307#ifdef HAVE_GETTIMEOFDAY
8308 /*
8309 * GETTIMEOFDAY_BASED_CLOCK_REALTIME is used for
8310 * CLOCK_REALTIME if clock_gettime is not available.
8311 */
8312#define RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME ID2SYM(id_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
8313 if (clk_id == RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME) {
8314 struct timeval tv;
8315 ret = gettimeofday(&tv, 0);
8316 if (ret != 0)
8317 rb_sys_fail("gettimeofday");
8318 tt.giga_count = tv.tv_sec;
8319 tt.count = (int32_t)tv.tv_usec * 1000;
8320 denominators[num_denominators++] = 1000000000;
8321 goto success;
8322 }
8323#endif
8324
8325#define RUBY_TIME_BASED_CLOCK_REALTIME ID2SYM(id_TIME_BASED_CLOCK_REALTIME)
8326 if (clk_id == RUBY_TIME_BASED_CLOCK_REALTIME) {
8327 time_t t;
8328 t = time(NULL);
8329 if (t == (time_t)-1)
8330 rb_sys_fail("time");
8331 tt.giga_count = t;
8332 tt.count = 0;
8333 denominators[num_denominators++] = 1000000000;
8334 goto success;
8335 }
8336
8337#ifdef HAVE_TIMES
8338#define RUBY_TIMES_BASED_CLOCK_MONOTONIC \
8339 ID2SYM(id_TIMES_BASED_CLOCK_MONOTONIC)
8340 if (clk_id == RUBY_TIMES_BASED_CLOCK_MONOTONIC) {
8341 struct tms buf;
8342 clock_t c;
8343 unsigned_clock_t uc;
8344 c = times(&buf);
8345 if (c == (clock_t)-1)
8346 rb_sys_fail("times");
8347 uc = (unsigned_clock_t)c;
8348 tt.count = (int32_t)(uc % 1000000000);
8349 tt.giga_count = (uc / 1000000000);
8350 denominators[num_denominators++] = get_clk_tck();
8351 goto success;
8352 }
8353#endif
8354
8355#ifdef RUSAGE_SELF
8356#define RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID \
8357 ID2SYM(id_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
8358 if (clk_id == RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8359 struct rusage usage;
8360 int32_t usec;
8361 ret = getrusage(RUSAGE_SELF, &usage);
8362 if (ret != 0)
8363 rb_sys_fail("getrusage");
8364 tt.giga_count = usage.ru_utime.tv_sec + usage.ru_stime.tv_sec;
8365 usec = (int32_t)(usage.ru_utime.tv_usec + usage.ru_stime.tv_usec);
8366 if (1000000 <= usec) {
8367 tt.giga_count++;
8368 usec -= 1000000;
8369 }
8370 tt.count = usec * 1000;
8371 denominators[num_denominators++] = 1000000000;
8372 goto success;
8373 }
8374#endif
8375
8376#ifdef HAVE_TIMES
8377#define RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID \
8378 ID2SYM(id_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID)
8379 if (clk_id == RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8380 struct tms buf;
8381 unsigned_clock_t utime, stime;
8382 if (times(&buf) == (clock_t)-1)
8383 rb_sys_fail("times");
8384 utime = (unsigned_clock_t)buf.tms_utime;
8385 stime = (unsigned_clock_t)buf.tms_stime;
8386 tt.count = (int32_t)((utime % 1000000000) + (stime % 1000000000));
8387 tt.giga_count = (utime / 1000000000) + (stime / 1000000000);
8388 if (1000000000 <= tt.count) {
8389 tt.count -= 1000000000;
8390 tt.giga_count++;
8391 }
8392 denominators[num_denominators++] = get_clk_tck();
8393 goto success;
8394 }
8395#endif
8396
8397#define RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID \
8398 ID2SYM(id_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID)
8399 if (clk_id == RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8400 clock_t c;
8401 unsigned_clock_t uc;
8402 errno = 0;
8403 c = clock();
8404 if (c == (clock_t)-1)
8405 rb_sys_fail("clock");
8406 uc = (unsigned_clock_t)c;
8407 tt.count = (int32_t)(uc % 1000000000);
8408 tt.giga_count = uc / 1000000000;
8409 denominators[num_denominators++] = CLOCKS_PER_SEC;
8410 goto success;
8411 }
8412
8413#ifdef __APPLE__
8414 if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
8415 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8416 uint64_t t = mach_absolute_time();
8417 tt.count = (int32_t)(t % 1000000000);
8418 tt.giga_count = t / 1000000000;
8419 numerators[num_numerators++] = info->numer;
8420 denominators[num_denominators++] = info->denom;
8421 denominators[num_denominators++] = 1000000000;
8422 goto success;
8423 }
8424#endif
8425 }
8426 else if (NUMERIC_CLOCKID) {
8427#if defined(HAVE_CLOCK_GETTIME)
8428 struct timespec ts;
8429 c = NUM2CLOCKID(clk_id);
8430 gettime:
8431 ret = clock_gettime(c, &ts);
8432 if (ret == -1)
8433 clock_failed("gettime", errno, clk_id);
8434 tt.count = (int32_t)ts.tv_nsec;
8435 tt.giga_count = ts.tv_sec;
8436 denominators[num_denominators++] = 1000000000;
8437 goto success;
8438#endif
8439 }
8440 else {
8442 }
8443 clock_failed("gettime", EINVAL, clk_id);
8444
8445 success:
8446 return make_clock_result(&tt, numerators, num_numerators, denominators, num_denominators, unit);
8447}
8448
8449/*
8450 * call-seq:
8451 * Process.clock_getres(clock_id, unit = :float_second) -> number
8452 *
8453 * Returns a clock resolution as determined by POSIX function
8454 * {clock_getres()}[https://man7.org/linux/man-pages/man3/clock_getres.3.html]:
8455 *
8456 * Process.clock_getres(:CLOCK_REALTIME) # => 1.0e-09
8457 *
8458 * See Process.clock_gettime for the values of +clock_id+ and +unit+.
8459 *
8460 * Examples:
8461 *
8462 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond) # => 0.001
8463 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond) # => 1.0e-06
8464 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 1.0e-09
8465 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :microsecond) # => 0
8466 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :millisecond) # => 0
8467 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond) # => 1
8468 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :second) # => 0
8469 *
8470 * In addition to the values for +unit+ supported in Process.clock_gettime,
8471 * this method supports +:hertz+, the integer number of clock ticks per second
8472 * (which is the reciprocal of +:float_second+):
8473 *
8474 * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz) # => 100.0
8475 * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 0.01
8476 *
8477 * <b>Accuracy</b>:
8478 * Note that the returned resolution may be inaccurate on some platforms
8479 * due to underlying bugs.
8480 * Inaccurate resolutions have been reported for various clocks including
8481 * +:CLOCK_MONOTONIC+ and +:CLOCK_MONOTONIC_RAW+
8482 * on Linux, macOS, BSD or AIX platforms, when using ARM processors,
8483 * or when using virtualization.
8484 */
8485static VALUE
8486rb_clock_getres(int argc, VALUE *argv, VALUE _)
8487{
8488 int ret;
8489
8490 struct timetick tt;
8491 timetick_int_t numerators[2];
8492 timetick_int_t denominators[2];
8493 int num_numerators = 0;
8494 int num_denominators = 0;
8495#ifdef HAVE_CLOCK_GETRES
8496 clockid_t c;
8497#endif
8498
8499 VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
8500 VALUE clk_id = argv[0];
8501
8502 if (SYMBOL_P(clk_id)) {
8503#ifdef CLOCK_REALTIME
8504 if (clk_id == RUBY_CLOCK_REALTIME) {
8505 c = CLOCK_REALTIME;
8506 goto getres;
8507 }
8508#endif
8509
8510#ifdef CLOCK_MONOTONIC
8511 if (clk_id == RUBY_CLOCK_MONOTONIC) {
8512 c = CLOCK_MONOTONIC;
8513 goto getres;
8514 }
8515#endif
8516
8517#ifdef CLOCK_PROCESS_CPUTIME_ID
8518 if (clk_id == RUBY_CLOCK_PROCESS_CPUTIME_ID) {
8519 c = CLOCK_PROCESS_CPUTIME_ID;
8520 goto getres;
8521 }
8522#endif
8523
8524#ifdef CLOCK_THREAD_CPUTIME_ID
8525 if (clk_id == RUBY_CLOCK_THREAD_CPUTIME_ID) {
8526 c = CLOCK_THREAD_CPUTIME_ID;
8527 goto getres;
8528 }
8529#endif
8530
8531#ifdef RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
8532 if (clk_id == RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME) {
8533 tt.giga_count = 0;
8534 tt.count = 1000;
8535 denominators[num_denominators++] = 1000000000;
8536 goto success;
8537 }
8538#endif
8539
8540#ifdef RUBY_TIME_BASED_CLOCK_REALTIME
8541 if (clk_id == RUBY_TIME_BASED_CLOCK_REALTIME) {
8542 tt.giga_count = 1;
8543 tt.count = 0;
8544 denominators[num_denominators++] = 1000000000;
8545 goto success;
8546 }
8547#endif
8548
8549#ifdef RUBY_TIMES_BASED_CLOCK_MONOTONIC
8550 if (clk_id == RUBY_TIMES_BASED_CLOCK_MONOTONIC) {
8551 tt.count = 1;
8552 tt.giga_count = 0;
8553 denominators[num_denominators++] = get_clk_tck();
8554 goto success;
8555 }
8556#endif
8557
8558#ifdef RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
8559 if (clk_id == RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8560 tt.giga_count = 0;
8561 tt.count = 1000;
8562 denominators[num_denominators++] = 1000000000;
8563 goto success;
8564 }
8565#endif
8566
8567#ifdef RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
8568 if (clk_id == RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8569 tt.count = 1;
8570 tt.giga_count = 0;
8571 denominators[num_denominators++] = get_clk_tck();
8572 goto success;
8573 }
8574#endif
8575
8576#ifdef RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
8577 if (clk_id == RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8578 tt.count = 1;
8579 tt.giga_count = 0;
8580 denominators[num_denominators++] = CLOCKS_PER_SEC;
8581 goto success;
8582 }
8583#endif
8584
8585#ifdef RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
8586 if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
8587 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8588 tt.count = 1;
8589 tt.giga_count = 0;
8590 numerators[num_numerators++] = info->numer;
8591 denominators[num_denominators++] = info->denom;
8592 denominators[num_denominators++] = 1000000000;
8593 goto success;
8594 }
8595#endif
8596 }
8597 else if (NUMERIC_CLOCKID) {
8598#if defined(HAVE_CLOCK_GETRES)
8599 struct timespec ts;
8600 c = NUM2CLOCKID(clk_id);
8601 getres:
8602 ret = clock_getres(c, &ts);
8603 if (ret == -1)
8604 clock_failed("getres", errno, clk_id);
8605 tt.count = (int32_t)ts.tv_nsec;
8606 tt.giga_count = ts.tv_sec;
8607 denominators[num_denominators++] = 1000000000;
8608 goto success;
8609#endif
8610 }
8611 else {
8613 }
8614 clock_failed("getres", EINVAL, clk_id);
8615
8616 success:
8617 if (unit == ID2SYM(id_hertz)) {
8618 return timetick2dblnum_reciprocal(&tt, numerators, num_numerators, denominators, num_denominators);
8619 }
8620 else {
8621 return make_clock_result(&tt, numerators, num_numerators, denominators, num_denominators, unit);
8622 }
8623}
8624
8625static VALUE
8626get_CHILD_STATUS(ID _x, VALUE *_y)
8627{
8628 return rb_last_status_get();
8629}
8630
8631static VALUE
8632get_PROCESS_ID(ID _x, VALUE *_y)
8633{
8634 return get_pid();
8635}
8636
8637/*
8638 * call-seq:
8639 * Process.kill(signal, *ids) -> count
8640 *
8641 * Sends a signal to each process specified by +ids+
8642 * (which must specify at least one ID);
8643 * returns the count of signals sent.
8644 *
8645 * For each given +id+, if +id+ is:
8646 *
8647 * - Positive, sends the signal to the process whose process ID is +id+.
8648 * - Zero, send the signal to all processes in the current process group.
8649 * - Negative, sends the signal to a system-dependent collection of processes.
8650 *
8651 * Argument +signal+ specifies the signal to be sent;
8652 * the argument may be:
8653 *
8654 * - An integer signal number: e.g., +-29+, +0+, +29+.
8655 * - A signal name (string), with or without leading <tt>'SIG'</tt>,
8656 * and with or without a further prefixed minus sign (<tt>'-'</tt>):
8657 * e.g.:
8658 *
8659 * - <tt>'SIGPOLL'</tt>.
8660 * - <tt>'POLL'</tt>,
8661 * - <tt>'-SIGPOLL'</tt>.
8662 * - <tt>'-POLL'</tt>.
8663 *
8664 * - A signal symbol, with or without leading <tt>'SIG'</tt>,
8665 * and with or without a further prefixed minus sign (<tt>'-'</tt>):
8666 * e.g.:
8667 *
8668 * - +:SIGPOLL+.
8669 * - +:POLL+.
8670 * - <tt>:'-SIGPOLL'</tt>.
8671 * - <tt>:'-POLL'</tt>.
8672 *
8673 * If +signal+ is:
8674 *
8675 * - A non-negative integer, or a signal name or symbol
8676 * without prefixed <tt>'-'</tt>,
8677 * each process with process ID +id+ is signalled.
8678 * - A negative integer, or a signal name or symbol
8679 * with prefixed <tt>'-'</tt>,
8680 * each process group with group ID +id+ is signalled.
8681 *
8682 * Use method Signal.list to see which signals are supported
8683 * by Ruby on the underlying platform;
8684 * the method returns a hash of the string names
8685 * and non-negative integer values of the supported signals.
8686 * The size and content of the returned hash varies widely
8687 * among platforms.
8688 *
8689 * Additionally, signal +0+ is useful to determine if the process exists.
8690 *
8691 * Example:
8692 *
8693 * pid = fork do
8694 * Signal.trap('HUP') { puts 'Ouch!'; exit }
8695 * # ... do some work ...
8696 * end
8697 * # ...
8698 * Process.kill('HUP', pid)
8699 * Process.wait
8700 *
8701 * Output:
8702 *
8703 * Ouch!
8704 *
8705 * Exceptions:
8706 *
8707 * - Raises Errno::EINVAL or RangeError if +signal+ is an integer
8708 * but invalid.
8709 * - Raises ArgumentError if +signal+ is a string or symbol
8710 * but invalid.
8711 * - Raises Errno::ESRCH or RangeError if one of +ids+ is invalid.
8712 * - Raises Errno::EPERM if needed permissions are not in force.
8713 *
8714 * In the last two cases, signals may have been sent to some processes.
8715 */
8716
8717static VALUE
8718proc_rb_f_kill(int c, const VALUE *v, VALUE _)
8719{
8720 return rb_f_kill(c, v);
8721}
8722
8724static VALUE rb_mProcUID;
8725static VALUE rb_mProcGID;
8726static VALUE rb_mProcID_Syscall;
8727
8728/*
8729 * call-seq:
8730 * Process.warmup -> true
8731 *
8732 * Notify the Ruby virtual machine that the boot sequence is finished,
8733 * and that now is a good time to optimize the application. This is useful
8734 * for long running applications.
8735 *
8736 * This method is expected to be called at the end of the application boot.
8737 * If the application is deployed using a pre-forking model, +Process.warmup+
8738 * should be called in the original process before the first fork.
8739 *
8740 * The actual optimizations performed are entirely implementation specific
8741 * and may change in the future without notice.
8742 *
8743 * On CRuby, +Process.warmup+:
8744 *
8745 * * Performs a major GC.
8746 * * Compacts the heap.
8747 * * Promotes all surviving objects to the old generation.
8748 * * Precomputes the coderange of all strings.
8749 * * Frees all empty heap pages and increments the allocatable pages counter
8750 * by the number of pages freed.
8751 * * Invoke +malloc_trim+ if available to free empty malloc pages.
8752 */
8753
8754static VALUE
8755proc_warmup(VALUE _)
8756{
8757 RB_VM_LOCKING() {
8758 rb_gc_prepare_heap();
8759 }
8760 return Qtrue;
8761}
8762
8763/*
8764 * Document-module: Process
8765 *
8766 * Module +Process+ represents a process in the underlying operating system.
8767 * Its methods support management of the current process and its child processes.
8768 *
8769 * == Process Creation
8770 *
8771 * Each of the following methods executes a given command in a new process or subshell,
8772 * or multiple commands in new processes and/or subshells.
8773 * The choice of process or subshell depends on the form of the command;
8774 * see {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
8775 *
8776 * - Process.spawn, Kernel#spawn: Executes the command;
8777 * returns the new pid without waiting for completion.
8778 * - Process.exec: Replaces the current process by executing the command.
8779 *
8780 * In addition:
8781 *
8782 * - Method Kernel#system executes a given command-line (string) in a subshell;
8783 * returns +true+, +false+, or +nil+.
8784 * - Method Kernel#` executes a given command-line (string) in a subshell;
8785 * returns its $stdout string.
8786 * - Module Open3 supports creating child processes
8787 * with access to their $stdin, $stdout, and $stderr streams.
8788 *
8789 * === Execution Environment
8790 *
8791 * Optional leading argument +env+ is a hash of name/value pairs,
8792 * where each name is a string and each value is a string or +nil+;
8793 * each name/value pair is added to ENV in the new process.
8794 *
8795 * Process.spawn( 'ruby -e "p ENV[\"Foo\"]"')
8796 * Process.spawn({'Foo' => '0'}, 'ruby -e "p ENV[\"Foo\"]"')
8797 *
8798 * Output:
8799 *
8800 * "0"
8801 *
8802 * The effect is usually similar to that of calling ENV#update with argument +env+,
8803 * where each named environment variable is created or updated
8804 * (if the value is non-+nil+),
8805 * or deleted (if the value is +nil+).
8806 *
8807 * However, some modifications to the calling process may remain
8808 * if the new process fails.
8809 * For example, hard resource limits are not restored.
8810 *
8811 * === Argument +command_line+ or +exe_path+
8812 *
8813 * The required string argument is one of the following:
8814 *
8815 * - +command_line+ if it begins with a shell reserved word or special built-in,
8816 * or if it contains one or more meta characters.
8817 * - +exe_path+ otherwise.
8818 *
8819 * ==== Argument +command_line+
8820 *
8821 * \String argument +command_line+ is a command line to be passed to a shell;
8822 * it must begin with a shell reserved word, begin with a special built-in,
8823 * or contain meta characters:
8824 *
8825 * system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
8826 * system('exit') # => true # Built-in.
8827 * system('date > /tmp/date.tmp') # => true # Contains meta character.
8828 * system('date > /nop/date.tmp') # => false
8829 * system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
8830 *
8831 * The command line may also contain arguments and options for the command:
8832 *
8833 * system('echo "Foo"') # => true
8834 *
8835 * Output:
8836 *
8837 * Foo
8838 *
8839 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
8840 *
8841 * ==== Argument +exe_path+
8842 *
8843 * Argument +exe_path+ is one of the following:
8844 *
8845 * - The string path to an executable file to be called:
8846 *
8847 * Example:
8848 *
8849 * system('/usr/bin/date') # => true # Path to date on Unix-style system.
8850 * system('foo') # => nil # Command execlution failed.
8851 *
8852 * Output:
8853 *
8854 * Thu Aug 31 10:06:48 AM CDT 2023
8855 *
8856 * A path or command name containing spaces without arguments cannot
8857 * be distinguished from +command_line+ above, so you must quote or
8858 * escape the entire command name using a shell in platform
8859 * dependent manner, or use the array form below.
8860 *
8861 * If +exe_path+ does not contain any path separator, an executable
8862 * file is searched from directories specified with the +PATH+
8863 * environment variable. What the word "executable" means here is
8864 * depending on platforms.
8865 *
8866 * Even if the file considered "executable", its content may not be
8867 * in proper executable format. In that case, Ruby tries to run it
8868 * by using <tt>/bin/sh</tt> on a Unix-like system, like system(3)
8869 * does.
8870 *
8871 * File.write('shell_command', 'echo $SHELL', perm: 0o755)
8872 * system('./shell_command') # prints "/bin/sh" or something.
8873 *
8874 * - A 2-element array containing the path to an executable
8875 * and the string to be used as the name of the executing process:
8876 *
8877 * Example:
8878 *
8879 * pid = spawn(['sleep', 'Hello!'], '1') # 2-element array.
8880 * p `ps -p #{pid} -o command=`
8881 *
8882 * Output:
8883 *
8884 * "Hello! 1\n"
8885 *
8886 * === Arguments +args+
8887 *
8888 * If +command_line+ does not contain shell meta characters except for
8889 * spaces and tabs, or +exe_path+ is given, Ruby invokes the
8890 * executable directly. This form does not use the shell:
8891 *
8892 * spawn("doesnt_exist") # Raises Errno::ENOENT
8893 * spawn("doesnt_exist", "\n") # Raises Errno::ENOENT
8894 *
8895 * spawn("doesnt_exist\n") # => false
8896 * # sh: 1: doesnot_exist: not found
8897 *
8898 * The error message is from a shell and would vary depending on your
8899 * system.
8900 *
8901 * If one or more +args+ is given after +exe_path+, each is an
8902 * argument or option to be passed to the executable:
8903 *
8904 * Example:
8905 *
8906 * system('echo', '<', 'C*', '|', '$SHELL', '>') # => true
8907 *
8908 * Output:
8909 *
8910 * < C* | $SHELL >
8911 *
8912 * However, there are exceptions on Windows. See {Execution Shell on
8913 * Windows}[rdoc-ref:Process@Execution+Shell+on+Windows].
8914 *
8915 * If you want to invoke a path containing spaces with no arguments
8916 * without shell, you will need to use a 2-element array +exe_path+.
8917 *
8918 * Example:
8919 *
8920 * path = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
8921 * spawn(path) # Raises Errno::ENOENT; No such file or directory - /Applications/Google
8922 * spawn([path] * 2)
8923 *
8924 * === Execution Options
8925 *
8926 * Optional trailing argument +options+ is a hash of execution options.
8927 *
8928 * ==== Working Directory (+:chdir+)
8929 *
8930 * By default, the working directory for the new process is the same as
8931 * that of the current process:
8932 *
8933 * Dir.chdir('/var')
8934 * Process.spawn('ruby -e "puts Dir.pwd"')
8935 *
8936 * Output:
8937 *
8938 * /var
8939 *
8940 * Use option +:chdir+ to set the working directory for the new process:
8941 *
8942 * Process.spawn('ruby -e "puts Dir.pwd"', {chdir: '/tmp'})
8943 *
8944 * Output:
8945 *
8946 * /tmp
8947 *
8948 * The working directory of the current process is not changed:
8949 *
8950 * Dir.pwd # => "/var"
8951 *
8952 * ==== \File Redirection (\File Descriptor)
8953 *
8954 * Use execution options for file redirection in the new process.
8955 *
8956 * The key for such an option may be an integer file descriptor (fd),
8957 * specifying a source,
8958 * or an array of fds, specifying multiple sources.
8959 *
8960 * An integer source fd may be specified as:
8961 *
8962 * - _n_: Specifies file descriptor _n_.
8963 *
8964 * There are these shorthand symbols for fds:
8965 *
8966 * - +:in+: Specifies file descriptor 0 (STDIN).
8967 * - +:out+: Specifies file descriptor 1 (STDOUT).
8968 * - +:err+: Specifies file descriptor 2 (STDERR).
8969 *
8970 * The value given with a source is one of:
8971 *
8972 * - _n_:
8973 * Redirects to fd _n_ in the parent process.
8974 * - +filepath+:
8975 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>,
8976 * where +mode+ is <tt>'r'</tt> for source +:in+,
8977 * or <tt>'w'</tt> for source +:out+ or +:err+.
8978 * - <tt>[filepath]</tt>:
8979 * Redirects from the file at +filepath+ via <tt>open(filepath, 'r', 0644)</tt>.
8980 * - <tt>[filepath, mode]</tt>:
8981 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>.
8982 * - <tt>[filepath, mode, perm]</tt>:
8983 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, perm)</tt>.
8984 * - <tt>[:child, fd]</tt>:
8985 * Redirects to the redirected +fd+.
8986 * - +:close+: Closes the file descriptor in child process.
8987 *
8988 * See {Access Modes}[rdoc-ref:File@Access+Modes]
8989 * and {File Permissions}[rdoc-ref:File@File+Permissions].
8990 *
8991 * ==== Environment Variables (+:unsetenv_others+)
8992 *
8993 * By default, the new process inherits environment variables
8994 * from the parent process;
8995 * use execution option key +:unsetenv_others+ with value +true+
8996 * to clear environment variables in the new process.
8997 *
8998 * Any changes specified by execution option +env+ are made after the new process
8999 * inherits or clears its environment variables;
9000 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
9001 *
9002 * ==== \File-Creation Access (+:umask+)
9003 *
9004 * Use execution option +:umask+ to set the file-creation access
9005 * for the new process;
9006 * see {Access Modes}[rdoc-ref:File@Access+Modes]:
9007 *
9008 * command = 'ruby -e "puts sprintf(\"0%o\", File.umask)"'
9009 * options = {:umask => 0644}
9010 * Process.spawn(command, options)
9011 *
9012 * Output:
9013 *
9014 * 0644
9015 *
9016 * ==== Process Groups (+:pgroup+ and +:new_pgroup+)
9017 *
9018 * By default, the new process belongs to the same
9019 * {process group}[https://en.wikipedia.org/wiki/Process_group]
9020 * as the parent process.
9021 *
9022 * To specify a different process group.
9023 * use execution option +:pgroup+ with one of the following values:
9024 *
9025 * - +true+: Create a new process group for the new process.
9026 * - _pgid_: Create the new process in the process group
9027 * whose id is _pgid_.
9028 *
9029 * On Windows only, use execution option +:new_pgroup+ with value +true+
9030 * to create a new process group for the new process.
9031 *
9032 * ==== Resource Limits
9033 *
9034 * Use execution options to set resource limits.
9035 *
9036 * The keys for these options are symbols of the form
9037 * <tt>:rlimit_<i>resource_name</i></tt>,
9038 * where _resource_name_ is the downcased form of one of the string
9039 * resource names described at method Process.setrlimit.
9040 * For example, key +:rlimit_cpu+ corresponds to resource limit <tt>'CPU'</tt>.
9041 *
9042 * The value for such as key is one of:
9043 *
9044 * - An integer, specifying both the current and maximum limits.
9045 * - A 2-element array of integers, specifying the current and maximum limits.
9046 *
9047 * ==== \File Descriptor Inheritance
9048 *
9049 * By default, the new process inherits file descriptors from the parent process.
9050 *
9051 * Use execution option <tt>:close_others => true</tt> to modify that inheritance
9052 * by closing non-standard fds (3 and greater) that are not otherwise redirected.
9053 *
9054 * === Execution Shell
9055 *
9056 * On a Unix-like system, the shell invoked is <tt>/bin/sh</tt>;
9057 * the entire string +command_line+ is passed as an argument
9058 * to {shell option -c}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/sh.html].
9059 *
9060 * The shell performs normal shell expansion on the command line:
9061 *
9062 * Example:
9063 *
9064 * system('echo $SHELL: C*') # => true
9065 *
9066 * Output:
9067 *
9068 * /bin/bash: CONTRIBUTING.md COPYING COPYING.ja
9069 *
9070 * ==== Execution Shell on Windows
9071 *
9072 * On Windows, the shell invoked is determined by environment variable
9073 * +RUBYSHELL+, if defined, or +COMSPEC+ otherwise; the entire string
9074 * +command_line+ is passed as an argument to <tt>-c</tt> option for
9075 * +RUBYSHELL+, as well as <tt>/bin/sh</tt>, and {/c
9076 * option}[https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/cmd]
9077 * for +COMSPEC+. The shell is invoked automatically in the following
9078 * cases:
9079 *
9080 * - The command is a built-in of +cmd.exe+, such as +echo+.
9081 * - The executable file is a batch file; its name ends with +.bat+ or
9082 * +.cmd+.
9083 *
9084 * Note that the command will still be invoked as +command_line+ form
9085 * even when called in +exe_path+ form, because +cmd.exe+ does not
9086 * accept a script name like <tt>/bin/sh</tt> does but only works with
9087 * <tt>/c</tt> option.
9088 *
9089 * The standard shell +cmd.exe+ performs environment variable
9090 * expansion but does not have globbing functionality:
9091 *
9092 * Example:
9093 *
9094 * system("echo %COMSPEC%: C*")' # => true
9095 *
9096 * Output:
9097 *
9098 * C:\WINDOWS\system32\cmd.exe: C*
9099 *
9100 * == What's Here
9101 *
9102 * === Current-Process Getters
9103 *
9104 * - ::argv0: Returns the process name as a frozen string.
9105 * - ::egid: Returns the effective group ID.
9106 * - ::euid: Returns the effective user ID.
9107 * - ::getpgrp: Return the process group ID.
9108 * - ::getrlimit: Returns the resource limit.
9109 * - ::gid: Returns the (real) group ID.
9110 * - ::pid: Returns the process ID.
9111 * - ::ppid: Returns the process ID of the parent process.
9112 * - ::uid: Returns the (real) user ID.
9113 *
9114 * === Current-Process Setters
9115 *
9116 * - ::egid=: Sets the effective group ID.
9117 * - ::euid=: Sets the effective user ID.
9118 * - ::gid=: Sets the (real) group ID.
9119 * - ::setproctitle: Sets the process title.
9120 * - ::setpgrp: Sets the process group ID of the process to zero.
9121 * - ::setrlimit: Sets a resource limit.
9122 * - ::setsid: Establishes the process as a new session and process group leader,
9123 * with no controlling tty.
9124 * - ::uid=: Sets the user ID.
9125 *
9126 * === Current-Process Execution
9127 *
9128 * - ::abort: Immediately terminates the process.
9129 * - ::daemon: Detaches the process from its controlling terminal
9130 * and continues running it in the background as system daemon.
9131 * - ::exec: Replaces the process by running a given external command.
9132 * - ::exit: Initiates process termination by raising exception SystemExit
9133 * (which may be caught).
9134 * - ::exit!: Immediately exits the process.
9135 * - ::warmup: Notifies the Ruby virtual machine that the boot sequence
9136 * for the application is completed,
9137 * and that the VM may begin optimizing the application.
9138 *
9139 * === Child Processes
9140 *
9141 * - ::detach: Guards against a child process becoming a zombie.
9142 * - ::fork: Creates a child process.
9143 * - ::kill: Sends a given signal to processes.
9144 * - ::spawn: Creates a child process.
9145 * - ::wait, ::waitpid: Waits for a child process to exit; returns its process ID.
9146 * - ::wait2, ::waitpid2: Waits for a child process to exit; returns its process ID and status.
9147 * - ::waitall: Waits for all child processes to exit;
9148 * returns their process IDs and statuses.
9149 *
9150 * === Process Groups
9151 *
9152 * - ::getpgid: Returns the process group ID for a process.
9153 * - ::getpriority: Returns the scheduling priority
9154 * for a process, process group, or user.
9155 * - ::getsid: Returns the session ID for a process.
9156 * - ::groups: Returns an array of the group IDs
9157 * in the supplemental group access list for this process.
9158 * - ::groups=: Sets the supplemental group access list
9159 * to the given array of group IDs.
9160 * - ::initgroups: Initializes the supplemental group access list.
9161 * - ::last_status: Returns the status of the last executed child process
9162 * in the current thread.
9163 * - ::maxgroups: Returns the maximum number of group IDs allowed
9164 * in the supplemental group access list.
9165 * - ::maxgroups=: Sets the maximum number of group IDs allowed
9166 * in the supplemental group access list.
9167 * - ::setpgid: Sets the process group ID of a process.
9168 * - ::setpriority: Sets the scheduling priority
9169 * for a process, process group, or user.
9170 *
9171 * === Timing
9172 *
9173 * - ::clock_getres: Returns the resolution of a system clock.
9174 * - ::clock_gettime: Returns the time from a system clock.
9175 * - ::times: Returns a Process::Tms object containing times
9176 * for the current process and its child processes.
9177 *
9178 */
9179
9180void
9181InitVM_process(void)
9182{
9183 rb_define_virtual_variable("$?", get_CHILD_STATUS, 0);
9184 rb_define_virtual_variable("$$", get_PROCESS_ID, 0);
9185
9186 rb_gvar_ractor_local("$$");
9187 rb_gvar_ractor_local("$?");
9188
9189 rb_define_global_function("exec", f_exec, -1);
9190 rb_define_global_function("fork", rb_f_fork, 0);
9191 rb_define_global_function("exit!", rb_f_exit_bang, -1);
9192 rb_define_global_function("system", rb_f_system, -1);
9193 rb_define_global_function("spawn", rb_f_spawn, -1);
9194 rb_define_global_function("sleep", rb_f_sleep, -1);
9195 rb_define_global_function("exit", f_exit, -1);
9196 rb_define_global_function("abort", f_abort, -1);
9197
9198 rb_mProcess = rb_define_module("Process");
9199
9200#ifdef WNOHANG
9201 /* see Process.wait */
9202 rb_define_const(rb_mProcess, "WNOHANG", INT2FIX(WNOHANG));
9203#else
9204 /* see Process.wait */
9205 rb_define_const(rb_mProcess, "WNOHANG", INT2FIX(0));
9206#endif
9207#ifdef WUNTRACED
9208 /* see Process.wait */
9209 rb_define_const(rb_mProcess, "WUNTRACED", INT2FIX(WUNTRACED));
9210#else
9211 /* see Process.wait */
9212 rb_define_const(rb_mProcess, "WUNTRACED", INT2FIX(0));
9213#endif
9214
9215 rb_define_singleton_method(rb_mProcess, "exec", f_exec, -1);
9216 rb_define_singleton_method(rb_mProcess, "fork", rb_f_fork, 0);
9217 rb_define_singleton_method(rb_mProcess, "spawn", rb_f_spawn, -1);
9218 rb_define_singleton_method(rb_mProcess, "exit!", rb_f_exit_bang, -1);
9219 rb_define_singleton_method(rb_mProcess, "exit", f_exit, -1);
9220 rb_define_singleton_method(rb_mProcess, "abort", f_abort, -1);
9221 rb_define_singleton_method(rb_mProcess, "last_status", proc_s_last_status, 0);
9222 rb_define_singleton_method(rb_mProcess, "_fork", rb_proc__fork, 0);
9223
9224 rb_define_module_function(rb_mProcess, "kill", proc_rb_f_kill, -1);
9225 rb_define_module_function(rb_mProcess, "wait", proc_m_wait, -1);
9226 rb_define_module_function(rb_mProcess, "wait2", proc_wait2, -1);
9227 rb_define_module_function(rb_mProcess, "waitpid", proc_m_wait, -1);
9228 rb_define_module_function(rb_mProcess, "waitpid2", proc_wait2, -1);
9229 rb_define_module_function(rb_mProcess, "waitall", proc_waitall, 0);
9230 rb_define_module_function(rb_mProcess, "detach", proc_detach, 1);
9231
9232 /* :nodoc: */
9233 rb_cWaiter = rb_define_class_under(rb_mProcess, "Waiter", rb_cThread);
9234 rb_undef_alloc_func(rb_cWaiter);
9235 rb_undef_method(CLASS_OF(rb_cWaiter), "new");
9236 rb_define_method(rb_cWaiter, "pid", detach_process_pid, 0);
9237
9238 rb_cProcessStatus = rb_define_class_under(rb_mProcess, "Status", rb_cObject);
9239 rb_define_alloc_func(rb_cProcessStatus, rb_process_status_allocate);
9240 rb_undef_method(CLASS_OF(rb_cProcessStatus), "new");
9241 rb_marshal_define_compat(rb_cProcessStatus, rb_cObject,
9242 process_status_dump, process_status_load);
9243
9244 rb_define_singleton_method(rb_cProcessStatus, "wait", rb_process_status_waitv, -1);
9245
9246 rb_define_method(rb_cProcessStatus, "==", pst_equal, 1);
9247 rb_define_method(rb_cProcessStatus, "to_i", pst_to_i, 0);
9248 rb_define_method(rb_cProcessStatus, "to_s", pst_to_s, 0);
9249 rb_define_method(rb_cProcessStatus, "inspect", pst_inspect, 0);
9250
9251 rb_define_method(rb_cProcessStatus, "pid", pst_pid_m, 0);
9252
9253 rb_define_method(rb_cProcessStatus, "stopped?", pst_wifstopped, 0);
9254 rb_define_method(rb_cProcessStatus, "stopsig", pst_wstopsig, 0);
9255 rb_define_method(rb_cProcessStatus, "signaled?", pst_wifsignaled, 0);
9256 rb_define_method(rb_cProcessStatus, "termsig", pst_wtermsig, 0);
9257 rb_define_method(rb_cProcessStatus, "exited?", pst_wifexited, 0);
9258 rb_define_method(rb_cProcessStatus, "exitstatus", pst_wexitstatus, 0);
9259 rb_define_method(rb_cProcessStatus, "success?", pst_success_p, 0);
9260 rb_define_method(rb_cProcessStatus, "coredump?", pst_wcoredump, 0);
9261
9262 rb_define_module_function(rb_mProcess, "pid", proc_get_pid, 0);
9263 rb_define_module_function(rb_mProcess, "ppid", proc_get_ppid, 0);
9264
9265 rb_define_module_function(rb_mProcess, "getpgrp", proc_getpgrp, 0);
9266 rb_define_module_function(rb_mProcess, "setpgrp", proc_setpgrp, 0);
9267 rb_define_module_function(rb_mProcess, "getpgid", proc_getpgid, 1);
9268 rb_define_module_function(rb_mProcess, "setpgid", proc_setpgid, 2);
9269
9270 rb_define_module_function(rb_mProcess, "getsid", proc_getsid, -1);
9271 rb_define_module_function(rb_mProcess, "setsid", proc_setsid, 0);
9272
9273 rb_define_module_function(rb_mProcess, "getpriority", proc_getpriority, 2);
9274 rb_define_module_function(rb_mProcess, "setpriority", proc_setpriority, 3);
9275
9276 rb_define_module_function(rb_mProcess, "warmup", proc_warmup, 0);
9277
9278#ifdef HAVE_GETPRIORITY
9279 /* see Process.setpriority */
9280 rb_define_const(rb_mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS));
9281 /* see Process.setpriority */
9282 rb_define_const(rb_mProcess, "PRIO_PGRP", INT2FIX(PRIO_PGRP));
9283 /* see Process.setpriority */
9284 rb_define_const(rb_mProcess, "PRIO_USER", INT2FIX(PRIO_USER));
9285#endif
9286
9287 rb_define_module_function(rb_mProcess, "getrlimit", proc_getrlimit, 1);
9288 rb_define_module_function(rb_mProcess, "setrlimit", proc_setrlimit, -1);
9289#if defined(RLIM2NUM) && defined(RLIM_INFINITY)
9290 {
9291 VALUE inf = RLIM2NUM(RLIM_INFINITY);
9292#ifdef RLIM_SAVED_MAX
9293 {
9294 VALUE v = RLIM_INFINITY == RLIM_SAVED_MAX ? inf : RLIM2NUM(RLIM_SAVED_MAX);
9295 /* see Process.setrlimit */
9296 rb_define_const(rb_mProcess, "RLIM_SAVED_MAX", v);
9297 }
9298#endif
9299 /* see Process.setrlimit */
9300 rb_define_const(rb_mProcess, "RLIM_INFINITY", inf);
9301#ifdef RLIM_SAVED_CUR
9302 {
9303 VALUE v = RLIM_INFINITY == RLIM_SAVED_CUR ? inf : RLIM2NUM(RLIM_SAVED_CUR);
9304 /* see Process.setrlimit */
9305 rb_define_const(rb_mProcess, "RLIM_SAVED_CUR", v);
9306 }
9307#endif
9308 }
9309#ifdef RLIMIT_AS
9310 /* Maximum size of the process's virtual memory (address space) in bytes.
9311 *
9312 * see the system getrlimit(2) manual for details.
9313 */
9314 rb_define_const(rb_mProcess, "RLIMIT_AS", INT2FIX(RLIMIT_AS));
9315#endif
9316#ifdef RLIMIT_CORE
9317 /* Maximum size of the core file.
9318 *
9319 * see the system getrlimit(2) manual for details.
9320 */
9321 rb_define_const(rb_mProcess, "RLIMIT_CORE", INT2FIX(RLIMIT_CORE));
9322#endif
9323#ifdef RLIMIT_CPU
9324 /* CPU time limit in seconds.
9325 *
9326 * see the system getrlimit(2) manual for details.
9327 */
9328 rb_define_const(rb_mProcess, "RLIMIT_CPU", INT2FIX(RLIMIT_CPU));
9329#endif
9330#ifdef RLIMIT_DATA
9331 /* Maximum size of the process's data segment.
9332 *
9333 * see the system getrlimit(2) manual for details.
9334 */
9335 rb_define_const(rb_mProcess, "RLIMIT_DATA", INT2FIX(RLIMIT_DATA));
9336#endif
9337#ifdef RLIMIT_FSIZE
9338 /* Maximum size of files that the process may create.
9339 *
9340 * see the system getrlimit(2) manual for details.
9341 */
9342 rb_define_const(rb_mProcess, "RLIMIT_FSIZE", INT2FIX(RLIMIT_FSIZE));
9343#endif
9344#ifdef RLIMIT_MEMLOCK
9345 /* Maximum number of bytes of memory that may be locked into RAM.
9346 *
9347 * see the system getrlimit(2) manual for details.
9348 */
9349 rb_define_const(rb_mProcess, "RLIMIT_MEMLOCK", INT2FIX(RLIMIT_MEMLOCK));
9350#endif
9351#ifdef RLIMIT_MSGQUEUE
9352 /* Specifies the limit on the number of bytes that can be allocated
9353 * for POSIX message queues for the real user ID of the calling process.
9354 *
9355 * see the system getrlimit(2) manual for details.
9356 */
9357 rb_define_const(rb_mProcess, "RLIMIT_MSGQUEUE", INT2FIX(RLIMIT_MSGQUEUE));
9358#endif
9359#ifdef RLIMIT_NICE
9360 /* Specifies a ceiling to which the process's nice value can be raised.
9361 *
9362 * see the system getrlimit(2) manual for details.
9363 */
9364 rb_define_const(rb_mProcess, "RLIMIT_NICE", INT2FIX(RLIMIT_NICE));
9365#endif
9366#ifdef RLIMIT_NOFILE
9367 /* Specifies a value one greater than the maximum file descriptor
9368 * number that can be opened by this process.
9369 *
9370 * see the system getrlimit(2) manual for details.
9371 */
9372 rb_define_const(rb_mProcess, "RLIMIT_NOFILE", INT2FIX(RLIMIT_NOFILE));
9373#endif
9374#ifdef RLIMIT_NPROC
9375 /* The maximum number of processes that can be created for the
9376 * real user ID of the calling process.
9377 *
9378 * see the system getrlimit(2) manual for details.
9379 */
9380 rb_define_const(rb_mProcess, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC));
9381#endif
9382#ifdef RLIMIT_NPTS
9383 /* The maximum number of pseudo-terminals that can be created for the
9384 * real user ID of the calling process.
9385 *
9386 * see the system getrlimit(2) manual for details.
9387 */
9388 rb_define_const(rb_mProcess, "RLIMIT_NPTS", INT2FIX(RLIMIT_NPTS));
9389#endif
9390#ifdef RLIMIT_RSS
9391 /* Specifies the limit (in pages) of the process's resident set.
9392 *
9393 * see the system getrlimit(2) manual for details.
9394 */
9395 rb_define_const(rb_mProcess, "RLIMIT_RSS", INT2FIX(RLIMIT_RSS));
9396#endif
9397#ifdef RLIMIT_RTPRIO
9398 /* Specifies a ceiling on the real-time priority that may be set for this process.
9399 *
9400 * see the system getrlimit(2) manual for details.
9401 */
9402 rb_define_const(rb_mProcess, "RLIMIT_RTPRIO", INT2FIX(RLIMIT_RTPRIO));
9403#endif
9404#ifdef RLIMIT_RTTIME
9405 /* Specifies limit on CPU time this process scheduled under a real-time
9406 * scheduling policy can consume.
9407 *
9408 * see the system getrlimit(2) manual for details.
9409 */
9410 rb_define_const(rb_mProcess, "RLIMIT_RTTIME", INT2FIX(RLIMIT_RTTIME));
9411#endif
9412#ifdef RLIMIT_SBSIZE
9413 /* Maximum size of the socket buffer.
9414 */
9415 rb_define_const(rb_mProcess, "RLIMIT_SBSIZE", INT2FIX(RLIMIT_SBSIZE));
9416#endif
9417#ifdef RLIMIT_SIGPENDING
9418 /* Specifies a limit on the number of signals that may be queued for
9419 * the real user ID of the calling process.
9420 *
9421 * see the system getrlimit(2) manual for details.
9422 */
9423 rb_define_const(rb_mProcess, "RLIMIT_SIGPENDING", INT2FIX(RLIMIT_SIGPENDING));
9424#endif
9425#ifdef RLIMIT_STACK
9426 /* Maximum size of the stack, in bytes.
9427 *
9428 * see the system getrlimit(2) manual for details.
9429 */
9430 rb_define_const(rb_mProcess, "RLIMIT_STACK", INT2FIX(RLIMIT_STACK));
9431#endif
9432#endif
9433
9434 rb_define_module_function(rb_mProcess, "uid", proc_getuid, 0);
9435 rb_define_module_function(rb_mProcess, "uid=", proc_setuid, 1);
9436 rb_define_module_function(rb_mProcess, "gid", proc_getgid, 0);
9437 rb_define_module_function(rb_mProcess, "gid=", proc_setgid, 1);
9438 rb_define_module_function(rb_mProcess, "euid", proc_geteuid, 0);
9439 rb_define_module_function(rb_mProcess, "euid=", proc_seteuid_m, 1);
9440 rb_define_module_function(rb_mProcess, "egid", proc_getegid, 0);
9441 rb_define_module_function(rb_mProcess, "egid=", proc_setegid_m, 1);
9442 rb_define_module_function(rb_mProcess, "initgroups", proc_initgroups, 2);
9443 rb_define_module_function(rb_mProcess, "groups", proc_getgroups, 0);
9444 rb_define_module_function(rb_mProcess, "groups=", proc_setgroups, 1);
9445 rb_define_module_function(rb_mProcess, "maxgroups", proc_getmaxgroups, 0);
9446 rb_define_module_function(rb_mProcess, "maxgroups=", proc_setmaxgroups, 1);
9447
9448 rb_define_module_function(rb_mProcess, "daemon", proc_daemon, -1);
9449
9450 rb_define_module_function(rb_mProcess, "times", rb_proc_times, 0);
9451
9452#if defined(RUBY_CLOCK_REALTIME)
9453#elif defined(RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
9454# define RUBY_CLOCK_REALTIME RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
9455#elif defined(RUBY_TIME_BASED_CLOCK_REALTIME)
9456# define RUBY_CLOCK_REALTIME RUBY_TIME_BASED_CLOCK_REALTIME
9457#endif
9458#if defined(CLOCK_REALTIME) && defined(CLOCKID2NUM)
9459 /* see Process.clock_gettime */
9460 rb_define_const(rb_mProcess, "CLOCK_REALTIME", CLOCKID2NUM(CLOCK_REALTIME));
9461#elif defined(RUBY_CLOCK_REALTIME)
9462 rb_define_const(rb_mProcess, "CLOCK_REALTIME", RUBY_CLOCK_REALTIME);
9463#endif
9464
9465#if defined(RUBY_CLOCK_MONOTONIC)
9466#elif defined(RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC)
9467# define RUBY_CLOCK_MONOTONIC RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
9468#endif
9469#if defined(CLOCK_MONOTONIC) && defined(CLOCKID2NUM)
9470 /* see Process.clock_gettime */
9471 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC", CLOCKID2NUM(CLOCK_MONOTONIC));
9472#elif defined(RUBY_CLOCK_MONOTONIC)
9473 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC", RUBY_CLOCK_MONOTONIC);
9474#endif
9475
9476#if defined(RUBY_CLOCK_PROCESS_CPUTIME_ID)
9477#elif defined(RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
9478# define RUBY_CLOCK_PROCESS_CPUTIME_ID RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
9479#endif
9480#if defined(CLOCK_PROCESS_CPUTIME_ID) && defined(CLOCKID2NUM)
9481 /* see Process.clock_gettime */
9482 rb_define_const(rb_mProcess, "CLOCK_PROCESS_CPUTIME_ID", CLOCKID2NUM(CLOCK_PROCESS_CPUTIME_ID));
9483#elif defined(RUBY_CLOCK_PROCESS_CPUTIME_ID)
9484 rb_define_const(rb_mProcess, "CLOCK_PROCESS_CPUTIME_ID", RUBY_CLOCK_PROCESS_CPUTIME_ID);
9485#endif
9486
9487#if defined(CLOCK_THREAD_CPUTIME_ID) && defined(CLOCKID2NUM)
9488 /* see Process.clock_gettime */
9489 rb_define_const(rb_mProcess, "CLOCK_THREAD_CPUTIME_ID", CLOCKID2NUM(CLOCK_THREAD_CPUTIME_ID));
9490#elif defined(RUBY_CLOCK_THREAD_CPUTIME_ID)
9491 rb_define_const(rb_mProcess, "CLOCK_THREAD_CPUTIME_ID", RUBY_CLOCK_THREAD_CPUTIME_ID);
9492#endif
9493
9494#ifdef CLOCKID2NUM
9495#ifdef CLOCK_VIRTUAL
9496 /* see Process.clock_gettime */
9497 rb_define_const(rb_mProcess, "CLOCK_VIRTUAL", CLOCKID2NUM(CLOCK_VIRTUAL));
9498#endif
9499#ifdef CLOCK_PROF
9500 /* see Process.clock_gettime */
9501 rb_define_const(rb_mProcess, "CLOCK_PROF", CLOCKID2NUM(CLOCK_PROF));
9502#endif
9503#ifdef CLOCK_REALTIME_FAST
9504 /* see Process.clock_gettime */
9505 rb_define_const(rb_mProcess, "CLOCK_REALTIME_FAST", CLOCKID2NUM(CLOCK_REALTIME_FAST));
9506#endif
9507#ifdef CLOCK_REALTIME_PRECISE
9508 /* see Process.clock_gettime */
9509 rb_define_const(rb_mProcess, "CLOCK_REALTIME_PRECISE", CLOCKID2NUM(CLOCK_REALTIME_PRECISE));
9510#endif
9511#ifdef CLOCK_REALTIME_COARSE
9512 /* see Process.clock_gettime */
9513 rb_define_const(rb_mProcess, "CLOCK_REALTIME_COARSE", CLOCKID2NUM(CLOCK_REALTIME_COARSE));
9514#endif
9515#ifdef CLOCK_REALTIME_ALARM
9516 /* see Process.clock_gettime */
9517 rb_define_const(rb_mProcess, "CLOCK_REALTIME_ALARM", CLOCKID2NUM(CLOCK_REALTIME_ALARM));
9518#endif
9519#ifdef CLOCK_MONOTONIC_FAST
9520 /* see Process.clock_gettime */
9521 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_FAST", CLOCKID2NUM(CLOCK_MONOTONIC_FAST));
9522#endif
9523#ifdef CLOCK_MONOTONIC_PRECISE
9524 /* see Process.clock_gettime */
9525 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_PRECISE", CLOCKID2NUM(CLOCK_MONOTONIC_PRECISE));
9526#endif
9527#ifdef CLOCK_MONOTONIC_RAW
9528 /* see Process.clock_gettime */
9529 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_RAW", CLOCKID2NUM(CLOCK_MONOTONIC_RAW));
9530#endif
9531#ifdef CLOCK_MONOTONIC_RAW_APPROX
9532 /* see Process.clock_gettime */
9533 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_RAW_APPROX", CLOCKID2NUM(CLOCK_MONOTONIC_RAW_APPROX));
9534#endif
9535#ifdef CLOCK_MONOTONIC_COARSE
9536 /* see Process.clock_gettime */
9537 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_COARSE", CLOCKID2NUM(CLOCK_MONOTONIC_COARSE));
9538#endif
9539#ifdef CLOCK_BOOTTIME
9540 /* see Process.clock_gettime */
9541 rb_define_const(rb_mProcess, "CLOCK_BOOTTIME", CLOCKID2NUM(CLOCK_BOOTTIME));
9542#endif
9543#ifdef CLOCK_BOOTTIME_ALARM
9544 /* see Process.clock_gettime */
9545 rb_define_const(rb_mProcess, "CLOCK_BOOTTIME_ALARM", CLOCKID2NUM(CLOCK_BOOTTIME_ALARM));
9546#endif
9547#ifdef CLOCK_UPTIME
9548 /* see Process.clock_gettime */
9549 rb_define_const(rb_mProcess, "CLOCK_UPTIME", CLOCKID2NUM(CLOCK_UPTIME));
9550#endif
9551#ifdef CLOCK_UPTIME_FAST
9552 /* see Process.clock_gettime */
9553 rb_define_const(rb_mProcess, "CLOCK_UPTIME_FAST", CLOCKID2NUM(CLOCK_UPTIME_FAST));
9554#endif
9555#ifdef CLOCK_UPTIME_PRECISE
9556 /* see Process.clock_gettime */
9557 rb_define_const(rb_mProcess, "CLOCK_UPTIME_PRECISE", CLOCKID2NUM(CLOCK_UPTIME_PRECISE));
9558#endif
9559#ifdef CLOCK_UPTIME_RAW
9560 /* see Process.clock_gettime */
9561 rb_define_const(rb_mProcess, "CLOCK_UPTIME_RAW", CLOCKID2NUM(CLOCK_UPTIME_RAW));
9562#endif
9563#ifdef CLOCK_UPTIME_RAW_APPROX
9564 /* see Process.clock_gettime */
9565 rb_define_const(rb_mProcess, "CLOCK_UPTIME_RAW_APPROX", CLOCKID2NUM(CLOCK_UPTIME_RAW_APPROX));
9566#endif
9567#ifdef CLOCK_SECOND
9568 /* see Process.clock_gettime */
9569 rb_define_const(rb_mProcess, "CLOCK_SECOND", CLOCKID2NUM(CLOCK_SECOND));
9570#endif
9571#ifdef CLOCK_TAI
9572 /* see Process.clock_gettime */
9573 rb_define_const(rb_mProcess, "CLOCK_TAI", CLOCKID2NUM(CLOCK_TAI));
9574#endif
9575#endif
9576 rb_define_module_function(rb_mProcess, "clock_gettime", rb_clock_gettime, -1);
9577 rb_define_module_function(rb_mProcess, "clock_getres", rb_clock_getres, -1);
9578
9579#if defined(HAVE_TIMES) || defined(_WIN32)
9580 rb_cProcessTms = rb_struct_define_under(rb_mProcess, "Tms", "utime", "stime", "cutime", "cstime", NULL);
9581#if 0 /* for RDoc */
9582 /* user time used in this process */
9583 rb_define_attr(rb_cProcessTms, "utime", TRUE, TRUE);
9584 /* system time used in this process */
9585 rb_define_attr(rb_cProcessTms, "stime", TRUE, TRUE);
9586 /* user time used in the child processes */
9587 rb_define_attr(rb_cProcessTms, "cutime", TRUE, TRUE);
9588 /* system time used in the child processes */
9589 rb_define_attr(rb_cProcessTms, "cstime", TRUE, TRUE);
9590#endif
9591#endif
9592
9593 SAVED_USER_ID = geteuid();
9594 SAVED_GROUP_ID = getegid();
9595
9596 rb_mProcUID = rb_define_module_under(rb_mProcess, "UID");
9597 rb_mProcGID = rb_define_module_under(rb_mProcess, "GID");
9598
9599 rb_define_module_function(rb_mProcUID, "rid", proc_getuid, 0);
9600 rb_define_module_function(rb_mProcGID, "rid", proc_getgid, 0);
9601 rb_define_module_function(rb_mProcUID, "eid", proc_geteuid, 0);
9602 rb_define_module_function(rb_mProcGID, "eid", proc_getegid, 0);
9603 rb_define_module_function(rb_mProcUID, "change_privilege", p_uid_change_privilege, 1);
9604 rb_define_module_function(rb_mProcGID, "change_privilege", p_gid_change_privilege, 1);
9605 rb_define_module_function(rb_mProcUID, "grant_privilege", p_uid_grant_privilege, 1);
9606 rb_define_module_function(rb_mProcGID, "grant_privilege", p_gid_grant_privilege, 1);
9607 rb_define_alias(rb_singleton_class(rb_mProcUID), "eid=", "grant_privilege");
9608 rb_define_alias(rb_singleton_class(rb_mProcGID), "eid=", "grant_privilege");
9609 rb_define_module_function(rb_mProcUID, "re_exchange", p_uid_exchange, 0);
9610 rb_define_module_function(rb_mProcGID, "re_exchange", p_gid_exchange, 0);
9611 rb_define_module_function(rb_mProcUID, "re_exchangeable?", p_uid_exchangeable, 0);
9612 rb_define_module_function(rb_mProcGID, "re_exchangeable?", p_gid_exchangeable, 0);
9613 rb_define_module_function(rb_mProcUID, "sid_available?", p_uid_have_saved_id, 0);
9614 rb_define_module_function(rb_mProcGID, "sid_available?", p_gid_have_saved_id, 0);
9615 rb_define_module_function(rb_mProcUID, "switch", p_uid_switch, 0);
9616 rb_define_module_function(rb_mProcGID, "switch", p_gid_switch, 0);
9617#ifdef p_uid_from_name
9618 rb_define_module_function(rb_mProcUID, "from_name", p_uid_from_name, 1);
9619#endif
9620#ifdef p_gid_from_name
9621 rb_define_module_function(rb_mProcGID, "from_name", p_gid_from_name, 1);
9622#endif
9623
9624 rb_mProcID_Syscall = rb_define_module_under(rb_mProcess, "Sys");
9625
9626 rb_define_module_function(rb_mProcID_Syscall, "getuid", proc_getuid, 0);
9627 rb_define_module_function(rb_mProcID_Syscall, "geteuid", proc_geteuid, 0);
9628 rb_define_module_function(rb_mProcID_Syscall, "getgid", proc_getgid, 0);
9629 rb_define_module_function(rb_mProcID_Syscall, "getegid", proc_getegid, 0);
9630
9631 rb_define_module_function(rb_mProcID_Syscall, "setuid", p_sys_setuid, 1);
9632 rb_define_module_function(rb_mProcID_Syscall, "setgid", p_sys_setgid, 1);
9633
9634 rb_define_module_function(rb_mProcID_Syscall, "setruid", p_sys_setruid, 1);
9635 rb_define_module_function(rb_mProcID_Syscall, "setrgid", p_sys_setrgid, 1);
9636
9637 rb_define_module_function(rb_mProcID_Syscall, "seteuid", p_sys_seteuid, 1);
9638 rb_define_module_function(rb_mProcID_Syscall, "setegid", p_sys_setegid, 1);
9639
9640 rb_define_module_function(rb_mProcID_Syscall, "setreuid", p_sys_setreuid, 2);
9641 rb_define_module_function(rb_mProcID_Syscall, "setregid", p_sys_setregid, 2);
9642
9643 rb_define_module_function(rb_mProcID_Syscall, "setresuid", p_sys_setresuid, 3);
9644 rb_define_module_function(rb_mProcID_Syscall, "setresgid", p_sys_setresgid, 3);
9645 rb_define_module_function(rb_mProcID_Syscall, "issetugid", p_sys_issetugid, 0);
9646}
9647
9648void
9649Init_process(void)
9650{
9651#define define_id(name) id_##name = rb_intern_const(#name)
9652 define_id(in);
9653 define_id(out);
9654 define_id(err);
9655 define_id(pid);
9656 define_id(uid);
9657 define_id(gid);
9658 define_id(close);
9659 define_id(child);
9660#ifdef HAVE_SETPGID
9661 define_id(pgroup);
9662#endif
9663#ifdef _WIN32
9664 define_id(new_pgroup);
9665#endif
9666 define_id(unsetenv_others);
9667 define_id(chdir);
9668 define_id(umask);
9669 define_id(close_others);
9670 define_id(nanosecond);
9671 define_id(microsecond);
9672 define_id(millisecond);
9673 define_id(second);
9674 define_id(float_microsecond);
9675 define_id(float_millisecond);
9676 define_id(float_second);
9677 define_id(GETTIMEOFDAY_BASED_CLOCK_REALTIME);
9678 define_id(TIME_BASED_CLOCK_REALTIME);
9679#ifdef CLOCK_REALTIME
9680 define_id(CLOCK_REALTIME);
9681#endif
9682#ifdef CLOCK_MONOTONIC
9683 define_id(CLOCK_MONOTONIC);
9684#endif
9685#ifdef CLOCK_PROCESS_CPUTIME_ID
9686 define_id(CLOCK_PROCESS_CPUTIME_ID);
9687#endif
9688#ifdef CLOCK_THREAD_CPUTIME_ID
9689 define_id(CLOCK_THREAD_CPUTIME_ID);
9690#endif
9691#ifdef HAVE_TIMES
9692 define_id(TIMES_BASED_CLOCK_MONOTONIC);
9693 define_id(TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID);
9694#endif
9695#ifdef RUSAGE_SELF
9696 define_id(GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID);
9697#endif
9698 define_id(CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID);
9699#ifdef __APPLE__
9700 define_id(MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC);
9701#endif
9702 define_id(hertz);
9703
9704 InitVM(process);
9705}
#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:2780
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1501
VALUE rb_define_module(const char *name)
Defines a top-level module.
Definition class.c:1583
VALUE rb_define_module_under(VALUE outer, const char *name)
Defines a module under the namespace of outer.
Definition class.c:1611
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition class.c:2828
void rb_define_attr(VALUE klass, const char *name, int read, int write)
Defines public accessor method(s) for an attribute.
Definition class.c:2834
void rb_undef_method(VALUE klass, const char *name)
Defines an undef of a method.
Definition class.c:2649
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:942
#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:681
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:4355
VALUE rb_mProcess
Process module.
Definition process.c:8723
VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
Allocates, then initialises an instance of the given class.
Definition object.c:2131
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:174
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1264
VALUE rb_to_int(VALUE val)
Identical to rb_check_to_int(), except it raises in case of conversion mismatch.
Definition object.c:3188
#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:4437
VALUE rb_f_exit(int argc, const VALUE *argv)
Identical to rb_exit(), except how arguments are passed.
Definition process.c:4368
int rb_cloexec_dup2(int oldfd, int newfd)
Identical to rb_cloexec_dup(), except you can specify the destination file descriptor.
Definition io.c:374
void rb_update_max_fd(int fd)
Informs the interpreter that the passed fd can be the max.
Definition io.c:248
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Opens a file that closes on exec.
Definition io.c:328
void rb_close_before_exec(int lowfd, int maxhint, VALUE noclose_fds)
Closes everything.
int rb_reserved_fd_p(int fd)
Queries if the given FD is reserved or not.
int rb_pipe(int *pipes)
This is an rb_cloexec_pipe() + rb_update_max_fd() combo.
Definition io.c:7381
int rb_cloexec_fcntl_dupfd(int fd, int minfd)
Duplicates a file descriptor with closing on exec.
Definition io.c:461
int rb_cloexec_dup(int oldfd)
Identical to rb_cloexec_fcntl_dupfd(), except it implies minfd is 3.
Definition io.c:367
int rb_proc_exec(const char *cmd)
Executes a shell command.
Definition process.c: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:4614
void rb_syswait(rb_pid_t pid)
This is a shorthand of rb_waitpid without status and flags.
Definition process.c:4485
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:4620
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:429
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
Definition string.c:4107
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:2064
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:3465
#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:1339
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition string.c:1846
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:2309
void rb_str_set_len(VALUE str, long len)
Overwrites the length of the string.
Definition string.c:3697
VALUE rb_check_string_type(VALUE obj)
Try converting an object to its stringised representation using its to_str method,...
Definition string.c:3262
#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:3056
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition string.c:2036
#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:3723
#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:1389
void rb_thread_wait_for(struct timeval time)
Identical to rb_thread_sleep(), except it takes struct timeval instead.
Definition thread.c:1421
void rb_thread_check_ints(void)
Checks for interrupts.
Definition thread.c:1442
void rb_thread_atfork(void)
A pthread_atfork(3posix)-like API.
Definition thread.c:4989
VALUE rb_thread_local_aset(VALUE thread, ID key, VALUE val)
This badly named function writes to a Fiber local storage.
Definition thread.c:3871
#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:1465
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:2100
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1383
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition symbol.c:1117
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:972
int rb_io_modestr_oflags(const char *modestr)
Identical to rb_io_modestr_fmode(), except it returns a mixture of O_ flags.
Definition io.c:6598
#define GetOpenFile
This is an old name of RB_IO_POINTER.
Definition io.h:442
VALUE rb_io_check_io(VALUE io)
Try converting an object to its IO representation using its to_io method, if any.
Definition io.c:817
int len
Length of the buffer.
Definition io.h:8
void * rb_thread_call_without_gvl2(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
Identical to rb_thread_call_without_gvl(), except it does not interface with signals etc.
Definition thread.c:1686
void * rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
Allows the passed function to run in parallel with other Ruby threads.
#define RB_NUM2INT
Just another name of rb_num2int_inline.
Definition int.h:38
#define RB_INT2NUM
Just another name of rb_int2num_inline.
Definition int.h:37
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
Shim for block function parameters.
Definition iterator.h:58
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1372
void rb_marshal_define_compat(VALUE newclass, VALUE oldclass, VALUE(*dumper)(VALUE), VALUE(*loader)(VALUE, VALUE))
Marshal format compatibility layer.
Definition marshal.c:137
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:360
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
#define NUM2MODET
Converts a C's mode_t into an instance of rb_cInteger.
Definition mode_t.h:28
VALUE rb_thread_create(type *q, void *w)
Creates a rb_cThread instance.
VALUE rb_block_call(VALUE q, ID w, int e, const VALUE *r, type *t, VALUE y)
Call a method with a block.
void rb_define_virtual_variable(const char *q, type *w, void_type *e)
Define a function-backended global variable.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
#define PIDT2NUM
Converts a C's pid_t into an instance of rb_cInteger.
Definition pid_t.h:28
#define NUM2PIDT
Converts an instance of rb_cNumeric into C's pid_t.
Definition pid_t.h:33
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:281
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:386
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define RUBY_DEFAULT_FREE
This is a value you can set to RData::dfree.
Definition rdata.h:78
#define RHASH_SIZE(h)
Queries the size of the hash.
Definition rhash.h:69
#define RHASH_EMPTY_P(h)
Checks if the hash is empty.
Definition rhash.h:79
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
Definition rtypeddata.h:79
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:515
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:497
const char * rb_class2name(VALUE klass)
Queries the name of the passed class.
Definition variable.c:499
#define FilePathValue(v)
Ensures that the parameter object is a path.
Definition ruby.h:90
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
#define InitVM(ext)
This macro is for internal use.
Definition ruby.h:231
Scheduler APIs.
VALUE rb_fiber_scheduler_current(void)
Identical to rb_fiber_scheduler_get(), except it also returns RUBY_Qnil in case of a blocking fiber.
Definition scheduler.c:458
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:528
VALUE rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags)
Non-blocking waitpid.
Definition scheduler.c:598
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
#define RTEST
This is an old name of RB_TEST.
Defines old _.
#define _(args)
This was a transition path from K&R to ANSI.
Definition stdarg.h:35
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:203
const char * wrap_struct_name
Name of structs of this kind.
Definition rtypeddata.h:210
Ruby's IO, metadata and buffers.
Definition io.h:295
VALUE tied_io_for_writing
Duplex IO object, if set.
Definition io.h:345
int fd
file descriptor.
Definition io.h:306
Definition st.h:79
Definition win32.h:721
#define UIDT2NUM
Converts a C's uid_t into an instance of rb_cInteger.
Definition uid_t.h:28
#define NUM2UIDT
Converts an instance of rb_cNumeric into C's uid_t.
Definition uid_t.h:33
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static enum ruby_value_type RB_BUILTIN_TYPE(VALUE obj)
Queries the type of the object.
Definition value_type.h:182
static void Check_Type(VALUE v, enum ruby_value_type t)
Identical to RB_TYPE_P(), except it raises exceptions on predication failure.
Definition value_type.h:433
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376