Bug #16787 » allow-dir.home-for-non-login-procs-v4.patch
configure.ac | ||
---|---|---|
AC_CHECK_FUNCS(getgrnam)
|
||
AC_CHECK_FUNCS(getgrnam_r)
|
||
AC_CHECK_FUNCS(getgroups)
|
||
AC_CHECK_FUNCS(getlogin)
|
||
AC_CHECK_FUNCS(getlogin_r)
|
||
AC_CHECK_FUNCS(getpgid)
|
||
AC_CHECK_FUNCS(getpgrp)
|
||
AC_CHECK_FUNCS(getpriority)
|
||
AC_CHECK_FUNCS(getpwnam)
|
||
AC_CHECK_FUNCS(getpwnam_r)
|
||
AC_CHECK_FUNCS(getpwuid)
|
||
AC_CHECK_FUNCS(getpwuid_r)
|
||
AC_CHECK_FUNCS(getrandom)
|
||
AC_CHECK_FUNCS(getresgid)
|
||
AC_CHECK_FUNCS(getresuid)
|
file.c | ||
---|---|---|
#include "ruby/thread.h"
|
||
#include "ruby/util.h"
|
||
#if defined(HAVE_UNISTD_H)
|
||
# if defined(HAVE_GETLOGIN_R)
|
||
# define USE_GETLOGIN_R 1
|
||
# define GETLOGIN_R_SIZE_DEFAULT 0x100
|
||
# define GETLOGIN_R_SIZE_LIMIT 0x1000
|
||
# if defined(_SC_LOGIN_NAME_MAX)
|
||
# define GETLOGIN_R_SIZE_INIT sysconf(_SC_LOGIN_NAME_MAX)
|
||
# else
|
||
# define GETLOGIN_R_SIZE_INIT GETLOGIN_R_SIZE_DEFAULT
|
||
# endif
|
||
# elif defined(HAVE_GETLOGIN)
|
||
# define USE_GETLOGIN 1
|
||
# endif
|
||
#endif
|
||
#if defined(HAVE_PWD_H)
|
||
# define GETPW_R_SIZE_DEFAULT 0x1000
|
||
# define GETPW_R_SIZE_LIMIT 0x10000
|
||
# if defined(_SC_GETPW_R_SIZE_MAX)
|
||
# define GETPW_R_SIZE_INIT sysconf(_SC_GETPW_R_SIZE_MAX)
|
||
# else
|
||
# define GETPW_R_SIZE_INIT GETPW_R_SIZE_DEFAULT
|
||
# endif
|
||
# if defined(HAVE_GETPWNAM_R)
|
||
# define USE_GETPWNAM_R 1
|
||
# elif defined(HAVE_GETPWNAM)
|
||
# define USE_GETPWNAM 1
|
||
# endif
|
||
# if defined(HAVE_GETPWUID_R)
|
||
# define USE_GETPWUID_R 1
|
||
# elif defined(HAVE_GETPWUID)
|
||
# define USE_GETPWUID 1
|
||
# endif
|
||
#endif
|
||
VALUE rb_cFile;
|
||
VALUE rb_mFileTest;
|
||
VALUE rb_cStat;
|
||
... | ... | |
rb_default_home_dir(VALUE result)
|
||
{
|
||
const char *dir = getenv("HOME");
|
||
if (dir)
|
||
return copy_home_path(result, dir);
|
||
#if defined HAVE_PWD_H
|
||
if (!dir) {
|
||
const char *login = getlogin();
|
||
if (login) {
|
||
struct passwd *pw = getpwnam(login);
|
||
if (pw) {
|
||
copy_home_path(result, pw->pw_dir);
|
||
endpwent();
|
||
return result;
|
||
}
|
||
endpwent();
|
||
rb_raise(rb_eArgError, "couldn't find HOME for login `%s' -- expanding `~'",
|
||
login);
|
||
#if !defined(USE_GETLOGIN_R) && !defined(USE_GETLOGIN) \
|
||
&& !defined(USE_GETPWNAM_R) && !defined(USE_GETPWNAM) \
|
||
&& !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID)
|
||
rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
|
||
#else
|
||
/* We'll look up the user's default home dir in the password db by login
|
||
* name, if possible, and failing that will fall back to looking the
|
||
* information up by uid (as would be needed for processes that are not a
|
||
* descendant of login(1) or a work-alike).
|
||
*
|
||
* While the lookup by uid is more likely to succeed (since we always have
|
||
* a uid, but may or may not have a login name), we prefer first looking
|
||
* up by name to accommodate the possibility of multiple login names (each
|
||
* with its own record in the password database, so each with a
|
||
* potentially different home directory) being mapped to the same uid (as
|
||
* explicitly allowed for by POSIX; see getlogin(3posix)).
|
||
*/
|
||
struct passwd MAYBE_UNUSED(*pwptr);
|
||
char MAYBE_UNUSED(*login) = NULL;
|
||
# if ( defined(USE_GETLOGIN_R) || defined(USE_GETLOGIN) ) \
|
||
&& ( defined(USE_GETPWNAM_R) || defined(USE_GETPWNAM) )
|
||
/*
|
||
* Try to look up the user's dflt home dir in the password db, by login name
|
||
*/
|
||
# ifdef USE_GETLOGIN_R
|
||
long loginsize = GETLOGIN_R_SIZE_INIT; /* maybe -1 */
|
||
if (loginsize < 0)
|
||
loginsize = GETLOGIN_R_SIZE_DEFAULT;
|
||
VALUE getlogin_tmp = rb_str_tmp_new(loginsize);
|
||
login = RSTRING_PTR(getlogin_tmp);
|
||
loginsize = rb_str_capacity(getlogin_tmp);
|
||
rb_str_set_len(getlogin_tmp, loginsize);
|
||
int gle;
|
||
errno = 0;
|
||
while ((gle = getlogin_r(login, loginsize)) != 0) {
|
||
if (gle == ENOTTY || gle == ENXIO) {
|
||
rb_str_resize(getlogin_tmp, 0);
|
||
goto MAYBE_TRY_LOOKUP_BY_UID;
|
||
}
|
||
if (gle != ERANGE || loginsize >= GETLOGIN_R_SIZE_LIMIT) {
|
||
rb_str_resize(getlogin_tmp, 0);
|
||
rb_syserr_fail(gle, "getlogin_r");
|
||
}
|
||
else {
|
||
rb_raise(rb_eArgError, "couldn't find login name -- expanding `~'");
|
||
rb_str_modify_expand(getlogin_tmp, loginsize);
|
||
login = RSTRING_PTR(getlogin_tmp);
|
||
loginsize = rb_str_capacity(getlogin_tmp);
|
||
}
|
||
if (login == NULL) {
|
||
rb_str_resize(getlogin_tmp, 0);
|
||
/* Fall through to lookup by uid functionality (if possible) */
|
||
}
|
||
# elif USE_GETLOGIN
|
||
errno = 0;
|
||
login = getlogin();
|
||
if (errno == ENOMEM) {
|
||
rb_syserr_fail(errno, "getlogin");
|
||
}
|
||
/* Ignore all other possible values for errno here; none
|
||
* would cause us to not want to attempt our lookup by uid
|
||
* below. Keep going...
|
||
*/
|
||
# else
|
||
# error Hello! Ruby developers believe this message must not happen.
|
||
# error BUG: Either USE_GETLOGIN_R or USE_GETLOGIN should be defined here.
|
||
# error If you encounter this message, can you file a bug report?
|
||
# error Remember to attach a detailed description of your environment.
|
||
# error Thank you!
|
||
# endif
|
||
if (!login)
|
||
goto MAYBE_TRY_LOOKUP_BY_UID;
|
||
# ifdef USE_GETPWNAM_R
|
||
struct passwd pwdnm;
|
||
char *bufnm;
|
||
long bufsizenm = GETPW_R_SIZE_INIT; /* maybe -1 */
|
||
if (bufsizenm < 0)
|
||
bufsizenm = GETPW_R_SIZE_DEFAULT;
|
||
VALUE getpwnm_tmp = rb_str_tmp_new(bufsizenm);
|
||
bufnm = RSTRING_PTR(getpwnm_tmp);
|
||
bufsizenm = rb_str_capacity(getpwnm_tmp);
|
||
rb_str_set_len(getpwnm_tmp, bufsizenm);
|
||
int enm;
|
||
errno = 0;
|
||
while ((enm = getpwnam_r(login, &pwdnm, bufnm, bufsizenm, &pwptr)) != 0) {
|
||
if (enm == ENOENT || enm== ESRCH || enm == EBADF || enm == EPERM) {
|
||
rb_str_resize(getpwnm_tmp, 0);
|
||
goto MAYBE_TRY_LOOKUP_BY_UID;
|
||
}
|
||
if (enm != ERANGE || bufsizenm >= GETPW_R_SIZE_LIMIT) {
|
||
rb_str_resize(getpwnm_tmp, 0);
|
||
rb_syserr_fail(enm, "getpwnam_r");
|
||
}
|
||
rb_str_modify_expand(getpwnm_tmp, bufsizenm);
|
||
bufnm = RSTRING_PTR(getpwnm_tmp);
|
||
bufsizenm = rb_str_capacity(getpwnm_tmp);
|
||
}
|
||
#endif
|
||
if (!dir) {
|
||
rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
|
||
if (pwptr == NULL) {
|
||
/* no record in the password database for the login name */
|
||
rb_str_resize(getpwnm_tmp, 0);
|
||
goto MAYBE_TRY_LOOKUP_BY_UID;
|
||
}
|
||
copy_home_path(result, pwptr->pw_dir);
|
||
rb_str_resize(getpwnm_tmp, 0);
|
||
return result;
|
||
# elif USE_GETPWNAM
|
||
errno = 0;
|
||
pwptr = getpwnam(login);
|
||
if (pwptr) {
|
||
copy_home_path(result, pwptr->pw_dir);
|
||
return result;
|
||
}
|
||
if (errno && ( errno == EINTR || errno == EIO || errno == ENOMEM )) {
|
||
rb_syserr_fail(errno, "getpwnam");
|
||
}
|
||
return copy_home_path(result, dir);
|
||
/* Ignore all other possible values for errno here; none
|
||
* would cause us to not want to attempt our lookup by uid
|
||
* below. Keep going...
|
||
*/
|
||
# else
|
||
# error Hello! Ruby developers believe this message must not happen.
|
||
# error BUG: Either USE_GETPWNAM_R or USE_GETPWNAM should be defined here.
|
||
# error If you encounter this message, can you file a bug report?
|
||
# error Remember to attach a detailed description of your environment.
|
||
# error Thank you!
|
||
# endif
|
||
# endif
|
||
MAYBE_TRY_LOOKUP_BY_UID: MAYBE_UNUSED();
|
||
# if !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID)
|
||
/* Should never happen... </famous-last-words> */
|
||
rb_raise(rb_eArgError, "couldn't find home dir by login name (%s)",
|
||
((login) ? login : "<UNAVAILABLE>"));
|
||
# else
|
||
/*
|
||
* Try to look up the user's dflt home dir in the password db, by uid
|
||
*/
|
||
uid_t ruid = getuid();
|
||
# ifdef USE_GETPWUID_R
|
||
struct passwd pwdid;
|
||
char *bufid;
|
||
long bufsizeid = GETPW_R_SIZE_INIT; /* maybe -1 */
|
||
if (bufsizeid < 0)
|
||
bufsizeid = GETPW_R_SIZE_DEFAULT;
|
||
VALUE getpwid_tmp = rb_str_tmp_new(bufsizeid);
|
||
bufid = RSTRING_PTR(getpwid_tmp);
|
||
bufsizeid = rb_str_capacity(getpwid_tmp);
|
||
rb_str_set_len(getpwid_tmp, bufsizeid);
|
||
int eid;
|
||
errno = 0;
|
||
while ((eid = getpwuid_r(ruid, &pwdid, bufid, bufsizeid, &pwptr)) != 0) {
|
||
if (eid != ERANGE || bufsizeid >= GETPW_R_SIZE_LIMIT) {
|
||
rb_str_resize(getpwid_tmp, 0);
|
||
rb_syserr_fail(eid, "getpwuid_r");
|
||
}
|
||
rb_str_modify_expand(getpwid_tmp, bufsizeid);
|
||
bufid = RSTRING_PTR(getpwid_tmp);
|
||
bufsizeid = rb_str_capacity(getpwid_tmp);
|
||
}
|
||
if (pwptr == NULL) {
|
||
/* no record in the password database for the uid */
|
||
rb_str_resize(getpwid_tmp, 0);
|
||
rb_raise(rb_eArgError, "getpwuid_r couldn't find HOME for uid `%ld' -- expanding `~'",
|
||
(long)ruid);
|
||
}
|
||
copy_home_path(result, pwptr->pw_dir);
|
||
rb_str_resize(getpwid_tmp, 0);
|
||
return result;
|
||
# elif defined(USE_GETPWUID)
|
||
errno = 0;
|
||
pwptr = getpwuid(ruid);
|
||
if (pwptr == NULL) {
|
||
if (errno) {
|
||
rb_syserr_fail(errno, "getpwuid");
|
||
}
|
||
rb_raise(rb_eArgError, "getpwuid couldn't find HOME for uid `%ld' -- expanding `~'",
|
||
(long)ruid);
|
||
}
|
||
|
||
copy_home_path(result, pwptr->pw_dir);
|
||
return result;
|
||
# else
|
||
# error Hello! Ruby developers believe this message must not happen.
|
||
# error BUG: Either USE_GETPWUID_R or USE_GETPWUID should be defined here.
|
||
# error If you encounter this message, can you file a bug report?
|
||
# error Remember to attach a detailed description of your environment.
|
||
# error Thank you!
|
||
# endif
|
||
# endif /* !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID) */
|
||
#endif
|
||
}
|
||
static VALUE
|