Process.getrlimit と Process.setrlimit が欲しいので実装してみました。

きっかけは、とあるシェルスクリプトを Ruby に移植しようと考えた時に、
ulimit が移植できなかったことです。

ちなみにそのシェルスクリプトというのは、しばらく前から
http://www.rubyist.net/~akr/autobuild/ で試している Ruby の自動 build
のスクリプトで、Ruby の build やテストが (万が一) 無限ループに陥ったり
スタックを食いつぶした場合にプロセスを終了させたり、core がちゃんと残
るように設定するのに ulimit を使っています。

また、[ruby-dev:24405] で指摘されたような、デフォルトのスタックサイズ
が小さい OS に対処するために、アプリケーションからスタックを大きくする
ことにも使えるかも知れません。まぁ、アプリケーションにそういう対処をい
れるのを推奨するのはどうかという気もするので、これは微妙な所ですが。

規格としては、SUSv3 に載っています。

実装としては、BSD と SVR4 の両方にあったようなので、現在の Unix ならた
いていのにはあるんじゃないかと思います。

パッチはちょっと長いんですが、ポイントとしては

* struct rlimit に対応するクラスは作らず、[cur,max] という配列を使う
* rlim_t が定義されていない場合には、NotImplementedError にする
* rlim_t が規格上 Unsigned integer type となっていて長さが決まっていな
  いのに真面目に対応している
  * ちなみに手元の Debian GNU/Linux では 64bit
* NUM2ULL がなかったので足した
* 関数定義が ANSI style なのは意図的

というところでしょうか。

とりあえず 1.9 にいれたいんですが、どうでしょう?

Index: configure.in
===================================================================
RCS file: /src/ruby/configure.in,v
retrieving revision 1.249
diff -u -p -r1.249 configure.in
--- configure.in	7 Nov 2004 15:16:06 -0000	1.249
+++ configure.in	13 Nov 2004 17:12:08 -0000
@@ -205,6 +205,12 @@ AC_CHECK_SIZEOF(float, 4)
 AC_CHECK_SIZEOF(double, 8)
 AC_CHECK_SIZEOF(time_t, 0)
 
