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