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