libc
/* $Id$ */
/* Copyright (c) 2004-2019 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS System libc */
/* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include "sys/mman.h"
#include "sys/stat.h"
#include "sys/sysctl.h"
#include "sys/wait.h"
#include "assert.h"
#include "fcntl.h"
#include "unistd.h"
#include "string.h"
#include "strings.h"
#include "signal.h"
#include "ctype.h"
#include "errno.h"
#include "limits.h"
#include "stdio.h"
#include "stdint.h"
#include "stdlib.h"
#define ECRYPT_ENCRYPTS_BYTES
#include "chacha/chacha.c"
#ifndef min
# define min(a, b) (((a) > (b)) ? (b) : (a))
#endif
#ifndef max
# define max(a, b) (((a) > (b)) ? (a) : (b))
#endif
/* private */
/* types */
typedef struct _Alloc
{
size_t size;
struct _Alloc * prev;
struct _Alloc * next;
} Alloc;
/* variables */
extern char ** environ;
static Alloc _alloc = { 0, NULL, NULL };
static unsigned int _seed = 1;
/* prototypes */
static void _mktemp_template(char * template);
static long double _strtold(char const * str, char ** endptr);
static unsigned long long _strtoull(char const * str, char ** endptr, int base,
int * neg);
/* public */
/* functions */
/* abort */
void abort(void)
{
/* FIXME complete */
raise(SIGABRT);
}
/* abs */
int abs(int x)
{
return (x >= 0) ? x : -x;
}
/* arc4random */
uint32_t arc4random(void)
{
static int initialized = 0;
int fd;
static ECRYPT_ctx ctx;
struct
{
unsigned char key[64];
unsigned char iv[8];
} ki;
const unsigned char input[4] = { 0 };
union
{
uint8_t u8[4];
uint32_t u32;
} ret;
if(!initialized)
{
if((fd = open("/dev/urandom", O_RDONLY)) < 0
|| read(fd, &ki, sizeof(ki)) != sizeof(ki))
abort();
close(fd);
ECRYPT_init();
ECRYPT_keysetup(&ctx, (unsigned char *)&ki.key,
sizeof(ki.key) * 8, sizeof(ki.iv) * 8);
ECRYPT_ivsetup(&ctx, (unsigned char *)&ki.iv);
initialized = 1;
}
ECRYPT_encrypt_bytes(&ctx, (unsigned char *)&input, ret.u8,
sizeof(input));
return ret.u32;
}
/* atexit */
typedef enum _AtexitFunction { AF_EXEC, AF_PURGE, AF_REGISTER } AtexitFunction;
typedef void (*AtexitCallback)(void);
static int _atexit_do(AtexitFunction function, void (*callback)(void));
int atexit(void (*function)(void))
{
return _atexit_do(AF_REGISTER, function);
}
static int _atexit_do(AtexitFunction function, void (*callback)(void))
{
static AtexitCallback * cb = NULL;
static size_t cb_size = 0;
static size_t cb_pos = 0;
void * p;
if(cb == NULL && cb_size == 0)
{
if((cb = malloc(32 * sizeof(*cb))) == NULL)
return -1;
cb_size = 32;
}
switch(function)
{
case AF_EXEC:
while(cb_pos-- > 0)
cb[cb_pos]();
/* fallthrough */
case AF_PURGE:
free(cb);
cb = NULL;
cb_size = 0;
cb_pos = 0;
break;
case AF_REGISTER:
if(cb_pos == cb_size)
{
if((p = realloc(cb, (cb_size + 4)
* sizeof(*cb)))
== NULL)
return -1;
cb = p;
cb_size+=4;
}
cb[cb_pos++] = callback;
break;
}
return 0;
}
/* atof */
double atof(char const * str)
{
return strtod(str, NULL);
}
/* atoi */
int atoi(char const * str)
{
int ret = 0;
int pos = 1;
for(; isspace((int)(unsigned char)*str); str++);
if(*str == '-')
{
pos = -1;
str++;
}
while(*str >= '0' && *str <= '9')
{
ret *= 10;
ret += pos * (*str++ - '0');
}
return ret;
}
/* atol */
long atol(char const * str)
{
long ret = 0;
int pos = 1;
for(; isspace((int)(unsigned char)*str); str++);
if(*str == '-')
{
pos = -1;
str++;
}
while(*str >= '0' && *str <= '9')
{
ret *= 10;
ret += pos * (*str++ - '0');
}
return ret;
}
/* atol */
long long atoll(char const * nptr)
{
long long ret = 0;
int pos = 1;
for(; isspace((int)(unsigned char)*nptr); nptr++);
if(*nptr == '-')
{
pos = -1;
nptr++;
}
while(*nptr >= '0' && *nptr <= '9')
{
ret *= 10;
ret += pos * (*nptr++ - '0');
}
return ret;
}
/* bsearch */
void * bsearch(const void * key, const void * base, size_t nel,
size_t size, int (*compar)(const void *, const void *))
{
/* XXX from dietlibc */
size_t m;
int tmp;
void * p;
while(nel)
{
m = nel / 2;
p = (void *) (((const char *) base) + (m * size));
if((tmp = compar(key, p)) < 0)
nel = m;
else if(tmp > 0)
{
base = (char *)p + size;
nel -= m + 1;
}
else
return p;
}
return 0;
}
/* calloc */
void * calloc(size_t nmemb, size_t size)
{
void * ptr;
size_t sz;
if(nmemb != 0 && size != 0 && SIZE_MAX / nmemb < size)
{
errno = ENOMEM;
return NULL;
}
sz = nmemb * size;
if((ptr = malloc(sz)) == NULL)
return NULL;
memset(ptr, 0, sz);
return ptr;
}
/* exit */
void exit(int status)
{
/* FIXME flush all opened files really */
fflush(stdout);
fflush(stderr);
_atexit_do(AF_EXEC, NULL);
_exit(status);
}
/* free */
static void _free_abort(void);
void free(void * ptr)
{
Alloc * a;
Alloc * b;
if(ptr == NULL)
return;
if(ptr < (void *)sizeof(*a))
{
_free_abort();
return;
}
a = (Alloc *)ptr - 1;
b = a->prev;
if(b->next != a)
{
_free_abort();
return;
}
b->next = a->next;
if(a->next != NULL) /* memory is allocated past a */
a->next->prev = b;
else if(b != &_alloc)
/* decrease to lowest possible value */
sbrk(-((uintptr_t)a + a->size - (uintptr_t)b - b->size));
else
/* remove the last object */
sbrk(-sizeof(*a) - a->size);
}
static void _free_abort(void)
{
const char buf[] = "invalid free detected: terminated\n";
write(2, buf, sizeof(buf) - 1);
abort();
}
/* getenv */
char * getenv(char const * name)
{
size_t len = strlen(name);
char ** p;
if(strchr(name, '=') != NULL) /* XXX not required in the standard */
{
errno = EINVAL;
return NULL;
}
for(p = environ; *p != NULL; p++)
{
if(strncmp(*p, name, len) != 0 || (*p)[len] != '=')
continue;
return &((*p)[len+1]);
}
return NULL;
}
/* getloadavg */
int getloadavg(double loadavg[], int nelem)
{
#if defined(CTL_VM) && defined(VM_LOADAVG)
int mib[2] = { CTL_VM, VM_LOADAVG };
struct loadavg
{
uint32_t lo_avg[3];
long lo_scale;
} lo;
size_t len = sizeof(lo);
int i;
if(sysctl(mib, sizeof(mib) / sizeof(*mib), &lo, &len, NULL, 0) != 0)
return -1;
for(i = 0; i < nelem && i < 3; i++)
loadavg[i] = lo.lo_avg[i];
return i;
#elif defined(__linux__)
FILE * fp;
double lo[3];
int i;
if((fp = fopen("/proc/loadavg", "r")) == NULL)
return -1;
if(fscanf(fp, "%f %f %f %d/%d %d", &lo[0], &lo[1], &lo[2], &i, &i, &i)
!= 6)
{
fclose(fp);
return -1;
}
fclose(fp);
for(i = 0; i < nelem && i < 3; i++)
loadavg[i] = lo[i];
return i;
#else
# warning Unsupported platform: getloadavg() is missing
errno = ENOSYS;
return -1;
#endif
}
/* grantpt */
int grantpt(int fildes)
{
/* FIXME implement */
errno = ENOSYS;
return -1;
}
/* labs */
long labs(long x)
{
return (x >= 0) ? x : -x;
}
/* llabs */
long long llabs(long long x)
{
return (x >= 0) ? x : -x;
}
/* malloc */
void * malloc(size_t size)
{
Alloc * a = &_alloc;
Alloc * b = NULL;
uintptr_t inc;
if(size >= SSIZE_MAX - sizeof(*b) - 0x8)
{
errno = ENOMEM;
return NULL;
}
if((size & 0x7) != 0x0)
size = (size | 0x7) + 1; /* round up to 64 bits */
inc = size + sizeof(*b);
if(_alloc.next != NULL) /* look for available space */
for(a = _alloc.next; a->next != NULL; a = a->next)
if(inc <= (uintptr_t)a->next - (uintptr_t)a - sizeof(*a)
- a->size)
{
b = (Alloc *)((uintptr_t)a + sizeof(*a)
+ a->size);
a->next->prev = b;
break;
}
if(b == NULL) /* increase process size to allocate memory */
if((b = sbrk(inc)) == (void *)-1)
return NULL;
b->size = size;
b->prev = a;
b->next = a->next;
a->next = b;
return b + 1;
}
/* mktemp */
char * mktemp(char * template)
{
size_t i;
struct stat st;
for(i = 0; i < 20; i++)
{
_mktemp_template(template);
if(lstat(template, &st) != 0)
{
if(errno == ENOENT)
return template;
}
else
errno = EEXIST;
}
return NULL;
}
/* mkstemp */
int mkstemp(char * template)
{
size_t i;
int fd;
const int flags = O_WRONLY | O_CREAT | O_EXCL;
for(i = 0; i < 20; i++)
{
_mktemp_template(template);
if((fd = open(template, flags, 0600)) >= 0)
return fd;
}
return -1;
}
/* ptsname */
char * ptsname(int fildes)
{
/* FIXME implement */
errno = ENOSYS;
return NULL;
}
/* putenv */
int putenv(char * string)
{
/* FIXME implement */
errno = ENOSYS;
return -1;
}
/* qsort */
static void _qsort_do(char * base, size_t size, size_t l, size_t r,
int (*compar)(const void *, const void *));
static void _qsort_exch(char * base, size_t size, size_t a, size_t b);
void qsort(void * base, size_t nel, size_t size,
int (*compar)(const void *, const void *))
{
/* XXX from dietlibc */
if(nel > 1)
_qsort_do(base, size, 0, nel - 1, compar);
}
static void _qsort_do(char * base, size_t size, size_t l, size_t r,
int (*compar)(const void *, const void *))
/* If I put it correctly:
* l is the left cursor
* i is the current position "left-wise"
* j is the current position "right-wise"
* r is the right cursor
* p is the last element */
{
size_t i = l - 1;
size_t j = r;
void * p = base + r * size;
if(r <= l)
return;
for(;; _qsort_exch(base, size, i, j))
{
while(compar(base + (++i) * size, p) < 0);
while(compar(p, base + (--j) * size) < 0)
if(j == l)
break;
if(i >= j)
break;
}
_qsort_exch(base, size, i, r);
_qsort_do(base, size, l, i - 1, compar);
_qsort_do(base, size, i + 1, r, compar);
}
static void _qsort_exch(char * base, size_t size, size_t a, size_t b)
{
char * pa = base + a * size;
char * pb = base + b * size;
char tmp;
for(; size; size--)
{
tmp = *(pa++);
*pa = *pb;
*(pb++) = tmp;
}
}
/* rand */
int rand(void)
{
_seed *= 0x23456789; /* FIXME totally poor randomness */
return _seed % RAND_MAX;
}
/* realloc */
void * realloc(void * ptr, size_t size)
{
Alloc * a = (Alloc *)ptr - 1;
void * p;
if(ptr == NULL)
return malloc(size);
if((size & 0x7) != 0x0)
size = (size | 0x7) + 1; /* round up to 64 bits */
if(size == a->size)
return ptr;
if(a->next == NULL)
{
/* reallocate the space */
if(sbrk(size - a->size) == (void *)-1)
return NULL;
a->size = size;
return ptr;
}
if(size < a->size || (uintptr_t)a->next - (uintptr_t)a - sizeof(*a)
>= size)
{
/* update the size */
a->size = size;
return ptr;
}
if((p = malloc(size)) == NULL)
return NULL;
memcpy(p, ptr, min(a->size, size));
free(ptr);
return p;
}
/* setenv */
static int _setenv_do(char const * name, char const * value, int overwrite);
int setenv(char const * name, char const * value, int overwrite)
{
if(name == NULL || name[0] == '\0' || strchr(name, '=') != NULL
|| value == NULL)
{
errno = EINVAL;
return -1;
}
return _setenv_do(name, value, overwrite);
}
/* _setenv_do
* sets or unsets variables in the environment
* PRE name is a valid name for an environment variable
* POST the environ global variable is allocated on the heap if not already
* 0 if value is NULL the name variable is unset
* 0 if value is not NULL the variable was updated if overwrite > 0
* -1 an error occured */
static char ** _setenv_init(size_t * cnt);
static int _setenv_do(char const * name, char const * value, int overwrite)
{
static char ** env = NULL;
static size_t env_cnt;
const size_t nlen = strlen(name);
const size_t vlen = (value == NULL) ? 0 : strlen(value);
char ** p;
char * pos;
if(env == NULL && (env = _setenv_init(&env_cnt)) == NULL)
return -1;
for(p = env; (pos = *p) != NULL; p++)
{
if(strncmp(pos, name, nlen) != 0 || pos[nlen] != '=')
continue;
if(overwrite == 0)
return 0;
if(value == NULL) /* unset variable */
{
free(pos);
for(; *p != NULL; p++)
*p = *(p + 1);
env_cnt--; /* may want to realloc(env) here */
return 0;
}
if(strlen(pos + nlen + 1) >= vlen) /* if we can avoid realloc */
{
strcpy(pos + nlen + 1, value);
return 0;
}
if((pos = realloc(*p, nlen + vlen + 2)) == NULL)
return -1;
*p = pos;
strcpy(&pos[nlen + 1], value);
return 0;
}
if(value == NULL) /* variable is already unset */
return 0;
if((p = realloc(env, (env_cnt + 2) * sizeof(*env))) == NULL)
return -1;
env = p;
environ = env;
if((env[env_cnt] = malloc(nlen + vlen + 2)) == NULL)
return -1;
sprintf(env[env_cnt++], "%s=%s", name, value);
env[env_cnt] = NULL;
return 0;
}
static void _init_atexit(void);
static char ** _setenv_init(size_t * cnt)
{
char ** env;
size_t i;
for(*cnt = 0; environ[*cnt] != NULL; (*cnt)++);
if((env = malloc((*cnt + 1) * sizeof(*env))) == NULL)
return NULL;
for(i = 0; i < *cnt; i++)
if((env[i] = strdup(environ[i])) == NULL)
{
while(i != 0)
free(env[--i]);
free(env);
return NULL;
}
env[i] = NULL;
environ = env;
_atexit_do(AF_REGISTER, _init_atexit);
return env;
}
static void _init_atexit(void)
{
char ** p;
for(p = environ; *p != NULL; p++)
free(*p);
free(environ);
environ = NULL;
}
/* srand */
void srand(unsigned int seed)
{
_seed = seed;
}
/* strtod */
double strtod(char const * str, char ** endptr)
{
return _strtold(str, endptr);
}
/* strtof */
float strtof(char const * str, char ** endptr)
{
return _strtold(str, endptr);
}
/* strtol */
long strtol(char const * str, char ** endptr, int base)
{
unsigned long ret;
int neg = 0;
ret = _strtoull(str, endptr, base, &neg);
if(neg != 0)
{
if(ret > (unsigned long)LONG_MAX + 1)
{
errno = ERANGE;
return LONG_MIN;
}
return -ret;
}
if(ret > LONG_MAX)
{
errno = ERANGE;
return LONG_MAX;
}
return ret;
}
/* strtold */
long double strtold(char const * str, char ** endptr)
{
return _strtold(str, endptr);
}
/* strtoll */
long long strtoll(char const * str, char ** endptr, int base)
{
unsigned long long ret;
int neg = 0;
ret = _strtoull(str, endptr, base, &neg);
if(neg != 0)
{
if(ret > (unsigned long long)LLONG_MAX + 1)
{
errno = ERANGE;
return LLONG_MIN;
}
return -ret;
}
if(ret > LLONG_MAX)
{
errno = ERANGE;
return LLONG_MAX;
}
return ret;
}
/* strtoul */
unsigned long strtoul(char const * str, char ** endptr, int base)
{
return _strtoull(str, endptr, base, NULL);
}
/* strtoull */
unsigned long long strtoull(char const * str, char ** endptr, int base)
{
return _strtoull(str, endptr, base, NULL);
}
/* system */
int system(char const * command)
{
pid_t pid;
pid_t p;
/* FIXME also block some signals etc */
if((pid = fork()) == -1)
return -1;
if(pid == 0)
{
execl("/bin/sh", "sh", "-c", command, NULL);
_exit(127);
}
for(;;)
if((p = waitpid(pid, NULL, 0)) != -1 || errno != EINTR)
break;
return (p == pid) ? 0 : -1;
}
/* unlockpt */
int unlockpt(int fildes)
{
/* FIXME implement */
errno = ENOSYS;
return -1;
}
/* unsetenv */
int unsetenv(char const * name)
{
if(name == NULL || name[0] == '\0' || strchr(name, '=') != NULL)
{
errno = EINVAL;
return -1;
}
return _setenv_do(name, NULL, 1);
}
/* private */
/* functions */
/* mktemp_template */
static void _mktemp_template(char * template)
{
static char const tab[62] = "0123456789abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
size_t i;
for(i = strlen(template); i > 0 && template[i - 1] == 'X'; i--)
template[i - 1] = tab[rand() % sizeof(tab)];
}
/* strtold */
static long double _strtold(char const * str, char ** endptr)
{
/* FIXME support additional notations */
long double ret;
char const * e;
int neg = 0;
unsigned long u = 0;
unsigned long cnt;
long double f;
/* skip initial spaces */
for(e = str; *e != '\0'; e++)
if(!isspace((unsigned char)*e))
break;
/* skip optional '-' or '+' sign */
if(*e == '-')
{
neg = 1;
e++;
}
else if(*e == '+')
e++;
for(cnt = 0; isdigit((unsigned char)e[cnt]); cnt++)
u = u * 10 + (e[cnt] - '0');
if(cnt == 0)
return 0.0 / 0.0;
e += cnt;
ret = u;
if(*e == '.')
{
e++;
u = 0;
for(cnt = 0; isdigit((unsigned char)e[cnt]); cnt++)
u = u * 10 + (e[cnt] - '0');
if(cnt == 0)
return 0.0 / 0.0;
e += cnt;
f = u;
for(; cnt > 0; cnt--)
f = f / 10;
ret += f;
}
if(endptr != NULL)
*endptr = (char *)e;
return neg ? -ret : ret;
}
/* strtoull */
static int _strtoull_base(char const ** p);
static unsigned long long _strtoull(char const * str, char ** endptr, int base,
int * neg)
{
unsigned long long ret = 0;
char const * p;
int r;
size_t i;
if(base > 36 || base < 0 || base == 1)
{
errno = EINVAL;
return 0;
}
for(p = str; isspace((int)(unsigned char)*p); p++);
if(*p == '\0')
{
if(endptr != NULL)
*endptr = (char *)str; /* XXX cast */
errno = ERANGE;
return 0;
}
if((*p == '+' || *p == '-') && *(p++) == '-' && neg != NULL)
*neg = 1;
if(base == 0)
base = _strtoull_base(&p);
if(base == 16 && *p == '0')
{
p++;
if(*p == 'x' || *p == 'X')
p++;
}
for(i = 0; p[i] != '\0'; i++)
{
if(p[i] >= '0' && p[i] - '0' < min(10, base))
r = p[i] - '0';
else if(base > 10 && (((r = p[i] - 'a') >= 0 && r < 26)
|| ((r = p[i] - 'A') >= 0 && r < 26))
&& r < base - 10)
r += 10;
else
break;
/* FIXME add integer overflow detection code */
ret = (ret * base) + r;
}
if(i == 0)
{
errno = EINVAL;
return 0;
}
if(endptr != NULL)
*endptr = (char *)&p[i]; /* XXX cast */
return ret;
}
static int _strtoull_base(char const ** p)
{
if(**p == '0')
{
(*p)++;
if(**p == 'x' || **p == 'X')
{
(*p)++;
return 16;
}
return 8;
}
return 10;
}