/* $Id$ */ /* Copyright (c) 2004-2020 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/stat.h" #include "sys/sysctl.h" #include "sys/types.h" #include "sys/utsname.h" #include "inttypes.h" #include "stdarg.h" #include "stddef.h" #include "stdlib.h" #include "stdio.h" #include "dirent.h" #include "errno.h" #include "string.h" #include "termios.h" #include "time.h" #include "limits.h" #include "signal.h" #include "syscalls.h" #include "unistd.h" /* variables */ char * optarg = NULL; int optind = 1; int opterr = 1; int optopt = -1; char ** environ; /* functions */ /* access */ /* XXX when wrongly used this is a potential security flaw */ #ifndef SYS_access # warning Unsupported platform: access() is missing int access(char const * filename, int mode) { errno = ENOSYS; return -1; } #endif /* alarm */ #ifndef SYS_alarm # ifdef SYS_setitimer # include "sys/time.h" unsigned int alarm(unsigned int seconds) { struct itimerval cur; struct itimerval old; memset(&cur, 0, sizeof(cur)); cur.it_interval.tv_sec = seconds; memset(&old, 0, sizeof(old)); if(setitimer(ITIMER_REAL, &cur, &old) != 0) return -1; return old.it_interval.tv_sec; } # else # warning Unsupported platform: alarm() is missing unsigned int alarm(unsigned int seconds) { errno = ENOSYS; return -1; } # endif #endif /* brk */ #if defined(SYS_brk) extern int _brk(void * addr); /* XXX in-kernel prototype */ int brk(void * addr) { return _brk(addr); } #elif defined(SYS_sbrk) /* !SYS_brk && SYS_sbrk */ int brk(void * addr) { void * cur; if((cur = sbrk(0)) == (void *)-1) return -1; return (sbrk(addr - cur) != (void *)-1) ? 0 : -1; } #else /* !SYS_brk && !SYS_sbrk */ # warning Unsupported platform: brk() is missing #endif /* chdir */ #ifndef SYS_chdir # warning Unsupported platform: chdir() is missing int chdir(char const * filename) { errno = ENOSYS; return -1; } #endif /* chown */ #ifndef SYS_chown # warning Unsupported platform: chown() is missing int chown(char const * filename, uid_t owner, gid_t group) { errno = ENOSYS; return -1; } #endif /* chroot */ #ifndef SYS_chroot # warning Unsupported platform: chroot() is missing int chroot(char const * filename) { errno = ENOSYS; return -1; } #endif /* close */ #ifndef SYS_close # warning Unsupported platform: close() is missing int close(int fildes) { errno = ENOSYS; return -1; } #endif /* dup */ #ifndef SYS_dup # warning Unsupported platform: dup() is missing int dup(int fildes) { errno = ENOSYS; return -1; } #endif /* dup2 */ #ifndef SYS_dup2 # warning Unsupported platform: dup2() is missing int dup2(int fildes1, int fildes2) { errno = ENOSYS; return -1; } #endif /* execl */ int execl(char const * filename, char * const arg, ...) { int ret = 0; va_list ap; char const ** argv = NULL; size_t argv_cnt = 0; char * p = arg; char const ** q; va_start(ap, arg); do { if((q = realloc(argv, sizeof(*argv) * (argv_cnt + 2))) == NULL) { free(argv); va_end(ap); return -1; } argv = q; argv[argv_cnt++] = p; } while((p = va_arg(ap, char *)) != NULL); va_end(ap); argv[argv_cnt] = NULL; ret = execv(filename, argv); free(argv); return ret; } /* execle */ int execle(char const * filename, char * const arg, ...) { int ret = 0; va_list ap; char const ** argv = NULL; size_t argv_cnt = 0; char * p = arg; char const ** q; char ** envp; va_start(ap, arg); do { if((q = realloc(argv, sizeof(*argv) * (argv_cnt + 1))) == NULL) { free(argv); va_end(ap); return -1; } argv = q; argv[argv_cnt++] = p; } while((p = va_arg(ap, char *)) != NULL); va_end(ap); envp = (char **)argv[--argv_cnt]; argv[argv_cnt] = NULL; ret = execve(filename, argv, envp); free(argv); return ret; } /* execlp */ int execlp(char const * filename, char * const arg, ...) { int ret = 0; va_list ap; char const ** argv = NULL; size_t argv_cnt = 0; char * p = arg; char const ** q; va_start(ap, arg); do { if((q = realloc(argv, sizeof(*argv) * (argv_cnt + 2))) == NULL) { free(argv); va_end(ap); return -1; } argv = q; argv[argv_cnt++] = p; } while((p = va_arg(ap, char *)) != NULL); va_end(ap); argv[argv_cnt] = NULL; ret = execvp(filename, argv); free(argv); return ret; } /* execv */ int execv(char const * filename, char * const argv[]) { return execve(filename, argv, environ); } /* execve */ #ifndef SYS_execve # warning Unsupported platform: execve() is missing int execve(char const * filename, char * const argv[], char * const envp[]) { errno = ENOSYS; return -1; } #endif /* execvp */ static void _execvp_do(char const * filename, char * const argv[]); int execvp(char const * filename, char * const argv[]) { char const * path; size_t i = 0; size_t oldi = 0; char * buf = NULL; size_t buf_cnt = 0; size_t len; char * p; if(strchr(filename, '/') != NULL) return execve(filename, argv, environ); if((path = getenv("PATH")) == NULL || path[0] == '\0') { errno = ENOENT; return -1; } len = strlen(filename) + 2; do { if(path[i] != ':' && path[i] != '\0') continue; if(i - oldi + len > buf_cnt) { buf_cnt = i - oldi + len; if((p = realloc(buf, buf_cnt)) == NULL) { free(buf); return -1; } buf = p; } strncpy(buf, &path[oldi], i - oldi); buf[i - oldi] = '/'; strcpy(&buf[i - oldi + 1], filename); _execvp_do(buf, argv); oldi = i + 1; } while(path[i++] != '\0'); free(buf); return -1; } static void _execvp_do(char const * filename, char * const argv[]) { size_t i; size_t len; char const ** arg; execve(filename, argv, environ); if(errno != ENOEXEC) return; /* try with a shell instead */ for(i = 0; argv[i] != NULL; i++); len = (i + 2) * sizeof(char *); if((arg = malloc(len)) == NULL) return; arg[0] = argv[0]; arg[1] = filename; for(i = 0; argv[i++] != NULL;) arg[i + 1] = argv[i]; execv("/bin/sh", (char * const *)arg); free(arg); } /* exit */ #ifndef SYS_exit # warning Unsupported platform: exit() is missing void _exit(int status) { char ** p = NULL; /* XXX make it crash */ p = *p; } #endif /* fchdir */ #ifndef SYS_fchdir # warning Unsupported platform: fchdir() is missing int fchdir(int fildes) { errno = ENOSYS; return -1; } #endif /* fchown */ #ifndef SYS_fchown # warning Unsupported platform: fchown() is missing int fchown(int fildes, uid_t uid, gid_t gid) { errno = ENOSYS; return -1; } #endif /* fork */ #ifndef SYS_fork # warning Unsupported platform: fork() is missing pid_t fork(void) { errno = ENOSYS; return -1; } #endif /* fsync */ #ifndef SYS_fsync # warning Unsupported platform: fsync() is missing int fsync(int fildes) { errno = ENOSYS; return -1; } #endif /* ftruncate */ #ifndef SYS_ftruncate # warning Unsupported platform: ftruncate() is missing int ftruncate(int fildes, off_t offset) { errno = ENOSYS; return -1; } #endif /* getcwd */ #ifdef SYS__getcwd int _getcwd(char * buf, size_t size); char * getcwd(char * buf, size_t size) { if(buf == NULL) { if((buf = malloc(PATH_MAX)) == NULL) return NULL; size = PATH_MAX; } if(_getcwd(buf, size) < 0) return NULL; return buf; } #else # warning Unsupported platform: getcwd() is missing char * getcwd(char * buf, size_t size) { errno = ENOSYS; return NULL; } #endif /* getegid */ #ifndef SYS_getegid # warning Unsupported platform: getegid() is missing #endif /* geteuid */ #ifndef SYS_geteuid # warning Unsupported platform: geteuid() is missing #endif /* getgid */ #ifndef SYS_getgid # warning Unsupported platform: getgid() is missing gid_t getgid(void) { /* XXX not allowed to fail */ return 0; } #endif /* getgroups */ #ifndef SYS_getgroups # warning Unsupported platform: getgroups() is missing int getgroups(int gidsetsize, gid_t grouplist[]) { (void) gidsetsize; (void) grouplist; errno = ENOSYS; return -1; } #endif /* gethostname */ int gethostname(char * name, size_t size) { #if defined(SYS_sysctl) && defined(CTL_KERN) && defined(KERN_HOSTNAME) int mib[2] = { CTL_KERN, KERN_HOSTNAME }; if(sysctl(mib, 2, name, &size, NULL, 0) != 0) return -1; return 0; #elif defined(SYS_uname) struct utsname u; if(uname(&u) != 0) return -1; snprintf(name, size, "%s", u.nodename); return 0; #else # warning Unsupported platform: gethostname() is missing errno = ENOSYS; return -1; #endif } /* getlogin */ char * getlogin(void) { static char login[LOGIN_NAME_MAX + 1]; if(getlogin_r(login, sizeof(login)) != 0) return NULL; return login; } /* getlogin_r */ #ifndef SYS_getlogin_r # include "pwd.h" int getlogin_r(char * buf, size_t size) { struct passwd * pw; if((pw = getpwuid(getuid())) == NULL) /* FIXME maybe not re-entrant */ return 1; if(strlen(pw->pw_name) + 1 > size) { errno = ERANGE; return 1; } strcpy(buf, pw->pw_name); return 0; } #endif /* getopt */ static void _getopt_reset(char * const ** oldargv, char * const * argv, int * i); int getopt(int argc, char * const argv[], char const * optstring) { static char * const * oldargv = NULL; static int i = 0; size_t j; if(argv == NULL) { _getopt_reset(&oldargv, NULL, &i); errno = EINVAL; return -1; } if(argv != oldargv) /* reset state */ _getopt_reset(&oldargv, argv, &i); if(optind == argc || (i == 0 && argv[optind][0] != '-')) { oldargv = NULL; return -1; /* there is nothing to parse */ } if(argv[optind][++i] == '\0') { if(++optind == argc) /* every argument had options */ return -1; if(argv[optind][0] != '-' /* next argument has no options */ || argv[optind][1] == '\0') return -1; if(argv[optind][0] == '-' && argv[optind][1] == '-' && argv[optind][2] == '\0') /* "--" stops */ { optind++; return -1; } i = 1; } optopt = argv[optind][i]; for(j = 0; optstring[j] != '\0'; j++) { if(optstring[j] == ':') continue; if(optopt == optstring[j]) break; } if(optstring[j] == '\0') return optstring[0] != ':' ? '?' : ':'; if(optstring[j+1] == ':') { if(argv[optind][i+1] != '\0') { optarg = &argv[optind][i+1]; optind++; i = 0; return optopt; } optarg = argv[optind+1]; optind+=2; i = 0; } return optopt; } static void _getopt_reset(char * const ** oldargv, char * const * argv, int * i) { optarg = NULL; optind = 1; opterr = 1; optopt = -1; *oldargv = argv; *i = 0; } /* getpgrp */ #ifndef SYS_getpgrp # warning Unsupported platform: getpgrp() is missing pid_t getpgrp(void) { /* XXX not allowed to fail */ errno = ENOSYS; return -1; } #endif /* getpid */ #ifndef SYS_getpid # warning Unsupported platform: getpid() is missing pid_t getpid(void) { /* XXX not allowed to fail */ errno = ENOSYS; return -1; } #endif /* getppid */ #ifndef SYS_getppid # warning Unsupported platform: getppid() is missing pid_t getppid(void) { /* XXX not allowed to fail */ return 1; } #endif /* getuid */ #ifndef SYS_getuid # warning Unsupported platform: getuid() is missing uid_t getuid(void) { /* XXX not allowed to fail */ return 0; } #endif /* getwd */ char * getwd(char * buf) { /* XXX this is a potential security flaw */ return getcwd(buf, PATH_MAX); } /* isatty */ int isatty(int fildes) { struct termios t; return tcgetattr(fildes, &t) == 0 ? 1 : 0; } /* lchown */ #ifndef SYS_lchown # warning Unsupported platform: lchown() is missing int lchown(char const * filename, uid_t owner, gid_t group) { errno = ENOSYS; return -1; } #endif /* link */ #ifndef SYS_link # warning Unsupported platform: link() is missing int link(char const * from, char const * to) { errno = ENOSYS; return -1; } #endif /* linkat */ #ifndef SYS_linkat # warning Unsupported platform: linkat() is missing int linkat(int fromfd, char const * from, int tofd, char const * to) { errno = ENOSYS; return -1; } #endif /* lseek */ #ifndef SYS_lseek # warning Unsupported platform: lseek() is missing off_t lseek(int fildes, off_t offset, int whence) { errno = ENOSYS; return -1; } #endif /* nice */ #if !defined(SYS_nice) # if defined(SYS_setpriority) # include "sys/resource.h" int nice(int inc) { int prio; errno = 0; if((prio = getpriority(PRIO_PROCESS, 0)) == -1 && errno != 0) return -1; return setpriority(PRIO_PROCESS, 0, prio + inc); } # else # warning Unsupported platform: nice() is missing # endif #endif /* !SYS_nice */ /* pipe */ #ifndef SYS_pipe # warning Unsupported platform: pipe() is missing int pipe(int fildes[2]) { errno = ENOSYS; return -1; } #endif /* read */ #ifndef SYS_read # warning Unsupported platform: read() is missing ssize_t read(int fildes, void * buf, size_t count) { errno = ENOSYS; return -1; } #endif /* readlink */ #ifndef SYS_readlink # warning Unsupported platform: readlink() is missing ssize_t readlink(char const * filename, char * buf, size_t bufsiz) { errno = ENOSYS; return -1; } #endif /* readlinkat */ #ifndef SYS_readlinkat # warning Unsupported platform: readlinkat() is missing ssize_t readlinkat(int fd, char const * filename, char * buf, size_t bufsiz) { errno = ENOSYS; return -1; } #endif /* rmdir */ #ifndef SYS_rmdir # warning Unsupported platform: rmdir() is missing int rmdir(char const * filename) { errno = ENOSYS; return -1; } #endif /* sbrk */ #if defined(SYS_sbrk) extern void * _sbrk(intptr_t increment); /* XXX in-kernel prototype */ # if defined(SYS_brk) /* SYS_sbrk && SYS_brk */ extern int _brk(void * addr); /* XXX in-kernel prototype */ void * sbrk(intptr_t increment) /* _brk is used to actually allocate memory */ { void * cur; if((cur = _sbrk(0)) == (void *)-1 || increment == 0) return cur; return (_brk(cur + increment) != (void *)-1) ? cur : (void *)-1; } # else /* SYS_sbrk && !SYS_brk */ void * sbrk(intptr_t increment) { return _sbrk(increment); } # endif #elif defined(SYS_brk) /* !SYS_sbrk && SYS_brk */ extern int _brk(void * addr); /* XXX in-kernel prototype */ extern void * end; void * sbrk(intptr_t increment) { static void * cur = &end; void * ptr; if(increment == 0) return cur; if(_brk(cur + increment) == -1) return (void *)-1; ptr = cur; cur += increment; return ptr; } #else /* !SYS_sbrk && !SYS_brk */ # warning Unsupported platform: sbrk() is missing void * sbrk(intptr_t increment) { errno = ENOSYS; return (void *)-1; } #endif /* setgid */ #ifndef SYS_setgid # warning Unsupported platform: setgid() is missing int setgid(gid_t gid) { errno = ENOSYS; return -1; } #endif /* sethostname */ #ifndef SYS_sethostname int sethostname(char const * name, size_t size) { # if defined(SYS_sysctl) && defined(CTL_KERN) && defined(KERN_HOSTNAME) int mib[2] = { CTL_KERN, KERN_HOSTNAME }; if(sysctl(mib, 2, NULL, 0, name, size) != 0) return -1; return 0; # else # warning Unsupported platform: sethostname() is missing errno = ENOSYS; return -1; # endif } #endif /* setpgid */ #ifndef SYS_setpgid # warning Unsupported platform: setpgid() is missing int setpgid(pid_t pid, pid_t pgid) { errno = ENOSYS; return -1; } #endif /* setpgrp */ #ifndef SYS_setpgrp pid_t setpgrp(void) { # ifdef SYS_setpgid return setpgid(0, 0); # else # warning Unsupported platform: setpgrp() is missing /* XXX not allowed to fail */ errno = ENOSYS; return -1; # endif } #endif /* setregid */ #ifndef SYS_setregid # warning Unsupported platform: setregid() is missing int setregid(gid_t rgid, gid_t egid) { errno = ENOSYS; return -1; } #endif /* setreuid */ #ifndef SYS_setreuid # warning Unsupported platform: setreuid() is missing int setreuid(uid_t ruid, uid_t euid) { errno = ENOSYS; return -1; } #endif /* setsid */ #ifndef SYS_setsid # warning Unsupported platform: setsid() is missing int setsid(void) { errno = ENOSYS; return -1; } #endif /* setuid */ #ifndef SYS_setuid # warning Unsupported platform: setuid() is missing int setuid(uid_t uid) { errno = ENOSYS; return -1; } #endif /* sleep */ unsigned int sleep(unsigned int seconds) { struct timespec ts; ts.tv_sec = seconds; ts.tv_nsec = 0; if(nanosleep(&ts, &ts) == 0) return 0; return ts.tv_sec; } /* strsignal */ char * strsignal(int sig) { static const struct { int signal; const char * name; } names[] = { { SIGABRT, "SIGABRT" }, { SIGCONT, "SIGCONT" }, { SIGHUP, "SIGHUP" }, { SIGILL, "SIGILL" }, { SIGINT, "SIGINT" }, { SIGKILL, "SIGKILL" }, { SIGPIPE, "SIGPIPE" }, { SIGQUIT, "SIGQUIT" }, { SIGSEGV, "SIGSEGV" }, { SIGSTOP, "SIGSTOP" }, { SIGTRAP, "SIGTRAP" }, { SIGUSR1, "SIGUSR1" }, { SIGUSR2, "SIGUSR2" }, { 0, NULL } }; static char buf[8]; size_t i; for(i = 0; names[i].name != NULL; i++) if(names[i].signal == sig) { snprintf(buf, sizeof(buf), "%s", names[i].name); return buf; } return NULL; } /* symlink */ #ifndef SYS_symlink # warning Unsupported platform: symlink() is missing int symlink(char const * from, char const * to) { errno = -ENOSYS; return -1; } #endif /* symlinkat */ #ifndef SYS_symlinkat # warning Unsupported platform: symlinkat() is missing int symlinkat(char const * from, int fd, char const * to) { errno = -ENOSYS; return -1; } #endif /* sync */ #ifndef SYS_sync # warning Unsupported platform: sync() is missing #endif /* sysconf */ long sysconf(int name) { #if defined(__linux__) switch(name) { case _SC_CLK_TCK: return 100; case _SC_PAGESIZE: return 4096; } #elif defined(CTL_KERN) && defined(KERN_CLOCKRATE) && \ defined(CTL_HW) && defined(HW_PAGESIZE) int mib[2]; size_t len; struct clockinfo ci; switch(name) { case _SC_CLK_TCK: mib[0] = CTL_KERN; mib[1] = KERN_CLOCKRATE; len = sizeof(ci); return sysctl(mib, 2, &ci, &len, NULL, 0) != -1 ? ci.hz : -1; case _SC_PAGESIZE: mib[0] = CTL_HW; mib[1] = HW_PAGESIZE; len = sizeof(name); return sysctl(mib, 2, &name, &len, NULL, 0) != -1 ? name : -1; } #else # warning Unsupported platform: sysconf() is missing #endif errno = ENOSYS; return -1; } /* truncate */ #ifndef SYS_truncate # warning Unsupported platform: truncate() is missing int truncate(char const * filename, off_t offset) { errno = ENOSYS; return -1; } #endif /* ttyname */ char * ttyname(int fildes) { static char buf[260]; /* XXX strlen(_PATH_DEV) + NAME_MAX + 2 */ struct stat st; dev_t rdev; DIR * dir; struct dirent * de; if(isatty(fildes) != 1) return NULL; if(fstat(fildes, &st) != 0) return NULL; if(!S_ISCHR(st.st_mode)) { errno = ENOTTY; return NULL; } if((dir = opendir("/dev")) == NULL) return NULL; rdev = st.st_rdev; strcpy(buf, "/dev/"); while((de = readdir(dir)) != NULL) { strcpy(&buf[5], de->d_name); if(stat(buf, &st) != 0) continue; if(!S_ISCHR(st.st_mode) || rdev != st.st_rdev) continue; closedir(dir); return buf; } closedir(dir); errno = ENOTTY; return NULL; } /* unlink */ #ifndef SYS_unlink # warning Unsupported platform: unlink() is missing int unlink(char const * filename) { errno = ENOSYS; return -1; } #endif /* unlinkat */ #ifndef SYS_unlinkat # warning Unsupported platform: unlinkat() is missing int unlinkat(int fd, char const * filename, int flags) { errno = ENOSYS; return -1; } #endif /* usleep */ int usleep(useconds_t useconds) { struct timespec tv; tv.tv_sec = 0; tv.tv_nsec = useconds * 1000; return nanosleep(&tv, NULL); } /* vfork */ #ifndef SYS_vfork # warning Unsupported platform: vfork() is missing pid_t vfork(void) { errno = ENOSYS; return -1; } #endif /* write */ #ifndef SYS_write # warning Unsupported platform: write() is missing ssize_t write(int fildes, const void * buf, size_t count) { errno = ENOSYS; return -1; } #endif