Skip to content
126 changes: 108 additions & 18 deletions ext/etc/etc.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "ruby.h"
#include "ruby/encoding.h"
#include "ruby/io.h"
#include "ruby/thread.h"

#include <sys/types.h>
#ifdef HAVE_UNISTD_H
Expand Down Expand Up @@ -94,6 +95,29 @@ atomic_exchange(volatile rb_atomic_t *var, rb_atomic_t newval)
}
#endif

static VALUE mEtc_mutex;

struct mEtc_mutex_args {
void * (*func)(void *);
void *arg;
};

static VALUE
mEtc_mutex_nogvl(VALUE arg)
{
struct mEtc_mutex_args *args = (struct mEtc_mutex_args *)arg;
return (VALUE)rb_thread_call_without_gvl(args->func, args->arg, RUBY_UBF_IO, 0);
}

static void *
mEtc_mutex_synchronize(void * (*func)(void *), void *arg)
{
struct mEtc_mutex_args args;
args.func = func;
args.arg = arg;
return (void *)rb_mutex_synchronize(mEtc_mutex, mEtc_mutex_nogvl, (VALUE)&args);
}

/* call-seq:
* getlogin -> String
*
Expand Down Expand Up @@ -157,6 +181,37 @@ safe_setup_filesystem_str(const char *str)
#endif

#ifdef HAVE_GETPWENT
static void *
nogvl_getpwuid(void *uid)
{
return getpwuid((uid_t)(VALUE)uid);
}

static void *
nogvl_getpwnam(void *name)
{
return getpwnam((const char *)name);
}

static void *
nogvl_getpwent(void *_)
{
return getpwent();
}

static void *
nogvl_setpwent(void *_)
{
setpwent();
return NULL;
}

static void *
nogvl_endpwent(void *_)
{
endpwent();
return NULL;
}
# ifdef __APPLE__
# define PW_TIME2VAL(t) INT2NUM((int)(t))
# else
Expand Down Expand Up @@ -234,7 +289,7 @@ etc_getpwuid(int argc, VALUE *argv, VALUE obj)
else {
uid = getuid();
}
pwd = getpwuid(uid);
pwd = mEtc_mutex_synchronize(nogvl_getpwuid, (void *)(VALUE)uid);
if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %d", (int)uid);
return setup_passwd(pwd);
#else
Expand Down Expand Up @@ -264,7 +319,7 @@ etc_getpwnam(VALUE obj, VALUE nam)
struct passwd *pwd;
const char *p = StringValueCStr(nam);

pwd = getpwnam(p);
pwd = mEtc_mutex_synchronize(nogvl_getpwnam, (void *)p);
if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, nam);
return setup_passwd(pwd);
#else
Expand All @@ -277,7 +332,7 @@ static rb_atomic_t passwd_blocking;
static VALUE
passwd_ensure(VALUE _)
{
endpwent();
mEtc_mutex_synchronize(nogvl_endpwent, NULL);
if (RUBY_ATOMIC_EXCHANGE(passwd_blocking, 0) != 1) {
rb_raise(rb_eRuntimeError, "unexpected passwd_blocking");
}
Expand All @@ -289,8 +344,8 @@ passwd_iterate(VALUE _)
{
struct passwd *pw;

setpwent();
while ((pw = getpwent()) != 0) {
mEtc_mutex_synchronize(nogvl_setpwent, NULL);
while ((pw = mEtc_mutex_synchronize(nogvl_getpwent, NULL)) != 0) {
rb_yield(setup_passwd(pw));
}
return Qnil;
Expand Down Expand Up @@ -335,7 +390,7 @@ etc_passwd(VALUE obj)
if (rb_block_given_p()) {
each_passwd();
}
else if ((pw = getpwent()) != 0) {
else if ((pw = mEtc_mutex_synchronize(nogvl_getpwent, NULL)) != 0) {
return setup_passwd(pw);
}
#endif
Expand Down Expand Up @@ -387,7 +442,7 @@ static VALUE
etc_setpwent(VALUE obj)
{
#ifdef HAVE_GETPWENT
setpwent();
mEtc_mutex_synchronize(nogvl_setpwent, NULL);
#endif
return Qnil;
}
Expand All @@ -402,7 +457,7 @@ static VALUE
etc_endpwent(VALUE obj)
{
#ifdef HAVE_GETPWENT
endpwent();
mEtc_mutex_synchronize(nogvl_endpwent, NULL);
#endif
return Qnil;
}
Expand All @@ -427,14 +482,46 @@ etc_getpwent(VALUE obj)
#ifdef HAVE_GETPWENT
struct passwd *pw;

if ((pw = getpwent()) != 0) {
if ((pw = mEtc_mutex_synchronize(nogvl_getpwent, NULL)) != 0) {
return setup_passwd(pw);
}
#endif
return Qnil;
}

#ifdef HAVE_GETGRENT
static void *
nogvl_getgrgid(void *gid)
{
return getgrgid((gid_t)(VALUE)gid);
}

static void *
nogvl_getgrnam(void *name)
{
return getgrnam((const char *)name);
}

static void *
nogvl_getgrent(void *_)
{
return getgrent();
}

static void *
nogvl_setgrent(void *_)
{
setgrent();
return NULL;
}

static void *
nogvl_endgrent(void *_)
{
endgrent();
return NULL;
}

static VALUE
setup_group(struct group *grp)
{
Expand Down Expand Up @@ -487,7 +574,7 @@ etc_getgrgid(int argc, VALUE *argv, VALUE obj)
else {
gid = getgid();
}
grp = getgrgid(gid);
grp = mEtc_mutex_synchronize(nogvl_getgrgid, (void *)(VALUE)gid);
if (grp == 0) rb_raise(rb_eArgError, "can't find group for %d", (int)gid);
return setup_group(grp);
#else
Expand Down Expand Up @@ -518,7 +605,7 @@ etc_getgrnam(VALUE obj, VALUE nam)
struct group *grp;
const char *p = StringValueCStr(nam);

grp = getgrnam(p);
grp = mEtc_mutex_synchronize(nogvl_getgrnam, (void *)p);
if (grp == 0) rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, nam);
return setup_group(grp);
#else
Expand All @@ -531,7 +618,7 @@ static rb_atomic_t group_blocking;
static VALUE
group_ensure(VALUE _)
{
endgrent();
mEtc_mutex_synchronize(nogvl_endgrent, NULL);
if (RUBY_ATOMIC_EXCHANGE(group_blocking, 0) != 1) {
rb_raise(rb_eRuntimeError, "unexpected group_blocking");
}
Expand All @@ -543,8 +630,8 @@ group_iterate(VALUE _)
{
struct group *pw;

setgrent();
while ((pw = getgrent()) != 0) {
mEtc_mutex_synchronize(nogvl_setgrent, NULL);
while ((pw = mEtc_mutex_synchronize(nogvl_getgrent, NULL)) != 0) {
rb_yield(setup_group(pw));
}
return Qnil;
Expand Down Expand Up @@ -589,7 +676,7 @@ etc_group(VALUE obj)
if (rb_block_given_p()) {
each_group();
}
else if ((grp = getgrent()) != 0) {
else if ((grp = mEtc_mutex_synchronize(nogvl_getgrent, NULL)) != 0) {
return setup_group(grp);
}
#endif
Expand Down Expand Up @@ -639,7 +726,7 @@ static VALUE
etc_setgrent(VALUE obj)
{
#ifdef HAVE_GETGRENT
setgrent();
mEtc_mutex_synchronize(nogvl_setgrent, NULL);
#endif
return Qnil;
}
Expand All @@ -654,7 +741,7 @@ static VALUE
etc_endgrent(VALUE obj)
{
#ifdef HAVE_GETGRENT
endgrent();
mEtc_mutex_synchronize(nogvl_endgrent, NULL);
#endif
return Qnil;
}
Expand All @@ -678,7 +765,7 @@ etc_getgrent(VALUE obj)
#ifdef HAVE_GETGRENT
struct group *gr;

if ((gr = getgrent()) != 0) {
if ((gr = mEtc_mutex_synchronize(nogvl_getgrent, NULL)) != 0) {
return setup_group(gr);
}
#endif
Expand Down Expand Up @@ -1157,6 +1244,9 @@ Init_etc(void)
rb_define_const(mEtc, "VERSION", rb_str_new_cstr(RUBY_ETC_VERSION));
init_constants(mEtc);

mEtc_mutex = rb_mutex_new();
rb_gc_register_mark_object(mEtc_mutex);

rb_define_module_function(mEtc, "getlogin", etc_getlogin, 0);

rb_define_module_function(mEtc, "getpwuid", etc_getpwuid, -1);
Expand Down