永井@知能.九工大です.

From: matz / ruby-lang.org (Yukihiro Matsumoto)
Subject: [ruby-dev:20681] Re: frozen ThreadGroup
Date: Tue, 15 Jul 2003 16:46:40 +0900
Message-ID: <1058255237.819358.15871.nullmailer / picachu.netlab.jp>
> |う゛...
> |これって,もしかして例の portable な user id / group id の
> |コントロールメソッドを目指そうという件でしょうか?
> |だとしたらごめんなさい.
> |周囲からの要望もないし,なんだか一人で騒いでいるだけという気がして
> |完全に放置してました.
> |確か,メソッド名がまつもとさんの好みに合わないということと
> |本当にあのメソッドセットで OK なのかということとが問題で
> |そのままになっていたと思います.
> です。私はuser id/gruop idを操作する欲求も経験もありませんか
> ら、機能の善し悪しについては判断できませんでした。が、名前の
> 善し悪しについては判断するという。

とりあえず,メソッド名変更と CVS head への適用を行ってみました.
メソッド名はあくまでも案ですので,修正提案歓迎です.
まずは,仕様のメモ書きを示して,末尾にパッチを添えます.
お試しいただけると幸いです.

# 本当はサンプルまで用意しておくといいのでしょうけど...

=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=

[ruby-dev:16609] の定義に基づく [ruby-dev:16883] の patch を
CVS head に適用したもの (メソッド名は短く変更).
getgroups/setgroups は Process.group/group= で実装済なのでそのまま.
initgroups を Process.initgroups として追加

module Process::ID_Syscall   単なるシステムコールの直接実装
    単純な操作がどうしても欲しい人のためのサービス提供
    ポータビリティは期待できない
  getuid
  geteuid
  getgid
  getegid
  setuid
  setgid
  setruid
  setrgid
  seteuid
  setegid
  setreuid
  setregid
  setresuid
  setresgid

module Process::UserID   ユーザ ID 操作
  [patch]    [別名候補]                                   [簡易説明]
   rid        real                                       実 ID の獲得
   eid        eid, effective                             実効 ID の獲得
   to         id=, set                                   指定 ID への完全移行
   eid=       effective=                                 実効 ID の設定
   ex_reid    ex_re, re_exchange, exchange, exchange_re  実/実効 ID の交換
   ex_reid?   ex_re?, re_exchangeable?, exchangable?     実/実効 ID の交換可否
   has_sid?   has_saved?                                 保存 ID の有無
   switch     toggle                                     権限の一時的な変更

module Process::GroupID   グループ ID 操作
    メソッド集合は module Process::UserID と同じ

============================================================

Process::UserID.rid [Process::GroupID.rid]

  仕様 : 現在の実ユーザ ID を得る

============================================================

Process::UserID.eid [Process::GroupID.eid]

  仕様 : 現在の実効ユーザ ID を得る

============================================================

Process::UserID.to(id) [Process::GroupID.to(id)]

  仕様 : 実/実効/保存ユーザ ID のすべてを変更して,
         他のユーザとしての権限を放棄する.
         成功時は id を返す.
         完全放棄できない場合(一部ユーザ ID の変更ができない場合)は
         例外を発生する.
         例外の発生時に,このメソッドを呼び出す前の各ユーザ ID の値が
         保存されているかどうかは保証されない.

  注意 : 以前の Process.uid= とは互換性がないことに注意.
         setreuid(id,-1) によって実装されていたものを用いていたケースは,
         例えば次のように書き換える必要があるだろう.

         Process::UserID.ex_reid   # (r,e,s)==(u1,u2,??) ==> (u2,u1,??)
         Process::UserID.eid = id  # (u2,u1,??) ==> (u2,id,??)
         Process::UserID.ex_reid   # (u2,id,??) ==> (id,u2,??)

============================================================

Process::UserID.eid=(id) [Process::GroupID.eid=(id)]

  仕様 : 実効ユーザ ID を変更する.
         成功時は id を返す.
         保存ユーザ ID の変化は,Process::UserID.ex_reid? が
         true かどうかで決まる.
         定義されない環境では,保存ユーザ ID は変化しない.
         定義されている環境では,id が実ユーザ ID と異なる値に設定された場合,
         保存ユーザ ID は新しい実効ユーザ ID の値に設定される.
         設定できない場合は例外を発生する.

============================================================

Process::UserID.ex_reid [Process::GroupID.ex_reid]

  仕様 : 実ユーザ ID と実効ユーザ ID とを入れ換える.
         保存ユーザ ID は新しい実効ユーザ ID と同じになる.
         環境依存により実装されていない場合は例外を発生する.
         戻り値としては,新しい実効ユーザ ID を返す.

============================================================

