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