/* $Id$ */ /* Copyright (c) 2004-2019 Pierre Pronchery */ /* 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; }