Process::UserID.ex_reid? [Process::GroupID.ex_reid?]

  仕様 : 実ユーザ ID と実効ユーザ ID との入れ換えが可能な環境かどうか
         (Process::UserID.ex_reid が実装されているか) を真偽値で返す.
         入れ換え可能なら true を返す.

============================================================

Process::UserID.has_sid? [Process::GroupID.has_sid?]

  仕様 : 保存ユーザ ID を持つ環境かどうかを真偽値で返す.
         保存ユーザ ID を持つなら true を返す.
         ただし,保存ユーザ ID の有無を明確に判定できない環境では,
         この値は推定値である (誤りの可能性もある) ことに注意が必要.
         現在は,次の条件のいずれかが満足される場合に
         保存ユーザ ID を持つものと判定する.
          ・setresuid を持つ
          ・seteuid を持つ
          ・_POSIX_SAVED_IDS が真として定義されている

============================================================

Process::UserID.switch         [Process::GroupID.switch]
Process::UserID.switch { ... } [Process::GroupID.switch { ... }]

  仕様 : ユーザ権限を(一時的に)変更する.
         ブロックが与えられたならば,権限を移行してそのブロックを実行した後,
         元の権限に復帰する.
         途中で実/実効/保存ユーザ ID を変更しなければ,
         ブロックを与えない呼び出しを二度実行することで,
         権限の一時的変更と元の権限への復帰とが可能となる.
         ブロックが与えられない場合の戻り値は元に戻せる(変更できる) ID 値,
         ブロックが与えられた場合の戻り値はブロックの値を返す.
         実/実効/保存ユーザ ID が二つのユーザ権限を移行できる状態でなければ
         例外を発生する (変更できるのは実ユーザ ID の権限へか,
         保存ユーザ ID の権限へかのいずれかである).
         ブロックを与えた際に,ブロック内でユーザ ID が変更されたなどの理由で
         元の権限に復帰できない場合も例外を発生する.
         保存ユーザ ID を持たない環境では,このメソッドの実行によって
         実ユーザ ID が変化する (ブロックを与えた場合はブロック実行中のみ).

============================================================

=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=

Index: configure.in
===================================================================
RCS file: /src/ruby/configure.in,v
retrieving revision 1.179
diff -u -r1.179 configure.in
--- configure.in	11 Jul 2003 13:37:22 -0000	1.179
+++ configure.in	16 Jul 2003 03:45:50 -0000
@@ -371,9 +371,9 @@
 	      truncate chsize times utimes fcntl lockf lstat symlink readlink\
 	      setitimer setruid seteuid setreuid setresuid setproctitle\
 	      setrgid setegid setregid setresgid pause lchown lchmod\
