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