mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
2f3c6e7cc3
This reverts commit 5488f0b2ca
which was a
good experiment to try, that didn't work out due to #1176
Fixes #1176
316 lines
8.6 KiB
C
316 lines
8.6 KiB
C
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │
|
|
╚──────────────────────────────────────────────────────────────────────────────╝
|
|
│ │
|
|
│ Musl Libc │
|
|
│ Copyright © 2005-2014 Rich Felker, et al. │
|
|
│ │
|
|
│ Permission is hereby granted, free of charge, to any person obtaining │
|
|
│ a copy of this software and associated documentation files (the │
|
|
│ "Software"), to deal in the Software without restriction, including │
|
|
│ without limitation the rights to use, copy, modify, merge, publish, │
|
|
│ distribute, sublicense, and/or sell copies of the Software, and to │
|
|
│ permit persons to whom the Software is furnished to do so, subject to │
|
|
│ the following conditions: │
|
|
│ │
|
|
│ The above copyright notice and this permission notice shall be │
|
|
│ included in all copies or substantial portions of the Software. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
|
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
|
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
|
│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │
|
|
│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │
|
|
│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │
|
|
│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
|
│ │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/weirdtypes.h"
|
|
#include "libc/dce.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/limits.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/paths.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/stdio/append.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/thread/thread.h"
|
|
#include "third_party/musl/passwd.h"
|
|
__static_yoink("musl_libc_notice");
|
|
|
|
#ifdef FTRACE
|
|
// if the default mode debugging tools are enabled, and we're linking
|
|
// something as substantive as this library, then we shall assume the
|
|
// application is meaty enough to benefit from the performance of the
|
|
// chromium zlib library (costs ~40kb binary) versus just having puff
|
|
__static_yoink("_Cz_inflateInit2");
|
|
__static_yoink("_Cz_inflate");
|
|
__static_yoink("_Cz_inflateEnd");
|
|
#endif
|
|
|
|
static char *
|
|
__create_synthetic_passwd_file(void)
|
|
{
|
|
int uid, gid;
|
|
char login[256], cwd[PATH_MAX];
|
|
char *user, *home, *shell, *res = 0;
|
|
uid = getuid();
|
|
gid = getgid();
|
|
user = getenv("USER");
|
|
home = getenv("HOME");
|
|
shell = getenv("SHELL");
|
|
if (user && strchr(user, ':'))
|
|
user = 0;
|
|
if (home && strchr(home, ':'))
|
|
user = 0;
|
|
if (shell && strchr(shell, ':'))
|
|
user = 0;
|
|
if (!shell)
|
|
shell = _PATH_BSHELL;
|
|
if (!user && getlogin_r(login, sizeof(login)) != -1)
|
|
user = login;
|
|
if (!home && getcwd(cwd, sizeof(cwd))) {
|
|
if (!strchr(cwd, ':'))
|
|
home = cwd;
|
|
else
|
|
home = "/";
|
|
}
|
|
if (uid)
|
|
appendf(&res, "root:x:0:0:root:/root:%s\n", shell);
|
|
if (user && home) {
|
|
appendf(&res, "%s:x:%d:%d:%s:%s:%s\n",
|
|
user, uid, gid, user, home, shell);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static FILE *
|
|
__fopen_passwd(void)
|
|
{
|
|
FILE *f;
|
|
char *s;
|
|
// MacOS has a fake /etc/passwd file without any user details.
|
|
if (!IsXnu() && (f = fopen("/etc/passwd", "rbe")))
|
|
return f;
|
|
if (!(s = __create_synthetic_passwd_file()))
|
|
return 0;
|
|
if (!(f = fmemopen(s, strlen(s), "rbe")))
|
|
free(s);
|
|
return f;
|
|
}
|
|
|
|
static unsigned
|
|
atou(char **s)
|
|
{
|
|
unsigned x;
|
|
for (x = 0; **s - '0' < 10U; ++*s) {
|
|
x = 10 * x + (**s - '0');
|
|
}
|
|
return x;
|
|
}
|
|
|
|
int
|
|
__getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size,
|
|
struct passwd **res)
|
|
{
|
|
ssize_t l;
|
|
char *s;
|
|
int rv = 0;
|
|
int cs;
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
|
for (;;) {
|
|
if ((l = getline(line, size, f)) < 0) {
|
|
rv = ferror(f) ? errno : 0;
|
|
free(*line);
|
|
*line = 0;
|
|
pw = 0;
|
|
break;
|
|
}
|
|
line[0][l - 1] = 0;
|
|
s = line[0];
|
|
pw->pw_name = s++;
|
|
if (!(s = strchr(s, ':'))) continue;
|
|
*s++ = 0;
|
|
pw->pw_passwd = s;
|
|
if (!(s = strchr(s, ':'))) continue;
|
|
*s++ = 0;
|
|
pw->pw_uid = atou(&s);
|
|
if (*s != ':') continue;
|
|
*s++ = 0;
|
|
pw->pw_gid = atou(&s);
|
|
if (*s != ':') continue;
|
|
*s++ = 0;
|
|
pw->pw_gecos = s;
|
|
if (!(s = strchr(s, ':'))) continue;
|
|
*s++ = 0;
|
|
pw->pw_dir = s;
|
|
if (!(s = strchr(s, ':'))) continue;
|
|
*s++ = 0;
|
|
pw->pw_shell = s;
|
|
break;
|
|
}
|
|
pthread_setcancelstate(cs, 0);
|
|
*res = pw;
|
|
if (rv) errno = rv;
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
__getpw_a(const char *name, uid_t uid, struct passwd *pw, char **buf,
|
|
size_t *size, struct passwd **res)
|
|
{
|
|
FILE *f;
|
|
int cs;
|
|
int rv = 0;
|
|
*res = 0;
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
|
if ((f = __fopen_passwd())) {
|
|
while (!(rv = __getpwent_a(f, pw, buf, size, res)) && *res) {
|
|
if ((name && !strcmp(name, (*res)->pw_name)) ||
|
|
(!name && (*res)->pw_uid == uid)) {
|
|
break;
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
pthread_setcancelstate(cs, 0);
|
|
if (rv) errno = rv;
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
getpw_r(const char *name, uid_t uid, struct passwd *pw, char *buf,
|
|
size_t size, struct passwd **res)
|
|
{
|
|
#define FIX(x) (pw->pw_##x = pw->pw_##x - line + buf)
|
|
char *line = 0;
|
|
size_t len = 0;
|
|
int rv = 0;
|
|
int cs;
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
|
rv = __getpw_a(name, uid, pw, &line, &len, res);
|
|
if (*res && size < len) {
|
|
*res = 0;
|
|
rv = ERANGE;
|
|
}
|
|
if (*res) {
|
|
memcpy(buf, line, len);
|
|
FIX(name);
|
|
FIX(passwd);
|
|
FIX(gecos);
|
|
FIX(dir);
|
|
FIX(shell);
|
|
}
|
|
free(line);
|
|
pthread_setcancelstate(cs, 0);
|
|
if (rv) errno = rv;
|
|
return rv;
|
|
#undef FIX
|
|
}
|
|
|
|
int
|
|
getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t size,
|
|
struct passwd **res)
|
|
{
|
|
return getpw_r(name, 0, pw, buf, size, res);
|
|
}
|
|
|
|
int
|
|
getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t size,
|
|
struct passwd **res)
|
|
{
|
|
return getpw_r(0, uid, pw, buf, size, res);
|
|
}
|
|
|
|
static struct GetpwentState {
|
|
FILE *f;
|
|
char *line;
|
|
struct passwd pw;
|
|
size_t size;
|
|
} g_getpwent;
|
|
|
|
/**
|
|
* Closes global handle to password database.
|
|
*
|
|
* @see getpwent()
|
|
* @threadunsafe
|
|
*/
|
|
void
|
|
endpwent(void)
|
|
{
|
|
setpwent();
|
|
}
|
|
|
|
/**
|
|
* Rewinds global handle to password database.
|
|
*
|
|
* @see getpwent()
|
|
* @threadunsafe
|
|
*/
|
|
void
|
|
setpwent(void)
|
|
{
|
|
if (g_getpwent.f)
|
|
fclose(g_getpwent.f);
|
|
g_getpwent.f = 0;
|
|
}
|
|
|
|
/**
|
|
* Returns next entry in password database.
|
|
*
|
|
* @return pointer to entry static memory, or NULL on EOF
|
|
* @see setpwent()
|
|
* @threadunsafe
|
|
*/
|
|
struct passwd *
|
|
getpwent()
|
|
{
|
|
struct passwd *res;
|
|
if (!g_getpwent.f)
|
|
g_getpwent.f = __fopen_passwd();
|
|
if (!g_getpwent.f)
|
|
return 0;
|
|
__getpwent_a(g_getpwent.f, &g_getpwent.pw, &g_getpwent.line,
|
|
&g_getpwent.size, &res);
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Returns password database entry for user id.
|
|
*
|
|
* This is essentially guaranteed to succeed if `uid == getuid()`, since
|
|
* this implementation will generate an entry based on the environment
|
|
* if `/etc/passwd` doesn't exist, or is fake (e.g. MacOS).
|
|
*
|
|
* @return pointer to passwd entry static memory, or NULL if not found
|
|
* @threadunsafe
|
|
*/
|
|
struct passwd *
|
|
getpwuid(uid_t uid)
|
|
{
|
|
struct passwd *res;
|
|
__getpw_a(0, uid, &g_getpwent.pw, &g_getpwent.line, &g_getpwent.size,
|
|
&res);
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Returns password database entry for user name.
|
|
*
|
|
* This is essentially guaranteed to succeed if `uid == getenv("USER")`,
|
|
* since this implementation will generate an entry based on `environ`
|
|
* if `/etc/passwd` doesn't exist, or is fake (e.g. MacOS).
|
|
*
|
|
* @return pointer to passwd entry static memory, or NULL if not found
|
|
* @threadunsafe
|
|
*/
|
|
struct passwd *
|
|
getpwnam(const char *name)
|
|
{
|
|
struct passwd *res;
|
|
__getpw_a(name, 0, &g_getpwent.pw, &g_getpwent.line,
|
|
&g_getpwent.size, &res);
|
|
return res;
|
|
}
|