-	      getpgrp setpgrp getpgid setpgid getgroups setgroups getpriority getrlimit\
-	      dlopen sigprocmask sigaction _setjmp setsid telldir seekdir fchmod\
-	      mktime timegm cosh sinh tanh)
+	      getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups\
+	      getpriority getrlimit dlopen sigprocmask sigaction _setjmp\
+	      setsid telldir seekdir fchmod mktime timegm cosh sinh tanh)
 AC_STRUCT_TIMEZONE
 AC_CACHE_CHECK(for struct tm.tm_gmtoff, rb_cv_member_struct_tm_tm_gmtoff,
   [AC_TRY_COMPILE([#include <time.h>],
Index: process.c
===================================================================
RCS file: /src/ruby/process.c,v
retrieving revision 1.73
diff -u -r1.73 process.c
--- process.c	15 Jul 2003 07:35:10 -0000	1.73
+++ process.c	16 Jul 2003 03:45:52 -0000
@@ -1158,6 +1158,66 @@
 }
 
 static VALUE
+p_sys_setuid(obj, id)
+    VALUE obj, id;
+{
+#if defined HAVE_SETUID
+    if (setuid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return Qnil;
+}
+
+static VALUE
+p_sys_setruid(obj, id)
+    VALUE obj, id;
+{
+#if defined HAVE_SETRUID
+    if (setruid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return Qnil;
+}
+
+static VALUE
+p_sys_seteuid(obj, id)
+    VALUE obj, id;
+{
+#if defined HAVE_SETEUID
+    if (seteuid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return Qnil;
+}
+
+static VALUE
+p_sys_setreuid(obj, rid, eid)
+    VALUE obj, rid, eid;
+{
+#if defined HAVE_SETREUID
+    if (setreuid(NUM2INT(rid),NUM2INT(eid)) != 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return Qnil;
+}
+
+static VALUE
+p_sys_setresuid(obj, rid, eid, sid)
+    VALUE obj, rid, eid, sid;
+{
+#if defined HAVE_SETRESUID
+    if (setresuid(NUM2INT(rid),NUM2INT(eid),NUM2INT(sid)) != 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return Qnil;
+}
+
+static VALUE
 proc_getuid(obj)
     VALUE obj;
 {
@@ -1177,7 +1237,7 @@
     if (setreuid(uid, -1) < 0) rb_sys_fail(0);
 #elif defined HAVE_SETRUID
     if (setruid(uid) < 0) rb_sys_fail(0);
-#else
+#elif defined HAVE_SETUID
     {
 	if (geteuid() == uid) {
 	    if (setuid(uid) < 0) rb_sys_fail(0);
@@ -1186,10 +1246,203 @@
 	    rb_notimplement();
 	}
     }
+#else
+    rb_notimplement();
 #endif
     return INT2FIX(uid);
 }
 
+static int SAVED_USER_ID;
+
+static VALUE
+p_uid_set_to(obj, id)
+    VALUE obj, id;
+{
+    extern int errno;
+    int uid;
+
+    uid = NUM2INT(id);
+
+    if (geteuid() == 0) { /* root-user */
+#if defined(HAVE_SETRESUID)
+      if (setresuid(uid, uid, uid) < 0) rb_sys_fail(0);
+      SAVED_USER_ID = uid;
+#elif defined HAVE_SETUID
+      if (setuid(uid) < 0) rb_sys_fail(0);
+      SAVED_USER_ID = uid;
+#elif defined HAVE_SETREUID
+      if (getuid() == uid) {
+	if (SAVED_USER_ID == uid) {
+	  if (setreuid(-1, uid) < 0) rb_sys_fail(0);
+	} else {
+	  if (uid == 0) { /* (r,e,s) == (root, root, x) */
+	    if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
+	    if (setreuid(SAVED_USER_ID, 0) < 0) rb_sys_fail(0);
+	    SAVED_USER_ID = 0; /* (r,e,s) == (x, root, root) */
+	    if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+	    SAVED_USER_ID = uid;
+	  } else {
+	    if (setreuid(0, -1) < 0) rb_sys_fail(0);
+	    SAVED_USER_ID = 0;
+	    if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+	    SAVED_USER_ID = uid;
+	  }
+	}
+      } else {
+	if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+	SAVED_USER_ID = uid;
+      }
+#elif defined HAVE_SETRUID && defined HAVE_SETEUID
+      if (getuid() == uid) {
+	if (SAVED_USER_ID == uid) {
+	  if (seteuid(uid) < 0) rb_sys_fail(0);
+	} else {
+	  if (uid == 0) {
+	    if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+	    SAVED_USER_ID = 0;
+	    if (setruid(0) < 0) rb_sys_fail(0);
+	  } else {
+	    if (setruid(0) < 0) rb_sys_fail(0);
+	    SAVED_USER_ID = 0;
+	    if (seteuid(uid) < 0) rb_sys_fail(0);
+	    if (setruid(uid) < 0) rb_sys_fail(0);
+	    SAVED_USER_ID = uid;
+	  }
+	}
+      } else {
+	if (seteuid(uid) < 0) rb_sys_fail(0);
+	if (setruid(uid) < 0) rb_sys_fail(0);
+	SAVED_USER_ID = uid;
+      }
+#else
+      rb_notimplement();
+#endif
+    } else { /* unprivileged user */
+#if defined(HAVE_SETRESUID)
+      if (setresuid((getuid() == uid)? -1: uid, 
+		    (geteuid() == uid)? -1: uid, 
+		    (SAVED_USER_ID == uid)? -1: uid) < 0) rb_sys_fail(0);
+      SAVED_USER_ID = uid;
+#elif defined HAVE_SETREUID
+      if (SAVED_USER_ID == uid) {
+	if (setreuid((getuid() == uid)? -1: uid, 
+		     (geteuid() == uid)? -1: uid) < 0) rb_sys_fail(0);
+      } else if (getuid() != uid) {
+	if (setreuid(uid, (geteuid() == uid)? -1: uid) < 0) rb_sys_fail(0);
+	SAVED_USER_ID = uid;
+      } else if (/* getuid() == uid && */ geteuid() != uid) {
+	if (setreuid(geteuid(), uid) < 0) rb_sys_fail(0);
+	SAVED_USER_ID = uid;
+	if (setreuid(uid, -1) < 0) rb_sys_fail(0);
+      } else { /* getuid() == uid && geteuid() == uid */
+	if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
+	if (setreuid(SAVED_USER_ID, uid) < 0) rb_sys_fail(0);
+	SAVED_USER_ID = uid;
+	if (setreuid(uid, -1) < 0) rb_sys_fail(0);
+      }
+#elif defined HAVE_SETRUID && defined HAVE_SETEUID
+      if (SAVED_USER_ID == uid) {
+	if (geteuid() != uid && seteuid(uid) < 0) rb_sys_fail(0);
+	if (getuid() != uid && setruid(uid) < 0) rb_sys_fail(0);
+      } else if (/* SAVED_USER_ID != uid && */ geteuid() == uid) {
+	if (getuid() != uid) {
+	  if (setruid(uid) < 0) rb_sys_fail(0);
+	  SAVED_USER_ID = uid;
+	} else {
+	  if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+	  SAVED_USER_ID = uid;
+	  if (setruid(uid) < 0) rb_sys_fail(0);
+	}
+      } else if (/* geteuid() != uid && */ getuid() == uid) {
+	if (seteuid(uid) < 0) rb_sys_fail(0);
+	if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+	SAVED_USER_ID = uid;
+	if (setruid(uid) < 0) rb_sys_fail(0);
+      } else {
+	errno = EPERM;
+	rb_sys_fail(0);
+      }
+#elif defined HAVE_SETEUID
+      if (getuid() == uid && SAVED_USER_ID == uid) {
+	if (seteuid(uid) < 0) rb_sys_fail(0);
+      } else {
+	errno = EPERM;
+	rb_sys_fail(0);
+      }
+#elif defined HAVE_SETUID
+      if (getuid() == uid && SAVED_USER_ID == uid) {
+	if (setuid(uid) < 0) rb_sys_fail(0);
+      } else {
+	errno = EPERM;
+	rb_sys_fail(0);
+      }
+#else
+      rb_notimplement();
+#endif
+    }
+    return INT2FIX(uid);
+}
+
+static VALUE
+p_sys_setgid(obj, id)
+    VALUE obj, id;
+{
+#if defined HAVE_SETGID
+    if (setgid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return Qnil;
+}
+
+static VALUE
+p_sys_setrgid(obj, id)
+    VALUE obj, id;
+{
+#if defined HAVE_SETRGID
+    if (setrgid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return Qnil;
+}
+
+static VALUE
+p_sys_setegid(obj, id)
+    VALUE obj, id;
+{
+#if defined HAVE_SETEGID
+    if (setegid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return Qnil;
+}
+
+static VALUE
+p_sys_setregid(obj, rid, eid)
+    VALUE obj, rid, eid;
+{
+#if defined HAVE_SETREGID
+    if (setregid(NUM2INT(rid),NUM2INT(eid)) != 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return Qnil;
+}
+
+static VALUE
+p_sys_setresgid(obj, rid, eid, sid)
+    VALUE obj, rid, eid, sid;
+{
+#if defined HAVE_SETRESGID
+    if (setresgid(NUM2INT(rid),NUM2INT(eid),NUM2INT(sid)) != 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return Qnil;
+}
+
 static VALUE
 proc_getgid(obj)
     VALUE obj;
@@ -1208,9 +1461,9 @@
     if (setresgid(gid, -1, -1) < 0) rb_sys_fail(0);
 #elif defined HAVE_SETREGID
     if (setregid(gid, -1) < 0) rb_sys_fail(0);
-#elif defined HAS_SETRGID
+#elif defined HAVE_SETRGID
     if (setrgid((GIDTYPE)gid) < 0) rb_sys_fail(0);
-#else
+#elif defined HAVE_SETGID
     {
 	if (getegid() == gid) {
 	    if (setgid(gid) < 0) rb_sys_fail(0);
@@ -1219,6 +1472,8 @@
 	    rb_notimplement();
 	}
     }
+#else
+    rb_notimplement();
 #endif
     return INT2FIX(gid);
 }
@@ -1302,6 +1557,21 @@
 }
 
 static VALUE
+proc_initgroups(obj, uname, base_grp)
+     VALUE obj, uname, base_grp;
+{
+#ifdef HAVE_INITGROUPS
+    if (initgroups(StringValuePtr(uname), (gid_t)NUM2INT(base_grp)) != 0) {
+	rb_sys_fail(0);
+    }
+    return proc_getgroups(obj);
+#else
+    rb_notimplement();
+    return Qnil;
+#endif
+}
+
+static VALUE
 proc_getmaxgroups(obj)
     VALUE obj;
 {
@@ -1322,6 +1592,138 @@
     return INT2FIX(maxgroups);
 }
 
+static int SAVED_GROUP_ID;
+
+static VALUE
+p_gid_set_to(obj, id)
+    VALUE obj, id;
+{
+    extern int errno;
+    int gid;
+
+    gid = NUM2INT(id);
+
+    if (geteuid() == 0) { /* root-user */
+#if defined(HAVE_SETRESGID)
+      if (setresgid(gid, gid, gid) < 0) rb_sys_fail(0);
+      SAVED_GROUP_ID = gid;
+#elif defined HAVE_SETGID
+      if (setgid(gid) < 0) rb_sys_fail(0);
+      SAVED_GROUP_ID = gid;
+#elif defined HAVE_SETREGID
+      if (getgid() == gid) {
+	if (SAVED_GROUP_ID == gid) {
+	  if (setregid(-1, gid) < 0) rb_sys_fail(0);
+	} else {
+	  if (gid == 0) { /* (r,e,s) == (root, y, x) */
+	    if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+	    if (setregid(SAVED_GROUP_ID, 0) < 0) rb_sys_fail(0);
+	    SAVED_GROUP_ID = 0; /* (r,e,s) == (x, root, root) */
+	    if (setregid(gid, gid) < 0) rb_sys_fail(0);
+	    SAVED_GROUP_ID = gid;
+	  } else { /* (r,e,s) == (z, y, x) */
+	    if (setregid(0, 0) < 0) rb_sys_fail(0);
+	    SAVED_GROUP_ID = 0;
+	    if (setregid(gid, gid) < 0) rb_sys_fail(0);
+	    SAVED_GROUP_ID = gid;
+	  }
+	}
+      } else {
+	if (setregid(gid, gid) < 0) rb_sys_fail(0);
+	SAVED_GROUP_ID = gid;
+      }
+#elif defined HAVE_SETRGID && defined HAVE_SETEGID
+      if (getgid() == gid) {
+	if (SAVED_GROUP_ID == gid) {
+	  if (setegid(gid) < 0) rb_sys_fail(0);
+	} else {
+	  if (gid == 0) {
+	    if (setegid(gid) < 0) rb_sys_fail(0);
+	    if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+	    SAVED_GROUP_ID = 0;
+	    if (setrgid(0) < 0) rb_sys_fail(0);
+	  } else {
+	    if (setrgid(0) < 0) rb_sys_fail(0);
+	    SAVED_GROUP_ID = 0;
+	    if (setegid(gid) < 0) rb_sys_fail(0);
+	    if (setrgid(gid) < 0) rb_sys_fail(0);
+	    SAVED_GROUP_ID = gid;
+	  }
+	}
+      } else {
+	if (setegid(gid) < 0) rb_sys_fail(0);
+	if (setrgid(gid) < 0) rb_sys_fail(0);
+	SAVED_GROUP_ID = gid;
+      }
+#else
+      rb_notimplement();
+#endif
+    } else { /* unprivileged user */
+#if defined(HAVE_SETRESGID)
+      if (setresgid((getgid() == gid)? -1: gid, 
+		    (getegid() == gid)? -1: gid, 
+		    (SAVED_GROUP_ID == gid)? -1: gid) < 0) rb_sys_fail(0);
+      SAVED_GROUP_ID = gid;
+#elif defined HAVE_SETREGID
+      if (SAVED_GROUP_ID == gid) {
+	if (setregid((getgid() == gid)? -1: gid, 
+		     (getegid() == gid)? -1: gid) < 0) rb_sys_fail(0);
+      } else if (getgid() != gid) {
+	if (setregid(gid, (getegid() == gid)? -1: gid) < 0) rb_sys_fail(0);
+	SAVED_GROUP_ID = gid;
+      } else if (/* getgid() == gid && */ getegid() != gid) {
+	if (setregid(getegid(), gid) < 0) rb_sys_fail(0);
+	SAVED_GROUP_ID = gid;
+	if (setregid(gid, -1) < 0) rb_sys_fail(0);
+      } else { /* getgid() == gid && getegid() == gid */
+	if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+	if (setregid(SAVED_GROUP_ID, gid) < 0) rb_sys_fail(0);
+	SAVED_GROUP_ID = gid;
+	if (setregid(gid, -1) < 0) rb_sys_fail(0);
+      }
+#elif defined HAVE_SETRGID && defined HAVE_SETEGID
+      if (SAVED_GROUP_ID == gid) {
+	if (getegid() != gid && setegid(gid) < 0) rb_sys_fail(0);
+	if (getgid() != gid && setrgid(gid) < 0) rb_sys_fail(0);
+      } else if (/* SAVED_GROUP_ID != gid && */ getegid() == gid) {
+	if (getgid() != gid) {
+	  if (setrgid(gid) < 0) rb_sys_fail(0);
+	  SAVED_GROUP_ID = gid;
+	} else {
+	  if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+	  SAVED_GROUP_ID = gid;
+	  if (setrgid(gid) < 0) rb_sys_fail(0);
+	}
+      } else if (/* getegid() != gid && */ getgid() == gid) {
+	if (setegid(gid) < 0) rb_sys_fail(0);
+	if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+	SAVED_GROUP_ID = gid;
+	if (setrgid(gid) < 0) rb_sys_fail(0);
+      } else {
+	errno = EPERM;
+	rb_sys_fail(0);
+      }
+#elif defined HAVE_SETEGID
+      if (getgid() == gid && SAVED_GROUP_ID == gid) {
+	if (setegid(gid) < 0) rb_sys_fail(0);
+      } else {
+	errno = EPERM;
+	rb_sys_fail(0);
+      }
+#elif defined HAVE_SETGID
+      if (getgid() == gid && SAVED_GROUP_ID == gid) {
+	if (setgid(gid) < 0) rb_sys_fail(0);
+      } else {
+	errno = EPERM;
+	rb_sys_fail(0);
+      }
+#else
+      rb_notimplement();
+#endif
+    }
+    return INT2FIX(gid);
+}
+
 static VALUE
 proc_geteuid(obj)
     VALUE obj;
@@ -1340,7 +1742,7 @@
     if (setreuid(-1, NUM2INT(euid)) < 0) rb_sys_fail(0);
 #elif defined HAVE_SETEUID
     if (seteuid(NUM2INT(euid)) < 0) rb_sys_fail(0);
-#else
+#elif defined HAVE_SETUID
     euid = NUM2INT(euid);
     if (euid == getuid()) {
 	if (setuid(euid) < 0) rb_sys_fail(0);
@@ -1348,11 +1750,53 @@
     else {
 	rb_notimplement();
     }
+#else
+    rb_notimplement();
 #endif
     return euid;
 }
 
 static VALUE
+rb_seteuid_core(euid)
+    int euid;
+{
+    int uid;
+
+    uid = getuid();
+
+#if defined(HAVE_SETRESUID) && !defined(__CHECKER__)
+    if (uid != euid) {
+      if (setresuid(-1,euid,euid) < 0) rb_sys_fail(0);
+      SAVED_USER_ID = euid;
+    } else {
+      if (setresuid(-1,euid,-1) < 0) rb_sys_fail(0);
+    }
+#elif defined HAVE_SETREUID && !defined(__OpenBSD__)
+    if (setreuid(-1, euid) < 0) rb_sys_fail(0);
+    if (uid != euid) {
+      if (setreuid(euid,uid) < 0) rb_sys_fail(0);
+      if (setreuid(uid,euid) < 0) rb_sys_fail(0);
+      SAVED_USER_ID = euid;
+    }
+#elif defined HAVE_SETEUID
+    if (seteuid(euid) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETUID
+    if (geteuid() == 0) rb_sys_fail(0);
+    if (setuid(euid) < 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return INT2FIX(euid);
+}
+
+static VALUE
+p_uid_set_eid(obj, id)
+    VALUE obj, id;
+{
+  return rb_seteuid_core(NUM2INT(id));
+}
+
+static VALUE
 proc_getegid(obj)
     VALUE obj;
 {
@@ -1372,7 +1816,7 @@
     if (setregid(-1, NUM2INT(egid)) < 0) rb_sys_fail(0);
 #elif defined HAVE_SETEGID
     if (setegid(NUM2INT(egid)) < 0) rb_sys_fail(0);
-#else
+#elif defined HAVE_SETGID
     egid = NUM2INT(egid);
     if (egid == getgid()) {
 	if (setgid(egid) < 0) rb_sys_fail(0);
@@ -1380,10 +1824,224 @@
     else {
 	rb_notimplement();
     }
+#else
+    rb_notimplement();
 #endif
     return egid;
 }
 
+static VALUE
+rb_setegid_core(egid)
+    int egid;
+{
+    int gid;
+
+    gid = getgid();
+
+#if defined(HAVE_SETRESGID) && !defined(__CHECKER__)
+    if (gid != egid) {
+      if (setresgid(-1,egid,egid) < 0) rb_sys_fail(0);
+      SAVED_GROUP_ID = egid;
+    } else {
+      if (setresgid(-1,egid,-1) < 0) rb_sys_fail(0);
+    }
+#elif defined HAVE_SETREGID && !defined(__OpenBSD__)
+    if (setregid(-1, egid) < 0) rb_sys_fail(0);
+    if (gid != egid) {
+      if (setregid(egid,gid) < 0) rb_sys_fail(0);
+      if (setregid(gid,egid) < 0) rb_sys_fail(0);
+      SAVED_GROUP_ID = egid;
+    }
+#elif defined HAVE_SETEGID
+    if (setegid(egid) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETGID
+    if (getegid() == 0) rb_sys_fail(0);
+    if (setgid(egid) < 0) rb_sys_fail(0);
+#else
+    rb_notimplement();
+#endif
+    return INT2FIX(egid);
+}
+
+static VALUE
+p_gid_set_eid(obj, id)
+    VALUE obj, id;
+{
+  return rb_setegid_core(NUM2INT(id));
+}
+
+static VALUE
+p_uid_exchangeable()
+{
+#if defined(HAVE_SETRESUID) &&  !defined(__CHECKER__)
+  return Qtrue;
+#elif defined HAVE_SETREUID && !defined(__OpenBSD__)
+  return Qtrue;
+#else
+  return Qfalse;
+#endif
+}
+
+static VALUE
+p_uid_exchange(obj)
+    VALUE obj;
+{
+    int uid, euid;
+
+    uid = getuid();
+    euid = geteuid();
+
+#if defined(HAVE_SETRESUID) &&  !defined(__CHECKER__)
+    if (setresuid(euid, uid, uid) < 0) rb_sys_fail(0);
+    SAVED_USER_ID = uid;
+#elif defined HAVE_SETREUID && !defined(__OpenBSD__)
+    if (setreuid(euid,uid) < 0) rb_sys_fail(0);
+    SAVED_USER_ID = uid;
+#else
+    rb_notimplement();
+#endif
+    return INT2FIX(uid);
+}
+
+static VALUE
+p_gid_exchangeable()
+{
+#if defined(HAVE_SETRESGID) &&  !defined(__CHECKER__)
+  return Qtrue;
+#elif defined HAVE_SETREGID && !defined(__OpenBSD__)
+  return Qtrue;
+#else
+  return Qfalse;
+#endif
+}
+
+static VALUE
+p_gid_exchange(obj)
+    VALUE obj;
+{
+    int gid, egid;
+
+    gid = getgid();
+    egid = getegid();
+
+#if defined(HAVE_SETRESGID) &&  !defined(__CHECKER__)
+    if (setresgid(egid, gid, gid) < 0) rb_sys_fail(0);
+    SAVED_GROUP_ID = gid;
+#elif defined HAVE_SETREGID && !defined(__OpenBSD__)
+    if (setregid(egid,gid) < 0) rb_sys_fail(0);
+    SAVED_GROUP_ID = gid;
+#else
+    rb_notimplement();
+#endif
+    return INT2FIX(gid);
+}
+
+static VALUE
+p_uid_has_saved_id()
+{
+#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || _POSIX_SAVED_IDS
+  return Qtrue;
+#else
+  return Qfalse;
+#endif
+}
+
+static VALUE
+p_uid_switch_auth(obj)
+    VALUE obj;
+{
+    extern int errno;
+    int uid, euid;
+
+    uid = getuid();
+    euid = geteuid();
+
+#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || _POSIX_SAVED_IDS
+    if (uid != euid) {
+      proc_seteuid(obj, INT2FIX(uid));
+      if (rb_block_given_p()) {
+	return rb_ensure(rb_yield, Qnil, rb_seteuid_core, SAVED_USER_ID);
+      } else {
+	return INT2FIX(euid);
+      }
+    } else if (euid != SAVED_USER_ID) {
+      proc_seteuid(obj, INT2FIX(SAVED_USER_ID));
+      if (rb_block_given_p()) {
+	return rb_ensure(rb_yield, Qnil, rb_seteuid_core, euid);
+      } else {
+	return INT2FIX(uid);
+      }
+    } else {
+      errno = EPERM;
+      rb_sys_fail(0);
+    }
+#else
+    if (uid == euid) {
+      errno = EPERM;
+      rb_sys_fail(0);
+    }
+    proc_swap_uid(obj);
+    if (rb_block_given_p()) {
+      return rb_ensure(rb_yield, Qnil, proc_swap_uid, obj);
+    } else {
+      return INT2FIX(euid);
+    }
+#endif
+}
+
+static VALUE
+p_gid_has_saved_id()
+{
+#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || _POSIX_SAVED_IDS
+  return Qtrue;
+#else
+  return Qfalse;
+#endif
+}
+
+static VALUE
+p_gid_switch_auth(obj)
+    VALUE obj;
+{
+    extern int errno;
+    int gid, egid;
+
+    gid = getgid();
+    egid = getegid();
+
+#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || _POSIX_SAVED_IDS
+    if (gid != egid) {
+      proc_setegid(obj, INT2FIX(gid));
+      if (rb_block_given_p()) {
+	return rb_ensure(rb_yield, Qnil, proc_setegid, INT2FIX(SAVED_GROUP_ID));
+      } else {
+	return INT2FIX(egid);
+      }
+    } else if (egid != SAVED_GROUP_ID) {
+      proc_setegid(obj, INT2FIX(SAVED_GROUP_ID));
+      if (rb_block_given_p()) {
+	return rb_ensure(rb_yield, Qnil, proc_setegid, INT2FIX(egid));
+      } else {
+	return INT2FIX(gid);
+      }
+    } else {
+      errno = EPERM;
+      rb_sys_fail(0);
+    }
+#else
+    if (gid == egid) {
+      errno = EPERM;
+      rb_sys_fail(0);
+    }
+    proc_swap_gid(obj);
+    if (rb_block_given_p()) {
+      return rb_ensure(rb_yield, Qnil, proc_swap_gid, obj);
+    } else {
+      return INT2FIX(egid);
+    }
+#endif
+}
+
 VALUE
 rb_proc_times(obj)
     VALUE obj;
@@ -1411,6 +2069,9 @@
 }
 
 VALUE rb_mProcess;
+VALUE rb_mProcUID;
+VALUE rb_mProcGID;
+VALUE rb_mProcID_Syscall;
 
 void
 Init_process()
@@ -1499,6 +2160,7 @@
     rb_define_module_function(rb_mProcess, "euid=", proc_seteuid, 1);
     rb_define_module_function(rb_mProcess, "egid", proc_getegid, 0);
     rb_define_module_function(rb_mProcess, "egid=", proc_setegid, 1);
+    rb_define_module_function(rb_mProcess, "initgroups", proc_initgroups, 2);
     rb_define_module_function(rb_mProcess, "groups", proc_getgroups, 0);
     rb_define_module_function(rb_mProcess, "groups=", proc_setgroups, 1);
     rb_define_module_function(rb_mProcess, "maxgroups", proc_getmaxgroups, 0);
@@ -1509,4 +2171,53 @@
 #if defined(HAVE_TIMES) || defined(_WIN32)
     S_Tms = rb_struct_define("Tms", "utime", "stime", "cutime", "cstime", 0);
 #endif
+
+    SAVED_USER_ID = geteuid();
+    SAVED_GROUP_ID = getegid();
+
+    rb_mProcUID = rb_define_module_under(rb_mProcess, "UserID");
+    rb_mProcGID = rb_define_module_under(rb_mProcess, "GroupID");
+
+    rb_define_module_function(rb_mProcUID, "rid", proc_getuid, 0);
+    rb_define_module_function(rb_mProcGID, "rid", proc_getgid, 0);
+    rb_define_module_function(rb_mProcUID, "eid", proc_geteuid, 0);
+    rb_define_module_function(rb_mProcGID, "eid", proc_getegid, 0);
+    rb_define_module_function(rb_mProcUID, "to", p_uid_set_to, 1);
+    rb_define_module_function(rb_mProcGID, "to", p_gid_set_to, 1);
+    rb_define_module_function(rb_mProcUID, "eid=", p_uid_set_eid, 1);
+    rb_define_module_function(rb_mProcGID, "eid=", p_gid_set_eid, 1);
+    rb_define_module_function(rb_mProcUID, "ex_reid", p_uid_exchange, 0);
+    rb_define_module_function(rb_mProcGID, "ex_reid", p_gid_exchange, 0);
+    rb_define_module_function(rb_mProcUID, "ex_reid?", p_uid_exchangeable, 0);
+    rb_define_module_function(rb_mProcGID, "ex_reid?", p_gid_exchangeable, 0);
+    rb_define_module_function(rb_mProcUID, "has_sid?", p_uid_has_saved_id, 0);
+    rb_define_module_function(rb_mProcGID, "has_sid?", p_gid_has_saved_id, 0);
+    rb_define_module_function(rb_mProcUID, "switch", p_uid_switch_auth, 0);
+    rb_define_module_function(rb_mProcGID, "switch", p_uid_switch_auth, 0);
+
+    rb_mProcID_Syscall = rb_define_module_under(rb_mProcess, "ID_Syscall");
+
+    rb_define_module_function(rb_mProcID_Syscall, "getuid", proc_getuid, 0);
+    rb_define_module_function(rb_mProcID_Syscall, "geteuid", proc_geteuid, 0);
+    rb_define_module_function(rb_mProcID_Syscall, "getgid", proc_getgid, 0);
+    rb_define_module_function(rb_mProcID_Syscall, "getegid", proc_getegid, 0);
+
+    rb_define_module_function(rb_mProcID_Syscall, "setuid", p_sys_setuid, 1);
+    rb_define_module_function(rb_mProcID_Syscall, "setgid", p_sys_setgid, 1);
+
+    rb_define_module_function(rb_mProcID_Syscall, "setruid", p_sys_setruid, 1);
+    rb_define_module_function(rb_mProcID_Syscall, "setrgid", p_sys_setrgid, 1);
+
+    rb_define_module_function(rb_mProcID_Syscall, "seteuid", p_sys_seteuid, 1);
+    rb_define_module_function(rb_mProcID_Syscall, "setegid", p_sys_setegid, 1);
+
+    rb_define_module_function(rb_mProcID_Syscall, "setreuid", 
+			      p_sys_setreuid, 2);
+    rb_define_module_function(rb_mProcID_Syscall, "setregid", 
+			      p_sys_setregid, 2);
+
+    rb_define_module_function(rb_mProcID_Syscall, "setresuid", 
+			      p_sys_setresuid, 3);
+    rb_define_module_function(rb_mProcID_Syscall, "setresgid", 
+			      p_sys_setresgid, 3);
 }

-- 
                                         永井 秀利 (九工大 知能情報)
                                             nagai / ai.kyutech.ac.jp