+AC_CHECK_TYPE(rlim_t, [AC_DEFINE(HAVE_RLIM_T)], [], [#include <sys/resource.h>])
+AC_CHECK_SIZEOF(rlim_t, 0, [
+  #include <stdio.h>
+  #include <sys/resource.h>
+])
+
 AC_CACHE_CHECK(for prototypes, rb_cv_have_prototypes,
   [AC_TRY_COMPILE([int foo(int x) { return 0; }], [return foo(10);],
 	rb_cv_have_prototypes=yes,
@@ -416,7 +422,8 @@ AC_CHECK_FUNCS(fmod killpg wait4 waitpid
 	      setitimer setruid seteuid setreuid setresuid setproctitle\
 	      setrgid setegid setregid setresgid issetugid pause lchown lchmod\
 	      getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups\
-	      getpriority getrlimit dlopen sigprocmask sigaction _setjmp\
+	      getpriority getrlimit setrlimit\
+	      dlopen sigprocmask sigaction _setjmp\
 	      setsid telldir seekdir fchmod mktime timegm cosh sinh tanh\
 	      setuid setgid daemon)
 AC_ARG_ENABLE(setreuid,
Index: process.c
===================================================================
RCS file: /src/ruby/process.c,v
retrieving revision 1.118
diff -u -p -r1.118 process.c
--- process.c	1 Nov 2004 02:49:06 -0000	1.118
+++ process.c	13 Nov 2004 17:12:09 -0000
@@ -45,7 +45,7 @@ struct timeval rb_time_interval _((VALUE
 #ifdef HAVE_SYS_WAIT_H
 # include <sys/wait.h>
 #endif
-#ifdef HAVE_GETPRIORITY
+#ifdef HAVE_SYS_RESOURCE_H
 # include <sys/resource.h>
 #endif
 #include "st.h"
@@ -1931,6 +1931,111 @@ proc_setpriority(obj, which, who, prio)
 #endif
 }
 
+#ifdef HAVE_RLIM_T
+
+#if SIZEOF_RLIM_T == SIZEOF_INT
+# define RLIM2NUM(v) UINT2NUM(v)
+# define NUM2RLIM(v) NUM2UINT(v)
+#elif SIZEOF_RLIM_T == SIZEOF_LONG
+# define RLIM2NUM(v) ULONG2NUM(v)
+# define NUM2RLIM(v) NUM2ULONG(v)
+#elif SIZEOF_RLIM_T == SIZEOF_LONG_LONG
+# define RLIM2NUM(v) ULL2NUM(v)
+# define NUM2RLIM(v) NUM2ULL(v)
+#else
+# error cannot find an integer type which size is same as rlim_t.
+#endif
+
+/*
+ *  call-seq:
+ *     Process.getrlimit(resource)   => [cur_limit, max_limit]
+ *
+ *  Gets the resource limit of the process.
+ *  _cur_limit_ means current (soft) limit and
+ *  _max_limit_ means maximum (hard) limit.
+ *
+ *  _resource_ indicates the kind of resource to limit:
+ *  such as <code>Process::RLIMIT_CORE</code>,
+ *  <code>Process::RLIMIT_CPU</code>, etc.
+ *  See Process.setrlimit for details.
+ *
+ *  _cur_limit_ and _max_limit_ may be <code>Process::RLIM_INFINITY</code>,
+ *  <code>Process::RLIM_SAVED_MAX</code> or
+ *  <code>Process::RLIM_SAVED_CUR</code>.
+ *  See Process.setrlimit and the system getrlimit(2) manual for details.
+ */
+
+static VALUE
+proc_getrlimit(
+    VALUE obj,
+    VALUE resource)
+{
+#ifdef HAVE_GETRLIMIT
+    struct rlimit rlim;
+
+    if (getrlimit(NUM2INT(resource), &rlim) < 0) {
+       rb_sys_fail("getrlimit");
+    }
+    return rb_ary_new3(2, RLIM2NUM(rlim.rlim_cur), RLIM2NUM(rlim.rlim_max));
+#else
+    rb_notimplement();
+#endif
+}
+
+/*
+ *  call-seq:
+ *     Process.setrlimit(resource, cur_limit, max_limit)        => nil
+ *
+ *  Sets the resource limit of the process.
+ *  _cur_limit_ means current (soft) limit and
+ *  _max_limit_ means maximum (hard) limit.
+ *
+ *  _resource_ indicates the kind of resource to limit.
+ *  Although the list of resources are OS dependent,
+ *  SUSv3 defines following resources.
+ *
+ *  [Process::RLIMIT_CORE] core size (bytes)
+ *  [Process::RLIMIT_CPU] CPU time (seconds)
+ *  [Process::RLIMIT_DATA] data segment (bytes)
+ *  [Process::RLIMIT_FSIZE] file size (bytes)
+ *  [Process::RLIMIT_NOFILE] file descriptors (number)
+ *  [Process::RLIMIT_STACK] stack size (bytes)
+ *  [Process::RLIMIT_AS] total available memory (bytes)
+ *
+ *  Other <code>Process::RLIMIT_???</code> constants may be defined.
+ *
+ *  _cur_limit_ and _max_limit_ may be <code>Process::RLIM_INFINITY</code>,
+ *  which means that the resource is not limited.
+ *  They may be <code>Process::RLIM_SAVED_MAX</code> or
+ *  <code>Process::RLIM_SAVED_CUR</code> too.
+ *  See system setrlimit(2) manual for details.
+ *
+ */
+
+static VALUE
+proc_setrlimit(
+    VALUE obj,
+    VALUE resource,
+    VALUE rlim_cur,
+    VALUE rlim_max)
+{
+#ifdef HAVE_SETRLIMIT
+    struct rlimit rlim;
+
+    rlim.rlim_cur = NUM2RLIM(rlim_cur);
+    rlim.rlim_max = NUM2RLIM(rlim_max);
+
+    if (setrlimit(NUM2INT(resource), &rlim) < 0) {
+       rb_sys_fail("setrlimit");
+    }
+    return Qnil;
+#else
+    rb_notimplement();
+#endif
+}
+
+#endif
+
 static int under_uid_switch = 0;
 static void
 check_uid_switch()
@@ -3608,6 +3713,57 @@ Init_process()
     rb_define_const(rb_mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS));
     rb_define_const(rb_mProcess, "PRIO_PGRP", INT2FIX(PRIO_PGRP));
     rb_define_const(rb_mProcess, "PRIO_USER", INT2FIX(PRIO_USER));
+#endif
+
+#ifdef HAVE_RLIM_T
+#ifdef HAVE_GETRLIMIT
+    rb_define_module_function(rb_mProcess, "getrlimit", proc_getrlimit, 1);
+#endif
+#ifdef HAVE_SETRLIMIT
+    rb_define_module_function(rb_mProcess, "setrlimit", proc_setrlimit, 3);
+#endif
+#ifdef RLIM_INFINITY
+    rb_define_const(rb_mProcess, "RLIM_INFINITY", RLIM2NUM(RLIM_INFINITY));
+#endif
+#ifdef RLIM_SAVED_MAX
+    rb_define_const(rb_mProcess, "RLIM_SAVED_MAX", RLIM2NUM(RLIM_SAVED_MAX));
+#endif
+#ifdef RLIM_SAVED_CUR
+    rb_define_const(rb_mProcess, "RLIM_SAVED_CUR", RLIM2NUM(RLIM_SAVED_CUR));
+#endif
+#ifdef RLIMIT_CORE
+    rb_define_const(rb_mProcess, "RLIMIT_CORE", INT2FIX(RLIMIT_CORE));
+#endif
+#ifdef RLIMIT_CPU
+    rb_define_const(rb_mProcess, "RLIMIT_CPU", INT2FIX(RLIMIT_CPU));
+#endif
+#ifdef RLIMIT_DATA
+    rb_define_const(rb_mProcess, "RLIMIT_DATA", INT2FIX(RLIMIT_DATA));
+#endif
+#ifdef RLIMIT_FSIZE
+    rb_define_const(rb_mProcess, "RLIMIT_FSIZE", INT2FIX(RLIMIT_FSIZE));
+#endif
+#ifdef RLIMIT_NOFILE
+    rb_define_const(rb_mProcess, "RLIMIT_NOFILE", INT2FIX(RLIMIT_NOFILE));
+#endif
+#ifdef RLIMIT_STACK
+    rb_define_const(rb_mProcess, "RLIMIT_STACK", INT2FIX(RLIMIT_STACK));
+#endif
+#ifdef RLIMIT_AS
+    rb_define_const(rb_mProcess, "RLIMIT_AS", INT2FIX(RLIMIT_AS));
+#endif
+#ifdef RLIMIT_LOCKS
+    rb_define_const(rb_mProcess, "RLIMIT_LOCKS", INT2FIX(RLIMIT_LOCKS));
+#endif
+#ifdef RLIMIT_MEMLOCK
+    rb_define_const(rb_mProcess, "RLIMIT_MEMLOCK", INT2FIX(RLIMIT_MEMLOCK));
+#endif
+#ifdef RLIMIT_NPROC
+    rb_define_const(rb_mProcess, "RLIMIT_NPROC", INT2FIX(RLIMIT_NPROC));
+#endif
+#ifdef RLIMIT_RSS
+    rb_define_const(rb_mProcess, "RLIMIT_RSS", INT2FIX(RLIMIT_RSS));
+#endif
 #endif
 
     rb_define_module_function(rb_mProcess, "uid", proc_getuid, 0);
Index: ruby.h
===================================================================
RCS file: /src/ruby/ruby.h,v
retrieving revision 1.107
diff -u -p -r1.107 ruby.h
--- ruby.h	27 Oct 2004 09:29:25 -0000	1.107
+++ ruby.h	13 Nov 2004 17:12:09 -0000
@@ -265,6 +265,7 @@ unsigned long rb_fix2uint _((VALUE));
 LONG_LONG rb_num2ll _((VALUE));
 unsigned LONG_LONG rb_num2ull _((VALUE));
 # define NUM2LL(x) (FIXNUM_P(x)?FIX2LONG(x):rb_num2ll((VALUE)x))
+# define NUM2ULL(x) rb_num2ull((VALUE)x)
 #endif
 
 #if HAVE_LONG_LONG && SIZEOF_OFF_T > SIZEOF_LONG
-- 
[田中 哲][たなか あきら][Tanaka Akira]