Project

General

Profile

Bug #16787 » allow-dir.home-for-non-login-procs-v5.patch

patch: allow-dir.home-for-non-login-procs-v5.patch - salewski (Alan Salewski), 04/17/2020 10:04 AM

View differences:

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;
......
}
#ifndef _WIN32
VALUE rb_getlogin(void);
VALUE rb_getpwdirnam_for_login(void); /* read as: "get pwd db home dir by username for login" */
VALUE rb_getpwdiruid(void); /* read as: "get pwd db home dir by uid" */
VALUE
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);
}
else {
rb_raise(rb_eArgError, "couldn't find login name -- expanding `~'");
}
/* 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)).
*/
VALUE pw_dir_str_nm = rb_getpwdirnam_for_login();
if (!NIL_P(pw_dir_str_nm)) {
/* found it */
copy_home_path(result, RSTRING_PTR(pw_dir_str_nm));
rb_str_resize(pw_dir_str_nm, 0);
return result;
}
VALUE pw_dir_str_uid = rb_getpwdiruid();
if (!NIL_P(pw_dir_str_uid)) {
/* found it */
copy_home_path(result, RSTRING_PTR(pw_dir_str_uid));
rb_str_resize(pw_dir_str_uid, 0);
return result;
}
rb_raise(rb_eArgError, "couldn't find home for uid `%ld'", (long)getpid());
}
/**
* Best-effort attempt to obtain the name of the login user, if any,
* associated with the process. Processes not descended from login(1) (or
* similar) may not have a logged-in user; returns Qnil in that case.
*/
VALUE
rb_getlogin(void)
{
#if ( !defined(USE_GETLOGIN_R) && !defined(USE_GETLOGIN) )
return Qnil;
#else
char MAYBE_UNUSED(*login) = NULL;
# 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 || ENOENT) {
rb_str_resize(getlogin_tmp, 0);
return Qnil;
}
if (gle != ERANGE || loginsize >= GETLOGIN_R_SIZE_LIMIT) {
rb_str_resize(getlogin_tmp, 0);
rb_syserr_fail(gle, "getlogin_r");
}
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);
return Qnil;
}
VALUE result = rb_str_new_cstr(login);
rb_str_resize(getlogin_tmp, 0);
return result;
# elif USE_GETLOGIN
errno = 0;
login = getlogin();
if (errno) {
if (errno == ENOTTY || errno == ENXIO || errno == ENOENT) {
return Qnil;
}
rb_syserr_fail(errno, "getlogin");
}
return login ? rb_str_new_cstr(login) : Qnil;
# 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
#endif
}
VALUE
rb_getpwdirnam_for_login(void)
{
#if ( !defined(USE_GETPWNAM_R) && !defined(USE_GETPWNAM) )
return Qnil;
#else
VALUE login_name = rb_getlogin();
if (NIL_P(login_name)) {
/* nothing to do; no name with which to query the password database */
return Qnil;
}
char *login = RSTRING_PTR(login_name);
struct passwd *pwptr;
# 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) {
/* not found; non-errors */
rb_str_resize(getpwnm_tmp, 0);
return Qnil;
}
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);
}
if (pwptr == NULL) {
/* no record in the password database for the login name */
rb_str_resize(getpwnm_tmp, 0);
return Qnil;
}
/* found it */
VALUE result = rb_str_new_cstr(pwptr->pw_dir);
rb_str_resize(getpwnm_tmp, 0);
return result;
# elif USE_GETPWNAM
errno = 0;
pwptr = getpwnam(login);
if (pwptr) {
/* found it */
return rb_str_new_cstr(pwptr->pw_dir);
}
if (errno && ( errno == ENOENT || errno == ESRCH || errno == EBADF || errno == EPERM)) {
/* not found; non-errors */
return Qnil;
}
rb_syserr_fail(errno, "getpwnam");
# 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
if (!dir) {
rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `~'");
}
/**
* Look up the user's dflt home dir in the password db, by uid.
*/
VALUE
rb_getpwdiruid(void)
{
# if !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID)
/* Should never happen... </famous-last-words> */
return Qnil;
# else
uid_t ruid = getuid();
struct passwd *pwptr;
# 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 == ENOENT || eid== ESRCH || eid == EBADF || eid == EPERM) {
/* not found; non-errors */
rb_str_resize(getpwid_tmp, 0);
return Qnil;
}
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);
return Qnil;
}
/* found it */
VALUE result = rb_str_new_cstr(pwptr->pw_dir);
rb_str_resize(getpwid_tmp, 0);
return result;
# elif defined(USE_GETPWUID)
errno = 0;
pwptr = getpwuid(ruid);
if (pwptr) {
/* found it */
return rb_str_new_cstr(pwptr->pw_dir);
}
if (errno && ( errno == ENOENT || errno == ESRCH || errno == EBADF || errno == EPERM)) {
/* not found; non-errors */
return Qnil;
}
return copy_home_path(result, dir);
rb_syserr_fail(errno, "getpwuid");
# 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) */
}
static VALUE
(5-5/10)