Ruby 4.1.0dev (2026-09-27 revision 25f659ba9f8b541331b56d6959167a4805892a14)
process.c (25f659ba9f8b541331b56d6959167a4805892a14)
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#endif
1591
1592#if defined(HAVE_WORKING_FORK)
1593
1594COMPILER_WARNING_PUSH
1595#if __has_warning("-Wdeprecated-declarations") || RBIMPL_COMPILER_IS(GCC)
1596COMPILER_WARNING_IGNORED(-Wdeprecated-declarations)
1597#endif
1598static inline rb_pid_t
1599rb_fork(void)
1600{
1601 return fork();
1602}
1603COMPILER_WARNING_POP
1604
1605/* try_with_sh and exec_with_sh should be async-signal-safe. Actually it is.*/
1606#define try_with_sh(err, prog, argv, envp) ((err == ENOEXEC) ? exec_with_sh((prog), (argv), (envp)) : (void)0)
1607static void
1608exec_with_sh(const char *prog, char **argv, char **envp)
1609{
1610 *argv = (char *)prog;
1611 *--argv = (char *)"sh";
1612 if (envp)
1613 execve("/bin/sh", argv, envp); /* async-signal-safe */
1614 else
1615 execv("/bin/sh", argv); /* async-signal-safe (since SUSv4) */
1616}
1617
1618#else
1619#define try_with_sh(err, prog, argv, envp) (void)0
1620#endif
1621
1622/* This function should be async-signal-safe. Actually it is. */
1623static int
1624proc_exec_cmd(const char *prog, VALUE argv_str, VALUE envp_str)
1625{
1626 char **argv;
1627#ifndef _WIN32
1628 char **envp;
1629 int err;
1630#endif
1631
1632 argv = ARGVSTR2ARGV(argv_str);
1633
1634 if (!prog) {
1635 return ENOENT;
1636 }
1637
1638#ifdef _WIN32
1639 rb_w32_uaspawn(P_OVERLAY, prog, argv);
1640 return errno;
1641#else
1642 envp = envp_str ? RB_IMEMO_TMPBUF_PTR(envp_str) : NULL;
1643 if (envp_str)
1644 execve(prog, argv, envp); /* async-signal-safe */
1645 else
1646 execv(prog, argv); /* async-signal-safe (since SUSv4) */
1647 err = errno;
1648 try_with_sh(err, prog, argv, envp); /* try_with_sh() is async-signal-safe. */
1649 return err;
1650#endif
1651}
1652
1653/* This function should be async-signal-safe. Actually it is. */
1654static int
1655proc_exec_sh(const char *str, VALUE envp_str)
1656{
1657 const char *s;
1658
1659 s = str;
1660 while (*s == ' ' || *s == '\t' || *s == '\n')
1661 s++;
1662
1663 if (!*s) {
1664 return ENOENT;
1665 }
1666
1667#ifdef _WIN32
1668 rb_w32_uspawn(P_OVERLAY, (char *)str, 0);
1669#else
1670 if (envp_str)
1671 execle("/bin/sh", "sh", "-c", str, (char *)NULL, RB_IMEMO_TMPBUF_PTR(envp_str)); /* async-signal-safe */
1672 else
1673 execl("/bin/sh", "sh", "-c", str, (char *)NULL); /* async-signal-safe (since SUSv4) */
1674#endif /* _WIN32 */
1675 return errno;
1676}
1677
1678int
1679rb_proc_exec(const char *str)
1680{
1681 int ret;
1682 before_exec();
1683 ret = proc_exec_sh(str, Qfalse);
1684 after_exec();
1685 errno = ret;
1686 return -1;
1687}
1688
1689static void
1690mark_exec_arg(void *ptr)
1691{
1692 struct rb_execarg *eargp = ptr;
1693 if (eargp->use_shell)
1694 rb_gc_mark(eargp->invoke.sh.shell_script);
1695 else {
1696 rb_gc_mark(eargp->invoke.cmd.command_name);
1697 rb_gc_mark(eargp->invoke.cmd.command_abspath);
1698 rb_gc_mark(eargp->invoke.cmd.argv_str);
1699 rb_gc_mark(eargp->invoke.cmd.argv_buf);
1700 }
1701 rb_gc_mark(eargp->redirect_fds);
1702 rb_gc_mark(eargp->envp_str);
1703 rb_gc_mark(eargp->envp_buf);
1704 rb_gc_mark(eargp->dup2_tmpbuf);
1705 rb_gc_mark(eargp->rlimit_limits);
1706 rb_gc_mark(eargp->fd_dup2);
1707 rb_gc_mark(eargp->fd_close);
1708 rb_gc_mark(eargp->fd_open);
1709 rb_gc_mark(eargp->fd_dup2_child);
1710 rb_gc_mark(eargp->env_modification);
1711 rb_gc_mark(eargp->path_env);
1712 rb_gc_mark(eargp->chdir_dir);
1713}
1714
1715static size_t
1716memsize_exec_arg(const void *ptr)
1717{
1718 return sizeof(struct rb_execarg);
1719}
1720
1721static const rb_data_type_t exec_arg_data_type = {
1722 "exec_arg",
1723 {mark_exec_arg, RUBY_TYPED_DEFAULT_FREE, memsize_exec_arg},
1724 0, 0, RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_EMBEDDABLE
1725};
1726
1727#ifdef _WIN32
1728# define DEFAULT_PROCESS_ENCODING rb_utf8_encoding()
1729#endif
1730#ifdef DEFAULT_PROCESS_ENCODING
1731# define EXPORT_STR(str) rb_str_export_to_enc((str), DEFAULT_PROCESS_ENCODING)
1732# define EXPORT_DUP(str) export_dup(str)
1733static VALUE
1734export_dup(VALUE str)
1735{
1736 VALUE newstr = EXPORT_STR(str);
1737 if (newstr == str) newstr = rb_str_dup(str);
1738 return newstr;
1739}
1740#else
1741# define EXPORT_STR(str) (str)
1742# define EXPORT_DUP(str) rb_str_dup(str)
1743#endif
1744
1745#if !defined(HAVE_WORKING_FORK) && defined(HAVE_SPAWNV)
1746# define USE_SPAWNV 1
1747#else
1748# define USE_SPAWNV 0
1749#endif
1750#ifndef P_NOWAIT
1751# define P_NOWAIT _P_NOWAIT
1752#endif
1753
1754#if USE_SPAWNV
1755static rb_pid_t
1756proc_spawn_cmd(char **argv, VALUE prog, struct rb_execarg *eargp)
1757{
1758 rb_pid_t pid = -1;
1759
1760 if (argv[0]) {
1761 DWORD flags = 0;
1762 if (eargp->new_pgroup_given && eargp->new_pgroup_flag) {
1763 flags = CREATE_NEW_PROCESS_GROUP;
1764 }
1765 pid = rb_w32_uaspawn_flags(P_NOWAIT, prog ? RSTRING_PTR(prog) : 0, argv, flags);
1766 }
1767 return pid;
1768}
1769
1770#define proc_spawn_sh(str) rb_w32_uspawn(P_NOWAIT, (str), 0)
1771#endif
1772
1773static VALUE
1774hide_obj(VALUE obj)
1775{
1776 RBASIC_CLEAR_CLASS(obj);
1777 return obj;
1778}
1779
1780static VALUE
1781check_exec_redirect_fd(VALUE v, int iskey)
1782{
1783 VALUE tmp;
1784 int fd;
1785 if (FIXNUM_P(v)) {
1786 fd = FIX2INT(v);
1787 }
1788 else if (SYMBOL_P(v)) {
1789 ID id = rb_check_id(&v);
1790 if (id == id_in)
1791 fd = 0;
1792 else if (id == id_out)
1793 fd = 1;
1794 else if (id == id_err)
1795 fd = 2;
1796 else
1797 goto wrong;
1798 }
1799 else if (!NIL_P(tmp = rb_io_check_io(v))) {
1800 rb_io_t *fptr;
1801 GetOpenFile(tmp, fptr);
1802 if (fptr->tied_io_for_writing)
1803 rb_raise(rb_eArgError, "duplex IO redirection");
1804 fd = fptr->fd;
1805 }
1806 else {
1807 goto wrong;
1808 }
1809 if (fd < 0) {
1810 rb_raise(rb_eArgError, "negative file descriptor");
1811 }
1812#ifdef _WIN32
1813 else if (fd >= 3 && iskey) {
1814 rb_raise(rb_eArgError, "wrong file descriptor (%d)", fd);
1815 }
1816#endif
1817 return INT2FIX(fd);
1818
1819 wrong:
1820 rb_raise(rb_eArgError, "wrong exec redirect");
1822}
1823
1824static VALUE
1825check_exec_redirect1(VALUE ary, VALUE key, VALUE param)
1826{
1827 if (ary == Qfalse) {
1828 ary = hide_obj(rb_ary_new());
1829 }
1830 if (!RB_TYPE_P(key, T_ARRAY)) {
1831 VALUE fd = check_exec_redirect_fd(key, !NIL_P(param));
1832 rb_ary_push(ary, hide_obj(rb_assoc_new(fd, param)));
1833 }
1834 else {
1835 int i;
1836 for (i = 0 ; i < RARRAY_LEN(key); i++) {
1837 VALUE v = RARRAY_AREF(key, i);
1838 VALUE fd = check_exec_redirect_fd(v, !NIL_P(param));
1839 rb_ary_push(ary, hide_obj(rb_assoc_new(fd, param)));
1840 }
1841 }
1842 return ary;
1843}
1844
1845static void
1846check_exec_redirect(VALUE key, VALUE val, struct rb_execarg *eargp)
1847{
1848 VALUE param;
1849 VALUE path, flags, perm;
1850 VALUE tmp;
1851 ID id;
1852
1853 switch (TYPE(val)) {
1854 case T_SYMBOL:
1855 id = rb_check_id(&val);
1856 if (id == id_close) {
1857 param = Qnil;
1858 eargp->fd_close = check_exec_redirect1(eargp->fd_close, key, param);
1859 }
1860 else if (id == id_in) {
1861 param = INT2FIX(0);
1862 eargp->fd_dup2 = check_exec_redirect1(eargp->fd_dup2, key, param);
1863 }
1864 else if (id == id_out) {
1865 param = INT2FIX(1);
1866 eargp->fd_dup2 = check_exec_redirect1(eargp->fd_dup2, key, param);
1867 }
1868 else if (id == id_err) {
1869 param = INT2FIX(2);
1870 eargp->fd_dup2 = check_exec_redirect1(eargp->fd_dup2, key, param);
1871 }
1872 else {
1873 rb_raise(rb_eArgError, "wrong exec redirect symbol: %"PRIsVALUE,
1874 val);
1875 }
1876 break;
1877
1878 case T_FILE:
1879 io:
1880 val = check_exec_redirect_fd(val, 0);
1881 /* fall through */
1882 case T_FIXNUM:
1883 param = val;
1884 eargp->fd_dup2 = check_exec_redirect1(eargp->fd_dup2, key, param);
1885 break;
1886
1887 case T_ARRAY:
1888 path = rb_ary_entry(val, 0);
1889 if (RARRAY_LEN(val) == 2 && SYMBOL_P(path) &&
1890 path == ID2SYM(id_child)) {
1891 param = check_exec_redirect_fd(rb_ary_entry(val, 1), 0);
1892 eargp->fd_dup2_child = check_exec_redirect1(eargp->fd_dup2_child, key, param);
1893 }
1894 else {
1895 FilePathValue(path);
1896 flags = rb_ary_entry(val, 1);
1897 if (NIL_P(flags))
1898 flags = INT2NUM(O_RDONLY);
1899 else if (RB_TYPE_P(flags, T_STRING))
1901 else
1902 flags = rb_to_int(flags);
1903 perm = rb_ary_entry(val, 2);
1904 perm = NIL_P(perm) ? INT2FIX(0644) : rb_to_int(perm);
1905 param = hide_obj(rb_ary_new3(4, hide_obj(EXPORT_DUP(path)),
1906 flags, perm, Qnil));
1907 eargp->fd_open = check_exec_redirect1(eargp->fd_open, key, param);
1908 }
1909 break;
1910
1911 case T_STRING:
1912 path = val;
1913 FilePathValue(path);
1914 if (RB_TYPE_P(key, T_FILE))
1915 key = check_exec_redirect_fd(key, 1);
1916 if (FIXNUM_P(key) && (FIX2INT(key) == 1 || FIX2INT(key) == 2))
1917 flags = INT2NUM(O_WRONLY|O_CREAT|O_TRUNC);
1918 else if (RB_TYPE_P(key, T_ARRAY)) {
1919 int i;
1920 for (i = 0; i < RARRAY_LEN(key); i++) {
1921 VALUE v = RARRAY_AREF(key, i);
1922 VALUE fd = check_exec_redirect_fd(v, 1);
1923 if (FIX2INT(fd) != 1 && FIX2INT(fd) != 2) break;
1924 }
1925 if (i == RARRAY_LEN(key))
1926 flags = INT2NUM(O_WRONLY|O_CREAT|O_TRUNC);
1927 else
1928 flags = INT2NUM(O_RDONLY);
1929 }
1930 else
1931 flags = INT2NUM(O_RDONLY);
1932 perm = INT2FIX(0644);
1933 param = hide_obj(rb_ary_new3(4, hide_obj(EXPORT_DUP(path)),
1934 flags, perm, Qnil));
1935 eargp->fd_open = check_exec_redirect1(eargp->fd_open, key, param);
1936 break;
1937
1938 default:
1939 tmp = val;
1940 val = rb_io_check_io(tmp);
1941 if (!NIL_P(val)) goto io;
1942 rb_raise(rb_eArgError, "wrong exec redirect action");
1943 }
1944
1945}
1946
1947#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
1948static int rlimit_type_by_sym(VALUE key);
1949
1950static void
1951rb_execarg_addopt_rlimit(struct rb_execarg *eargp, int rtype, VALUE val)
1952{
1953 VALUE ary = eargp->rlimit_limits;
1954 VALUE tmp, softlim, hardlim;
1955 if (eargp->rlimit_limits == Qfalse)
1956 ary = eargp->rlimit_limits = hide_obj(rb_ary_new());
1957 else
1958 ary = eargp->rlimit_limits;
1959 tmp = rb_check_array_type(val);
1960 if (!NIL_P(tmp)) {
1961 if (RARRAY_LEN(tmp) == 1)
1962 softlim = hardlim = rb_to_int(rb_ary_entry(tmp, 0));
1963 else if (RARRAY_LEN(tmp) == 2) {
1964 softlim = rb_to_int(rb_ary_entry(tmp, 0));
1965 hardlim = rb_to_int(rb_ary_entry(tmp, 1));
1966 }
1967 else {
1968 rb_raise(rb_eArgError, "wrong exec rlimit option");
1969 }
1970 }
1971 else {
1972 softlim = hardlim = rb_to_int(val);
1973 }
1974 tmp = hide_obj(rb_ary_new3(3, INT2NUM(rtype), softlim, hardlim));
1975 rb_ary_push(ary, tmp);
1976}
1977#endif
1978
1979#define TO_BOOL(val, name) (NIL_P(val) ? 0 : rb_bool_expected((val), name, TRUE))
1980int
1981rb_execarg_addopt(VALUE execarg_obj, VALUE key, VALUE val)
1982{
1983 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
1984
1985 ID id;
1986
1987 switch (TYPE(key)) {
1988 case T_SYMBOL:
1989#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
1990 {
1991 int rtype = rlimit_type_by_sym(key);
1992 if (rtype != -1) {
1993 rb_execarg_addopt_rlimit(eargp, rtype, val);
1994 RB_GC_GUARD(execarg_obj);
1995 return ST_CONTINUE;
1996 }
1997 }
1998#endif
1999 if (!(id = rb_check_id(&key))) return ST_STOP;
2000#ifdef HAVE_SETPGID
2001 if (id == id_pgroup) {
2002 rb_pid_t pgroup;
2003 if (eargp->pgroup_given) {
2004 rb_raise(rb_eArgError, "pgroup option specified twice");
2005 }
2006 if (!RTEST(val))
2007 pgroup = -1; /* asis(-1) means "don't call setpgid()". */
2008 else if (val == Qtrue)
2009 pgroup = 0; /* new process group. */
2010 else {
2011 pgroup = NUM2PIDT(val);
2012 if (pgroup < 0) {
2013 rb_raise(rb_eArgError, "negative process group ID : %ld", (long)pgroup);
2014 }
2015 }
2016 eargp->pgroup_given = 1;
2017 eargp->pgroup_pgid = pgroup;
2018 }
2019 else
2020#endif
2021#ifdef _WIN32
2022 if (id == id_new_pgroup) {
2023 if (eargp->new_pgroup_given) {
2024 rb_raise(rb_eArgError, "new_pgroup option specified twice");
2025 }
2026 eargp->new_pgroup_given = 1;
2027 eargp->new_pgroup_flag = TO_BOOL(val, "new_pgroup");
2028 }
2029 else
2030#endif
2031 if (id == id_unsetenv_others) {
2032 if (eargp->unsetenv_others_given) {
2033 rb_raise(rb_eArgError, "unsetenv_others option specified twice");
2034 }
2035 eargp->unsetenv_others_given = 1;
2036 eargp->unsetenv_others_do = TO_BOOL(val, "unsetenv_others");
2037 }
2038 else if (id == id_chdir) {
2039 if (eargp->chdir_given) {
2040 rb_raise(rb_eArgError, "chdir option specified twice");
2041 }
2042 FilePathValue(val);
2043 val = rb_str_encode_ospath(val);
2044 eargp->chdir_given = 1;
2045 eargp->chdir_dir = hide_obj(EXPORT_DUP(val));
2046 }
2047 else if (id == id_umask) {
2048 mode_t cmask = NUM2MODET(val);
2049 if (eargp->umask_given) {
2050 rb_raise(rb_eArgError, "umask option specified twice");
2051 }
2052 eargp->umask_given = 1;
2053 eargp->umask_mask = cmask;
2054 }
2055 else if (id == id_close_others) {
2056 if (eargp->close_others_given) {
2057 rb_raise(rb_eArgError, "close_others option specified twice");
2058 }
2059 eargp->close_others_given = 1;
2060 eargp->close_others_do = TO_BOOL(val, "close_others");
2061 }
2062 else if (id == id_in) {
2063 key = INT2FIX(0);
2064 goto redirect;
2065 }
2066 else if (id == id_out) {
2067 key = INT2FIX(1);
2068 goto redirect;
2069 }
2070 else if (id == id_err) {
2071 key = INT2FIX(2);
2072 goto redirect;
2073 }
2074 else if (id == id_uid) {
2075#ifdef HAVE_SETUID
2076 if (eargp->uid_given) {
2077 rb_raise(rb_eArgError, "uid option specified twice");
2078 }
2079 check_uid_switch();
2080 {
2081 eargp->uid = OBJ2UID(val);
2082 eargp->uid_given = 1;
2083 }
2084#else
2085 rb_raise(rb_eNotImpError,
2086 "uid option is unimplemented on this machine");
2087#endif
2088 }
2089 else if (id == id_gid) {
2090#ifdef HAVE_SETGID
2091 if (eargp->gid_given) {
2092 rb_raise(rb_eArgError, "gid option specified twice");
2093 }
2094 check_gid_switch();
2095 {
2096 eargp->gid = OBJ2GID(val);
2097 eargp->gid_given = 1;
2098 }
2099#else
2100 rb_raise(rb_eNotImpError,
2101 "gid option is unimplemented on this machine");
2102#endif
2103 }
2104 else if (id == id_exception) {
2105 if (eargp->exception_given) {
2106 rb_raise(rb_eArgError, "exception option specified twice");
2107 }
2108 eargp->exception_given = 1;
2109 eargp->exception = TO_BOOL(val, "exception");
2110 }
2111 else {
2112 return ST_STOP;
2113 }
2114 break;
2115
2116 case T_FIXNUM:
2117 case T_FILE:
2118 case T_ARRAY:
2119redirect:
2120 check_exec_redirect(key, val, eargp);
2121 break;
2122
2123 default:
2124 return ST_STOP;
2125 }
2126
2127 RB_GC_GUARD(execarg_obj);
2128 return ST_CONTINUE;
2129}
2130
2131static int
2132check_exec_options_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
2133{
2134 VALUE key = (VALUE)st_key;
2135 VALUE val = (VALUE)st_val;
2136 VALUE execarg_obj = (VALUE)arg;
2137 if (rb_execarg_addopt(execarg_obj, key, val) != ST_CONTINUE) {
2138 if (SYMBOL_P(key))
2139 rb_raise(rb_eArgError, "wrong exec option symbol: % "PRIsVALUE,
2140 key);
2141 rb_raise(rb_eArgError, "wrong exec option: %"PRIsVALUE, rb_obj_class(key));
2142 }
2143 return ST_CONTINUE;
2144}
2145
2146static int
2147check_exec_options_i_extract(st_data_t st_key, st_data_t st_val, st_data_t arg)
2148{
2149 VALUE key = (VALUE)st_key;
2150 VALUE val = (VALUE)st_val;
2151 VALUE *args = (VALUE *)arg;
2152 VALUE execarg_obj = args[0];
2153 if (rb_execarg_addopt(execarg_obj, key, val) != ST_CONTINUE) {
2154 VALUE nonopts = args[1];
2155 if (NIL_P(nonopts)) args[1] = nonopts = rb_hash_new();
2156 rb_hash_aset(nonopts, key, val);
2157 }
2158 return ST_CONTINUE;
2159}
2160
2161static int
2162check_exec_fds_1(struct rb_execarg *eargp, VALUE h, int maxhint, VALUE ary)
2163{
2164 long i;
2165
2166 if (ary != Qfalse) {
2167 for (i = 0; i < RARRAY_LEN(ary); i++) {
2168 VALUE elt = RARRAY_AREF(ary, i);
2169 int fd = FIX2INT(RARRAY_AREF(elt, 0));
2170 if (RTEST(rb_hash_lookup(h, INT2FIX(fd)))) {
2171 rb_raise(rb_eArgError, "fd %d specified twice", fd);
2172 }
2173 if (ary == eargp->fd_dup2)
2174 rb_hash_aset(h, INT2FIX(fd), Qtrue);
2175 else if (ary == eargp->fd_dup2_child)
2176 rb_hash_aset(h, INT2FIX(fd), RARRAY_AREF(elt, 1));
2177 else /* ary == eargp->fd_close */
2178 rb_hash_aset(h, INT2FIX(fd), INT2FIX(-1));
2179 if (maxhint < fd)
2180 maxhint = fd;
2181 if (ary == eargp->fd_dup2 || ary == eargp->fd_dup2_child) {
2182 fd = FIX2INT(RARRAY_AREF(elt, 1));
2183 if (maxhint < fd)
2184 maxhint = fd;
2185 }
2186 }
2187 }
2188 return maxhint;
2189}
2190
2191static VALUE
2192check_exec_fds(struct rb_execarg *eargp)
2193{
2194 VALUE h = rb_hash_new();
2195 VALUE ary;
2196 int maxhint = -1;
2197 long i;
2198
2199 maxhint = check_exec_fds_1(eargp, h, maxhint, eargp->fd_dup2);
2200 maxhint = check_exec_fds_1(eargp, h, maxhint, eargp->fd_close);
2201 maxhint = check_exec_fds_1(eargp, h, maxhint, eargp->fd_dup2_child);
2202
2203 if (eargp->fd_dup2_child) {
2204 ary = eargp->fd_dup2_child;
2205 for (i = 0; i < RARRAY_LEN(ary); i++) {
2206 VALUE elt = RARRAY_AREF(ary, i);
2207 int newfd = FIX2INT(RARRAY_AREF(elt, 0));
2208 int oldfd = FIX2INT(RARRAY_AREF(elt, 1));
2209 int lastfd = oldfd;
2210 VALUE val = rb_hash_lookup(h, INT2FIX(lastfd));
2211 long depth = 0;
2212 while (FIXNUM_P(val) && 0 <= FIX2INT(val)) {
2213 lastfd = FIX2INT(val);
2214 val = rb_hash_lookup(h, val);
2215 if (RARRAY_LEN(ary) < depth)
2216 rb_raise(rb_eArgError, "cyclic child fd redirection from %d", oldfd);
2217 depth++;
2218 }
2219 if (val != Qtrue)
2220 rb_raise(rb_eArgError, "child fd %d is not redirected", oldfd);
2221 if (oldfd != lastfd) {
2222 VALUE val2;
2223 rb_ary_store(elt, 1, INT2FIX(lastfd));
2224 rb_hash_aset(h, INT2FIX(newfd), INT2FIX(lastfd));
2225 val = INT2FIX(oldfd);
2226 while (FIXNUM_P(val2 = rb_hash_lookup(h, val))) {
2227 rb_hash_aset(h, val, INT2FIX(lastfd));
2228 val = val2;
2229 }
2230 }
2231 }
2232 }
2233
2234 eargp->close_others_maxhint = maxhint;
2235 return h;
2236}
2237
2238static void
2239rb_check_exec_options(VALUE opthash, VALUE execarg_obj)
2240{
2241 if (RHASH_EMPTY_P(opthash))
2242 return;
2243 rb_hash_stlike_foreach(opthash, check_exec_options_i, (st_data_t)execarg_obj);
2244}
2245
2246VALUE
2247rb_execarg_extract_options(VALUE execarg_obj, VALUE opthash)
2248{
2249 VALUE args[2];
2250 if (RHASH_EMPTY_P(opthash))
2251 return Qnil;
2252 args[0] = execarg_obj;
2253 args[1] = Qnil;
2254 rb_hash_stlike_foreach(opthash, check_exec_options_i_extract, (st_data_t)args);
2255 return args[1];
2256}
2257
2258#ifdef ENV_IGNORECASE
2259#define ENVMATCH(s1, s2) (STRCASECMP((s1), (s2)) == 0)
2260#else
2261#define ENVMATCH(n1, n2) (strcmp((n1), (n2)) == 0)
2262#endif
2263
2264static int
2265check_exec_env_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
2266{
2267 VALUE key = (VALUE)st_key;
2268 VALUE val = (VALUE)st_val;
2269 VALUE env = ((VALUE *)arg)[0];
2270 VALUE *path = &((VALUE *)arg)[1];
2271 char *k;
2272
2273 k = StringValueCStr(key);
2274 if (strchr(k, '='))
2275 rb_raise(rb_eArgError, "environment name contains a equal : %"PRIsVALUE, key);
2276
2277 if (!NIL_P(val))
2278 StringValueCStr(val);
2279
2280 key = EXPORT_STR(key);
2281 if (!NIL_P(val)) val = EXPORT_STR(val);
2282
2283 if (ENVMATCH(k, PATH_ENV)) {
2284 *path = val;
2285 }
2286 rb_ary_push(env, hide_obj(rb_assoc_new(key, val)));
2287
2288 return ST_CONTINUE;
2289}
2290
2291static VALUE
2292rb_check_exec_env(VALUE hash, VALUE *path)
2293{
2294 VALUE env[2];
2295
2296 env[0] = hide_obj(rb_ary_new());
2297 env[1] = Qfalse;
2298 rb_hash_stlike_foreach(hash, check_exec_env_i, (st_data_t)env);
2299 *path = env[1];
2300
2301 return env[0];
2302}
2303
2304static VALUE
2305rb_check_argv(int argc, VALUE *argv)
2306{
2307 VALUE tmp, prog;
2308 int i;
2309
2311
2312 prog = 0;
2313 tmp = rb_check_array_type(argv[0]);
2314 if (!NIL_P(tmp)) {
2315 if (RARRAY_LEN(tmp) != 2) {
2316 rb_raise(rb_eArgError, "wrong first argument");
2317 }
2318 prog = RARRAY_AREF(tmp, 0);
2319 argv[0] = RARRAY_AREF(tmp, 1);
2320 StringValue(prog);
2321 StringValueCStr(prog);
2322 prog = rb_str_new_frozen(prog);
2323 }
2324 for (i = 0; i < argc; i++) {
2325 StringValue(argv[i]);
2326 argv[i] = rb_str_new_frozen(argv[i]);
2327 StringValueCStr(argv[i]);
2328 }
2329 return prog;
2330}
2331
2332static VALUE
2333check_hash(VALUE obj)
2334{
2335 if (RB_SPECIAL_CONST_P(obj)) return Qnil;
2336 switch (RB_BUILTIN_TYPE(obj)) {
2337 case T_STRING:
2338 case T_ARRAY:
2339 return Qnil;
2340 default:
2341 break;
2342 }
2343 return rb_check_hash_type(obj);
2344}
2345
2346static VALUE
2347rb_exec_getargs(int *argc_p, VALUE **argv_p, int accept_shell, VALUE *env_ret, VALUE *opthash_ret)
2348{
2349 VALUE hash, prog;
2350
2351 if (0 < *argc_p) {
2352 hash = check_hash((*argv_p)[*argc_p-1]);
2353 if (!NIL_P(hash)) {
2354 *opthash_ret = hash;
2355 (*argc_p)--;
2356 }
2357 }
2358
2359 if (0 < *argc_p) {
2360 hash = check_hash((*argv_p)[0]);
2361 if (!NIL_P(hash)) {
2362 *env_ret = hash;
2363 (*argc_p)--;
2364 (*argv_p)++;
2365 }
2366 }
2367 prog = rb_check_argv(*argc_p, *argv_p);
2368 if (!prog) {
2369 prog = (*argv_p)[0];
2370 if (accept_shell && *argc_p == 1) {
2371 *argc_p = 0;
2372 *argv_p = 0;
2373 }
2374 }
2375 return prog;
2376}
2377
2378#ifndef _WIN32
2380 const char *ptr;
2381 size_t len;
2382};
2383
2384static int
2385compare_posix_sh(const void *key, const void *el)
2386{
2387 const struct string_part *word = key;
2388 int ret = strncmp(word->ptr, el, word->len);
2389 if (!ret && ((const char *)el)[word->len]) ret = -1;
2390 return ret;
2391}
2392#endif
2393
2394#define append_terminator(buf) rb_str_buf_cat(buf, "", 1) /* append '\0' */
2395
2396static void
2397rb_exec_fillarg(VALUE prog, int argc, VALUE *argv, VALUE env, VALUE opthash, VALUE execarg_obj)
2398{
2399 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2400 char fbuf[MAXPATHLEN];
2401
2402 MEMZERO(eargp, struct rb_execarg, 1);
2403
2404 if (!NIL_P(opthash)) {
2405 rb_check_exec_options(opthash, execarg_obj);
2406 }
2407 if (!NIL_P(env)) {
2408 env = rb_check_exec_env(env, &eargp->path_env);
2409 eargp->env_modification = env;
2410 }
2411
2412 prog = EXPORT_STR(prog);
2413 eargp->use_shell = argc == 0;
2414 if (eargp->use_shell)
2415 eargp->invoke.sh.shell_script = prog;
2416 else
2417 eargp->invoke.cmd.command_name = prog;
2418
2419#ifndef _WIN32
2420 if (eargp->use_shell) {
2421 static const char posix_sh_cmds[][9] = {
2422 "!", /* reserved */
2423 ".", /* special built-in */
2424 ":", /* special built-in */
2425 "break", /* special built-in */
2426 "case", /* reserved */
2427 "continue", /* special built-in */
2428 "do", /* reserved */
2429 "done", /* reserved */
2430 "elif", /* reserved */
2431 "else", /* reserved */
2432 "esac", /* reserved */
2433 "eval", /* special built-in */
2434 "exec", /* special built-in */
2435 "exit", /* special built-in */
2436 "export", /* special built-in */
2437 "fi", /* reserved */
2438 "for", /* reserved */
2439 "if", /* reserved */
2440 "in", /* reserved */
2441 "readonly", /* special built-in */
2442 "return", /* special built-in */
2443 "set", /* special built-in */
2444 "shift", /* special built-in */
2445 "then", /* reserved */
2446 "times", /* special built-in */
2447 "trap", /* special built-in */
2448 "unset", /* special built-in */
2449 "until", /* reserved */
2450 "while", /* reserved */
2451 };
2452 const char *p;
2453 const char *const s = rb_str_null_check(prog);
2454 const char *const e = RSTRING_END(prog);
2455 struct string_part first = {0, 0};
2456 int has_slash = 0;
2457 int has_meta = 0;
2458 /*
2459 * meta characters:
2460 *
2461 * * Pathname Expansion
2462 * ? Pathname Expansion
2463 * {} Grouping Commands
2464 * [] Pathname Expansion
2465 * <> Redirection
2466 * () Grouping Commands
2467 * ~ Tilde Expansion
2468 * & AND Lists, Asynchronous Lists
2469 * | OR Lists, Pipelines
2470 * \ Escape Character
2471 * $ Parameter Expansion
2472 * ; Sequential Lists
2473 * ' Single-Quotes
2474 * ` Command Substitution
2475 * " Double-Quotes
2476 * \n Lists
2477 *
2478 * # Comment
2479 * = Assignment preceding command name
2480 * % (used in Parameter Expansion)
2481 */
2482 for (p = s; p < e; p++) {
2483 if (*p == ' ' || *p == '\t') {
2484 if (first.ptr && !first.len) first.len = p - first.ptr;
2485 }
2486 else {
2487 if (!first.ptr) first.ptr = p;
2488 }
2489 if (!has_meta && strchr("*?{}[]<>()~&|\\$;'`\"\n#", *p))
2490 has_meta = 1;
2491 if (!first.len) {
2492 if (*p == '=') {
2493 has_meta = 1;
2494 }
2495 else if (*p == '/') {
2496 has_slash = 1;
2497 }
2498 }
2499 if (has_meta)
2500 break;
2501 }
2502 if (!has_meta) {
2503 if (!first.ptr) first.ptr = e;
2504 if (!first.len) first.len = p - first.ptr;
2505 if (first.len > 0 && first.len <= sizeof(posix_sh_cmds[0]) &&
2506 !has_slash &&
2507 bsearch(&first, posix_sh_cmds, numberof(posix_sh_cmds), sizeof(posix_sh_cmds[0]), compare_posix_sh))
2508 has_meta = 1;
2509 }
2510 if (!has_meta) {
2511 /* avoid shell since no shell meta character found. */
2512 eargp->use_shell = 0;
2513 }
2514 if (!eargp->use_shell) {
2515 VALUE argv_buf;
2516 argv_buf = hide_obj(rb_str_buf_new(0));
2517 rb_str_buf_cat(argv_buf, first.ptr, first.len);
2518 append_terminator(argv_buf);
2519 for (p = first.ptr + first.len; p < e;) {
2520 while (p < e && (*p == ' ' || *p == '\t'))
2521 p++;
2522 if (p < e) {
2523 const char *w = p;
2524 while (p < e && *p != ' ' && *p != '\t')
2525 p++;
2526 rb_str_buf_cat(argv_buf, w, p-w);
2527 append_terminator(argv_buf);
2528 }
2529 }
2530 eargp->invoke.cmd.argv_buf = argv_buf;
2531 eargp->invoke.cmd.command_name =
2532 hide_obj(rb_str_subseq(argv_buf, 0, first.len));
2533 rb_enc_copy(eargp->invoke.cmd.command_name, prog);
2534 }
2535 }
2536#endif
2537
2538 if (!eargp->use_shell) {
2539 const char *abspath;
2540 const char *path_env = 0;
2541 if (RTEST(eargp->path_env)) path_env = RSTRING_PTR(eargp->path_env);
2542 abspath = dln_find_exe_r(RSTRING_PTR(eargp->invoke.cmd.command_name),
2543 path_env, fbuf, sizeof(fbuf));
2544 if (abspath)
2545 eargp->invoke.cmd.command_abspath = rb_str_new_cstr(abspath);
2546 else
2547 eargp->invoke.cmd.command_abspath = Qnil;
2548 }
2549
2550 if (!eargp->use_shell && !eargp->invoke.cmd.argv_buf) {
2551 int i;
2552 VALUE argv_buf;
2553 argv_buf = rb_str_buf_new(0);
2554 hide_obj(argv_buf);
2555 for (i = 0; i < argc; i++) {
2556 VALUE arg = argv[i];
2557 const char *s = StringValueCStr(arg);
2558#ifdef DEFAULT_PROCESS_ENCODING
2559 arg = EXPORT_STR(arg);
2560 s = RSTRING_PTR(arg);
2561#endif
2562 rb_str_buf_cat(argv_buf, s, RSTRING_LEN(arg));
2563 append_terminator(argv_buf);
2564 }
2565 eargp->invoke.cmd.argv_buf = argv_buf;
2566 }
2567
2568 if (!eargp->use_shell) {
2569 const char *p, *ep, *null=NULL;
2570 VALUE argv_str;
2571 argv_str = hide_obj(rb_str_buf_new(sizeof(char*) * (argc + 2)));
2572 rb_str_buf_cat(argv_str, (char *)&null, sizeof(null)); /* place holder for /bin/sh of try_with_sh. */
2573 p = RSTRING_PTR(eargp->invoke.cmd.argv_buf);
2574 ep = p + RSTRING_LEN(eargp->invoke.cmd.argv_buf);
2575 while (p < ep) {
2576 rb_str_buf_cat(argv_str, (char *)&p, sizeof(p));
2577 p += strlen(p) + 1;
2578 }
2579 rb_str_buf_cat(argv_str, (char *)&null, sizeof(null)); /* terminator for execve. */
2580 eargp->invoke.cmd.argv_str =
2581 rb_imemo_tmpbuf_new_from_an_RString(argv_str);
2582 }
2583 RB_GC_GUARD(execarg_obj);
2584}
2585
2586struct rb_execarg *
2587rb_execarg_get(VALUE execarg_obj)
2588{
2589 struct rb_execarg *eargp;
2590 TypedData_Get_Struct(execarg_obj, struct rb_execarg, &exec_arg_data_type, eargp);
2591 return eargp;
2592}
2593
2594static VALUE
2595rb_execarg_init(int argc, const VALUE *orig_argv, int accept_shell, VALUE execarg_obj)
2596{
2597 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2598 VALUE prog, ret;
2599 VALUE env = Qnil, opthash = Qnil;
2600 VALUE argv_buf;
2601 VALUE *argv = ALLOCV_N(VALUE, argv_buf, argc);
2602 MEMCPY(argv, orig_argv, VALUE, argc);
2603 prog = rb_exec_getargs(&argc, &argv, accept_shell, &env, &opthash);
2604 rb_exec_fillarg(prog, argc, argv, env, opthash, execarg_obj);
2605 ALLOCV_END(argv_buf);
2606 ret = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
2607 RB_GC_GUARD(execarg_obj);
2608 return ret;
2609}
2610
2611VALUE
2612rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt)
2613{
2614 VALUE execarg_obj;
2615 struct rb_execarg *eargp;
2616 execarg_obj = TypedData_Make_Struct(0, struct rb_execarg, &exec_arg_data_type, eargp);
2617 rb_execarg_init(argc, argv, accept_shell, execarg_obj);
2618 if (!allow_exc_opt && eargp->exception_given) {
2619 rb_raise(rb_eArgError, "exception option is not allowed");
2620 }
2621 return execarg_obj;
2622}
2623
2624void
2625rb_execarg_setenv(VALUE execarg_obj, VALUE env)
2626{
2627 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2628 env = !NIL_P(env) ? rb_check_exec_env(env, &eargp->path_env) : Qfalse;
2629 eargp->env_modification = env;
2630 RB_GC_GUARD(execarg_obj);
2631}
2632
2633static int
2634fill_envp_buf_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
2635{
2636 VALUE key = (VALUE)st_key;
2637 VALUE val = (VALUE)st_val;
2638 VALUE envp_buf = (VALUE)arg;
2639
2640 rb_str_buf_cat2(envp_buf, StringValueCStr(key));
2641 rb_str_buf_cat2(envp_buf, "=");
2642 rb_str_buf_cat2(envp_buf, StringValueCStr(val));
2643 append_terminator(envp_buf);
2644
2645 return ST_CONTINUE;
2646}
2647
2648
2649static long run_exec_dup2_tmpbuf_size(long n);
2650
2652 VALUE fname;
2653 int oflags;
2654 mode_t perm;
2655 int ret;
2656 int err;
2657};
2658
2659static void *
2660open_func(void *ptr)
2661{
2662 struct open_struct *data = ptr;
2663 const char *fname = RSTRING_PTR(data->fname);
2664 data->ret = parent_redirect_open(fname, data->oflags, data->perm);
2665 data->err = errno;
2666 return NULL;
2667}
2668
2669static void
2670rb_execarg_allocate_dup2_tmpbuf(struct rb_execarg *eargp, long len)
2671{
2672 rb_alloc_tmp_buffer(&eargp->dup2_tmpbuf, run_exec_dup2_tmpbuf_size(len), false);
2673}
2674
2675static VALUE
2676rb_execarg_parent_start1(VALUE execarg_obj)
2677{
2678 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2679 int unsetenv_others;
2680 VALUE envopts;
2681 VALUE ary;
2682
2683 ary = eargp->fd_open;
2684 if (ary != Qfalse) {
2685 long i;
2686 for (i = 0; i < RARRAY_LEN(ary); i++) {
2687 VALUE elt = RARRAY_AREF(ary, i);
2688 int fd = FIX2INT(RARRAY_AREF(elt, 0));
2689 VALUE param = RARRAY_AREF(elt, 1);
2690 VALUE vpath = RARRAY_AREF(param, 0);
2691 int flags = NUM2INT(RARRAY_AREF(param, 1));
2692 mode_t perm = NUM2MODET(RARRAY_AREF(param, 2));
2693 VALUE fd2v = RARRAY_AREF(param, 3);
2694 int fd2;
2695 if (NIL_P(fd2v)) {
2696 struct open_struct open_data;
2697 again:
2698 open_data.fname = vpath;
2699 open_data.oflags = flags;
2700 open_data.perm = perm;
2701 open_data.ret = -1;
2702 open_data.err = EINTR;
2703 rb_thread_call_without_gvl2(open_func, (void *)&open_data, RUBY_UBF_IO, 0);
2704 if (open_data.ret == -1) {
2705 if (open_data.err == EINTR) {
2707 goto again;
2708 }
2709 rb_syserr_fail_str(open_data.err, vpath);
2710 }
2711 fd2 = open_data.ret;
2712 rb_update_max_fd(fd2);
2713 RARRAY_ASET(param, 3, INT2FIX(fd2));
2715 }
2716 else {
2717 fd2 = NUM2INT(fd2v);
2718 }
2719 rb_execarg_addopt(execarg_obj, INT2FIX(fd), INT2FIX(fd2));
2720 }
2721 }
2722
2723 eargp->redirect_fds = check_exec_fds(eargp);
2724
2725 ary = eargp->fd_dup2;
2726 if (ary != Qfalse) {
2727 rb_execarg_allocate_dup2_tmpbuf(eargp, RARRAY_LEN(ary));
2728 }
2729
2730 unsetenv_others = eargp->unsetenv_others_given && eargp->unsetenv_others_do;
2731 envopts = eargp->env_modification;
2732 if (ALWAYS_NEED_ENVP || unsetenv_others || envopts != Qfalse) {
2733 VALUE envtbl, envp_str, envp_buf;
2734 char *p, *ep;
2735 if (unsetenv_others) {
2736 envtbl = rb_hash_new();
2737 }
2738 else {
2739 envtbl = rb_env_to_hash();
2740 }
2741 hide_obj(envtbl);
2742 if (envopts != Qfalse) {
2743 long i;
2744 for (i = 0; i < RARRAY_LEN(envopts); i++) {
2745 VALUE pair = RARRAY_AREF(envopts, i);
2746 VALUE key = RARRAY_AREF(pair, 0);
2747 VALUE val = RARRAY_AREF(pair, 1);
2748 if (NIL_P(val)) {
2749 rb_hash_delete(envtbl, key);
2750 }
2751 else {
2752 rb_hash_aset(envtbl, key, val);
2753 }
2754 }
2755 }
2756 envp_buf = rb_str_buf_new(0);
2757 hide_obj(envp_buf);
2758 rb_hash_stlike_foreach(envtbl, fill_envp_buf_i, (st_data_t)envp_buf);
2759 envp_str = rb_str_buf_new(sizeof(char*) * (RHASH_SIZE(envtbl) + 1));
2760 hide_obj(envp_str);
2761 p = RSTRING_PTR(envp_buf);
2762 ep = p + RSTRING_LEN(envp_buf);
2763 while (p < ep) {
2764 rb_str_buf_cat(envp_str, (char *)&p, sizeof(p));
2765 p += strlen(p) + 1;
2766 }
2767 p = NULL;
2768 rb_str_buf_cat(envp_str, (char *)&p, sizeof(p));
2769 eargp->envp_str =
2770 rb_imemo_tmpbuf_new_from_an_RString(envp_str);
2771 eargp->envp_buf = envp_buf;
2772
2773 /*
2774 char **tmp_envp = (char **)RSTRING_PTR(envp_str);
2775 while (*tmp_envp) {
2776 printf("%s\n", *tmp_envp);
2777 tmp_envp++;
2778 }
2779 */
2780 }
2781
2782 RB_GC_GUARD(execarg_obj);
2783 return Qnil;
2784}
2785
2786void
2787rb_execarg_parent_start(VALUE execarg_obj)
2788{
2789 int state;
2790 rb_protect(rb_execarg_parent_start1, execarg_obj, &state);
2791 if (state) {
2792 rb_execarg_parent_end(execarg_obj);
2793 rb_jump_tag(state);
2794 }
2795}
2796
2797static VALUE
2798execarg_parent_end(VALUE execarg_obj)
2799{
2800 struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
2801 int err = errno;
2802 VALUE ary;
2803
2804 ary = eargp->fd_open;
2805 if (ary != Qfalse) {
2806 long i;
2807 for (i = 0; i < RARRAY_LEN(ary); i++) {
2808 VALUE elt = RARRAY_AREF(ary, i);
2809 VALUE param = RARRAY_AREF(elt, 1);
2810 VALUE fd2v;
2811 int fd2;
2812 fd2v = RARRAY_AREF(param, 3);
2813 if (!NIL_P(fd2v)) {
2814 fd2 = FIX2INT(fd2v);
2815 parent_redirect_close(fd2);
2816 RARRAY_ASET(param, 3, Qnil);
2817 }
2818 }
2819 }
2820
2821 errno = err;
2822 RB_GC_GUARD(execarg_obj);
2823 return execarg_obj;
2824}
2825
2826void
2827rb_execarg_parent_end(VALUE execarg_obj)
2828{
2829 execarg_parent_end(execarg_obj);
2830}
2831
2832static void
2833rb_exec_fail(struct rb_execarg *eargp, int err, const char *errmsg)
2834{
2835 if (!errmsg || !*errmsg) return;
2836 if (strcmp(errmsg, "chdir") == 0) {
2837 rb_sys_fail_str(eargp->chdir_dir);
2838 }
2839 rb_sys_fail(errmsg);
2840}
2841
2842#if 0
2843void
2844rb_execarg_fail(VALUE execarg_obj, int err, const char *errmsg)
2845{
2846 if (!errmsg || !*errmsg) return;
2847 rb_exec_fail(rb_execarg_get(execarg_obj), err, errmsg);
2848 RB_GC_GUARD(execarg_obj);
2849}
2850#endif
2851
2852VALUE
2853rb_f_exec(int argc, const VALUE *argv)
2854{
2855 VALUE execarg_obj, fail_str;
2856 struct rb_execarg *eargp;
2857#define CHILD_ERRMSG_BUFLEN 80
2858 char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' };
2859 int err, state;
2860
2861 execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
2862 eargp = rb_execarg_get(execarg_obj);
2863 before_exec(); /* stop timer thread before redirects */
2864
2865 rb_protect(rb_execarg_parent_start1, execarg_obj, &state);
2866 if (state) {
2867 execarg_parent_end(execarg_obj);
2868 after_exec(); /* restart timer thread */
2869 rb_jump_tag(state);
2870 }
2871
2872 fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
2873
2874 err = exec_async_signal_safe(eargp, errmsg, sizeof(errmsg));
2875 after_exec(); /* restart timer thread */
2876
2877 rb_exec_fail(eargp, err, errmsg);
2878 RB_GC_GUARD(execarg_obj);
2879 rb_syserr_fail_str(err, fail_str);
2881}
2882
2883NORETURN(static VALUE f_exec(int c, const VALUE *a, VALUE _));
2884
2885/*
2886 * call-seq:
2887 * exec([env, ] command_line, options = {})
2888 * exec([env, ] exe_path, *args, options = {})
2889 *
2890 * Replaces the current process by doing one of the following:
2891 *
2892 * - Passing string +command_line+ to the shell.
2893 * - Invoking the executable at +exe_path+.
2894 *
2895 * This method has potential security vulnerabilities if called with untrusted input;
2896 * see {Command Injection}[rdoc-ref:security/command_injection.rdoc].
2897 *
2898 * The new process is created using the
2899 * {exec system call}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/execve.html];
2900 * it may inherit some of its environment from the calling program
2901 * (possibly including open file descriptors).
2902 *
2903 * Argument +env+, if given, is a hash that affects +ENV+ for the new process;
2904 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
2905 *
2906 * Argument +options+ is a hash of options for the new process;
2907 * see {Execution Options}[rdoc-ref:Process@Execution+Options].
2908 *
2909 * The first required argument is one of the following:
2910 *
2911 * - +command_line+ if it is a string,
2912 * and if it begins with a shell reserved word or special built-in,
2913 * or if it contains one or more meta characters.
2914 * - +exe_path+ otherwise.
2915 *
2916 * <b>Argument +command_line+</b>
2917 *
2918 * \String argument +command_line+ is a command line to be passed to a shell;
2919 * it must begin with a shell reserved word, begin with a special built-in,
2920 * or contain meta characters:
2921 *
2922 * exec('if true; then echo "Foo"; fi') # Shell reserved word.
2923 * exec('exit') # Built-in.
2924 * exec('date > date.tmp') # Contains meta character.
2925 *
2926 * The command line may also contain arguments and options for the command:
2927 *
2928 * exec('echo "Foo"')
2929 *
2930 * Output:
2931 *
2932 * Foo
2933 *
2934 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
2935 *
2936 * Raises an exception if the new process could not execute.
2937 *
2938 * <b>Argument +exe_path+</b>
2939 *
2940 * Argument +exe_path+ is one of the following:
2941 *
2942 * - The string path to an executable to be called.
2943 * - A 2-element array containing the path to an executable
2944 * and the string to be used as the name of the executing process.
2945 *
2946 * Example:
2947 *
2948 * exec('/usr/bin/date')
2949 *
2950 * Output:
2951 *
2952 * Sat Aug 26 09:38:00 AM CDT 2023
2953 *
2954 * Ruby invokes the executable directly.
2955 * This form does not use the shell;
2956 * see {Arguments args}[rdoc-ref:Process@Arguments+args] for caveats.
2957 *
2958 * exec('doesnt_exist') # Raises Errno::ENOENT
2959 *
2960 * If one or more +args+ is given, each is an argument or option
2961 * to be passed to the executable:
2962 *
2963 * exec('echo', 'C*')
2964 * exec('echo', 'hello', 'world')
2965 *
2966 * Output:
2967 *
2968 * C*
2969 * hello world
2970 *
2971 * Raises an exception if the new process could not execute.
2972 */
2973
2974static VALUE
2975f_exec(int c, const VALUE *a, VALUE _)
2976{
2977 rb_f_exec(c, a);
2979}
2980
2981#define ERRMSG(str) \
2982 ((errmsg && 0 < errmsg_buflen) ? \
2983 (void)strlcpy(errmsg, (str), errmsg_buflen) : (void)0)
2984
2985#define ERRMSG_FMT(...) \
2986 ((errmsg && 0 < errmsg_buflen) ? \
2987 (void)snprintf(errmsg, errmsg_buflen, __VA_ARGS__) : (void)0)
2988
2989static int fd_get_cloexec(int fd, char *errmsg, size_t errmsg_buflen);
2990static int fd_set_cloexec(int fd, char *errmsg, size_t errmsg_buflen);
2991static int fd_clear_cloexec(int fd, char *errmsg, size_t errmsg_buflen);
2992
2993static int
2994save_redirect_fd(int fd, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
2995{
2996 if (sargp) {
2997 VALUE newary, redirection;
2998 int save_fd = redirect_cloexec_dup(fd), cloexec;
2999 if (save_fd == -1) {
3000 if (errno == EBADF)
3001 return 0;
3002 ERRMSG("dup");
3003 return -1;
3004 }
3005 rb_update_max_fd(save_fd);
3006 newary = sargp->fd_dup2;
3007 if (newary == Qfalse) {
3008 newary = hide_obj(rb_ary_new());
3009 sargp->fd_dup2 = newary;
3010 }
3011 cloexec = fd_get_cloexec(fd, errmsg, errmsg_buflen);
3012 redirection = hide_obj(rb_assoc_new(INT2FIX(fd), INT2FIX(save_fd)));
3013 if (cloexec) rb_ary_push(redirection, Qtrue);
3014 rb_ary_push(newary, redirection);
3015
3016 newary = sargp->fd_close;
3017 if (newary == Qfalse) {
3018 newary = hide_obj(rb_ary_new());
3019 sargp->fd_close = newary;
3020 }
3021 rb_ary_push(newary, hide_obj(rb_assoc_new(INT2FIX(save_fd), Qnil)));
3022 }
3023
3024 return 0;
3025}
3026
3027static int
3028intcmp(const void *a, const void *b)
3029{
3030 return *(int*)a - *(int*)b;
3031}
3032
3033static int
3034intrcmp(const void *a, const void *b)
3035{
3036 return *(int*)b - *(int*)a;
3037}
3038
3040 int oldfd;
3041 int newfd;
3042 long older_index;
3043 long num_newer;
3044 int cloexec;
3045};
3046
3047static long
3048run_exec_dup2_tmpbuf_size(long n)
3049{
3050 return sizeof(struct run_exec_dup2_fd_pair) * n;
3051}
3052
3053/* This function should be async-signal-safe. Actually it is. */
3054static int
3055fd_get_cloexec(int fd, char *errmsg, size_t errmsg_buflen)
3056{
3057#ifdef F_GETFD
3058 int ret = 0;
3059 ret = fcntl(fd, F_GETFD); /* async-signal-safe */
3060 if (ret == -1) {
3061 ERRMSG("fcntl(F_GETFD)");
3062 return -1;
3063 }
3064 if (ret & FD_CLOEXEC) return 1;
3065#endif
3066 return 0;
3067}
3068
3069/* This function should be async-signal-safe. Actually it is. */
3070static int
3071fd_set_cloexec(int fd, char *errmsg, size_t errmsg_buflen)
3072{
3073#ifdef F_GETFD
3074 int ret = 0;
3075 ret = fcntl(fd, F_GETFD); /* async-signal-safe */
3076 if (ret == -1) {
3077 ERRMSG("fcntl(F_GETFD)");
3078 return -1;
3079 }
3080 if (!(ret & FD_CLOEXEC)) {
3081 ret |= FD_CLOEXEC;
3082 ret = fcntl(fd, F_SETFD, ret); /* async-signal-safe */
3083 if (ret == -1) {
3084 ERRMSG("fcntl(F_SETFD)");
3085 return -1;
3086 }
3087 }
3088#endif
3089 return 0;
3090}
3091
3092/* This function should be async-signal-safe. Actually it is. */
3093static int
3094fd_clear_cloexec(int fd, char *errmsg, size_t errmsg_buflen)
3095{
3096#ifdef F_GETFD
3097 int ret;
3098 ret = fcntl(fd, F_GETFD); /* async-signal-safe */
3099 if (ret == -1) {
3100 ERRMSG("fcntl(F_GETFD)");
3101 return -1;
3102 }
3103 if (ret & FD_CLOEXEC) {
3104 ret &= ~FD_CLOEXEC;
3105 ret = fcntl(fd, F_SETFD, ret); /* async-signal-safe */
3106 if (ret == -1) {
3107 ERRMSG("fcntl(F_SETFD)");
3108 return -1;
3109 }
3110 }
3111#endif
3112 return 0;
3113}
3114
3115/* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3116static int
3117run_exec_dup2(VALUE ary, VALUE tmpbuf, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3118{
3119 long n, i;
3120 int ret;
3121 int extra_fd = -1;
3122 struct run_exec_dup2_fd_pair *pairs = RB_IMEMO_TMPBUF_PTR(tmpbuf);
3123
3124 n = RARRAY_LEN(ary);
3125
3126 /* initialize oldfd and newfd: O(n) */
3127 for (i = 0; i < n; i++) {
3128 VALUE elt = RARRAY_AREF(ary, i);
3129 pairs[i].oldfd = FIX2INT(RARRAY_AREF(elt, 1));
3130 pairs[i].newfd = FIX2INT(RARRAY_AREF(elt, 0)); /* unique */
3131 pairs[i].cloexec = RARRAY_LEN(elt) > 2 && RTEST(RARRAY_AREF(elt, 2));
3132 pairs[i].older_index = -1;
3133 }
3134
3135 /* sort the table by oldfd: O(n log n) */
3136 if (!sargp)
3137 qsort(pairs, n, sizeof(struct run_exec_dup2_fd_pair), intcmp); /* hopefully async-signal-safe */
3138 else
3139 qsort(pairs, n, sizeof(struct run_exec_dup2_fd_pair), intrcmp);
3140
3141 /* initialize older_index and num_newer: O(n log n) */
3142 for (i = 0; i < n; i++) {
3143 int newfd = pairs[i].newfd;
3144 struct run_exec_dup2_fd_pair key, *found;
3145 key.oldfd = newfd;
3146 found = bsearch(&key, pairs, n, sizeof(struct run_exec_dup2_fd_pair), intcmp); /* hopefully async-signal-safe */
3147 pairs[i].num_newer = 0;
3148 if (found) {
3149 while (pairs < found && (found-1)->oldfd == newfd)
3150 found--;
3151 while (found < pairs+n && found->oldfd == newfd) {
3152 pairs[i].num_newer++;
3153 found->older_index = i;
3154 found++;
3155 }
3156 }
3157 }
3158
3159 /* non-cyclic redirection: O(n) */
3160 for (i = 0; i < n; i++) {
3161 long j = i;
3162 while (j != -1 && pairs[j].oldfd != -1 && pairs[j].num_newer == 0) {
3163 if (save_redirect_fd(pairs[j].newfd, sargp, errmsg, errmsg_buflen) < 0) /* async-signal-safe */
3164 goto fail;
3165 ret = redirect_dup2(pairs[j].oldfd, pairs[j].newfd); /* async-signal-safe */
3166 if (ret == -1) {
3167 ERRMSG("dup2");
3168 goto fail;
3169 }
3170 if (pairs[j].cloexec &&
3171 fd_set_cloexec(pairs[j].newfd, errmsg, errmsg_buflen)) {
3172 goto fail;
3173 }
3174 rb_update_max_fd(pairs[j].newfd); /* async-signal-safe but don't need to call it in a child process. */
3175 pairs[j].oldfd = -1;
3176 j = pairs[j].older_index;
3177 if (j != -1)
3178 pairs[j].num_newer--;
3179 }
3180 }
3181
3182 /* cyclic redirection: O(n) */
3183 for (i = 0; i < n; i++) {
3184 long j;
3185 if (pairs[i].oldfd == -1)
3186 continue;
3187 if (pairs[i].oldfd == pairs[i].newfd) { /* self cycle */
3188 if (fd_clear_cloexec(pairs[i].oldfd, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3189 goto fail;
3190 pairs[i].oldfd = -1;
3191 continue;
3192 }
3193 if (extra_fd == -1) {
3194 extra_fd = redirect_dup(pairs[i].oldfd); /* async-signal-safe */
3195 if (extra_fd == -1) {
3196 ERRMSG("dup");
3197 goto fail;
3198 }
3199 // without this, kqueue timer_th.event_fd fails with a reserved FD did not have close-on-exec
3200 // in #assert_close_on_exec because the FD_CLOEXEC is not dup'd by default
3201 if (fd_get_cloexec(pairs[i].oldfd, errmsg, errmsg_buflen)) {
3202 if (fd_set_cloexec(extra_fd, errmsg, errmsg_buflen)) {
3203 close(extra_fd);
3204 goto fail;
3205 }
3206 }
3207 rb_update_max_fd(extra_fd);
3208 }
3209 else {
3210 ret = redirect_dup2(pairs[i].oldfd, extra_fd); /* async-signal-safe */
3211 if (ret == -1) {
3212 ERRMSG("dup2");
3213 goto fail;
3214 }
3215 rb_update_max_fd(extra_fd);
3216 }
3217 pairs[i].oldfd = extra_fd;
3218 j = pairs[i].older_index;
3219 pairs[i].older_index = -1;
3220 while (j != -1) {
3221 ret = redirect_dup2(pairs[j].oldfd, pairs[j].newfd); /* async-signal-safe */
3222 if (ret == -1) {
3223 ERRMSG("dup2");
3224 goto fail;
3225 }
3226 rb_update_max_fd(ret);
3227 pairs[j].oldfd = -1;
3228 j = pairs[j].older_index;
3229 }
3230 }
3231 if (extra_fd != -1) {
3232 ret = redirect_close(extra_fd); /* async-signal-safe */
3233 if (ret == -1) {
3234 ERRMSG("close");
3235 goto fail;
3236 }
3237 }
3238
3239 return 0;
3240
3241 fail:
3242 return -1;
3243}
3244
3245/* This function should be async-signal-safe. Actually it is. */
3246static int
3247run_exec_close(VALUE ary, char *errmsg, size_t errmsg_buflen)
3248{
3249 long i;
3250 int ret;
3251
3252 for (i = 0; i < RARRAY_LEN(ary); i++) {
3253 VALUE elt = RARRAY_AREF(ary, i);
3254 int fd = FIX2INT(RARRAY_AREF(elt, 0));
3255 ret = redirect_close(fd); /* async-signal-safe */
3256 if (ret == -1) {
3257 ERRMSG("close");
3258 return -1;
3259 }
3260 }
3261 return 0;
3262}
3263
3264/* This function should be async-signal-safe when sargp is NULL. Actually it is. */
3265static int
3266run_exec_dup2_child(VALUE ary, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3267{
3268 long i;
3269 int ret;
3270
3271 for (i = 0; i < RARRAY_LEN(ary); i++) {
3272 VALUE elt = RARRAY_AREF(ary, i);
3273 int newfd = FIX2INT(RARRAY_AREF(elt, 0));
3274 int oldfd = FIX2INT(RARRAY_AREF(elt, 1));
3275
3276 if (save_redirect_fd(newfd, sargp, errmsg, errmsg_buflen) < 0) /* async-signal-safe */
3277 return -1;
3278 ret = redirect_dup2(oldfd, newfd); /* async-signal-safe */
3279 if (ret == -1) {
3280 ERRMSG("dup2");
3281 return -1;
3282 }
3283 rb_update_max_fd(newfd);
3284 }
3285 return 0;
3286}
3287
3288#ifdef HAVE_SETPGID
3289/* This function should be async-signal-safe when sargp is NULL. Actually it is. */
3290static int
3291run_exec_pgroup(const struct rb_execarg *eargp, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3292{
3293 /*
3294 * If FD_CLOEXEC is available, rb_fork_async_signal_safe waits the child's execve.
3295 * So setpgid is done in the child when rb_fork_async_signal_safe is returned in
3296 * the parent.
3297 * No race condition, even without setpgid from the parent.
3298 * (Is there an environment which has setpgid but no FD_CLOEXEC?)
3299 */
3300 int ret;
3301 rb_pid_t pgroup;
3302
3303 pgroup = eargp->pgroup_pgid;
3304 if (pgroup == -1)
3305 return 0;
3306
3307 if (sargp) {
3308 /* maybe meaningless with no fork environment... */
3309 sargp->pgroup_given = 1;
3310 sargp->pgroup_pgid = getpgrp();
3311 }
3312
3313 if (pgroup == 0) {
3314 pgroup = getpid(); /* async-signal-safe */
3315 }
3316 ret = setpgid(getpid(), pgroup); /* async-signal-safe */
3317 if (ret == -1) ERRMSG("setpgid");
3318 return ret;
3319}
3320#endif
3321
3322#if defined(HAVE_SETRLIMIT) && defined(RLIM2NUM)
3323/* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3324static int
3325run_exec_rlimit(VALUE ary, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3326{
3327 long i;
3328 for (i = 0; i < RARRAY_LEN(ary); i++) {
3329 VALUE elt = RARRAY_AREF(ary, i);
3330 int rtype = NUM2INT(RARRAY_AREF(elt, 0));
3331 struct rlimit rlim;
3332 if (sargp) {
3333 VALUE tmp, newary;
3334 if (getrlimit(rtype, &rlim) == -1) {
3335 ERRMSG("getrlimit");
3336 return -1;
3337 }
3338 tmp = hide_obj(rb_ary_new3(3, RARRAY_AREF(elt, 0),
3339 RLIM2NUM(rlim.rlim_cur),
3340 RLIM2NUM(rlim.rlim_max)));
3341 if (sargp->rlimit_limits == Qfalse)
3342 newary = sargp->rlimit_limits = hide_obj(rb_ary_new());
3343 else
3344 newary = sargp->rlimit_limits;
3345 rb_ary_push(newary, tmp);
3346 }
3347 rlim.rlim_cur = NUM2RLIM(RARRAY_AREF(elt, 1));
3348 rlim.rlim_max = NUM2RLIM(RARRAY_AREF(elt, 2));
3349 if (setrlimit(rtype, &rlim) == -1) { /* hopefully async-signal-safe */
3350 ERRMSG("setrlimit");
3351 return -1;
3352 }
3353 }
3354 return 0;
3355}
3356#endif
3357
3358#if !defined(HAVE_WORKING_FORK)
3359static VALUE
3360save_env_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
3361{
3362 rb_ary_push(ary, hide_obj(rb_ary_dup(argv[0])));
3363 return Qnil;
3364}
3365
3366static void
3367save_env(struct rb_execarg *sargp)
3368{
3369 if (!sargp)
3370 return;
3371 if (sargp->env_modification == Qfalse) {
3372 VALUE env = rb_envtbl();
3373 if (RTEST(env)) {
3374 VALUE ary = hide_obj(rb_ary_new());
3375 rb_block_call(env, idEach, 0, 0, save_env_i,
3376 (VALUE)ary);
3377 sargp->env_modification = ary;
3378 }
3379 sargp->unsetenv_others_given = 1;
3380 sargp->unsetenv_others_do = 1;
3381 }
3382}
3383#endif
3384
3385#ifdef _WIN32
3386#undef chdir
3387#define chdir(p) rb_w32_uchdir(p)
3388#endif
3389
3390/* This function should be async-signal-safe when sargp is NULL. Hopefully it is. */
3391int
3392rb_execarg_run_options(const struct rb_execarg *eargp, struct rb_execarg *sargp, char *errmsg, size_t errmsg_buflen)
3393{
3394 VALUE obj;
3395
3396 if (sargp) {
3397 /* assume that sargp is always NULL on fork-able environments */
3398 MEMZERO(sargp, struct rb_execarg, 1);
3399 sargp->redirect_fds = Qnil;
3400 }
3401
3402#ifdef HAVE_SETPGID
3403 if (eargp->pgroup_given) {
3404 if (run_exec_pgroup(eargp, sargp, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3405 return -1;
3406 }
3407#endif
3408
3409#if defined(HAVE_SETRLIMIT) && defined(RLIM2NUM)
3410 obj = eargp->rlimit_limits;
3411 if (obj != Qfalse) {
3412 if (run_exec_rlimit(obj, sargp, errmsg, errmsg_buflen) == -1) /* hopefully async-signal-safe */
3413 return -1;
3414 }
3415#endif
3416
3417#if !defined(HAVE_WORKING_FORK)
3418 if (eargp->unsetenv_others_given && eargp->unsetenv_others_do) {
3419 save_env(sargp);
3420 rb_env_clear();
3421 }
3422
3423 obj = eargp->env_modification;
3424 if (obj != Qfalse) {
3425 long i;
3426 save_env(sargp);
3427 for (i = 0; i < RARRAY_LEN(obj); i++) {
3428 VALUE pair = RARRAY_AREF(obj, i);
3429 VALUE key = RARRAY_AREF(pair, 0);
3430 VALUE val = RARRAY_AREF(pair, 1);
3431 if (NIL_P(val))
3432 ruby_setenv(StringValueCStr(key), 0);
3433 else
3434 ruby_setenv(StringValueCStr(key), StringValueCStr(val));
3435 }
3436 }
3437#endif
3438
3439 if (eargp->umask_given) {
3440 mode_t mask = eargp->umask_mask;
3441 mode_t oldmask = umask(mask); /* never fail */ /* async-signal-safe */
3442 if (sargp) {
3443 sargp->umask_given = 1;
3444 sargp->umask_mask = oldmask;
3445 }
3446 }
3447
3448 obj = eargp->fd_dup2;
3449 if (obj != Qfalse) {
3450 if (run_exec_dup2(obj, eargp->dup2_tmpbuf, sargp, errmsg, errmsg_buflen) == -1) /* hopefully async-signal-safe */
3451 return -1;
3452 }
3453
3454 obj = eargp->fd_close;
3455 if (obj != Qfalse) {
3456 if (sargp)
3457 rb_warn("cannot close fd before spawn");
3458 else {
3459 if (run_exec_close(obj, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3460 return -1;
3461 }
3462 }
3463
3464#ifdef HAVE_WORKING_FORK
3465 if (eargp->close_others_do) {
3466 rb_close_before_exec(3, eargp->close_others_maxhint, eargp->redirect_fds); /* async-signal-safe */
3467 }
3468#endif
3469
3470 obj = eargp->fd_dup2_child;
3471 if (obj != Qfalse) {
3472 if (run_exec_dup2_child(obj, sargp, errmsg, errmsg_buflen) == -1) /* async-signal-safe */
3473 return -1;
3474 }
3475
3476 if (eargp->chdir_given) {
3477 if (sargp) {
3478 sargp->chdir_given = 1;
3479 sargp->chdir_dir = hide_obj(rb_dir_getwd_ospath());
3480 }
3481 if (chdir(RSTRING_PTR(eargp->chdir_dir)) == -1) { /* async-signal-safe */
3482 ERRMSG("chdir");
3483 return -1;
3484 }
3485 }
3486
3487#ifdef HAVE_SETGID
3488 if (eargp->gid_given) {
3489 if (setgid(eargp->gid) < 0) {
3490 ERRMSG("setgid");
3491 return -1;
3492 }
3493 }
3494#endif
3495#ifdef HAVE_SETUID
3496 if (eargp->uid_given) {
3497 if (setuid(eargp->uid) < 0) {
3498 ERRMSG("setuid");
3499 return -1;
3500 }
3501 }
3502#endif
3503
3504 if (sargp) {
3505 VALUE ary = sargp->fd_dup2;
3506 if (ary != Qfalse) {
3507 rb_execarg_allocate_dup2_tmpbuf(sargp, RARRAY_LEN(ary));
3508 }
3509 }
3510 {
3511 int preserve = errno;
3512 stdfd_clear_nonblock();
3513 errno = preserve;
3514 }
3515
3516 return 0;
3517}
3518
3519/* This function should be async-signal-safe. Hopefully it is. */
3520int
3521rb_exec_async_signal_safe(const struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
3522{
3523 errno = exec_async_signal_safe(eargp, errmsg, errmsg_buflen);
3524 return -1;
3525}
3526
3527static int
3528exec_async_signal_safe(const struct rb_execarg *eargp, char *errmsg, size_t errmsg_buflen)
3529{
3530#if !defined(HAVE_WORKING_FORK)
3531 struct rb_execarg sarg, *const sargp = &sarg;
3532#else
3533 struct rb_execarg *const sargp = NULL;
3534#endif
3535 int err;
3536
3537 if (rb_execarg_run_options(eargp, sargp, errmsg, errmsg_buflen) < 0) { /* hopefully async-signal-safe */
3538 return errno;
3539 }
3540
3541 if (eargp->use_shell) {
3542 err = proc_exec_sh(RSTRING_PTR(eargp->invoke.sh.shell_script), eargp->envp_str); /* async-signal-safe */
3543 }
3544 else {
3545 char *abspath = NULL;
3546 if (!NIL_P(eargp->invoke.cmd.command_abspath))
3547 abspath = RSTRING_PTR(eargp->invoke.cmd.command_abspath);
3548 err = proc_exec_cmd(abspath, eargp->invoke.cmd.argv_str, eargp->envp_str); /* async-signal-safe */
3549 }
3550#if !defined(HAVE_WORKING_FORK)
3551 rb_execarg_run_options(sargp, NULL, errmsg, errmsg_buflen);
3552#endif
3553
3554 return err;
3555}
3556
3557#ifdef HAVE_WORKING_FORK
3558/* This function should be async-signal-safe. Hopefully it is. */
3559static int
3560rb_exec_atfork(void* arg, char *errmsg, size_t errmsg_buflen)
3561{
3562 return rb_exec_async_signal_safe(arg, errmsg, errmsg_buflen); /* hopefully async-signal-safe */
3563}
3564
3565static VALUE
3566proc_syswait(VALUE pid)
3567{
3568 rb_syswait((rb_pid_t)pid);
3569 return Qnil;
3570}
3571
3572static int
3573move_fds_to_avoid_crash(int *fdp, int n, VALUE fds)
3574{
3575 int min = 0;
3576 int i;
3577 for (i = 0; i < n; i++) {
3578 int ret;
3579 while (RTEST(rb_hash_lookup(fds, INT2FIX(fdp[i])))) {
3580 if (min <= fdp[i])
3581 min = fdp[i]+1;
3582 while (RTEST(rb_hash_lookup(fds, INT2FIX(min))))
3583 min++;
3584 ret = rb_cloexec_fcntl_dupfd(fdp[i], min);
3585 if (ret == -1)
3586 return -1;
3587 rb_update_max_fd(ret);
3588 close(fdp[i]);
3589 fdp[i] = ret;
3590 }
3591 }
3592 return 0;
3593}
3594
3595static int
3596pipe_nocrash(int filedes[2], VALUE fds)
3597{
3598 int ret;
3599 ret = rb_pipe(filedes);
3600 if (ret == -1)
3601 return -1;
3602 if (RTEST(fds)) {
3603 int save = errno;
3604 if (move_fds_to_avoid_crash(filedes, 2, fds) == -1) {
3605 close(filedes[0]);
3606 close(filedes[1]);
3607 return -1;
3608 }
3609 errno = save;
3610 }
3611 return ret;
3612}
3613
3614#ifndef O_BINARY
3615#define O_BINARY 0
3616#endif
3617
3618static VALUE
3619rb_thread_sleep_that_takes_VALUE_as_sole_argument(VALUE n)
3620{
3622 return Qundef;
3623}
3624
3625static int
3626handle_fork_error(int err, struct rb_process_status *status, int *ep, volatile int *try_gc_p)
3627{
3628 int state = 0;
3629
3630 switch (err) {
3631 case ENOMEM:
3632 if ((*try_gc_p)-- > 0 && !rb_during_gc()) {
3633 rb_gc();
3634 return 0;
3635 }
3636 break;
3637 case EAGAIN:
3638#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
3639 case EWOULDBLOCK:
3640#endif
3641 if (!status && !ep) {
3642 rb_thread_sleep(1);
3643 return 0;
3644 }
3645 else {
3646 rb_protect(rb_thread_sleep_that_takes_VALUE_as_sole_argument, INT2FIX(1), &state);
3647 if (status) status->status = state;
3648 if (!state) return 0;
3649 }
3650 break;
3651 }
3652 if (ep) {
3653 close(ep[0]);
3654 close(ep[1]);
3655 errno = err;
3656 }
3657 if (state && !status) rb_jump_tag(state);
3658 return -1;
3659}
3660
3661#define prefork() ( \
3662 rb_io_flush(rb_stdout), \
3663 rb_io_flush(rb_stderr) \
3664 )
3665
3666/*
3667 * Forks child process, and returns the process ID in the parent
3668 * process.
3669 *
3670 * If +status+ is given, protects from any exceptions and sets the
3671 * jump status to it, and returns -1. If failed to fork new process
3672 * but no exceptions occurred, sets 0 to it. Otherwise, if forked
3673 * successfully, the value of +status+ is undetermined.
3674 *
3675 * In the child process, just returns 0 if +chfunc+ is +NULL+.
3676 * Otherwise +chfunc+ will be called with +charg+, and then the child
3677 * process exits with +EXIT_SUCCESS+ when it returned zero.
3678 *
3679 * In the case of the function is called and returns non-zero value,
3680 * the child process exits with non-+EXIT_SUCCESS+ value (normally
3681 * 127). And, on the platforms where +FD_CLOEXEC+ is available,
3682 * +errno+ is propagated to the parent process, and this function
3683 * returns -1 in the parent process. On the other platforms, just
3684 * returns pid.
3685 *
3686 * If fds is not Qnil, internal pipe for the errno propagation is
3687 * arranged to avoid conflicts of the hash keys in +fds+.
3688 *
3689 * +chfunc+ must not raise any exceptions.
3690 */
3691
3692static ssize_t
3693write_retry(int fd, const void *buf, size_t len)
3694{
3695 ssize_t w;
3696
3697 do {
3698 w = write(fd, buf, len);
3699 } while (w < 0 && errno == EINTR);
3700
3701 return w;
3702}
3703
3704static ssize_t
3705read_retry(int fd, void *buf, size_t len)
3706{
3707 ssize_t r;
3708
3709 if (set_blocking(fd) != 0) {
3710#ifndef _WIN32
3711 rb_async_bug_errno("set_blocking failed reading child error", errno);
3712#endif
3713 }
3714
3715 do {
3716 r = read(fd, buf, len);
3717 } while (r < 0 && errno == EINTR);
3718
3719 return r;
3720}
3721
3722static void
3723send_child_error(int fd, char *errmsg, size_t errmsg_buflen)
3724{
3725 int err;
3726
3727 err = errno;
3728 if (write_retry(fd, &err, sizeof(err)) < 0) err = errno;
3729 if (errmsg && 0 < errmsg_buflen) {
3730 errmsg[errmsg_buflen-1] = '\0';
3731 errmsg_buflen = strlen(errmsg);
3732 if (errmsg_buflen > 0 && write_retry(fd, errmsg, errmsg_buflen) < 0)
3733 err = errno;
3734 }
3735}
3736
3737static int
3738recv_child_error(int fd, int *errp, char *errmsg, size_t errmsg_buflen)
3739{
3740 int err;
3741 ssize_t size;
3742 if ((size = read_retry(fd, &err, sizeof(err))) < 0) {
3743 err = errno;
3744 }
3745 *errp = err;
3746 if (size == sizeof(err) &&
3747 errmsg && 0 < errmsg_buflen) {
3748 ssize_t ret = read_retry(fd, errmsg, errmsg_buflen-1);
3749 if (0 <= ret) {
3750 errmsg[ret] = '\0';
3751 }
3752 }
3753 close(fd);
3754 return size != 0;
3755}
3756
3757#ifdef HAVE_WORKING_VFORK
3758#if !defined(HAVE_GETRESUID) && defined(HAVE_GETUIDX)
3759/* AIX 7.1 */
3760static int
3761getresuid(rb_uid_t *ruid, rb_uid_t *euid, rb_uid_t *suid)
3762{
3763 rb_uid_t ret;
3764
3765 *ruid = getuid();
3766 *euid = geteuid();
3767 ret = getuidx(ID_SAVED);
3768 if (ret == (rb_uid_t)-1)
3769 return -1;
3770 *suid = ret;
3771 return 0;
3772}
3773#define HAVE_GETRESUID
3774#endif
3775
3776#if !defined(HAVE_GETRESGID) && defined(HAVE_GETGIDX)
3777/* AIX 7.1 */
3778static int
3779getresgid(rb_gid_t *rgid, rb_gid_t *egid, rb_gid_t *sgid)
3780{
3781 rb_gid_t ret;
3782
3783 *rgid = getgid();
3784 *egid = getegid();
3785 ret = getgidx(ID_SAVED);
3786 if (ret == (rb_gid_t)-1)
3787 return -1;
3788 *sgid = ret;
3789 return 0;
3790}
3791#define HAVE_GETRESGID
3792#endif
3793
3794#if !defined(RUBY_ASAN_ENABLED)
3795static int
3796has_privilege(void)
3797{
3798 /*
3799 * has_privilege() is used to choose vfork() or fork().
3800 *
3801 * If the process has privilege, the parent process or
3802 * the child process can change UID/GID.
3803 * If vfork() is used to create the child process and
3804 * the parent or child process change effective UID/GID,
3805 * different privileged processes shares memory.
3806 * It is a bad situation.
3807 * So, fork() should be used.
3808 */
3809
3810 rb_uid_t ruid, euid;
3811 rb_gid_t rgid, egid;
3812
3813#if defined HAVE_ISSETUGID
3814 if (issetugid())
3815 return 1;
3816#endif
3817
3818#ifdef HAVE_GETRESUID
3819 {
3820 int ret;
3821 rb_uid_t suid;
3822 ret = getresuid(&ruid, &euid, &suid);
3823 if (ret == -1)
3824 rb_sys_fail("getresuid(2)");
3825 if (euid != suid)
3826 return 1;
3827 }
3828#else
3829 ruid = getuid();
3830 euid = geteuid();
3831#endif
3832
3833 if (euid == 0 || euid != ruid)
3834 return 1;
3835
3836#ifdef HAVE_GETRESGID
3837 {
3838 int ret;
3839 rb_gid_t sgid;
3840 ret = getresgid(&rgid, &egid, &sgid);
3841 if (ret == -1)
3842 rb_sys_fail("getresgid(2)");
3843 if (egid != sgid)
3844 return 1;
3845 }
3846#else
3847 rgid = getgid();
3848 egid = getegid();
3849#endif
3850
3851 if (egid != rgid)
3852 return 1;
3853
3854 return 0;
3855}
3856#endif
3857#endif
3858
3859struct child_handler_disabler_state
3860{
3861 sigset_t sigmask;
3862};
3863
3864static void
3865disable_child_handler_before_fork(struct child_handler_disabler_state *old)
3866{
3867#ifdef HAVE_PTHREAD_SIGMASK
3868 int ret;
3869 sigset_t all;
3870
3871 ret = sigfillset(&all);
3872 if (ret == -1)
3873 rb_sys_fail("sigfillset");
3874
3875 ret = pthread_sigmask(SIG_SETMASK, &all, &old->sigmask); /* not async-signal-safe */
3876 if (ret != 0) {
3877 rb_syserr_fail(ret, "pthread_sigmask");
3878 }
3879#else
3880# pragma GCC warning "pthread_sigmask on fork is not available. potentially dangerous"
3881#endif
3882}
3883
3884static void
3885disable_child_handler_fork_parent(struct child_handler_disabler_state *old)
3886{
3887#ifdef HAVE_PTHREAD_SIGMASK
3888 int ret;
3889
3890 ret = pthread_sigmask(SIG_SETMASK, &old->sigmask, NULL); /* not async-signal-safe */
3891 if (ret != 0) {
3892 rb_syserr_fail(ret, "pthread_sigmask");
3893 }
3894#else
3895# pragma GCC warning "pthread_sigmask on fork is not available. potentially dangerous"
3896#endif
3897}
3898
3899/* This function should be async-signal-safe. Actually it is. */
3900static int
3901disable_child_handler_fork_child(struct child_handler_disabler_state *old, char *errmsg, size_t errmsg_buflen)
3902{
3903 int sig;
3904 int ret;
3905
3906 for (sig = 1; sig < NSIG; sig++) {
3907 sig_t handler = signal(sig, SIG_DFL);
3908
3909 if (handler == SIG_ERR && errno == EINVAL) {
3910 continue; /* Ignore invalid signal number */
3911 }
3912 if (handler == SIG_ERR) {
3913 ERRMSG("signal to obtain old action");
3914 return -1;
3915 }
3916#ifdef SIGPIPE
3917 if (sig == SIGPIPE) {
3918 continue;
3919 }
3920#endif
3921 /* it will be reset to SIG_DFL at execve time, instead */
3922 if (handler == SIG_IGN) {
3923 signal(sig, SIG_IGN);
3924 }
3925 }
3926
3927 /* non-Ruby child process, ensure cmake can see SIGCHLD */
3928 sigemptyset(&old->sigmask);
3929 ret = sigprocmask(SIG_SETMASK, &old->sigmask, NULL); /* async-signal-safe */
3930 if (ret != 0) {
3931 ERRMSG("sigprocmask");
3932 return -1;
3933 }
3934 return 0;
3935}
3936
3937static rb_pid_t
3938retry_fork_async_signal_safe(struct rb_process_status *status, int *ep,
3939 int (*chfunc)(void*, char *, size_t), void *charg,
3940 char *errmsg, size_t errmsg_buflen,
3941 struct waitpid_state *w)
3942{
3943 rb_pid_t pid;
3944 volatile int try_gc = 1;
3945 struct child_handler_disabler_state old;
3946 int err;
3947
3948 while (1) {
3949 prefork();
3950 disable_child_handler_before_fork(&old);
3951
3952 // Older versions of ASAN does not work with vfork
3953 // See https://github.com/google/sanitizers/issues/925
3954#if defined(HAVE_WORKING_VFORK) && !defined(RUBY_ASAN_ENABLED)
3955 if (!has_privilege())
3956 pid = vfork();
3957 else
3958 pid = rb_fork();
3959#else
3960 pid = rb_fork();
3961#endif
3962 if (pid == 0) {/* fork succeed, child process */
3963 int ret;
3964 close(ep[0]);
3965 ret = disable_child_handler_fork_child(&old, errmsg, errmsg_buflen); /* async-signal-safe */
3966 if (ret == 0) {
3967 ret = chfunc(charg, errmsg, errmsg_buflen);
3968 if (!ret) _exit(EXIT_SUCCESS);
3969 }
3970 send_child_error(ep[1], errmsg, errmsg_buflen);
3971#if EXIT_SUCCESS == 127
3972 _exit(EXIT_FAILURE);
3973#else
3974 _exit(127);
3975#endif
3976 }
3977 err = errno;
3978 disable_child_handler_fork_parent(&old);
3979 if (0 < pid) /* fork succeed, parent process */
3980 return pid;
3981 /* fork failed */
3982 if (handle_fork_error(err, status, ep, &try_gc))
3983 return -1;
3984 }
3985}
3986
3987static rb_pid_t
3988fork_check_err(struct rb_process_status *status, int (*chfunc)(void*, char *, size_t), void *charg,
3989 VALUE fds, char *errmsg, size_t errmsg_buflen,
3990 struct rb_execarg *eargp)
3991{
3992 rb_pid_t pid;
3993 int err;
3994 int ep[2];
3995 int error_occurred;
3996
3997 struct waitpid_state *w = eargp && eargp->waitpid_state ? eargp->waitpid_state : 0;
3998
3999 if (status) status->status = 0;
4000
4001 if (pipe_nocrash(ep, fds)) return -1;
4002
4003 pid = retry_fork_async_signal_safe(status, ep, chfunc, charg, errmsg, errmsg_buflen, w);
4004
4005 if (status) status->pid = pid;
4006
4007 if (pid < 0) {
4008 if (status) status->error = errno;
4009
4010 return pid;
4011 }
4012
4013 close(ep[1]);
4014
4015 error_occurred = recv_child_error(ep[0], &err, errmsg, errmsg_buflen);
4016
4017 if (error_occurred) {
4018 if (status) {
4019 int state = 0;
4020 status->error = err;
4021
4022 VM_ASSERT((w == 0) && "only used by extensions");
4023 rb_protect(proc_syswait, (VALUE)pid, &state);
4024
4025 status->status = state;
4026 }
4027 else if (!w) {
4028 rb_syswait(pid);
4029 }
4030
4031 errno = err;
4032 return -1;
4033 }
4034
4035 return pid;
4036}
4037
4038/*
4039 * The "async_signal_safe" name is a lie, but it is used by pty.c and
4040 * maybe other exts. fork() is not async-signal-safe due to pthread_atfork
4041 * and future POSIX revisions will remove it from a list of signal-safe
4042 * functions. rb_waitpid is not async-signal-safe.
4043 * For our purposes, we do not need async-signal-safety, here
4044 */
4045rb_pid_t
4046rb_fork_async_signal_safe(int *status,
4047 int (*chfunc)(void*, char *, size_t), void *charg,
4048 VALUE fds, char *errmsg, size_t errmsg_buflen)
4049{
4050 struct rb_process_status process_status;
4051
4052 rb_pid_t result = fork_check_err(&process_status, chfunc, charg, fds, errmsg, errmsg_buflen, 0);
4053
4054 if (status) {
4055 *status = process_status.status;
4056 }
4057
4058 return result;
4059}
4060
4061rb_pid_t
4062rb_fork_ruby(int *status)
4063{
4064 if (UNLIKELY(!rb_ractor_main_p())) {
4065 rb_raise(rb_eRactorIsolationError, "can not fork from non-main Ractors");
4066 }
4067
4068 struct rb_process_status child = {.status = 0};
4069 rb_pid_t pid;
4070 int try_gc = 1, err = 0;
4071 struct child_handler_disabler_state old;
4072
4073 do {
4074 prefork();
4075
4076 before_fork_ruby();
4077 rb_thread_acquire_fork_lock();
4078 disable_child_handler_before_fork(&old);
4079
4080 RB_VM_LOCKING() {
4081 child.pid = pid = rb_fork();
4082 child.error = err = errno;
4083 }
4084
4085 disable_child_handler_fork_parent(&old); /* yes, bad name */
4086 if (
4087#if defined(__FreeBSD__)
4088 pid != 0 &&
4089#endif
4090 true) {
4091 rb_thread_release_fork_lock();
4092 }
4093 if (pid == 0) {
4094 rb_thread_reset_fork_lock();
4095 }
4096 after_fork_ruby(pid);
4097
4098 /* repeat while fork failed but retryable */
4099 } while (pid < 0 && handle_fork_error(err, &child, 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__
4453char *
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 StringValueCStr(*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, long 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, long len)
5331{
5332 return rlimit_resource_name2int(name, len, 0);
5333}
5334
5335static int
5336rlimit_type_by_lname(const char *name, long 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 long 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 long 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 long 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 long 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, (long)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 long 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, (long)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 long 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, (long)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 long 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, (long)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 before_fork_ruby();
6914 err = daemon(nochdir, noclose);
6915 after_fork_ruby(0);
6916#else
6917 int n;
6918
6919 switch (rb_fork_ruby(NULL)) {
6920 case -1: return -1;
6921 case 0: break;
6922 default: _exit(EXIT_SUCCESS);
6923 }
6924
6925 /* ignore EPERM which means already being process-leader */
6926 if (setsid() < 0) (void)0;
6927
6928 if (!nochdir)
6929 err = chdir("/");
6930
6931 if (!noclose && (n = rb_cloexec_open(ruby_null_device, O_RDWR, 0)) != -1) {
6933 (void)dup2(n, 0);
6934 (void)dup2(n, 1);
6935 (void)dup2(n, 2);
6936 if (n > 2)
6937 (void)close (n);
6938 }
6939#endif
6940 return err;
6941}
6942#else
6943#define proc_daemon rb_f_notimplement
6944#endif
6945
6946/********************************************************************
6947 *
6948 * Document-class: Process::GID
6949 *
6950 * The Process::GID module contains a collection of
6951 * module functions which can be used to portably get, set, and
6952 * switch the current process's real, effective, and saved group IDs.
6953 *
6954 */
6955
6956static rb_gid_t SAVED_GROUP_ID = -1;
6957
6958/*
6959 * call-seq:
6960 * Process::GID.change_privilege(group) -> integer
6961 *
6962 * Change the current process's real and effective group ID to that
6963 * specified by _group_. Returns the new group ID. Not
6964 * available on all platforms.
6965 *
6966 * [Process.gid, Process.egid] #=> [0, 0]
6967 * Process::GID.change_privilege(33) #=> 33
6968 * [Process.gid, Process.egid] #=> [33, 33]
6969 */
6970
6971static VALUE
6972p_gid_change_privilege(VALUE obj, VALUE id)
6973{
6974 rb_gid_t gid;
6975
6976 check_gid_switch();
6977
6978 gid = OBJ2GID(id);
6979
6980 if (geteuid() == 0) { /* root-user */
6981#if defined(HAVE_SETRESGID)
6982 if (setresgid(gid, gid, gid) < 0) rb_sys_fail(0);
6983 SAVED_GROUP_ID = gid;
6984#elif defined HAVE_SETGID
6985 if (setgid(gid) < 0) rb_sys_fail(0);
6986 SAVED_GROUP_ID = gid;
6987#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
6988 if (getgid() == gid) {
6989 if (SAVED_GROUP_ID == gid) {
6990 if (setregid(-1, gid) < 0) rb_sys_fail(0);
6991 }
6992 else {
6993 if (gid == 0) { /* (r,e,s) == (root, y, x) */
6994 if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
6995 if (setregid(SAVED_GROUP_ID, 0) < 0) rb_sys_fail(0);
6996 SAVED_GROUP_ID = 0; /* (r,e,s) == (x, root, root) */
6997 if (setregid(gid, gid) < 0) rb_sys_fail(0);
6998 SAVED_GROUP_ID = gid;
6999 }
7000 else { /* (r,e,s) == (z, y, x) */
7001 if (setregid(0, 0) < 0) rb_sys_fail(0);
7002 SAVED_GROUP_ID = 0;
7003 if (setregid(gid, gid) < 0) rb_sys_fail(0);
7004 SAVED_GROUP_ID = gid;
7005 }
7006 }
7007 }
7008 else {
7009 if (setregid(gid, gid) < 0) rb_sys_fail(0);
7010 SAVED_GROUP_ID = gid;
7011 }
7012#elif defined(HAVE_SETRGID) && defined (HAVE_SETEGID)
7013 if (getgid() == gid) {
7014 if (SAVED_GROUP_ID == gid) {
7015 if (setegid(gid) < 0) rb_sys_fail(0);
7016 }
7017 else {
7018 if (gid == 0) {
7019 if (setegid(gid) < 0) rb_sys_fail(0);
7020 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7021 SAVED_GROUP_ID = 0;
7022 if (setrgid(0) < 0) rb_sys_fail(0);
7023 }
7024 else {
7025 if (setrgid(0) < 0) rb_sys_fail(0);
7026 SAVED_GROUP_ID = 0;
7027 if (setegid(gid) < 0) rb_sys_fail(0);
7028 if (setrgid(gid) < 0) rb_sys_fail(0);
7029 SAVED_GROUP_ID = gid;
7030 }
7031 }
7032 }
7033 else {
7034 if (setegid(gid) < 0) rb_sys_fail(0);
7035 if (setrgid(gid) < 0) rb_sys_fail(0);
7036 SAVED_GROUP_ID = gid;
7037 }
7038#else
7039 rb_notimplement();
7040#endif
7041 }
7042 else { /* unprivileged user */
7043#if defined(HAVE_SETRESGID)
7044 if (setresgid((getgid() == gid)? (rb_gid_t)-1: gid,
7045 (getegid() == gid)? (rb_gid_t)-1: gid,
7046 (SAVED_GROUP_ID == gid)? (rb_gid_t)-1: gid) < 0) rb_sys_fail(0);
7047 SAVED_GROUP_ID = gid;
7048#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7049 if (SAVED_GROUP_ID == gid) {
7050 if (setregid((getgid() == gid)? (rb_uid_t)-1: gid,
7051 (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
7052 rb_sys_fail(0);
7053 }
7054 else if (getgid() != gid) {
7055 if (setregid(gid, (getegid() == gid)? (rb_uid_t)-1: gid) < 0)
7056 rb_sys_fail(0);
7057 SAVED_GROUP_ID = gid;
7058 }
7059 else if (/* getgid() == gid && */ getegid() != gid) {
7060 if (setregid(getegid(), gid) < 0) rb_sys_fail(0);
7061 SAVED_GROUP_ID = gid;
7062 if (setregid(gid, -1) < 0) rb_sys_fail(0);
7063 }
7064 else { /* getgid() == gid && getegid() == gid */
7065 if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7066 if (setregid(SAVED_GROUP_ID, gid) < 0) rb_sys_fail(0);
7067 SAVED_GROUP_ID = gid;
7068 if (setregid(gid, -1) < 0) rb_sys_fail(0);
7069 }
7070#elif defined(HAVE_SETRGID) && defined(HAVE_SETEGID)
7071 if (SAVED_GROUP_ID == gid) {
7072 if (getegid() != gid && setegid(gid) < 0) rb_sys_fail(0);
7073 if (getgid() != gid && setrgid(gid) < 0) rb_sys_fail(0);
7074 }
7075 else if (/* SAVED_GROUP_ID != gid && */ getegid() == gid) {
7076 if (getgid() != gid) {
7077 if (setrgid(gid) < 0) rb_sys_fail(0);
7078 SAVED_GROUP_ID = gid;
7079 }
7080 else {
7081 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7082 SAVED_GROUP_ID = gid;
7083 if (setrgid(gid) < 0) rb_sys_fail(0);
7084 }
7085 }
7086 else if (/* getegid() != gid && */ getgid() == gid) {
7087 if (setegid(gid) < 0) rb_sys_fail(0);
7088 if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
7089 SAVED_GROUP_ID = gid;
7090 if (setrgid(gid) < 0) rb_sys_fail(0);
7091 }
7092 else {
7093 rb_syserr_fail(EPERM, 0);
7094 }
7095#elif defined HAVE_44BSD_SETGID
7096 if (getgid() == gid) {
7097 /* (r,e,s)==(gid,?,?) ==> (gid,gid,gid) */
7098 if (setgid(gid) < 0) rb_sys_fail(0);
7099 SAVED_GROUP_ID = gid;
7100 }
7101 else {
7102 rb_syserr_fail(EPERM, 0);
7103 }
7104#elif defined HAVE_SETEGID
7105 if (getgid() == gid && SAVED_GROUP_ID == gid) {
7106 if (setegid(gid) < 0) rb_sys_fail(0);
7107 }
7108 else {
7109 rb_syserr_fail(EPERM, 0);
7110 }
7111#elif defined HAVE_SETGID
7112 if (getgid() == gid && SAVED_GROUP_ID == gid) {
7113 if (setgid(gid) < 0) rb_sys_fail(0);
7114 }
7115 else {
7116 rb_syserr_fail(EPERM, 0);
7117 }
7118#else
7119 (void)gid;
7120 rb_notimplement();
7121#endif
7122 }
7123 return id;
7124}
7125
7126
7127/*
7128 * call-seq:
7129 * Process.euid -> integer
7130 * Process::UID.eid -> integer
7131 * Process::Sys.geteuid -> integer
7132 *
7133 * Returns the effective user ID for the current process.
7134 *
7135 * Process.euid # => 501
7136 *
7137 */
7138
7139static VALUE
7140proc_geteuid(VALUE obj)
7141{
7142 rb_uid_t euid = geteuid();
7143 return UIDT2NUM(euid);
7144}
7145
7146#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID) || defined(_POSIX_SAVED_IDS)
7147static void
7148proc_seteuid(rb_uid_t uid)
7149{
7150#if defined(HAVE_SETRESUID)
7151 if (setresuid(-1, uid, -1) < 0) rb_sys_fail(0);
7152#elif defined HAVE_SETREUID
7153 if (setreuid(-1, uid) < 0) rb_sys_fail(0);
7154#elif defined HAVE_SETEUID
7155 if (seteuid(uid) < 0) rb_sys_fail(0);
7156#elif defined HAVE_SETUID
7157 if (uid == getuid()) {
7158 if (setuid(uid) < 0) rb_sys_fail(0);
7159 }
7160 else {
7161 rb_notimplement();
7162 }
7163#else
7164 rb_notimplement();
7165#endif
7166}
7167#endif
7168
7169#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) || defined(HAVE_SETUID)
7170/*
7171 * call-seq:
7172 * Process.euid = new_euid -> new_euid
7173 *
7174 * Sets the effective user ID for the current process.
7175 *
7176 * Not available on all platforms.
7177 */
7178
7179static VALUE
7180proc_seteuid_m(VALUE mod, VALUE euid)
7181{
7182 check_uid_switch();
7183 proc_seteuid(OBJ2UID(euid));
7184 return euid;
7185}
7186#else
7187#define proc_seteuid_m rb_f_notimplement
7188#endif
7189
7190static rb_uid_t
7191rb_seteuid_core(rb_uid_t euid)
7192{
7193#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7194 rb_uid_t uid;
7195#endif
7196
7197 check_uid_switch();
7198
7199#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7200 uid = getuid();
7201#endif
7202
7203#if defined(HAVE_SETRESUID)
7204 if (uid != euid) {
7205 if (setresuid(-1,euid,euid) < 0) rb_sys_fail(0);
7206 SAVED_USER_ID = euid;
7207 }
7208 else {
7209 if (setresuid(-1,euid,-1) < 0) rb_sys_fail(0);
7210 }
7211#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7212 if (setreuid(-1, euid) < 0) rb_sys_fail(0);
7213 if (uid != euid) {
7214 if (setreuid(euid,uid) < 0) rb_sys_fail(0);
7215 if (setreuid(uid,euid) < 0) rb_sys_fail(0);
7216 SAVED_USER_ID = euid;
7217 }
7218#elif defined HAVE_SETEUID
7219 if (seteuid(euid) < 0) rb_sys_fail(0);
7220#elif defined HAVE_SETUID
7221 if (geteuid() == 0) rb_sys_fail(0);
7222 if (setuid(euid) < 0) rb_sys_fail(0);
7223#else
7224 rb_notimplement();
7225#endif
7226 return euid;
7227}
7228
7229
7230/*
7231 * call-seq:
7232 * Process::UID.grant_privilege(user) -> integer
7233 * Process::UID.eid= user -> integer
7234 *
7235 * Set the effective user ID, and if possible, the saved user ID of
7236 * the process to the given _user_. Returns the new
7237 * effective user ID. Not available on all platforms.
7238 *
7239 * [Process.uid, Process.euid] #=> [0, 0]
7240 * Process::UID.grant_privilege(31) #=> 31
7241 * [Process.uid, Process.euid] #=> [0, 31]
7242 */
7243
7244static VALUE
7245p_uid_grant_privilege(VALUE obj, VALUE id)
7246{
7247 rb_seteuid_core(OBJ2UID(id));
7248 return id;
7249}
7250
7251
7252/*
7253 * call-seq:
7254 * Process.egid -> integer
7255 * Process::GID.eid -> integer
7256 * Process::Sys.geteid -> integer
7257 *
7258 * Returns the effective group ID for the current process:
7259 *
7260 * Process.egid # => 500
7261 *
7262 * Not available on all platforms.
7263 */
7264
7265static VALUE
7266proc_getegid(VALUE obj)
7267{
7268 rb_gid_t egid = getegid();
7269
7270 return GIDT2NUM(egid);
7271}
7272
7273#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID) || defined(_POSIX_SAVED_IDS)
7274/*
7275 * call-seq:
7276 * Process.egid = new_egid -> new_egid
7277 *
7278 * Sets the effective group ID for the current process.
7279 *
7280 * Not available on all platforms.
7281 */
7282
7283static VALUE
7284proc_setegid(VALUE obj, VALUE egid)
7285{
7286#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7287 rb_gid_t gid;
7288#endif
7289
7290 check_gid_switch();
7291
7292#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7293 gid = OBJ2GID(egid);
7294#endif
7295
7296#if defined(HAVE_SETRESGID)
7297 if (setresgid(-1, gid, -1) < 0) rb_sys_fail(0);
7298#elif defined HAVE_SETREGID
7299 if (setregid(-1, gid) < 0) rb_sys_fail(0);
7300#elif defined HAVE_SETEGID
7301 if (setegid(gid) < 0) rb_sys_fail(0);
7302#elif defined HAVE_SETGID
7303 if (gid == getgid()) {
7304 if (setgid(gid) < 0) rb_sys_fail(0);
7305 }
7306 else {
7307 rb_notimplement();
7308 }
7309#else
7310 rb_notimplement();
7311#endif
7312 return egid;
7313}
7314#endif
7315
7316#if defined(HAVE_SETRESGID) || defined(HAVE_SETREGID) || defined(HAVE_SETEGID) || defined(HAVE_SETGID)
7317#define proc_setegid_m proc_setegid
7318#else
7319#define proc_setegid_m rb_f_notimplement
7320#endif
7321
7322static rb_gid_t
7323rb_setegid_core(rb_gid_t egid)
7324{
7325#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7326 rb_gid_t gid;
7327#endif
7328
7329 check_gid_switch();
7330
7331#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7332 gid = getgid();
7333#endif
7334
7335#if defined(HAVE_SETRESGID)
7336 if (gid != egid) {
7337 if (setresgid(-1,egid,egid) < 0) rb_sys_fail(0);
7338 SAVED_GROUP_ID = egid;
7339 }
7340 else {
7341 if (setresgid(-1,egid,-1) < 0) rb_sys_fail(0);
7342 }
7343#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7344 if (setregid(-1, egid) < 0) rb_sys_fail(0);
7345 if (gid != egid) {
7346 if (setregid(egid,gid) < 0) rb_sys_fail(0);
7347 if (setregid(gid,egid) < 0) rb_sys_fail(0);
7348 SAVED_GROUP_ID = egid;
7349 }
7350#elif defined HAVE_SETEGID
7351 if (setegid(egid) < 0) rb_sys_fail(0);
7352#elif defined HAVE_SETGID
7353 if (geteuid() == 0 /* root user */) rb_sys_fail(0);
7354 if (setgid(egid) < 0) rb_sys_fail(0);
7355#else
7356 rb_notimplement();
7357#endif
7358 return egid;
7359}
7360
7361
7362/*
7363 * call-seq:
7364 * Process::GID.grant_privilege(group) -> integer
7365 * Process::GID.eid = group -> integer
7366 *
7367 * Set the effective group ID, and if possible, the saved group ID of
7368 * the process to the given _group_. Returns the new
7369 * effective group ID. Not available on all platforms.
7370 *
7371 * [Process.gid, Process.egid] #=> [0, 0]
7372 * Process::GID.grant_privilege(31) #=> 33
7373 * [Process.gid, Process.egid] #=> [0, 33]
7374 */
7375
7376static VALUE
7377p_gid_grant_privilege(VALUE obj, VALUE id)
7378{
7379 rb_setegid_core(OBJ2GID(id));
7380 return id;
7381}
7382
7383
7384/*
7385 * call-seq:
7386 * Process::UID.re_exchangeable? -> true or false
7387 *
7388 * Returns +true+ if the real and effective user IDs of a
7389 * process may be exchanged on the current platform.
7390 *
7391 */
7392
7393static VALUE
7394p_uid_exchangeable(VALUE _)
7395{
7396#if defined(HAVE_SETRESUID)
7397 return Qtrue;
7398#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7399 return Qtrue;
7400#else
7401 return Qfalse;
7402#endif
7403}
7404
7405
7406/*
7407 * call-seq:
7408 * Process::UID.re_exchange -> integer
7409 *
7410 * Exchange real and effective user IDs and return the new effective
7411 * user ID. Not available on all platforms.
7412 *
7413 * [Process.uid, Process.euid] #=> [0, 31]
7414 * Process::UID.re_exchange #=> 0
7415 * [Process.uid, Process.euid] #=> [31, 0]
7416 */
7417
7418static VALUE
7419p_uid_exchange(VALUE obj)
7420{
7421 rb_uid_t uid;
7422#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7423 rb_uid_t euid;
7424#endif
7425
7426 check_uid_switch();
7427
7428 uid = getuid();
7429#if defined(HAVE_SETRESUID) || (defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID))
7430 euid = geteuid();
7431#endif
7432
7433#if defined(HAVE_SETRESUID)
7434 if (setresuid(euid, uid, uid) < 0) rb_sys_fail(0);
7435 SAVED_USER_ID = uid;
7436#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
7437 if (setreuid(euid,uid) < 0) rb_sys_fail(0);
7438 SAVED_USER_ID = uid;
7439#else
7440 rb_notimplement();
7441#endif
7442 return UIDT2NUM(uid);
7443}
7444
7445
7446/*
7447 * call-seq:
7448 * Process::GID.re_exchangeable? -> true or false
7449 *
7450 * Returns +true+ if the real and effective group IDs of a
7451 * process may be exchanged on the current platform.
7452 *
7453 */
7454
7455static VALUE
7456p_gid_exchangeable(VALUE _)
7457{
7458#if defined(HAVE_SETRESGID)
7459 return Qtrue;
7460#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7461 return Qtrue;
7462#else
7463 return Qfalse;
7464#endif
7465}
7466
7467
7468/*
7469 * call-seq:
7470 * Process::GID.re_exchange -> integer
7471 *
7472 * Exchange real and effective group IDs and return the new effective
7473 * group ID. Not available on all platforms.
7474 *
7475 * [Process.gid, Process.egid] #=> [0, 33]
7476 * Process::GID.re_exchange #=> 0
7477 * [Process.gid, Process.egid] #=> [33, 0]
7478 */
7479
7480static VALUE
7481p_gid_exchange(VALUE obj)
7482{
7483 rb_gid_t gid;
7484#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7485 rb_gid_t egid;
7486#endif
7487
7488 check_gid_switch();
7489
7490 gid = getgid();
7491#if defined(HAVE_SETRESGID) || (defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID))
7492 egid = getegid();
7493#endif
7494
7495#if defined(HAVE_SETRESGID)
7496 if (setresgid(egid, gid, gid) < 0) rb_sys_fail(0);
7497 SAVED_GROUP_ID = gid;
7498#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
7499 if (setregid(egid,gid) < 0) rb_sys_fail(0);
7500 SAVED_GROUP_ID = gid;
7501#else
7502 rb_notimplement();
7503#endif
7504 return GIDT2NUM(gid);
7505}
7506
7507/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
7508
7509/*
7510 * call-seq:
7511 * Process::UID.sid_available? -> true or false
7512 *
7513 * Returns +true+ if the current platform has saved user
7514 * ID functionality.
7515 *
7516 */
7517
7518static VALUE
7519p_uid_have_saved_id(VALUE _)
7520{
7521#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
7522 return Qtrue;
7523#else
7524 return Qfalse;
7525#endif
7526}
7527
7528
7529#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || defined(_POSIX_SAVED_IDS)
7530static VALUE
7531p_uid_sw_ensure(VALUE i)
7532{
7533 rb_uid_t id = (rb_uid_t/* narrowing */)i;
7534 under_uid_switch = 0;
7535 id = rb_seteuid_core(id);
7536 return UIDT2NUM(id);
7537}
7538
7539
7540/*
7541 * call-seq:
7542 * Process::UID.switch -> integer
7543 * Process::UID.switch {|| block} -> object
7544 *
7545 * Switch the effective and real user IDs of the current process. If
7546 * a <em>block</em> is given, the user IDs will be switched back
7547 * after the block is executed. Returns the new effective user ID if
7548 * called without a block, and the return value of the block if one
7549 * is given.
7550 *
7551 */
7552
7553static VALUE
7554p_uid_switch(VALUE obj)
7555{
7556 rb_uid_t uid, euid;
7557
7558 check_uid_switch();
7559
7560 uid = getuid();
7561 euid = geteuid();
7562
7563 if (uid != euid) {
7564 proc_seteuid(uid);
7565 if (rb_block_given_p()) {
7566 under_uid_switch = 1;
7567 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, SAVED_USER_ID);
7568 }
7569 else {
7570 return UIDT2NUM(euid);
7571 }
7572 }
7573 else if (euid != SAVED_USER_ID) {
7574 proc_seteuid(SAVED_USER_ID);
7575 if (rb_block_given_p()) {
7576 under_uid_switch = 1;
7577 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, euid);
7578 }
7579 else {
7580 return UIDT2NUM(uid);
7581 }
7582 }
7583 else {
7584 rb_syserr_fail(EPERM, 0);
7585 }
7586
7588}
7589#else
7590static VALUE
7591p_uid_sw_ensure(VALUE obj)
7592{
7593 under_uid_switch = 0;
7594 return p_uid_exchange(obj);
7595}
7596
7597static VALUE
7598p_uid_switch(VALUE obj)
7599{
7600 rb_uid_t uid, euid;
7601
7602 check_uid_switch();
7603
7604 uid = getuid();
7605 euid = geteuid();
7606
7607 if (uid == euid) {
7608 rb_syserr_fail(EPERM, 0);
7609 }
7610 p_uid_exchange(obj);
7611 if (rb_block_given_p()) {
7612 under_uid_switch = 1;
7613 return rb_ensure(rb_yield, Qnil, p_uid_sw_ensure, obj);
7614 }
7615 else {
7616 return UIDT2NUM(euid);
7617 }
7618}
7619#endif
7620
7621
7622/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
7623
7624/*
7625 * call-seq:
7626 * Process::GID.sid_available? -> true or false
7627 *
7628 * Returns +true+ if the current platform has saved group
7629 * ID functionality.
7630 *
7631 */
7632
7633static VALUE
7634p_gid_have_saved_id(VALUE _)
7635{
7636#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
7637 return Qtrue;
7638#else
7639 return Qfalse;
7640#endif
7641}
7642
7643#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || defined(_POSIX_SAVED_IDS)
7644static VALUE
7645p_gid_sw_ensure(VALUE i)
7646{
7647 rb_gid_t id = (rb_gid_t/* narrowing */)i;
7648 under_gid_switch = 0;
7649 id = rb_setegid_core(id);
7650 return GIDT2NUM(id);
7651}
7652
7653
7654/*
7655 * call-seq:
7656 * Process::GID.switch -> integer
7657 * Process::GID.switch {|| block} -> object
7658 *
7659 * Switch the effective and real group IDs of the current process. If
7660 * a <em>block</em> is given, the group IDs will be switched back
7661 * after the block is executed. Returns the new effective group ID if
7662 * called without a block, and the return value of the block if one
7663 * is given.
7664 *
7665 */
7666
7667static VALUE
7668p_gid_switch(VALUE obj)
7669{
7670 rb_gid_t gid, egid;
7671
7672 check_gid_switch();
7673
7674 gid = getgid();
7675 egid = getegid();
7676
7677 if (gid != egid) {
7678 proc_setegid(obj, GIDT2NUM(gid));
7679 if (rb_block_given_p()) {
7680 under_gid_switch = 1;
7681 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, SAVED_GROUP_ID);
7682 }
7683 else {
7684 return GIDT2NUM(egid);
7685 }
7686 }
7687 else if (egid != SAVED_GROUP_ID) {
7688 proc_setegid(obj, GIDT2NUM(SAVED_GROUP_ID));
7689 if (rb_block_given_p()) {
7690 under_gid_switch = 1;
7691 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, egid);
7692 }
7693 else {
7694 return GIDT2NUM(gid);
7695 }
7696 }
7697 else {
7698 rb_syserr_fail(EPERM, 0);
7699 }
7700
7702}
7703#else
7704static VALUE
7705p_gid_sw_ensure(VALUE obj)
7706{
7707 under_gid_switch = 0;
7708 return p_gid_exchange(obj);
7709}
7710
7711static VALUE
7712p_gid_switch(VALUE obj)
7713{
7714 rb_gid_t gid, egid;
7715
7716 check_gid_switch();
7717
7718 gid = getgid();
7719 egid = getegid();
7720
7721 if (gid == egid) {
7722 rb_syserr_fail(EPERM, 0);
7723 }
7724 p_gid_exchange(obj);
7725 if (rb_block_given_p()) {
7726 under_gid_switch = 1;
7727 return rb_ensure(rb_yield, Qnil, p_gid_sw_ensure, obj);
7728 }
7729 else {
7730 return GIDT2NUM(egid);
7731 }
7732}
7733#endif
7734
7735
7736#if defined(HAVE_TIMES)
7737static long
7738get_clk_tck(void)
7739{
7740#ifdef HAVE__SC_CLK_TCK
7741 return sysconf(_SC_CLK_TCK);
7742#elif defined CLK_TCK
7743 return CLK_TCK;
7744#elif defined HZ
7745 return HZ;
7746#else
7747 return 60;
7748#endif
7749}
7750
7751/*
7752 * call-seq:
7753 * Process.times -> process_tms
7754 *
7755 * Returns a Process::Tms structure that contains user and system CPU times
7756 * for the current process, and for its children processes:
7757 *
7758 * Process.times
7759 * # => #<struct Process::Tms utime=55.122118, stime=35.533068, cutime=0.0, cstime=0.002846>
7760 *
7761 * The precision is platform-defined.
7762 */
7763
7764VALUE
7765rb_proc_times(VALUE obj)
7766{
7767 VALUE utime, stime, cutime, cstime, ret;
7768#if defined(RUSAGE_SELF) && defined(RUSAGE_CHILDREN)
7769 struct rusage usage_s, usage_c;
7770
7771 if (getrusage(RUSAGE_SELF, &usage_s) != 0 || getrusage(RUSAGE_CHILDREN, &usage_c) != 0)
7772 rb_sys_fail("getrusage");
7773 utime = DBL2NUM((double)usage_s.ru_utime.tv_sec + (double)usage_s.ru_utime.tv_usec/1e6);
7774 stime = DBL2NUM((double)usage_s.ru_stime.tv_sec + (double)usage_s.ru_stime.tv_usec/1e6);
7775 cutime = DBL2NUM((double)usage_c.ru_utime.tv_sec + (double)usage_c.ru_utime.tv_usec/1e6);
7776 cstime = DBL2NUM((double)usage_c.ru_stime.tv_sec + (double)usage_c.ru_stime.tv_usec/1e6);
7777#else
7778 const double hertz = (double)get_clk_tck();
7779 struct tms buf;
7780
7781 times(&buf);
7782 utime = DBL2NUM(buf.tms_utime / hertz);
7783 stime = DBL2NUM(buf.tms_stime / hertz);
7784 cutime = DBL2NUM(buf.tms_cutime / hertz);
7785 cstime = DBL2NUM(buf.tms_cstime / hertz);
7786#endif
7787 ret = rb_struct_new(rb_cProcessTms, utime, stime, cutime, cstime);
7788 RB_GC_GUARD(utime);
7789 RB_GC_GUARD(stime);
7790 RB_GC_GUARD(cutime);
7791 RB_GC_GUARD(cstime);
7792 return ret;
7793}
7794#else
7795#define rb_proc_times rb_f_notimplement
7796#endif
7797
7798#ifdef HAVE_LONG_LONG
7799typedef LONG_LONG timetick_int_t;
7800#define TIMETICK_INT_MIN LLONG_MIN
7801#define TIMETICK_INT_MAX LLONG_MAX
7802#define TIMETICK_INT2NUM(v) LL2NUM(v)
7803#define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_LONG_P(a, b)
7804#else
7805typedef long timetick_int_t;
7806#define TIMETICK_INT_MIN LONG_MIN
7807#define TIMETICK_INT_MAX LONG_MAX
7808#define TIMETICK_INT2NUM(v) LONG2NUM(v)
7809#define MUL_OVERFLOW_TIMETICK_P(a, b) MUL_OVERFLOW_LONG_P(a, b)
7810#endif
7811
7812CONSTFUNC(static timetick_int_t gcd_timetick_int(timetick_int_t, timetick_int_t));
7813static timetick_int_t
7814gcd_timetick_int(timetick_int_t a, timetick_int_t b)
7815{
7816 timetick_int_t t;
7817
7818 if (a < b) {
7819 t = a;
7820 a = b;
7821 b = t;
7822 }
7823
7824 while (1) {
7825 t = a % b;
7826 if (t == 0)
7827 return b;
7828 a = b;
7829 b = t;
7830 }
7831}
7832
7833static void
7834reduce_fraction(timetick_int_t *np, timetick_int_t *dp)
7835{
7836 timetick_int_t gcd = gcd_timetick_int(*np, *dp);
7837 if (gcd != 1) {
7838 *np /= gcd;
7839 *dp /= gcd;
7840 }
7841}
7842
7843static void
7844reduce_factors(timetick_int_t *numerators, int num_numerators,
7845 timetick_int_t *denominators, int num_denominators)
7846{
7847 int i, j;
7848 for (i = 0; i < num_numerators; i++) {
7849 if (numerators[i] == 1)
7850 continue;
7851 for (j = 0; j < num_denominators; j++) {
7852 if (denominators[j] == 1)
7853 continue;
7854 reduce_fraction(&numerators[i], &denominators[j]);
7855 }
7856 }
7857}
7858
7859struct timetick {
7860 timetick_int_t giga_count;
7861 int32_t count; /* 0 .. 999999999 */
7862};
7863
7864static VALUE
7865timetick2dblnum(struct timetick *ttp,
7866 timetick_int_t *numerators, int num_numerators,
7867 timetick_int_t *denominators, int num_denominators)
7868{
7869 double d;
7870 int i;
7871
7872 reduce_factors(numerators, num_numerators,
7873 denominators, num_denominators);
7874
7875 d = ttp->giga_count * 1e9 + ttp->count;
7876
7877 for (i = 0; i < num_numerators; i++)
7878 d *= numerators[i];
7879 for (i = 0; i < num_denominators; i++)
7880 d /= denominators[i];
7881
7882 return DBL2NUM(d);
7883}
7884
7885static VALUE
7886timetick2dblnum_reciprocal(struct timetick *ttp,
7887 timetick_int_t *numerators, int num_numerators,
7888 timetick_int_t *denominators, int num_denominators)
7889{
7890 double d;
7891 int i;
7892
7893 reduce_factors(numerators, num_numerators,
7894 denominators, num_denominators);
7895
7896 d = 1.0;
7897 for (i = 0; i < num_denominators; i++)
7898 d *= denominators[i];
7899 for (i = 0; i < num_numerators; i++)
7900 d /= numerators[i];
7901 d /= ttp->giga_count * 1e9 + ttp->count;
7902
7903 return DBL2NUM(d);
7904}
7905
7906#define NDIV(x,y) (-(-((x)+1)/(y))-1)
7907#define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d))
7908
7909static VALUE
7910timetick2integer(struct timetick *ttp,
7911 timetick_int_t *numerators, int num_numerators,
7912 timetick_int_t *denominators, int num_denominators)
7913{
7914 VALUE v;
7915 int i;
7916
7917 reduce_factors(numerators, num_numerators,
7918 denominators, num_denominators);
7919
7920 if (!MUL_OVERFLOW_SIGNED_INTEGER_P(1000000000, ttp->giga_count,
7921 TIMETICK_INT_MIN, TIMETICK_INT_MAX-ttp->count)) {
7922 timetick_int_t t = ttp->giga_count * 1000000000 + ttp->count;
7923 for (i = 0; i < num_numerators; i++) {
7924 timetick_int_t factor = numerators[i];
7925 if (MUL_OVERFLOW_TIMETICK_P(factor, t))
7926 goto generic;
7927 t *= factor;
7928 }
7929 for (i = 0; i < num_denominators; i++) {
7930 t = DIV(t, denominators[i]);
7931 }
7932 return TIMETICK_INT2NUM(t);
7933 }
7934
7935 generic:
7936 v = TIMETICK_INT2NUM(ttp->giga_count);
7937 v = rb_funcall(v, '*', 1, LONG2FIX(1000000000));
7938 v = rb_funcall(v, '+', 1, LONG2FIX(ttp->count));
7939 for (i = 0; i < num_numerators; i++) {
7940 timetick_int_t factor = numerators[i];
7941 if (factor == 1)
7942 continue;
7943 v = rb_funcall(v, '*', 1, TIMETICK_INT2NUM(factor));
7944 }
7945 for (i = 0; i < num_denominators; i++) {
7946 v = rb_funcall(v, '/', 1, TIMETICK_INT2NUM(denominators[i])); /* Ruby's '/' is div. */
7947 }
7948 return v;
7949}
7950
7951static VALUE
7952make_clock_result(struct timetick *ttp,
7953 timetick_int_t *numerators, int num_numerators,
7954 timetick_int_t *denominators, int num_denominators,
7955 VALUE unit)
7956{
7957 if (unit == ID2SYM(id_nanosecond)) {
7958 numerators[num_numerators++] = 1000000000;
7959 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
7960 }
7961 else if (unit == ID2SYM(id_microsecond)) {
7962 numerators[num_numerators++] = 1000000;
7963 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
7964 }
7965 else if (unit == ID2SYM(id_millisecond)) {
7966 numerators[num_numerators++] = 1000;
7967 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
7968 }
7969 else if (unit == ID2SYM(id_second)) {
7970 return timetick2integer(ttp, numerators, num_numerators, denominators, num_denominators);
7971 }
7972 else if (unit == ID2SYM(id_float_microsecond)) {
7973 numerators[num_numerators++] = 1000000;
7974 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
7975 }
7976 else if (unit == ID2SYM(id_float_millisecond)) {
7977 numerators[num_numerators++] = 1000;
7978 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
7979 }
7980 else if (NIL_P(unit) || unit == ID2SYM(id_float_second)) {
7981 return timetick2dblnum(ttp, numerators, num_numerators, denominators, num_denominators);
7982 }
7983 else
7984 rb_raise(rb_eArgError, "unexpected unit: %"PRIsVALUE, unit);
7985}
7986
7987#ifdef __APPLE__
7988static const mach_timebase_info_data_t *
7989get_mach_timebase_info(void)
7990{
7991 static mach_timebase_info_data_t sTimebaseInfo;
7992
7993 if ( sTimebaseInfo.denom == 0 ) {
7994 (void) mach_timebase_info(&sTimebaseInfo);
7995 }
7996
7997 return &sTimebaseInfo;
7998}
7999
8000double
8001ruby_real_ms_time(void)
8002{
8003 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8004 uint64_t t = mach_absolute_time();
8005 return (double)t * info->numer / info->denom / 1e6;
8006}
8007#endif
8008
8009#if defined(NUM2CLOCKID)
8010# define NUMERIC_CLOCKID 1
8011#else
8012# define NUMERIC_CLOCKID 0
8013# define NUM2CLOCKID(x) 0
8014#endif
8015
8016#define clock_failed(name, err, arg) do { \
8017 int clock_error = (err); \
8018 rb_syserr_fail_str(clock_error, rb_sprintf("clock_" name "(%+"PRIsVALUE")", (arg))); \
8019 } while (0)
8020
8021/*
8022 * call-seq:
8023 * Process.clock_gettime(clock_id, unit = :float_second) -> number
8024 *
8025 * Returns a clock time as determined by POSIX function
8026 * {clock_gettime()}[https://man7.org/linux/man-pages/man3/clock_gettime.3.html]:
8027 *
8028 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID) # => 198.650379677
8029 *
8030 * Argument +clock_id+ should be a symbol or a constant that specifies
8031 * the clock whose time is to be returned;
8032 * see below.
8033 *
8034 * Optional argument +unit+ should be a symbol that specifies
8035 * the unit to be used in the returned clock time;
8036 * see below.
8037 *
8038 * <b>Argument +clock_id+</b>
8039 *
8040 * Argument +clock_id+ specifies the clock whose time is to be returned;
8041 * it may be a constant such as <tt>Process::CLOCK_REALTIME</tt>,
8042 * or a symbol shorthand such as +:CLOCK_REALTIME+.
8043 *
8044 * The supported clocks depend on the underlying operating system;
8045 * this method supports the following clocks on the indicated platforms
8046 * (raises Errno::EINVAL if called with an unsupported clock):
8047 *
8048 * - +:CLOCK_BOOTTIME+: Linux 2.6.39.
8049 * - +:CLOCK_BOOTTIME_ALARM+: Linux 3.0.
8050 * - +:CLOCK_MONOTONIC+: SUSv3 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4, macOS 10.12, Windows-2000.
8051 * - +:CLOCK_MONOTONIC_COARSE+: Linux 2.6.32.
8052 * - +:CLOCK_MONOTONIC_FAST+: FreeBSD 8.1.
8053 * - +:CLOCK_MONOTONIC_PRECISE+: FreeBSD 8.1.
8054 * - +:CLOCK_MONOTONIC_RAW+: Linux 2.6.28, macOS 10.12.
8055 * - +:CLOCK_MONOTONIC_RAW_APPROX+: macOS 10.12.
8056 * - +:CLOCK_PROCESS_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 9.3, OpenBSD 5.4, macOS 10.12.
8057 * - +:CLOCK_PROF+: FreeBSD 3.0, OpenBSD 2.1.
8058 * - +: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.
8059 * Time.now is recommended over +:CLOCK_REALTIME:.
8060 * - +:CLOCK_REALTIME_ALARM+: Linux 3.0.
8061 * - +:CLOCK_REALTIME_COARSE+: Linux 2.6.32.
8062 * - +:CLOCK_REALTIME_FAST+: FreeBSD 8.1.
8063 * - +:CLOCK_REALTIME_PRECISE+: FreeBSD 8.1.
8064 * - +:CLOCK_SECOND+: FreeBSD 8.1.
8065 * - +:CLOCK_TAI+: Linux 3.10.
8066 * - +:CLOCK_THREAD_CPUTIME_ID+: SUSv3 to 4, Linux 2.5.63, FreeBSD 7.1, OpenBSD 5.4, macOS 10.12.
8067 * - +:CLOCK_UPTIME+: FreeBSD 7.0, OpenBSD 5.5.
8068 * - +:CLOCK_UPTIME_FAST+: FreeBSD 8.1.
8069 * - +:CLOCK_UPTIME_PRECISE+: FreeBSD 8.1.
8070 * - +:CLOCK_UPTIME_RAW+: macOS 10.12.
8071 * - +:CLOCK_UPTIME_RAW_APPROX+: macOS 10.12.
8072 * - +:CLOCK_VIRTUAL+: FreeBSD 3.0, OpenBSD 2.1.
8073 *
8074 * Note that SUS stands for Single Unix Specification.
8075 * SUS contains POSIX and clock_gettime is defined in the POSIX part.
8076 * SUS defines +:CLOCK_REALTIME+ as mandatory but
8077 * +:CLOCK_MONOTONIC+, +:CLOCK_PROCESS_CPUTIME_ID+,
8078 * and +:CLOCK_THREAD_CPUTIME_ID+ are optional.
8079 *
8080 * +:CLOCK_THREAD_CPUTIME_ID+ measures the native thread that reads it, not
8081 * the Ruby thread. Under the M:N thread scheduler (see +RUBY_MN_THREADS+) a
8082 * Ruby thread can move between native threads, so two reads taken from one
8083 * Ruby thread may come from different native threads, and the later read can
8084 * be smaller than the earlier one. To measure elapsed time there, use
8085 * +:CLOCK_PROCESS_CPUTIME_ID+ or +:CLOCK_MONOTONIC+.
8086 *
8087 * Certain emulations are used when the given +clock_id+
8088 * is not supported directly:
8089 *
8090 * - Emulations for +:CLOCK_REALTIME+:
8091 *
8092 * - +:GETTIMEOFDAY_BASED_CLOCK_REALTIME+:
8093 * Use gettimeofday() defined by SUS (deprecated in SUSv4).
8094 * The resolution is 1 microsecond.
8095 * - +:TIME_BASED_CLOCK_REALTIME+:
8096 * Use time() defined by ISO C.
8097 * The resolution is 1 second.
8098 *
8099 * - Emulations for +:CLOCK_MONOTONIC+:
8100 *
8101 * - +:MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC+:
8102 * Use mach_absolute_time(), available on Darwin.
8103 * The resolution is CPU dependent.
8104 * - +:TIMES_BASED_CLOCK_MONOTONIC+:
8105 * Use the result value of times() defined by POSIX, thus:
8106 * >>>
8107 * Upon successful completion, times() shall return the elapsed real time,
8108 * in clock ticks, since an arbitrary point in the past
8109 * (for example, system start-up time).
8110 *
8111 * For example, GNU/Linux returns a value based on jiffies and it is monotonic.
8112 * However, 4.4BSD uses gettimeofday() and it is not monotonic.
8113 * (FreeBSD uses +:CLOCK_MONOTONIC+ instead, though.)
8114 *
8115 * The resolution is the clock tick.
8116 * "getconf CLK_TCK" command shows the clock ticks per second.
8117 * (The clock ticks-per-second is defined by HZ macro in older systems.)
8118 * If it is 100 and clock_t is 32 bits integer type,
8119 * the resolution is 10 millisecond and cannot represent over 497 days.
8120 *
8121 * - Emulations for +:CLOCK_PROCESS_CPUTIME_ID+:
8122 *
8123 * - +:GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8124 * Use getrusage() defined by SUS.
8125 * getrusage() is used with RUSAGE_SELF to obtain the time only for
8126 * the calling process (excluding the time for child processes).
8127 * The result is addition of user time (ru_utime) and system time (ru_stime).
8128 * The resolution is 1 microsecond.
8129 * - +:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8130 * Use times() defined by POSIX.
8131 * The result is addition of user time (tms_utime) and system time (tms_stime).
8132 * tms_cutime and tms_cstime are ignored to exclude the time for child processes.
8133 * The resolution is the clock tick.
8134 * "getconf CLK_TCK" command shows the clock ticks per second.
8135 * (The clock ticks per second is defined by HZ macro in older systems.)
8136 * If it is 100, the resolution is 10 millisecond.
8137 * - +:CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID+:
8138 * Use clock() defined by ISO C.
8139 * The resolution is <tt>1/CLOCKS_PER_SEC</tt>.
8140 * +CLOCKS_PER_SEC+ is the C-level macro defined by time.h.
8141 * SUS defines +CLOCKS_PER_SEC+ as 1000000;
8142 * other systems may define it differently.
8143 * If +CLOCKS_PER_SEC+ is 1000000 (as in SUS),
8144 * the resolution is 1 microsecond.
8145 * If +CLOCKS_PER_SEC+ is 1000000 and clock_t is a 32-bit integer type,
8146 * it cannot represent over 72 minutes.
8147 *
8148 * <b>Argument +unit+</b>
8149 *
8150 * Optional argument +unit+ (default +:float_second+)
8151 * specifies the unit for the returned value.
8152 *
8153 * - +:float_microsecond+: Number of microseconds as a float.
8154 * - +:float_millisecond+: Number of milliseconds as a float.
8155 * - +:float_second+: Number of seconds as a float.
8156 * - +:microsecond+: Number of microseconds as an integer.
8157 * - +:millisecond+: Number of milliseconds as an integer.
8158 * - +:nanosecond+: Number of nanoseconds as an integer.
8159 * - +:second+: Number of seconds as an integer.
8160 *
8161 * Examples:
8162 *
8163 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond)
8164 * # => 203605054.825
8165 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond)
8166 * # => 203643.696848
8167 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :float_second)
8168 * # => 203.762181929
8169 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :microsecond)
8170 * # => 204123212
8171 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :millisecond)
8172 * # => 204298
8173 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond)
8174 * # => 204602286036
8175 * Process.clock_gettime(:CLOCK_PROCESS_CPUTIME_ID, :second)
8176 * # => 204
8177 *
8178 * The underlying function, clock_gettime(), returns a number of nanoseconds.
8179 * Float object (IEEE 754 double) is not enough to represent
8180 * the return value for +:CLOCK_REALTIME+.
8181 * If the exact nanoseconds value is required, use +:nanosecond+ as the +unit+.
8182 *
8183 * The origin (time zero) of the returned value is system-dependent,
8184 * and may be, for example, system start up time,
8185 * process start up time, the Epoch, etc.
8186 *
8187 * The origin in +:CLOCK_REALTIME+ is defined as the Epoch:
8188 * <tt>1970-01-01 00:00:00 UTC</tt>;
8189 * some systems count leap seconds and others don't,
8190 * so the result may vary across systems.
8191 */
8192static VALUE
8193rb_clock_gettime(int argc, VALUE *argv, VALUE _)
8194{
8195 int ret;
8196
8197 struct timetick tt;
8198 timetick_int_t numerators[2];
8199 timetick_int_t denominators[2];
8200 int num_numerators = 0;
8201 int num_denominators = 0;
8202
8203 VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
8204 VALUE clk_id = argv[0];
8205#ifdef HAVE_CLOCK_GETTIME
8206 clockid_t c;
8207#endif
8208
8209 if (SYMBOL_P(clk_id)) {
8210#ifdef CLOCK_REALTIME
8211 if (clk_id == RUBY_CLOCK_REALTIME) {
8212 c = CLOCK_REALTIME;
8213 goto gettime;
8214 }
8215#endif
8216
8217#ifdef CLOCK_MONOTONIC
8218 if (clk_id == RUBY_CLOCK_MONOTONIC) {
8219 c = CLOCK_MONOTONIC;
8220 goto gettime;
8221 }
8222#endif
8223
8224#ifdef CLOCK_PROCESS_CPUTIME_ID
8225 if (clk_id == RUBY_CLOCK_PROCESS_CPUTIME_ID) {
8226 c = CLOCK_PROCESS_CPUTIME_ID;
8227 goto gettime;
8228 }
8229#endif
8230
8231#ifdef CLOCK_THREAD_CPUTIME_ID
8232 if (clk_id == RUBY_CLOCK_THREAD_CPUTIME_ID) {
8233 c = CLOCK_THREAD_CPUTIME_ID;
8234 goto gettime;
8235 }
8236#endif
8237
8238 /*
8239 * Non-clock_gettime clocks are provided by symbol clk_id.
8240 */
8241#ifdef HAVE_GETTIMEOFDAY
8242 /*
8243 * GETTIMEOFDAY_BASED_CLOCK_REALTIME is used for
8244 * CLOCK_REALTIME if clock_gettime is not available.
8245 */
8246#define RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME ID2SYM(id_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
8247 if (clk_id == RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME) {
8248 struct timeval tv;
8249 ret = gettimeofday(&tv, 0);
8250 if (ret != 0)
8251 rb_sys_fail("gettimeofday");
8252 tt.giga_count = tv.tv_sec;
8253 tt.count = (int32_t)tv.tv_usec * 1000;
8254 denominators[num_denominators++] = 1000000000;
8255 goto success;
8256 }
8257#endif
8258
8259#define RUBY_TIME_BASED_CLOCK_REALTIME ID2SYM(id_TIME_BASED_CLOCK_REALTIME)
8260 if (clk_id == RUBY_TIME_BASED_CLOCK_REALTIME) {
8261 time_t t;
8262 t = time(NULL);
8263 if (t == (time_t)-1)
8264 rb_sys_fail("time");
8265 tt.giga_count = t;
8266 tt.count = 0;
8267 denominators[num_denominators++] = 1000000000;
8268 goto success;
8269 }
8270
8271#ifdef HAVE_TIMES
8272#define RUBY_TIMES_BASED_CLOCK_MONOTONIC \
8273 ID2SYM(id_TIMES_BASED_CLOCK_MONOTONIC)
8274 if (clk_id == RUBY_TIMES_BASED_CLOCK_MONOTONIC) {
8275 struct tms buf;
8276 clock_t c;
8277 unsigned_clock_t uc;
8278 c = times(&buf);
8279 if (c == (clock_t)-1)
8280 rb_sys_fail("times");
8281 uc = (unsigned_clock_t)c;
8282 tt.count = (int32_t)(uc % 1000000000);
8283 tt.giga_count = (uc / 1000000000);
8284 denominators[num_denominators++] = get_clk_tck();
8285 goto success;
8286 }
8287#endif
8288
8289#ifdef RUSAGE_SELF
8290#define RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID \
8291 ID2SYM(id_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
8292 if (clk_id == RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8293 struct rusage usage;
8294 int32_t usec;
8295 ret = getrusage(RUSAGE_SELF, &usage);
8296 if (ret != 0)
8297 rb_sys_fail("getrusage");
8298 tt.giga_count = usage.ru_utime.tv_sec + usage.ru_stime.tv_sec;
8299 usec = (int32_t)(usage.ru_utime.tv_usec + usage.ru_stime.tv_usec);
8300 if (1000000 <= usec) {
8301 tt.giga_count++;
8302 usec -= 1000000;
8303 }
8304 tt.count = usec * 1000;
8305 denominators[num_denominators++] = 1000000000;
8306 goto success;
8307 }
8308#endif
8309
8310#ifdef HAVE_TIMES
8311#define RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID \
8312 ID2SYM(id_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID)
8313 if (clk_id == RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8314 struct tms buf;
8315 unsigned_clock_t utime, stime;
8316 if (times(&buf) == (clock_t)-1)
8317 rb_sys_fail("times");
8318 utime = (unsigned_clock_t)buf.tms_utime;
8319 stime = (unsigned_clock_t)buf.tms_stime;
8320 tt.count = (int32_t)((utime % 1000000000) + (stime % 1000000000));
8321 tt.giga_count = (utime / 1000000000) + (stime / 1000000000);
8322 if (1000000000 <= tt.count) {
8323 tt.count -= 1000000000;
8324 tt.giga_count++;
8325 }
8326 denominators[num_denominators++] = get_clk_tck();
8327 goto success;
8328 }
8329#endif
8330
8331#define RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID \
8332 ID2SYM(id_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID)
8333 if (clk_id == RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8334 clock_t c;
8335 unsigned_clock_t uc;
8336 errno = 0;
8337 c = clock();
8338 if (c == (clock_t)-1)
8339 rb_sys_fail("clock");
8340 uc = (unsigned_clock_t)c;
8341 tt.count = (int32_t)(uc % 1000000000);
8342 tt.giga_count = uc / 1000000000;
8343 denominators[num_denominators++] = CLOCKS_PER_SEC;
8344 goto success;
8345 }
8346
8347#ifdef __APPLE__
8348 if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
8349 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8350 uint64_t t = mach_absolute_time();
8351 tt.count = (int32_t)(t % 1000000000);
8352 tt.giga_count = t / 1000000000;
8353 numerators[num_numerators++] = info->numer;
8354 denominators[num_denominators++] = info->denom;
8355 denominators[num_denominators++] = 1000000000;
8356 goto success;
8357 }
8358#endif
8359 }
8360 else if (NUMERIC_CLOCKID) {
8361#if defined(HAVE_CLOCK_GETTIME)
8362 struct timespec ts;
8363 c = NUM2CLOCKID(clk_id);
8364 gettime:
8365 ret = clock_gettime(c, &ts);
8366 if (ret == -1)
8367 clock_failed("gettime", errno, clk_id);
8368 tt.count = (int32_t)ts.tv_nsec;
8369 tt.giga_count = ts.tv_sec;
8370 denominators[num_denominators++] = 1000000000;
8371 goto success;
8372#endif
8373 }
8374 else {
8376 }
8377 clock_failed("gettime", EINVAL, clk_id);
8378
8379 success:
8380 return make_clock_result(&tt, numerators, num_numerators, denominators, num_denominators, unit);
8381}
8382
8383/*
8384 * call-seq:
8385 * Process.clock_getres(clock_id, unit = :float_second) -> number
8386 *
8387 * Returns a clock resolution as determined by POSIX function
8388 * {clock_getres()}[https://man7.org/linux/man-pages/man3/clock_getres.3.html]:
8389 *
8390 * Process.clock_getres(:CLOCK_REALTIME) # => 1.0e-09
8391 *
8392 * See Process.clock_gettime for the values of +clock_id+ and +unit+.
8393 *
8394 * Examples:
8395 *
8396 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_microsecond) # => 0.001
8397 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_millisecond) # => 1.0e-06
8398 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 1.0e-09
8399 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :microsecond) # => 0
8400 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :millisecond) # => 0
8401 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :nanosecond) # => 1
8402 * Process.clock_getres(:CLOCK_PROCESS_CPUTIME_ID, :second) # => 0
8403 *
8404 * In addition to the values for +unit+ supported in Process.clock_gettime,
8405 * this method supports +:hertz+, the integer number of clock ticks per second
8406 * (which is the reciprocal of +:float_second+):
8407 *
8408 * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz) # => 100.0
8409 * Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :float_second) # => 0.01
8410 *
8411 * <b>Accuracy</b>:
8412 * Note that the returned resolution may be inaccurate on some platforms
8413 * due to underlying bugs.
8414 * Inaccurate resolutions have been reported for various clocks including
8415 * +:CLOCK_MONOTONIC+ and +:CLOCK_MONOTONIC_RAW+
8416 * on Linux, macOS, BSD or AIX platforms, when using ARM processors,
8417 * or when using virtualization.
8418 */
8419static VALUE
8420rb_clock_getres(int argc, VALUE *argv, VALUE _)
8421{
8422 int ret;
8423
8424 struct timetick tt;
8425 timetick_int_t numerators[2];
8426 timetick_int_t denominators[2];
8427 int num_numerators = 0;
8428 int num_denominators = 0;
8429#ifdef HAVE_CLOCK_GETRES
8430 clockid_t c;
8431#endif
8432
8433 VALUE unit = (rb_check_arity(argc, 1, 2) == 2) ? argv[1] : Qnil;
8434 VALUE clk_id = argv[0];
8435
8436 if (SYMBOL_P(clk_id)) {
8437#ifdef CLOCK_REALTIME
8438 if (clk_id == RUBY_CLOCK_REALTIME) {
8439 c = CLOCK_REALTIME;
8440 goto getres;
8441 }
8442#endif
8443
8444#ifdef CLOCK_MONOTONIC
8445 if (clk_id == RUBY_CLOCK_MONOTONIC) {
8446 c = CLOCK_MONOTONIC;
8447 goto getres;
8448 }
8449#endif
8450
8451#ifdef CLOCK_PROCESS_CPUTIME_ID
8452 if (clk_id == RUBY_CLOCK_PROCESS_CPUTIME_ID) {
8453 c = CLOCK_PROCESS_CPUTIME_ID;
8454 goto getres;
8455 }
8456#endif
8457
8458#ifdef CLOCK_THREAD_CPUTIME_ID
8459 if (clk_id == RUBY_CLOCK_THREAD_CPUTIME_ID) {
8460 c = CLOCK_THREAD_CPUTIME_ID;
8461 goto getres;
8462 }
8463#endif
8464
8465#ifdef RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
8466 if (clk_id == RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME) {
8467 tt.giga_count = 0;
8468 tt.count = 1000;
8469 denominators[num_denominators++] = 1000000000;
8470 goto success;
8471 }
8472#endif
8473
8474#ifdef RUBY_TIME_BASED_CLOCK_REALTIME
8475 if (clk_id == RUBY_TIME_BASED_CLOCK_REALTIME) {
8476 tt.giga_count = 1;
8477 tt.count = 0;
8478 denominators[num_denominators++] = 1000000000;
8479 goto success;
8480 }
8481#endif
8482
8483#ifdef RUBY_TIMES_BASED_CLOCK_MONOTONIC
8484 if (clk_id == RUBY_TIMES_BASED_CLOCK_MONOTONIC) {
8485 tt.count = 1;
8486 tt.giga_count = 0;
8487 denominators[num_denominators++] = get_clk_tck();
8488 goto success;
8489 }
8490#endif
8491
8492#ifdef RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
8493 if (clk_id == RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8494 tt.giga_count = 0;
8495 tt.count = 1000;
8496 denominators[num_denominators++] = 1000000000;
8497 goto success;
8498 }
8499#endif
8500
8501#ifdef RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
8502 if (clk_id == RUBY_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8503 tt.count = 1;
8504 tt.giga_count = 0;
8505 denominators[num_denominators++] = get_clk_tck();
8506 goto success;
8507 }
8508#endif
8509
8510#ifdef RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
8511 if (clk_id == RUBY_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID) {
8512 tt.count = 1;
8513 tt.giga_count = 0;
8514 denominators[num_denominators++] = CLOCKS_PER_SEC;
8515 goto success;
8516 }
8517#endif
8518
8519#ifdef RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
8520 if (clk_id == RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC) {
8521 const mach_timebase_info_data_t *info = get_mach_timebase_info();
8522 tt.count = 1;
8523 tt.giga_count = 0;
8524 numerators[num_numerators++] = info->numer;
8525 denominators[num_denominators++] = info->denom;
8526 denominators[num_denominators++] = 1000000000;
8527 goto success;
8528 }
8529#endif
8530 }
8531 else if (NUMERIC_CLOCKID) {
8532#if defined(HAVE_CLOCK_GETRES)
8533 struct timespec ts;
8534 c = NUM2CLOCKID(clk_id);
8535 getres:
8536 ret = clock_getres(c, &ts);
8537 if (ret == -1)
8538 clock_failed("getres", errno, clk_id);
8539 tt.count = (int32_t)ts.tv_nsec;
8540 tt.giga_count = ts.tv_sec;
8541 denominators[num_denominators++] = 1000000000;
8542 goto success;
8543#endif
8544 }
8545 else {
8547 }
8548 clock_failed("getres", EINVAL, clk_id);
8549
8550 success:
8551 if (unit == ID2SYM(id_hertz)) {
8552 return timetick2dblnum_reciprocal(&tt, numerators, num_numerators, denominators, num_denominators);
8553 }
8554 else {
8555 return make_clock_result(&tt, numerators, num_numerators, denominators, num_denominators, unit);
8556 }
8557}
8558
8559static VALUE
8560get_CHILD_STATUS(ID _x, VALUE *_y)
8561{
8562 return rb_last_status_get();
8563}
8564
8565static VALUE
8566get_PROCESS_ID(ID _x, VALUE *_y)
8567{
8568 return get_pid();
8569}
8570
8571/*
8572 * call-seq:
8573 * Process.kill(signal, *ids) -> count
8574 *
8575 * Sends a signal to each process specified by +ids+
8576 * (which must specify at least one ID);
8577 * returns the count of signals sent.
8578 *
8579 * For each given +id+, if +id+ is:
8580 *
8581 * - Positive, sends the signal to the process whose process ID is +id+.
8582 * - Zero, send the signal to all processes in the current process group.
8583 * - Negative, sends the signal to a system-dependent collection of processes.
8584 *
8585 * Argument +signal+ specifies the signal to be sent;
8586 * the argument may be:
8587 *
8588 * - An integer signal number: e.g., +-29+, +0+, +29+.
8589 * - A signal name (string), with or without leading <tt>'SIG'</tt>,
8590 * and with or without a further prefixed minus sign (<tt>'-'</tt>):
8591 * e.g.:
8592 *
8593 * - <tt>'SIGPOLL'</tt>.
8594 * - <tt>'POLL'</tt>,
8595 * - <tt>'-SIGPOLL'</tt>.
8596 * - <tt>'-POLL'</tt>.
8597 *
8598 * - A signal symbol, with or without leading <tt>'SIG'</tt>,
8599 * and with or without a further prefixed minus sign (<tt>'-'</tt>):
8600 * e.g.:
8601 *
8602 * - +:SIGPOLL+.
8603 * - +:POLL+.
8604 * - <tt>:'-SIGPOLL'</tt>.
8605 * - <tt>:'-POLL'</tt>.
8606 *
8607 * If +signal+ is:
8608 *
8609 * - A non-negative integer, or a signal name or symbol
8610 * without prefixed <tt>'-'</tt>,
8611 * each process with process ID +id+ is signalled.
8612 * - A negative integer, or a signal name or symbol
8613 * with prefixed <tt>'-'</tt>,
8614 * each process group with group ID +id+ is signalled.
8615 *
8616 * Use method Signal.list to see which signals are supported
8617 * by Ruby on the underlying platform;
8618 * the method returns a hash of the string names
8619 * and non-negative integer values of the supported signals.
8620 * The size and content of the returned hash varies widely
8621 * among platforms.
8622 *
8623 * Additionally, signal +0+ is useful to determine if the process exists.
8624 *
8625 * Example:
8626 *
8627 * pid = fork do
8628 * Signal.trap('HUP') { puts 'Ouch!'; exit }
8629 * # ... do some work ...
8630 * end
8631 * # ...
8632 * Process.kill('HUP', pid)
8633 * Process.wait
8634 *
8635 * Output:
8636 *
8637 * Ouch!
8638 *
8639 * Exceptions:
8640 *
8641 * - Raises Errno::EINVAL or RangeError if +signal+ is an integer
8642 * but invalid.
8643 * - Raises ArgumentError if +signal+ is a string or symbol
8644 * but invalid.
8645 * - Raises Errno::ESRCH or RangeError if one of +ids+ is invalid.
8646 * - Raises Errno::EPERM if needed permissions are not in force.
8647 *
8648 * In the last two cases, signals may have been sent to some processes.
8649 */
8650
8651static VALUE
8652proc_rb_f_kill(int c, const VALUE *v, VALUE _)
8653{
8654 return rb_f_kill(c, v);
8655}
8656
8658static VALUE rb_mProcUID;
8659static VALUE rb_mProcGID;
8660static VALUE rb_mProcID_Syscall;
8661
8662/*
8663 * call-seq:
8664 * Process.warmup -> true
8665 *
8666 * Notify the Ruby virtual machine that the boot sequence is finished,
8667 * and that now is a good time to optimize the application. This is useful
8668 * for long running applications.
8669 *
8670 * This method is expected to be called at the end of the application boot.
8671 * If the application is deployed using a pre-forking model, +Process.warmup+
8672 * should be called in the original process before the first fork.
8673 *
8674 * The actual optimizations performed are entirely implementation specific
8675 * and may change in the future without notice.
8676 *
8677 * On CRuby, +Process.warmup+:
8678 *
8679 * * Performs a major GC.
8680 * * Compacts the heap.
8681 * * Promotes all surviving objects to the old generation.
8682 * * Precomputes the coderange of all strings.
8683 * * Frees all empty heap pages and increments the allocatable pages counter
8684 * by the number of pages freed.
8685 * * Invoke +malloc_trim+ if available to free empty malloc pages.
8686 * * Eagerly loads the +error_highlight+, +did_you_mean+, and +syntax_suggest+
8687 * gems, which are otherwise loaded lazily on the first error display.
8688 */
8689
8690static VALUE
8691proc_warmup(VALUE _)
8692{
8693 // Load the error decoration gems now so that their detailed_message
8694 // decorators land in shared memory before a pre-forking server forks,
8695 // instead of being loaded lazily on the first error at runtime.
8696 rb_eager_load_detailed_message_extension();
8697
8698 RB_VM_LOCKING() {
8699 rb_gc_prepare_heap();
8700 }
8701 return Qtrue;
8702}
8703
8704/*
8705 * Document-module: Process
8706 *
8707 * Module +Process+ represents a process in the underlying operating system.
8708 * Its methods support management of the current process and its child processes.
8709 *
8710 * == Process Creation
8711 *
8712 * Each of the following methods executes a given command in a new process or subshell,
8713 * or multiple commands in new processes and/or subshells.
8714 * The choice of process or subshell depends on the form of the command;
8715 * see {Argument command_line or exe_path}[rdoc-ref:Process@Argument+command_line+or+exe_path].
8716 *
8717 * - Process.spawn, Kernel#spawn: Executes the command;
8718 * returns the new pid without waiting for completion.
8719 * - Process.exec: Replaces the current process by executing the command.
8720 *
8721 * In addition:
8722 *
8723 * - Method Kernel#system executes a given command-line (string) in a subshell;
8724 * returns +true+, +false+, or +nil+.
8725 * - Method Kernel#` executes a given command-line (string) in a subshell;
8726 * returns its $stdout string.
8727 * - Module Open3 supports creating child processes
8728 * with access to their $stdin, $stdout, and $stderr streams.
8729 *
8730 * === Execution Environment
8731 *
8732 * Optional leading argument +env+ is a hash of name/value pairs,
8733 * where each name is a string and each value is a string or +nil+;
8734 * each name/value pair is added to ENV in the new process.
8735 *
8736 * Process.spawn( 'ruby -e "p ENV[\"Foo\"]"')
8737 * Process.spawn({'Foo' => '0'}, 'ruby -e "p ENV[\"Foo\"]"')
8738 *
8739 * Output:
8740 *
8741 * "0"
8742 *
8743 * The effect is usually similar to that of calling ENV#update with argument +env+,
8744 * where each named environment variable is created or updated
8745 * (if the value is non-+nil+),
8746 * or deleted (if the value is +nil+).
8747 *
8748 * However, some modifications to the calling process may remain
8749 * if the new process fails.
8750 * For example, hard resource limits are not restored.
8751 *
8752 * === Argument +command_line+ or +exe_path+
8753 *
8754 * The required string argument is one of the following:
8755 *
8756 * - +command_line+ if it begins with a shell reserved word or special built-in,
8757 * or if it contains one or more meta characters.
8758 * - +exe_path+ otherwise.
8759 *
8760 * ==== Argument +command_line+
8761 *
8762 * \String argument +command_line+ is a command line to be passed to a shell;
8763 * it must begin with a shell reserved word, begin with a special built-in,
8764 * or contain meta characters:
8765 *
8766 * system('if true; then echo "Foo"; fi') # => true # Shell reserved word.
8767 * system('exit') # => true # Built-in.
8768 * system('date > /tmp/date.tmp') # => true # Contains meta character.
8769 * system('date > /nop/date.tmp') # => false
8770 * system('date > /nop/date.tmp', exception: true) # Raises RuntimeError.
8771 *
8772 * The command line may also contain arguments and options for the command:
8773 *
8774 * system('echo "Foo"') # => true
8775 *
8776 * Output:
8777 *
8778 * Foo
8779 *
8780 * See {Execution Shell}[rdoc-ref:Process@Execution+Shell] for details about the shell.
8781 *
8782 * ==== Argument +exe_path+
8783 *
8784 * Argument +exe_path+ is one of the following:
8785 *
8786 * - The string path to an executable file to be called:
8787 *
8788 * Example:
8789 *
8790 * system('/usr/bin/date') # => true # Path to date on Unix-style system.
8791 * system('foo') # => nil # Command execlution failed.
8792 *
8793 * Output:
8794 *
8795 * Thu Aug 31 10:06:48 AM CDT 2023
8796 *
8797 * A path or command name containing spaces without arguments cannot
8798 * be distinguished from +command_line+ above, so you must quote or
8799 * escape the entire command name using a shell in platform
8800 * dependent manner, or use the array form below.
8801 *
8802 * If +exe_path+ does not contain any path separator, an executable
8803 * file is searched from directories specified with the +PATH+
8804 * environment variable. What the word "executable" means here is
8805 * depending on platforms.
8806 *
8807 * Even if the file considered "executable", its content may not be
8808 * in proper executable format. In that case, Ruby tries to run it
8809 * by using <tt>/bin/sh</tt> on a Unix-like system, like system(3)
8810 * does.
8811 *
8812 * File.write('shell_command', 'echo $SHELL', perm: 0o755)
8813 * system('./shell_command') # prints "/bin/sh" or something.
8814 *
8815 * - A 2-element array containing the path to an executable
8816 * and the string to be used as the name of the executing process:
8817 *
8818 * Example:
8819 *
8820 * pid = spawn(['sleep', 'Hello!'], '1') # 2-element array.
8821 * p `ps -p #{pid} -o command=`
8822 *
8823 * Output:
8824 *
8825 * "Hello! 1\n"
8826 *
8827 * === Arguments +args+
8828 *
8829 * If +command_line+ does not contain shell meta characters except for
8830 * spaces and tabs, or +exe_path+ is given, Ruby invokes the
8831 * executable directly. This form does not use the shell:
8832 *
8833 * spawn("doesnt_exist") # Raises Errno::ENOENT
8834 * spawn("doesnt_exist", "\n") # Raises Errno::ENOENT
8835 *
8836 * spawn("doesnt_exist\n") # => false
8837 * # sh: 1: doesnot_exist: not found
8838 *
8839 * The error message is from a shell and would vary depending on your
8840 * system.
8841 *
8842 * If one or more +args+ is given after +exe_path+, each is an
8843 * argument or option to be passed to the executable:
8844 *
8845 * Example:
8846 *
8847 * system('echo', '<', 'C*', '|', '$SHELL', '>') # => true
8848 *
8849 * Output:
8850 *
8851 * < C* | $SHELL >
8852 *
8853 * However, there are exceptions on Windows. See {Execution Shell on
8854 * Windows}[rdoc-ref:Process@Execution+Shell+on+Windows].
8855 *
8856 * If you want to invoke a path containing spaces with no arguments
8857 * without shell, you will need to use a 2-element array +exe_path+.
8858 *
8859 * Example:
8860 *
8861 * path = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
8862 * spawn(path) # Raises Errno::ENOENT; No such file or directory - /Applications/Google
8863 * spawn([path] * 2)
8864 *
8865 * === Execution Options
8866 *
8867 * Optional trailing argument +options+ is a hash of execution options.
8868 *
8869 * ==== Working Directory (+:chdir+)
8870 *
8871 * By default, the working directory for the new process is the same as
8872 * that of the current process:
8873 *
8874 * Dir.chdir('/var')
8875 * Process.spawn('ruby -e "puts Dir.pwd"')
8876 *
8877 * Output:
8878 *
8879 * /var
8880 *
8881 * Use option +:chdir+ to set the working directory for the new process:
8882 *
8883 * Process.spawn('ruby -e "puts Dir.pwd"', {chdir: '/tmp'})
8884 *
8885 * Output:
8886 *
8887 * /tmp
8888 *
8889 * The working directory of the current process is not changed:
8890 *
8891 * Dir.pwd # => "/var"
8892 *
8893 * ==== \File Redirection (\File Descriptor)
8894 *
8895 * Use execution options for file redirection in the new process.
8896 *
8897 * The key for such an option may be an integer file descriptor (fd),
8898 * specifying a source,
8899 * or an array of fds, specifying multiple sources.
8900 *
8901 * An integer source fd may be specified as:
8902 *
8903 * - _n_: Specifies file descriptor _n_.
8904 *
8905 * There are these shorthand symbols for fds:
8906 *
8907 * - +:in+: Specifies file descriptor 0 (STDIN).
8908 * - +:out+: Specifies file descriptor 1 (STDOUT).
8909 * - +:err+: Specifies file descriptor 2 (STDERR).
8910 *
8911 * The value given with a source is one of:
8912 *
8913 * - _n_:
8914 * Redirects to fd _n_ in the parent process.
8915 * - +filepath+:
8916 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>,
8917 * where +mode+ is <tt>'r'</tt> for source +:in+,
8918 * or <tt>'w'</tt> for source +:out+ or +:err+.
8919 * - <tt>[filepath]</tt>:
8920 * Redirects from the file at +filepath+ via <tt>open(filepath, 'r', 0644)</tt>.
8921 * - <tt>[filepath, mode]</tt>:
8922 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, 0644)</tt>.
8923 * - <tt>[filepath, mode, perm]</tt>:
8924 * Redirects from or to the file at +filepath+ via <tt>open(filepath, mode, perm)</tt>.
8925 * - <tt>[:child, fd]</tt>:
8926 * Redirects to the redirected +fd+.
8927 * - +:close+: Closes the file descriptor in child process.
8928 *
8929 * See {Access Modes}[rdoc-ref:File@Access+Modes]
8930 * and {File Permissions}[rdoc-ref:File@File+Permissions].
8931 *
8932 * ==== Environment Variables (+:unsetenv_others+)
8933 *
8934 * By default, the new process inherits environment variables
8935 * from the parent process;
8936 * use execution option key +:unsetenv_others+ with value +true+
8937 * to clear environment variables in the new process.
8938 *
8939 * Any changes specified by execution option +env+ are made after the new process
8940 * inherits or clears its environment variables;
8941 * see {Execution Environment}[rdoc-ref:Process@Execution+Environment].
8942 *
8943 * ==== \File-Creation Access (+:umask+)
8944 *
8945 * Use execution option +:umask+ to set the file-creation access
8946 * for the new process;
8947 * see {Access Modes}[rdoc-ref:File@Access+Modes]:
8948 *
8949 * command = 'ruby -e "puts sprintf(\"0%o\", File.umask)"'
8950 * options = {:umask => 0644}
8951 * Process.spawn(command, options)
8952 *
8953 * Output:
8954 *
8955 * 0644
8956 *
8957 * ==== Process Groups (+:pgroup+ and +:new_pgroup+)
8958 *
8959 * By default, the new process belongs to the same
8960 * {process group}[https://en.wikipedia.org/wiki/Process_group]
8961 * as the parent process.
8962 *
8963 * To specify a different process group.
8964 * use execution option +:pgroup+ with one of the following values:
8965 *
8966 * - +true+: Create a new process group for the new process.
8967 * - _pgid_: Create the new process in the process group
8968 * whose id is _pgid_.
8969 *
8970 * On Windows only, use execution option +:new_pgroup+ with value +true+
8971 * to create a new process group for the new process.
8972 *
8973 * ==== Resource Limits
8974 *
8975 * Use execution options to set resource limits.
8976 *
8977 * The keys for these options are symbols of the form
8978 * <tt>:rlimit_<i>resource_name</i></tt>,
8979 * where _resource_name_ is the downcased form of one of the string
8980 * resource names described at method Process.setrlimit.
8981 * For example, key +:rlimit_cpu+ corresponds to resource limit <tt>'CPU'</tt>.
8982 *
8983 * The value for such as key is one of:
8984 *
8985 * - An integer, specifying both the current and maximum limits.
8986 * - A 2-element array of integers, specifying the current and maximum limits.
8987 *
8988 * ==== \File Descriptor Inheritance
8989 *
8990 * By default, the new process inherits file descriptors from the parent process.
8991 *
8992 * Use execution option <tt>:close_others => true</tt> to modify that inheritance
8993 * by closing non-standard fds (3 and greater) that are not otherwise redirected.
8994 *
8995 * === Execution Shell
8996 *
8997 * On a Unix-like system, the shell invoked is <tt>/bin/sh</tt>;
8998 * the entire string +command_line+ is passed as an argument
8999 * to {shell option -c}[https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/sh.html].
9000 *
9001 * The shell performs normal shell expansion on the command line:
9002 *
9003 * Example:
9004 *
9005 * system('echo $SHELL: C*') # => true
9006 *
9007 * Output:
9008 *
9009 * /bin/bash: CONTRIBUTING.md COPYING COPYING.ja
9010 *
9011 * ==== Execution Shell on Windows
9012 *
9013 * On Windows, the shell invoked is determined by environment variable
9014 * +RUBYSHELL+, if defined, or +COMSPEC+ otherwise; the entire string
9015 * +command_line+ is passed as an argument to <tt>-c</tt> option for
9016 * +RUBYSHELL+, as well as <tt>/bin/sh</tt>, and {/c
9017 * option}[https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/cmd]
9018 * for +COMSPEC+. The shell is invoked automatically in the following
9019 * cases:
9020 *
9021 * - The command is a built-in of +cmd.exe+, such as +echo+.
9022 * - The executable file is a batch file; its name ends with +.bat+ or
9023 * +.cmd+.
9024 *
9025 * Note that the command will still be invoked as +command_line+ form
9026 * even when called in +exe_path+ form, because +cmd.exe+ does not
9027 * accept a script name like <tt>/bin/sh</tt> does but only works with
9028 * <tt>/c</tt> option.
9029 *
9030 * The standard shell +cmd.exe+ performs environment variable
9031 * expansion but does not have globbing functionality:
9032 *
9033 * Example:
9034 *
9035 * system("echo %COMSPEC%: C*")' # => true
9036 *
9037 * Output:
9038 *
9039 * C:\WINDOWS\system32\cmd.exe: C*
9040 *
9041 * == What's Here
9042 *
9043 * === Current-Process Getters
9044 *
9045 * - ::argv0: Returns the process name as a frozen string.
9046 * - ::egid: Returns the effective group ID.
9047 * - ::euid: Returns the effective user ID.
9048 * - ::getpgrp: Return the process group ID.
9049 * - ::getrlimit: Returns the resource limit.
9050 * - ::gid: Returns the (real) group ID.
9051 * - ::pid: Returns the process ID.
9052 * - ::ppid: Returns the process ID of the parent process.
9053 * - ::uid: Returns the (real) user ID.
9054 *
9055 * === Current-Process Setters
9056 *
9057 * - ::egid=: Sets the effective group ID.
9058 * - ::euid=: Sets the effective user ID.
9059 * - ::gid=: Sets the (real) group ID.
9060 * - ::setproctitle: Sets the process title.
9061 * - ::setpgrp: Sets the process group ID of the process to zero.
9062 * - ::setrlimit: Sets a resource limit.
9063 * - ::setsid: Establishes the process as a new session and process group leader,
9064 * with no controlling tty.
9065 * - ::uid=: Sets the user ID.
9066 *
9067 * === Current-Process Execution
9068 *
9069 * - ::abort: Immediately terminates the process.
9070 * - ::daemon: Detaches the process from its controlling terminal
9071 * and continues running it in the background as system daemon.
9072 * - ::exec: Replaces the process by running a given external command.
9073 * - ::exit: Initiates process termination by raising exception SystemExit
9074 * (which may be caught).
9075 * - ::exit!: Immediately exits the process.
9076 * - ::warmup: Notifies the Ruby virtual machine that the boot sequence
9077 * for the application is completed,
9078 * and that the VM may begin optimizing the application.
9079 *
9080 * === Child Processes
9081 *
9082 * - ::detach: Guards against a child process becoming a zombie.
9083 * - ::fork: Creates a child process.
9084 * - ::kill: Sends a given signal to processes.
9085 * - ::spawn: Creates a child process.
9086 * - ::wait, ::waitpid: Waits for a child process to exit; returns its process ID.
9087 * - ::wait2, ::waitpid2: Waits for a child process to exit; returns its process ID and status.
9088 * - ::waitall: Waits for all child processes to exit;
9089 * returns their process IDs and statuses.
9090 *
9091 * === Process Groups
9092 *
9093 * - ::getpgid: Returns the process group ID for a process.
9094 * - ::getpriority: Returns the scheduling priority
9095 * for a process, process group, or user.
9096 * - ::getsid: Returns the session ID for a process.
9097 * - ::groups: Returns an array of the group IDs
9098 * in the supplemental group access list for this process.
9099 * - ::groups=: Sets the supplemental group access list
9100 * to the given array of group IDs.
9101 * - ::initgroups: Initializes the supplemental group access list.
9102 * - ::last_status: Returns the status of the last executed child process
9103 * in the current thread.
9104 * - ::maxgroups: Returns the maximum number of group IDs allowed
9105 * in the supplemental group access list.
9106 * - ::maxgroups=: Sets the maximum number of group IDs allowed
9107 * in the supplemental group access list.
9108 * - ::setpgid: Sets the process group ID of a process.
9109 * - ::setpriority: Sets the scheduling priority
9110 * for a process, process group, or user.
9111 *
9112 * === Timing
9113 *
9114 * - ::clock_getres: Returns the resolution of a system clock.
9115 * - ::clock_gettime: Returns the time from a system clock.
9116 * - ::times: Returns a Process::Tms object containing times
9117 * for the current process and its child processes.
9118 *
9119 */
9120
9121void
9122InitVM_process(void)
9123{
9124 rb_define_virtual_variable("$?", get_CHILD_STATUS, 0);
9125 rb_define_virtual_variable("$$", get_PROCESS_ID, 0);
9126
9127 rb_gvar_ractor_local("$$");
9128 rb_gvar_ractor_local("$?");
9129 rb_gvar_box_ready("$$");
9130 rb_gvar_box_ready("$?");
9131
9132 rb_define_global_function("exec", f_exec, -1);
9133 rb_define_global_function("fork", rb_f_fork, 0);
9134 rb_define_global_function("exit!", rb_f_exit_bang, -1);
9135 rb_define_global_function("system", rb_f_system, -1);
9136 rb_define_global_function("spawn", rb_f_spawn, -1);
9137 rb_define_global_function("sleep", rb_f_sleep, -1);
9138 rb_define_global_function("exit", f_exit, -1);
9139 rb_define_global_function("abort", f_abort, -1);
9140
9141 rb_mProcess = rb_define_module("Process");
9142
9143#ifdef WNOHANG
9144 /* see Process.wait */
9145 rb_define_const(rb_mProcess, "WNOHANG", INT2FIX(WNOHANG));
9146#else
9147 /* see Process.wait */
9148 rb_define_const(rb_mProcess, "WNOHANG", INT2FIX(0));
9149#endif
9150#ifdef WUNTRACED
9151 /* see Process.wait */
9152 rb_define_const(rb_mProcess, "WUNTRACED", INT2FIX(WUNTRACED));
9153#else
9154 /* see Process.wait */
9155 rb_define_const(rb_mProcess, "WUNTRACED", INT2FIX(0));
9156#endif
9157
9158 rb_define_singleton_method(rb_mProcess, "exec", f_exec, -1);
9159 rb_define_singleton_method(rb_mProcess, "fork", rb_f_fork, 0);
9160 rb_define_singleton_method(rb_mProcess, "spawn", rb_f_spawn, -1);
9161 rb_define_singleton_method(rb_mProcess, "exit!", rb_f_exit_bang, -1);
9162 rb_define_singleton_method(rb_mProcess, "exit", f_exit, -1);
9163 rb_define_singleton_method(rb_mProcess, "abort", f_abort, -1);
9164 rb_define_singleton_method(rb_mProcess, "last_status", proc_s_last_status, 0);
9165 rb_define_singleton_method(rb_mProcess, "_fork", rb_proc__fork, 0);
9166
9167 rb_define_module_function(rb_mProcess, "kill", proc_rb_f_kill, -1);
9168 rb_define_module_function(rb_mProcess, "wait", proc_m_wait, -1);
9169 rb_define_module_function(rb_mProcess, "wait2", proc_wait2, -1);
9170 rb_define_module_function(rb_mProcess, "waitpid", proc_m_wait, -1);
9171 rb_define_module_function(rb_mProcess, "waitpid2", proc_wait2, -1);
9172 rb_define_module_function(rb_mProcess, "waitall", proc_waitall, 0);
9173 rb_define_module_function(rb_mProcess, "detach", proc_detach, 1);
9174
9175 /* :nodoc: */
9176 rb_cWaiter = rb_define_class_under(rb_mProcess, "Waiter", rb_cThread);
9177 rb_undef_alloc_func(rb_cWaiter);
9178 rb_undef_method(CLASS_OF(rb_cWaiter), "new");
9179 rb_define_method(rb_cWaiter, "pid", detach_process_pid, 0);
9180
9181 rb_cProcessStatus = rb_define_class_under(rb_mProcess, "Status", rb_cObject);
9182 rb_define_alloc_func(rb_cProcessStatus, rb_process_status_allocate);
9183 rb_undef_method(CLASS_OF(rb_cProcessStatus), "new");
9184 rb_marshal_define_compat(rb_cProcessStatus, rb_cObject,
9185 process_status_dump, process_status_load);
9186
9187 rb_define_singleton_method(rb_cProcessStatus, "wait", rb_process_status_waitv, -1);
9188
9189 rb_define_method(rb_cProcessStatus, "==", pst_equal, 1);
9190 rb_define_method(rb_cProcessStatus, "to_i", pst_to_i, 0);
9191 rb_define_method(rb_cProcessStatus, "to_s", pst_to_s, 0);
9192 rb_define_method(rb_cProcessStatus, "inspect", pst_inspect, 0);
9193
9194 rb_define_method(rb_cProcessStatus, "pid", pst_pid_m, 0);
9195
9196 rb_define_method(rb_cProcessStatus, "stopped?", pst_wifstopped, 0);
9197 rb_define_method(rb_cProcessStatus, "stopsig", pst_wstopsig, 0);
9198 rb_define_method(rb_cProcessStatus, "signaled?", pst_wifsignaled, 0);
9199 rb_define_method(rb_cProcessStatus, "termsig", pst_wtermsig, 0);
9200 rb_define_method(rb_cProcessStatus, "exited?", pst_wifexited, 0);
9201 rb_define_method(rb_cProcessStatus, "exitstatus", pst_wexitstatus, 0);
9202 rb_define_method(rb_cProcessStatus, "success?", pst_success_p, 0);
9203 rb_define_method(rb_cProcessStatus, "coredump?", pst_wcoredump, 0);
9204
9205 rb_define_module_function(rb_mProcess, "pid", proc_get_pid, 0);
9206 rb_define_module_function(rb_mProcess, "ppid", proc_get_ppid, 0);
9207
9208 rb_define_module_function(rb_mProcess, "getpgrp", proc_getpgrp, 0);
9209 rb_define_module_function(rb_mProcess, "setpgrp", proc_setpgrp, 0);
9210 rb_define_module_function(rb_mProcess, "getpgid", proc_getpgid, 1);
9211 rb_define_module_function(rb_mProcess, "setpgid", proc_setpgid, 2);
9212
9213 rb_define_module_function(rb_mProcess, "getsid", proc_getsid, -1);
9214 rb_define_module_function(rb_mProcess, "setsid", proc_setsid, 0);
9215
9216 rb_define_module_function(rb_mProcess, "getpriority", proc_getpriority, 2);
9217 rb_define_module_function(rb_mProcess, "setpriority", proc_setpriority, 3);
9218
9219 rb_define_module_function(rb_mProcess, "warmup", proc_warmup, 0);
9220
9221#ifdef HAVE_GETPRIORITY
9222 /* see Process.setpriority */
9223 rb_define_const(rb_mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS));
9224 /* see Process.setpriority */
9225 rb_define_const(rb_mProcess, "PRIO_PGRP", INT2FIX(PRIO_PGRP));
9226 /* see Process.setpriority */
9227 rb_define_const(rb_mProcess, "PRIO_USER", INT2FIX(PRIO_USER));
9228#endif
9229
9230 rb_define_module_function(rb_mProcess, "getrlimit", proc_getrlimit, 1);
9231 rb_define_module_function(rb_mProcess, "setrlimit", proc_setrlimit, -1);
9232#if defined(RLIM2NUM) && defined(RLIM_INFINITY)
9233 {
9234 VALUE inf = RLIM2NUM(RLIM_INFINITY);
9235#ifdef RLIM_SAVED_MAX
9236 {
9237 VALUE v = RLIM_INFINITY == RLIM_SAVED_MAX ? inf : RLIM2NUM(RLIM_SAVED_MAX);
9238 /* see Process.setrlimit */
9239 rb_define_const(rb_mProcess, "RLIM_SAVED_MAX", v);
9240 }
9241#endif
9242 /* see Process.setrlimit */
9243 rb_define_const(rb_mProcess, "RLIM_INFINITY", inf);
9244#ifdef RLIM_SAVED_CUR
9245 {
9246 VALUE v = RLIM_INFINITY == RLIM_SAVED_CUR ? inf : RLIM2NUM(RLIM_SAVED_CUR);
9247 /* see Process.setrlimit */
9248 rb_define_const(rb_mProcess, "RLIM_SAVED_CUR", v);
9249 }
9250#endif
9251 }
9252#ifdef RLIMIT_AS
9253 /* Maximum size of the process's virtual memory (address space) in bytes.
9254 *
9255 * see the system getrlimit(2) manual for details.
9256 */
9257 rb_define_const(rb_mProcess, "RLIMIT_AS", INT2FIX(RLIMIT_AS));
9258#endif
9259#ifdef RLIMIT_CORE
9260 /* Maximum size of the core file.
9261 *
9262 * see the system getrlimit(2) manual for details.
9263 */
9264 rb_define_const(rb_mProcess, "RLIMIT_CORE", INT2FIX(RLIMIT_CORE));
9265#endif
9266#ifdef RLIMIT_CPU
9267 /* CPU time limit in seconds.
9268 *
9269 * see the system getrlimit(2) manual for details.
9270 */
9271 rb_define_const(rb_mProcess, "RLIMIT_CPU", INT2FIX(RLIMIT_CPU));
9272#endif
9273#ifdef RLIMIT_DATA
9274 /* Maximum size of the process's data segment.
9275 *
9276 * see the system getrlimit(2) manual for details.
9277 */
9278 rb_define_const(rb_mProcess, "RLIMIT_DATA", INT2FIX(RLIMIT_DATA));
9279#endif
9280#ifdef RLIMIT_FSIZE
9281 /* Maximum size of files that the process may create.
9282 *
9283 * see the system getrlimit(2) manual for details.
9284 */
9285 rb_define_const(rb_mProcess, "RLIMIT_FSIZE", INT2FIX(RLIMIT_FSIZE));
9286#endif
9287#ifdef RLIMIT_MEMLOCK
9288 /* Maximum number of bytes of memory that may be locked into RAM.
9289 *
9290 * see the system getrlimit(2) manual for details.
9291 */
9292 rb_define_const(rb_mProcess, "RLIMIT_MEMLOCK", INT2FIX(RLIMIT_MEMLOCK));
9293#endif
9294#ifdef RLIMIT_MSGQUEUE
9295 /* Specifies the limit on the number of bytes that can be allocated
9296 * for POSIX message queues for the real user ID of the calling process.
9297 *
9298 * see the system getrlimit(2) manual for details.
9299 */
9300 rb_define_const(rb_mProcess, "RLIMIT_MSGQUEUE", INT2FIX(RLIMIT_MSGQUEUE));
9301#endif
9302#ifdef RLIMIT_NICE
9303 /* Specifies a ceiling to which the process's nice value can be raised.
9304 *
9305 * see the system getrlimit(2) manual for details.
9306 */
9307 rb_define_const(rb_mProcess, "RLIMIT_NICE", INT2FIX(RLIMIT_NICE));
9308#endif
9309#ifdef RLIMIT_NOFILE
9310 /* Specifies a value one greater than the maximum file descriptor
9311 * number that can be opened by this process.
9312 *
9313 * see the system getrlimit(2) manual for details.
9314 */
9315 rb_define_const(rb_mProcess, "RLIMIT_NOFILE", INT2FIX(RLIMIT_NOFILE));
9316#endif
9317#ifdef RLIMIT_NPROC
9318 /* The maximum number of processes that can be created for the
9319 * real user ID of the calling process.
9320 *
9321 * see the system getrlimit(2) manual for details.
9322 */
9323 rb_define_const(rb_mProcess, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC));
9324#endif
9325#ifdef RLIMIT_NPTS
9326 /* The maximum number of pseudo-terminals that can be created for the
9327 * real user ID of the calling process.
9328 *
9329 * see the system getrlimit(2) manual for details.
9330 */
9331 rb_define_const(rb_mProcess, "RLIMIT_NPTS", INT2FIX(RLIMIT_NPTS));
9332#endif
9333#ifdef RLIMIT_RSS
9334 /* Specifies the limit (in pages) of the process's resident set.
9335 *
9336 * see the system getrlimit(2) manual for details.
9337 */
9338 rb_define_const(rb_mProcess, "RLIMIT_RSS", INT2FIX(RLIMIT_RSS));
9339#endif
9340#ifdef RLIMIT_RTPRIO
9341 /* Specifies a ceiling on the real-time priority that may be set for this process.
9342 *
9343 * see the system getrlimit(2) manual for details.
9344 */
9345 rb_define_const(rb_mProcess, "RLIMIT_RTPRIO", INT2FIX(RLIMIT_RTPRIO));
9346#endif
9347#ifdef RLIMIT_RTTIME
9348 /* Specifies limit on CPU time this process scheduled under a real-time
9349 * scheduling policy can consume.
9350 *
9351 * see the system getrlimit(2) manual for details.
9352 */
9353 rb_define_const(rb_mProcess, "RLIMIT_RTTIME", INT2FIX(RLIMIT_RTTIME));
9354#endif
9355#ifdef RLIMIT_SBSIZE
9356 /* Maximum size of the socket buffer.
9357 */
9358 rb_define_const(rb_mProcess, "RLIMIT_SBSIZE", INT2FIX(RLIMIT_SBSIZE));
9359#endif
9360#ifdef RLIMIT_SIGPENDING
9361 /* Specifies a limit on the number of signals that may be queued for
9362 * the real user ID of the calling process.
9363 *
9364 * see the system getrlimit(2) manual for details.
9365 */
9366 rb_define_const(rb_mProcess, "RLIMIT_SIGPENDING", INT2FIX(RLIMIT_SIGPENDING));
9367#endif
9368#ifdef RLIMIT_STACK
9369 /* Maximum size of the stack, in bytes.
9370 *
9371 * see the system getrlimit(2) manual for details.
9372 */
9373 rb_define_const(rb_mProcess, "RLIMIT_STACK", INT2FIX(RLIMIT_STACK));
9374#endif
9375#endif
9376
9377 rb_define_module_function(rb_mProcess, "uid", proc_getuid, 0);
9378 rb_define_module_function(rb_mProcess, "uid=", proc_setuid, 1);
9379 rb_define_module_function(rb_mProcess, "gid", proc_getgid, 0);
9380 rb_define_module_function(rb_mProcess, "gid=", proc_setgid, 1);
9381 rb_define_module_function(rb_mProcess, "euid", proc_geteuid, 0);
9382 rb_define_module_function(rb_mProcess, "euid=", proc_seteuid_m, 1);
9383 rb_define_module_function(rb_mProcess, "egid", proc_getegid, 0);
9384 rb_define_module_function(rb_mProcess, "egid=", proc_setegid_m, 1);
9385 rb_define_module_function(rb_mProcess, "initgroups", proc_initgroups, 2);
9386 rb_define_module_function(rb_mProcess, "groups", proc_getgroups, 0);
9387 rb_define_module_function(rb_mProcess, "groups=", proc_setgroups, 1);
9388 rb_define_module_function(rb_mProcess, "maxgroups", proc_getmaxgroups, 0);
9389 rb_define_module_function(rb_mProcess, "maxgroups=", proc_setmaxgroups, 1);
9390
9391 rb_define_module_function(rb_mProcess, "daemon", proc_daemon, -1);
9392
9393 rb_define_module_function(rb_mProcess, "times", rb_proc_times, 0);
9394
9395#if defined(RUBY_CLOCK_REALTIME)
9396#elif defined(RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME)
9397# define RUBY_CLOCK_REALTIME RUBY_GETTIMEOFDAY_BASED_CLOCK_REALTIME
9398#elif defined(RUBY_TIME_BASED_CLOCK_REALTIME)
9399# define RUBY_CLOCK_REALTIME RUBY_TIME_BASED_CLOCK_REALTIME
9400#endif
9401#if defined(CLOCK_REALTIME) && defined(CLOCKID2NUM)
9402 /* see Process.clock_gettime */
9403 rb_define_const(rb_mProcess, "CLOCK_REALTIME", CLOCKID2NUM(CLOCK_REALTIME));
9404#elif defined(RUBY_CLOCK_REALTIME)
9405 rb_define_const(rb_mProcess, "CLOCK_REALTIME", RUBY_CLOCK_REALTIME);
9406#endif
9407
9408#if defined(RUBY_CLOCK_MONOTONIC)
9409#elif defined(RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC)
9410# define RUBY_CLOCK_MONOTONIC RUBY_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
9411#endif
9412#if defined(CLOCK_MONOTONIC) && defined(CLOCKID2NUM)
9413 /* see Process.clock_gettime */
9414 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC", CLOCKID2NUM(CLOCK_MONOTONIC));
9415#elif defined(RUBY_CLOCK_MONOTONIC)
9416 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC", RUBY_CLOCK_MONOTONIC);
9417#endif
9418
9419#if defined(RUBY_CLOCK_PROCESS_CPUTIME_ID)
9420#elif defined(RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID)
9421# define RUBY_CLOCK_PROCESS_CPUTIME_ID RUBY_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
9422#endif
9423#if defined(CLOCK_PROCESS_CPUTIME_ID) && defined(CLOCKID2NUM)
9424 /* see Process.clock_gettime */
9425 rb_define_const(rb_mProcess, "CLOCK_PROCESS_CPUTIME_ID", CLOCKID2NUM(CLOCK_PROCESS_CPUTIME_ID));
9426#elif defined(RUBY_CLOCK_PROCESS_CPUTIME_ID)
9427 rb_define_const(rb_mProcess, "CLOCK_PROCESS_CPUTIME_ID", RUBY_CLOCK_PROCESS_CPUTIME_ID);
9428#endif
9429
9430#if defined(CLOCK_THREAD_CPUTIME_ID) && defined(CLOCKID2NUM)
9431 /* see Process.clock_gettime */
9432 rb_define_const(rb_mProcess, "CLOCK_THREAD_CPUTIME_ID", CLOCKID2NUM(CLOCK_THREAD_CPUTIME_ID));
9433#elif defined(RUBY_CLOCK_THREAD_CPUTIME_ID)
9434 rb_define_const(rb_mProcess, "CLOCK_THREAD_CPUTIME_ID", RUBY_CLOCK_THREAD_CPUTIME_ID);
9435#endif
9436
9437#ifdef CLOCKID2NUM
9438#ifdef CLOCK_VIRTUAL
9439 /* see Process.clock_gettime */
9440 rb_define_const(rb_mProcess, "CLOCK_VIRTUAL", CLOCKID2NUM(CLOCK_VIRTUAL));
9441#endif
9442#ifdef CLOCK_PROF
9443 /* see Process.clock_gettime */
9444 rb_define_const(rb_mProcess, "CLOCK_PROF", CLOCKID2NUM(CLOCK_PROF));
9445#endif
9446#ifdef CLOCK_REALTIME_FAST
9447 /* see Process.clock_gettime */
9448 rb_define_const(rb_mProcess, "CLOCK_REALTIME_FAST", CLOCKID2NUM(CLOCK_REALTIME_FAST));
9449#endif
9450#ifdef CLOCK_REALTIME_PRECISE
9451 /* see Process.clock_gettime */
9452 rb_define_const(rb_mProcess, "CLOCK_REALTIME_PRECISE", CLOCKID2NUM(CLOCK_REALTIME_PRECISE));
9453#endif
9454#ifdef CLOCK_REALTIME_COARSE
9455 /* see Process.clock_gettime */
9456 rb_define_const(rb_mProcess, "CLOCK_REALTIME_COARSE", CLOCKID2NUM(CLOCK_REALTIME_COARSE));
9457#endif
9458#ifdef CLOCK_REALTIME_ALARM
9459 /* see Process.clock_gettime */
9460 rb_define_const(rb_mProcess, "CLOCK_REALTIME_ALARM", CLOCKID2NUM(CLOCK_REALTIME_ALARM));
9461#endif
9462#ifdef CLOCK_MONOTONIC_FAST
9463 /* see Process.clock_gettime */
9464 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_FAST", CLOCKID2NUM(CLOCK_MONOTONIC_FAST));
9465#endif
9466#ifdef CLOCK_MONOTONIC_PRECISE
9467 /* see Process.clock_gettime */
9468 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_PRECISE", CLOCKID2NUM(CLOCK_MONOTONIC_PRECISE));
9469#endif
9470#ifdef CLOCK_MONOTONIC_RAW
9471 /* see Process.clock_gettime */
9472 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_RAW", CLOCKID2NUM(CLOCK_MONOTONIC_RAW));
9473#endif
9474#ifdef CLOCK_MONOTONIC_RAW_APPROX
9475 /* see Process.clock_gettime */
9476 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_RAW_APPROX", CLOCKID2NUM(CLOCK_MONOTONIC_RAW_APPROX));
9477#endif
9478#ifdef CLOCK_MONOTONIC_COARSE
9479 /* see Process.clock_gettime */
9480 rb_define_const(rb_mProcess, "CLOCK_MONOTONIC_COARSE", CLOCKID2NUM(CLOCK_MONOTONIC_COARSE));
9481#endif
9482#ifdef CLOCK_BOOTTIME
9483 /* see Process.clock_gettime */
9484 rb_define_const(rb_mProcess, "CLOCK_BOOTTIME", CLOCKID2NUM(CLOCK_BOOTTIME));
9485#endif
9486#ifdef CLOCK_BOOTTIME_ALARM
9487 /* see Process.clock_gettime */
9488 rb_define_const(rb_mProcess, "CLOCK_BOOTTIME_ALARM", CLOCKID2NUM(CLOCK_BOOTTIME_ALARM));
9489#endif
9490#ifdef CLOCK_UPTIME
9491 /* see Process.clock_gettime */
9492 rb_define_const(rb_mProcess, "CLOCK_UPTIME", CLOCKID2NUM(CLOCK_UPTIME));
9493#endif
9494#ifdef CLOCK_UPTIME_FAST
9495 /* see Process.clock_gettime */
9496 rb_define_const(rb_mProcess, "CLOCK_UPTIME_FAST", CLOCKID2NUM(CLOCK_UPTIME_FAST));
9497#endif
9498#ifdef CLOCK_UPTIME_PRECISE
9499 /* see Process.clock_gettime */
9500 rb_define_const(rb_mProcess, "CLOCK_UPTIME_PRECISE", CLOCKID2NUM(CLOCK_UPTIME_PRECISE));
9501#endif
9502#ifdef CLOCK_UPTIME_RAW
9503 /* see Process.clock_gettime */
9504 rb_define_const(rb_mProcess, "CLOCK_UPTIME_RAW", CLOCKID2NUM(CLOCK_UPTIME_RAW));
9505#endif
9506#ifdef CLOCK_UPTIME_RAW_APPROX
9507 /* see Process.clock_gettime */
9508 rb_define_const(rb_mProcess, "CLOCK_UPTIME_RAW_APPROX", CLOCKID2NUM(CLOCK_UPTIME_RAW_APPROX));
9509#endif
9510#ifdef CLOCK_SECOND
9511 /* see Process.clock_gettime */
9512 rb_define_const(rb_mProcess, "CLOCK_SECOND", CLOCKID2NUM(CLOCK_SECOND));
9513#endif
9514#ifdef CLOCK_TAI
9515 /* see Process.clock_gettime */
9516 rb_define_const(rb_mProcess, "CLOCK_TAI", CLOCKID2NUM(CLOCK_TAI));
9517#endif
9518#endif
9519 rb_define_module_function(rb_mProcess, "clock_gettime", rb_clock_gettime, -1);
9520 rb_define_module_function(rb_mProcess, "clock_getres", rb_clock_getres, -1);
9521
9522#if defined(HAVE_TIMES)
9523 rb_cProcessTms = rb_struct_define_under(rb_mProcess, "Tms", "utime", "stime", "cutime", "cstime", NULL);
9524#if 0 /* for RDoc */
9525 /* user time used in this process */
9526 rb_define_attr(rb_cProcessTms, "utime", TRUE, TRUE);
9527 /* system time used in this process */
9528 rb_define_attr(rb_cProcessTms, "stime", TRUE, TRUE);
9529 /* user time used in the child processes */
9530 rb_define_attr(rb_cProcessTms, "cutime", TRUE, TRUE);
9531 /* system time used in the child processes */
9532 rb_define_attr(rb_cProcessTms, "cstime", TRUE, TRUE);
9533#endif
9534#endif
9535
9536 SAVED_USER_ID = geteuid();
9537 SAVED_GROUP_ID = getegid();
9538
9539 rb_mProcUID = rb_define_module_under(rb_mProcess, "UID");
9540 rb_mProcGID = rb_define_module_under(rb_mProcess, "GID");
9541
9542 rb_define_module_function(rb_mProcUID, "rid", proc_getuid, 0);
9543 rb_define_module_function(rb_mProcGID, "rid", proc_getgid, 0);
9544 rb_define_module_function(rb_mProcUID, "eid", proc_geteuid, 0);
9545 rb_define_module_function(rb_mProcGID, "eid", proc_getegid, 0);
9546 rb_define_module_function(rb_mProcUID, "change_privilege", p_uid_change_privilege, 1);
9547 rb_define_module_function(rb_mProcGID, "change_privilege", p_gid_change_privilege, 1);
9548 rb_define_module_function(rb_mProcUID, "grant_privilege", p_uid_grant_privilege, 1);
9549 rb_define_module_function(rb_mProcGID, "grant_privilege", p_gid_grant_privilege, 1);
9550 rb_define_alias(rb_singleton_class(rb_mProcUID), "eid=", "grant_privilege");
9551 rb_define_alias(rb_singleton_class(rb_mProcGID), "eid=", "grant_privilege");
9552 rb_define_module_function(rb_mProcUID, "re_exchange", p_uid_exchange, 0);
9553 rb_define_module_function(rb_mProcGID, "re_exchange", p_gid_exchange, 0);
9554 rb_define_module_function(rb_mProcUID, "re_exchangeable?", p_uid_exchangeable, 0);
9555 rb_define_module_function(rb_mProcGID, "re_exchangeable?", p_gid_exchangeable, 0);
9556 rb_define_module_function(rb_mProcUID, "sid_available?", p_uid_have_saved_id, 0);
9557 rb_define_module_function(rb_mProcGID, "sid_available?", p_gid_have_saved_id, 0);
9558 rb_define_module_function(rb_mProcUID, "switch", p_uid_switch, 0);
9559 rb_define_module_function(rb_mProcGID, "switch", p_gid_switch, 0);
9560#ifdef p_uid_from_name
9561 rb_define_module_function(rb_mProcUID, "from_name", p_uid_from_name, 1);
9562#endif
9563#ifdef p_gid_from_name
9564 rb_define_module_function(rb_mProcGID, "from_name", p_gid_from_name, 1);
9565#endif
9566
9567 rb_mProcID_Syscall = rb_define_module_under(rb_mProcess, "Sys");
9568
9569 rb_define_module_function(rb_mProcID_Syscall, "getuid", proc_getuid, 0);
9570 rb_define_module_function(rb_mProcID_Syscall, "geteuid", proc_geteuid, 0);
9571 rb_define_module_function(rb_mProcID_Syscall, "getgid", proc_getgid, 0);
9572 rb_define_module_function(rb_mProcID_Syscall, "getegid", proc_getegid, 0);
9573
9574 rb_define_module_function(rb_mProcID_Syscall, "setuid", p_sys_setuid, 1);
9575 rb_define_module_function(rb_mProcID_Syscall, "setgid", p_sys_setgid, 1);
9576
9577 rb_define_module_function(rb_mProcID_Syscall, "setruid", p_sys_setruid, 1);
9578 rb_define_module_function(rb_mProcID_Syscall, "setrgid", p_sys_setrgid, 1);
9579
9580 rb_define_module_function(rb_mProcID_Syscall, "seteuid", p_sys_seteuid, 1);
9581 rb_define_module_function(rb_mProcID_Syscall, "setegid", p_sys_setegid, 1);
9582
9583 rb_define_module_function(rb_mProcID_Syscall, "setreuid", p_sys_setreuid, 2);
9584 rb_define_module_function(rb_mProcID_Syscall, "setregid", p_sys_setregid, 2);
9585
9586 rb_define_module_function(rb_mProcID_Syscall, "setresuid", p_sys_setresuid, 3);
9587 rb_define_module_function(rb_mProcID_Syscall, "setresgid", p_sys_setresgid, 3);
9588 rb_define_module_function(rb_mProcID_Syscall, "issetugid", p_sys_issetugid, 0);
9589}
9590
9591void
9592Init_process(void)
9593{
9594#define define_id(name) id_##name = rb_intern_const(#name)
9595 define_id(in);
9596 define_id(out);
9597 define_id(err);
9598 define_id(pid);
9599 define_id(uid);
9600 define_id(gid);
9601 define_id(close);
9602 define_id(child);
9603#ifdef HAVE_SETPGID
9604 define_id(pgroup);
9605#endif
9606#ifdef _WIN32
9607 define_id(new_pgroup);
9608#endif
9609 define_id(unsetenv_others);
9610 define_id(chdir);
9611 define_id(umask);
9612 define_id(close_others);
9613 define_id(nanosecond);
9614 define_id(microsecond);
9615 define_id(millisecond);
9616 define_id(second);
9617 define_id(float_microsecond);
9618 define_id(float_millisecond);
9619 define_id(float_second);
9620 define_id(GETTIMEOFDAY_BASED_CLOCK_REALTIME);
9621 define_id(TIME_BASED_CLOCK_REALTIME);
9622#ifdef CLOCK_REALTIME
9623 define_id(CLOCK_REALTIME);
9624#endif
9625#ifdef CLOCK_MONOTONIC
9626 define_id(CLOCK_MONOTONIC);
9627#endif
9628#ifdef CLOCK_PROCESS_CPUTIME_ID
9629 define_id(CLOCK_PROCESS_CPUTIME_ID);
9630#endif
9631#ifdef CLOCK_THREAD_CPUTIME_ID
9632 define_id(CLOCK_THREAD_CPUTIME_ID);
9633#endif
9634#ifdef HAVE_TIMES
9635 define_id(TIMES_BASED_CLOCK_MONOTONIC);
9636 define_id(TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID);
9637#endif
9638#ifdef RUSAGE_SELF
9639 define_id(GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID);
9640#endif
9641 define_id(CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID);
9642#ifdef __APPLE__
9643 define_id(MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC);
9644#endif
9645 define_id(hertz);
9646#ifdef HAVE_WORKING_FORK
9647 define_id(_fork);
9648#endif
9649
9650 InitVM(process);
9651}
#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:3051
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition class.c:3094
void rb_define_attr(VALUE klass, const char *name, int read, int write)
Defines public accessor method(s) for an attribute.
Definition class.c:3100
void rb_undef_method(VALUE klass, const char *name)
Defines an undef of a method.
Definition class.c:2897
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:1035
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1676
#define TYPE(_)
Old name of rb_type.
Definition value_type.h:108
#define T_FILE
Old name of RUBY_T_FILE.
Definition value_type.h:62
#define rb_str_buf_cat2
Old name of rb_usascii_str_new_cstr.
Definition string.h:1683
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1684
#define ISUPPER
Old name of rb_isupper.
Definition ctype.h:89
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
Definition value_type.h:57
#define T_FIXNUM
Old name of RUBY_T_FIXNUM.
Definition value_type.h:63
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition assume.h:29
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
#define TOUPPER
Old name of rb_toupper.
Definition ctype.h:100
#define NUM2UINT
Old name of RB_NUM2UINT.
Definition int.h:45
#define ISLOWER
Old name of rb_islower.
Definition ctype.h:90
#define rb_ary_new3
Old name of rb_ary_new_from_args.
Definition array.h:658
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define NIL_P
Old name of RB_NIL_P.
#define ALLOCV_N
Old name of RB_ALLOCV_N.
Definition memory.h:405
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
Definition value_type.h:80
#define DBL2NUM
Old name of rb_float_new.
Definition double.h:29
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define ALLOCV_END
Old name of RB_ALLOCV_END.
Definition memory.h:406
#define SYMBOL_P
Old name of RB_SYMBOL_P.
Definition value_type.h:88
void ruby_stop(int ex)
Calls ruby_cleanup() and exits the process.
Definition eval.c:307
VALUE rb_eNotImpError
NotImplementedError exception.
Definition error.c:1483
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:4084
VALUE rb_eSystemExit
SystemExit exception.
Definition error.c:1466
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:4090
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1471
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:1459
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:468
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
Definition error.c:1524
void rb_unexpected_type(VALUE x, int t)
Fails with the given object's type incompatibility to the type.
Definition error.c:1428
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:8657
VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
Allocates, then initialises an instance of the given class.
Definition object.c:2293
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:234
VALUE rb_cThread
Thread class.
Definition vm.c:694
VALUE rb_equal(VALUE lhs, VALUE rhs)
This function is an optimised version of calling #==.
Definition object.c:140
VALUE rb_obj_freeze(VALUE obj)
Same as RB_OBJ_FREEZE(), but returns the given object.
Definition object.c:1309
VALUE rb_to_int(VALUE val)
Identical to rb_check_to_int(), except it raises in case of conversion mismatch.
Definition object.c:3328
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_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1123
VALUE rb_ary_dup(VALUE ary)
Duplicates an array.
VALUE rb_check_array_type(VALUE obj)
Try converting an object to its array representation using its to_ary method, if any.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Identical to rb_ary_new_from_values(), except it expects exactly two parameters.
void rb_ary_store(VALUE ary, long key, VALUE val)
Destructively stores the passed value to the passed array's passed index.
#define UNLIMITED_ARGUMENTS
This macro is used in conjunction with rb_check_arity().
Definition error.h:35
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
Definition error.h:284
VALUE rb_f_abort(int argc, const VALUE *argv)
This is similar to rb_f_exit().
Definition process.c: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:413
void rb_update_max_fd(int fd)
Informs the interpreter that the passed fd can be the max.
Definition io.c:287
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Opens a file that closes on exec.
Definition io.c:367
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:7710
int rb_cloexec_fcntl_dupfd(int fd, int minfd)
Duplicates a file descriptor with closing on exec.
Definition io.c:521
int rb_cloexec_dup(int oldfd)
Identical to rb_cloexec_fcntl_dupfd(), except it implies minfd is 3.
Definition io.c:406
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:1679
VALUE rb_last_status_get(void)
Queries the "last status", or the $?.
Definition process.c:605
rb_pid_t rb_waitpid(rb_pid_t pid, int *status, int flags)
Waits for a process, with releasing GVL.
Definition process.c:1161
VALUE rb_process_status_for(rb_pid_t pid, int status, int error)
Returns the Ruby value representing a waitpid(2) outcome.
Definition process.c:639
rb_pid_t rb_spawn_err(int argc, const VALUE *argv, char *errbuf, size_t buflen)
Identical to rb_spawn(), except you can additionally know the detailed situation in case of abnormal ...
Definition process.c: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:2853
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
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:3898
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1783
VALUE rb_str_subseq(VALUE str, long beg, long len)
Identical to rb_str_substr(), except the numbers are interpreted as byte offsets instead of character...
Definition string.c:3251
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1499
#define rb_str_buf_cat
Just another name of rb_str_cat.
Definition string.h:1682
size_t rb_str_capacity(VALUE str)
Queries the capacity of the given string.
Definition string.c:1023
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition string.c:1555
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:2023
void rb_str_set_len(VALUE str, long len)
Overwrites the length of the string.
Definition string.c:3485
VALUE rb_check_string_type(VALUE obj)
Try converting an object to its stringised representation using its to_str method,...
Definition string.c:3032
#define rb_str_cat_cstr(buf, str)
Identical to rb_str_cat(), except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1657
void rb_str_modify_expand(VALUE str, long capa)
Identical to rb_str_modify(), except it additionally expands the capacity of the receiver.
Definition string.c:2801
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition string.c:1755
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1515
VALUE rb_struct_define_under(VALUE space, const char *name,...)
Identical to rb_struct_define(), except it defines the class under the specified namespace instead of...
Definition struct.c:512
VALUE rb_struct_new(VALUE klass,...)
Creates an instance of the given struct.
Definition struct.c:876
VALUE rb_thread_local_aref(VALUE thread, ID key)
This badly named function reads from a Fiber local storage.
Definition thread.c:4069
#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:1591
void rb_thread_wait_for(struct timeval time)
Identical to rb_thread_sleep(), except it takes struct timeval instead.
Definition thread.c:1624
void rb_thread_check_ints(void)
Checks for interrupts.
Definition thread.c:1645
void rb_thread_atfork(void)
A pthread_atfork(3posix)-like API.
Definition thread.c:5403
VALUE rb_thread_local_aset(VALUE thread, ID key, VALUE val)
This badly named function writes to a Fiber local storage.
Definition thread.c:4217
#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:1668
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:2141
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1843
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:3561
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:1289
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:1148
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:6932
#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:879
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:1924
void * rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
Allows the passed function to run in parallel with other Ruby threads.
#define RB_NUM2INT
Just another name of rb_num2int_inline.
Definition int.h:38
#define RB_INT2NUM
Just another name of rb_int2num_inline.
Definition int.h:37
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
Shim for block function parameters.
Definition iterator.h:58
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1378
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:360
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
#define NUM2MODET
Converts a C's mode_t into an instance of rb_cInteger.
Definition mode_t.h:28
VALUE rb_thread_create(type *q, void *w)
Creates a rb_cThread instance.
VALUE rb_block_call(VALUE q, ID w, int e, const VALUE *r, type *t, VALUE y)
Call a method with a block.
void rb_define_virtual_variable(const char *q, type *w, void_type *e)
Define a function-backended global variable.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
#define PIDT2NUM
Converts a C's pid_t into an instance of rb_cInteger.
Definition pid_t.h:28
#define NUM2PIDT
Converts an instance of rb_cNumeric into C's pid_t.
Definition pid_t.h:33
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:280
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:385
#define RARRAY_AREF(a, i)
Definition rarray.h:402
#define RUBY_DEFAULT_FREE
This is a value you can set to RData::dfree.
Definition rdata.h:56
#define RHASH_SIZE(h)
Queries the size of the hash.
Definition rhash.h:69
#define RHASH_EMPTY_P(h)
Checks if the hash is empty.
Definition rhash.h:79
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
static char * RSTRING_END(VALUE str)
Queries the end of the contents pointer of the string.
Definition rstring.h:409
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
Definition rtypeddata.h:81
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:773
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:604
const char * rb_class2name(VALUE klass)
Queries the name of the passed class.
Definition variable.c:524
#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
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