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