You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3899 lines
67 KiB
3899 lines
67 KiB
#include <littypes.h>
|
|
#include <litc.h>
|
|
|
|
#define SYS_READ 0
|
|
#define SYS_WRITE 1
|
|
#define SYS_OPEN 2
|
|
#define SYS_CLOSE 3
|
|
#define SYS_STAT 4
|
|
#define SYS_FSTAT 5
|
|
#define SYS_POLL 7
|
|
#define SYS_LSEEK 8
|
|
#define SYS_MMAP 9
|
|
#define SYS_MUNMAP 11
|
|
#define SYS_SIGACTION 13
|
|
#define SYS_READV 19
|
|
#define SYS_WRITEV 20
|
|
#define SYS_ACCESS 21
|
|
#define SYS_DUP2 33
|
|
#define SYS_PAUSE 34
|
|
#define SYS_GETPID 39
|
|
#define SYS_GETPPID 40
|
|
#define SYS_SOCKET 41
|
|
#define SYS_CONNECT 42
|
|
#define SYS_ACCEPT 43
|
|
#define SYS_SENDTO 44
|
|
#define SYS_RECVFROM 45
|
|
#define SYS_SOCKETPAIR 46
|
|
#define SYS_SHUTDOWN 48
|
|
#define SYS_BIND 49
|
|
#define SYS_LISTEN 50
|
|
#define SYS_RECVMSG 51
|
|
#define SYS_SENDMSG 52
|
|
#define SYS_GETSOCKOPT 55
|
|
#define SYS_SETSOCKOPT 56
|
|
#define SYS_FORK 57
|
|
#define SYS_EXECV 59
|
|
#define SYS_EXIT 60
|
|
#define SYS_WAIT4 61
|
|
#define SYS_KILL 62
|
|
#define SYS_FCNTL 72
|
|
#define SYS_TRUNC 76
|
|
#define SYS_FTRUNC 77
|
|
#define SYS_GETCWD 79
|
|
#define SYS_CHDIR 80
|
|
#define SYS_RENAME 82
|
|
#define SYS_MKDIR 83
|
|
#define SYS_LINK 86
|
|
#define SYS_UNLINK 87
|
|
#define SYS_GETTOD 96
|
|
#define SYS_GETRLIMIT 97
|
|
#define SYS_GETRUSAGE 98
|
|
#define SYS_MKNOD 133
|
|
#define SYS_SETRLIMIT 160
|
|
#define SYS_SYNC 162
|
|
#define SYS_REBOOT 169
|
|
#define SYS_GETDENTS64 217
|
|
#define SYS_NANOSLEEP 230
|
|
#define SYS_PIPE2 293
|
|
#define SYS_PROF 31337
|
|
#define SYS_THREXIT 31338
|
|
#define SYS_INFO 31339
|
|
#define SYS_PREAD 31340
|
|
#define SYS_PWRITE 31341
|
|
#define SYS_FUTEX 31342
|
|
#define SYS_GETTID 31343
|
|
|
|
__thread int errno;
|
|
|
|
static int dolock = 1;
|
|
|
|
static void pmsg(char *, long);
|
|
|
|
struct kinfo_t {
|
|
void *freshtls;
|
|
size_t len;
|
|
void *t0tls;
|
|
ulong pspercycle;
|
|
};
|
|
|
|
// initialized in _entry, given to us by kernel
|
|
static struct kinfo_t *kinfo;
|
|
|
|
// tfork_thread and _pcreate use inline asm to call some syscalls in order to
|
|
// 1) make sure the new thread immediately calls the destination function
|
|
// without executing any C code (which would be unsafe since the new thread is
|
|
// running on a different stack) 2) to make sure that, on thread exit, the
|
|
// stack is not used after munmapping it, but before calling exit(2). we use
|
|
// this macro to make sure the clobbers are coherent for these three pieces of
|
|
// code using syscalls.
|
|
#if defined(__x86_64__)
|
|
#define SYSCALL_CLOBBERS "cc", "memory", "r11", "r12", "r13", "r14", "r15"
|
|
#elif defined(__aarch64__)
|
|
#define SYSCALL_CLOBBERS "cc", "memory"
|
|
#endif
|
|
|
|
long
|
|
syscall6(long a1, long a2, long a3, long a4, long a5, long a6, long trap)
|
|
{
|
|
long ret;
|
|
|
|
#if defined(__x86_64__)
|
|
register long r10 asm("r10") = a4;
|
|
register long r8 asm("r8") = a5;
|
|
register long r9 asm("r9") = a6;
|
|
|
|
// we may want to follow the sys5 abi and have the kernel restore
|
|
// r14-r15 too...
|
|
asm volatile(
|
|
"movq %%rsp, %%r10\n"
|
|
"leaq 2(%%rip), %%r11\n"
|
|
"syscall\n"
|
|
: "=a"(ret)
|
|
: "0"(trap), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9)
|
|
: SYSCALL_CLOBBERS);
|
|
#elif defined(__aarch64__)
|
|
register long x8 asm("x8") = trap;
|
|
register long x0 asm("x0") = a1;
|
|
register long x1 asm("x1") = a2;
|
|
register long x2 asm("x2") = a3;
|
|
register long x3 asm("x3") = a4;
|
|
register long x4 asm("x4") = a5;
|
|
register long x5 asm("x5") = a6;
|
|
|
|
asm volatile(
|
|
"svc 0"
|
|
: "=r"(ret)
|
|
: "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5)
|
|
: SYSCALL_CLOBBERS);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
long
|
|
syscall(long a1, long a2, long a3, long a4,
|
|
long a5, long trap)
|
|
{
|
|
return syscall6(a1, a2, a3, a4, a5, 0, trap);
|
|
}
|
|
|
|
#define SA(x) ((long)x)
|
|
#define ERRNO_NZ(x) do { \
|
|
if (x != 0) { \
|
|
errno = -x; \
|
|
x = -1; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ERRNO_NEG(x) do { \
|
|
if (x < 0) { \
|
|
errno = -x; \
|
|
x = -1; \
|
|
} \
|
|
} while (0)
|
|
|
|
int
|
|
accept(int fd, struct sockaddr *s, socklen_t *sl)
|
|
{
|
|
int ret = syscall(SA(fd), SA(s), SA(sl), 0, 0, SYS_ACCEPT);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
access(const char *p, int mode)
|
|
{
|
|
int ret = syscall(SA(p), SA(mode), 0, 0, 0, SYS_ACCESS);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
bind(int sockfd, const struct sockaddr *s, socklen_t sl)
|
|
{
|
|
int ret = syscall(SA(sockfd), SA(s), SA(sl), 0, 0, SYS_BIND);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
close(int fd)
|
|
{
|
|
int ret = syscall(SA(fd), 0, 0, 0, 0, SYS_CLOSE);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
chdir(const char *path)
|
|
{
|
|
int ret = syscall(SA(path), 0, 0, 0, 0, SYS_CHDIR);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
dup(int o)
|
|
{
|
|
int fd;
|
|
if ((fd = open("/", O_RDONLY | O_DIRECTORY)) == -1)
|
|
return -1;
|
|
if (dup2(o, fd) == -1)
|
|
return -1;
|
|
return fd;
|
|
}
|
|
|
|
int
|
|
connect(int fd, const struct sockaddr *sa, socklen_t salen)
|
|
{
|
|
int ret = syscall(SA(fd), SA(sa), SA(salen), 0, 0, SYS_CONNECT);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
chmod(const char *path, mode_t mode)
|
|
{
|
|
printf("warning: chmod is a no-op\n");
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
dup2(int old, int new)
|
|
{
|
|
int ret = syscall(SA(old), SA(new), 0, 0, 0, SYS_DUP2);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
_exit(int status)
|
|
{
|
|
syscall(status, 0, 0, 0, 0, SYS_EXIT);
|
|
errx(-1, "exit returned");
|
|
while(1);
|
|
}
|
|
|
|
int
|
|
execv(const char *path, char * const argv[])
|
|
{
|
|
return execve(path, argv, environ);
|
|
}
|
|
|
|
int
|
|
execve(const char *path, char * const argv[], char * const envp[])
|
|
{
|
|
if (envp != environ)
|
|
errx(-1, "envp not supported yet");
|
|
int ret = syscall(SA(path), SA(argv), 0, 0, 0, SYS_EXECV);
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
static const char *
|
|
_binname(const char *bin)
|
|
{
|
|
static char buf[64];
|
|
// absoulte path
|
|
if (strchr(bin, '/')) {
|
|
snprintf(buf, sizeof(buf), "%s", bin);
|
|
return bin;
|
|
}
|
|
|
|
// try paths
|
|
char *paths[] = {"/bin/"};
|
|
const int elems = sizeof(paths)/sizeof(paths[0]);
|
|
int i;
|
|
for (i = 0; i < elems; i++) {
|
|
snprintf(buf, sizeof(buf), "%s%s", paths[i], bin);
|
|
struct stat st;
|
|
if (stat(buf, &st) == 0)
|
|
return buf;
|
|
else if (errno != ENOENT)
|
|
return NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
execvp(const char *path, char * const argv[])
|
|
{
|
|
const char *p = _binname(path);
|
|
if (!p)
|
|
return -1;
|
|
return execv(p, argv);
|
|
}
|
|
|
|
int
|
|
fcntl(int fd, int cmd, ...)
|
|
{
|
|
int ret;
|
|
long a1 = SA(fd);
|
|
long a2 = SA(cmd);
|
|
|
|
va_list ap;
|
|
va_start(ap, cmd);
|
|
switch (cmd) {
|
|
case F_GETFD:
|
|
case F_SETFD:
|
|
case F_GETFL:
|
|
case F_SETFL:
|
|
{
|
|
int fl = va_arg(ap, int);
|
|
ret = syscall(a1, a2, SA(fl), 0, 0, SYS_FCNTL);
|
|
ERRNO_NEG(ret);
|
|
break;
|
|
}
|
|
case F_SETOWN:
|
|
{
|
|
fprintf(stderr, "warning: F_SETOWN is no-op\n");
|
|
ret = 0;
|
|
break;
|
|
}
|
|
default:
|
|
errx(-1, "no imp");
|
|
}
|
|
va_end(ap);
|
|
return ret;
|
|
}
|
|
|
|
pid_t
|
|
fork(void)
|
|
{
|
|
long flags = FORK_PROCESS;
|
|
int ret = syscall(0, flags, 0, 0, 0, SYS_FORK);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
fstat(int fd, struct stat *buf)
|
|
{
|
|
int ret = syscall(SA(fd), SA(buf), 0, 0, 0, SYS_FSTAT);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
char *
|
|
getcwd(char *buf, size_t sz)
|
|
{
|
|
int ret = syscall(SA(buf), SA(sz), 0, 0, 0, SYS_GETCWD);
|
|
ERRNO_NZ(ret);
|
|
if (ret)
|
|
return NULL;
|
|
return buf;
|
|
}
|
|
|
|
pid_t
|
|
getpid(void)
|
|
{
|
|
return syscall(0, 0, 0, 0, 0, SYS_GETPID);
|
|
}
|
|
|
|
pid_t
|
|
getppid(void)
|
|
{
|
|
return syscall(0, 0, 0, 0, 0, SYS_GETPPID);
|
|
}
|
|
|
|
int
|
|
getsockopt(int fd, int level, int opt, void *optv, socklen_t *optlen)
|
|
{
|
|
int ret = syscall(SA(fd), SA(level), SA(opt), SA(optv), SA(optlen),
|
|
SYS_GETSOCKOPT);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
getpeername(int s, struct sockaddr *sa, socklen_t *sl)
|
|
{
|
|
return getsockopt(s, SOL_SOCKET, SO_PEER, sa, sl);
|
|
}
|
|
|
|
int
|
|
getsockname(int s, struct sockaddr *sa, socklen_t *sl)
|
|
{
|
|
return getsockopt(s, SOL_SOCKET, SO_NAME, sa, sl);
|
|
}
|
|
|
|
int
|
|
gettimeofday(struct timeval *tv, struct timezone *tz)
|
|
{
|
|
if (tz)
|
|
errx(-1, "timezone not supported");
|
|
//int ret = syscall(SA(tv), 0, 0, 0, 0, SYS_GETTOD);
|
|
//ERRNO_NZ(ret);
|
|
//return ret;
|
|
ulong c = rdtsc();
|
|
ulong us = c * kinfo->pspercycle / 1000000;
|
|
tv->tv_sec = us / 1000000;
|
|
tv->tv_usec = us % 1000000;
|
|
return 0;
|
|
}
|
|
|
|
// XXX should simply look at TCB
|
|
long
|
|
gettid(void)
|
|
{
|
|
return syscall(0, 0, 0, 0, 0, SYS_GETTID);
|
|
}
|
|
|
|
int
|
|
getrlimit(int res, struct rlimit *rlp)
|
|
{
|
|
int ret = syscall(SA(res), SA(rlp), 0, 0, 0, SYS_GETRLIMIT);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
getrusage(int who, struct rusage *r)
|
|
{
|
|
int ret = syscall(SA(who), SA(r), 0, 0, 0, SYS_GETRUSAGE);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
kill(int pid, int sig)
|
|
{
|
|
if (sig != SIGKILL) {
|
|
printf("%s: kill: only SIGKILL is supported\n", __progname);
|
|
return -1;
|
|
}
|
|
int ret = syscall(SA(pid), SA(sig), 0, 0, 0, SYS_KILL);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
link(const char *old, const char *new)
|
|
{
|
|
int ret = syscall(SA(old), SA(new), 0, 0, 0, SYS_LINK);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
listen(int fd, int backlog)
|
|
{
|
|
int ret = syscall(SA(fd), SA(backlog), 0, 0, 0, SYS_LISTEN);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
off_t
|
|
lseek(int fd, off_t off, int whence)
|
|
{
|
|
int ret = syscall(SA(fd), SA(off), SA(whence), 0, 0, SYS_LSEEK);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
mkdir(const char *p, long mode)
|
|
{
|
|
int ret = syscall(SA(p), mode, 0, 0, 0, SYS_MKDIR);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
mknod(const char *p, mode_t m, dev_t d)
|
|
{
|
|
if (MAJOR(d) == 0)
|
|
errx(-1, "bad major: 0");
|
|
int ret = syscall(SA(p), SA(m), SA(d), 0, 0, SYS_MKNOD);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
void *
|
|
mmap(void *addr, size_t len, int prot, int flags, int fd, long offset)
|
|
{
|
|
long ret;
|
|
ret = syscall6(SA(addr), SA(len), SA(prot), SA(flags), SA(fd),
|
|
SA(offset), SYS_MMAP);
|
|
if (ret < 0 && -ret >= ERRNO_FIRST && -ret <= ERRNO_LAST) {
|
|
errno = -ret;
|
|
ret = (long)MAP_FAILED;
|
|
}
|
|
return (void *)ret;
|
|
}
|
|
|
|
int
|
|
munmap(void *addr, size_t len)
|
|
{
|
|
int ret = syscall(SA(addr), SA(len), 0, 0, 0, SYS_MUNMAP);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
nanosleep(const struct timespec *timeout, struct timespec *remainder)
|
|
{
|
|
int ret = syscall(SA(timeout), SA(remainder), 0, 0, 0, SYS_NANOSLEEP);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
open(const char *path, int flags, ...)
|
|
{
|
|
mode_t mode = 0;
|
|
if (flags & O_CREAT) {
|
|
va_list l;
|
|
va_start(l, flags);
|
|
mode = va_arg(l, mode_t);
|
|
va_end(l);
|
|
}
|
|
int ret = syscall(SA(path), flags, mode, 0, 0, SYS_OPEN);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
pause(void)
|
|
{
|
|
int ret = syscall(0, 0, 0, 0, 0, SYS_PAUSE);
|
|
errno = ret;
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
pipe(int pfds[2])
|
|
{
|
|
int ret = pipe2(pfds, 0);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
pipe2(int pfds[2], int flags)
|
|
{
|
|
int ret = syscall(SA(pfds), SA(flags), 0, 0, 0, SYS_PIPE2);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
poll(struct pollfd *fds, nfds_t nfds, int timeout)
|
|
{
|
|
struct pollfd *end = fds + nfds;
|
|
struct pollfd *p;
|
|
for (p = fds; p < end; p++)
|
|
p->revents = 0;
|
|
int ret = syscall(SA(fds), SA(nfds), SA(timeout), 0, 0, SYS_POLL);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
ssize_t
|
|
pread(int fd, void *dst, size_t len, off_t off)
|
|
{
|
|
ssize_t ret = syscall(SA(fd), SA(dst), SA(len), SA(off), 0, SYS_PREAD);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
ssize_t
|
|
pwrite(int fd, const void *src, size_t len, off_t off)
|
|
{
|
|
ssize_t ret = syscall(SA(fd), SA(src), SA(len), SA(off), 0, SYS_PWRITE);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
long
|
|
read(int fd, void *buf, size_t c)
|
|
{
|
|
int ret = syscall(SA(fd), SA(buf), SA(c), 0, 0, SYS_READ);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
reboot(void)
|
|
{
|
|
int ret = syscall(0, 0, 0, 0, 0, SYS_REBOOT);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
|
|
}
|
|
|
|
ssize_t
|
|
getdents64(int fd, void *dirent, size_t count)
|
|
{
|
|
ssize_t ret = syscall(SA(fd), SA(dirent), SA(count), 0, 0, SYS_GETDENTS64);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
ssize_t
|
|
recv(int fd, void *buf, size_t len, int flags)
|
|
{
|
|
int ret = recvfrom(fd, buf, len, flags, NULL, NULL);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
ssize_t
|
|
recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *sa,
|
|
socklen_t *salen)
|
|
{
|
|
if (len >= (1ULL << 32))
|
|
errx(-1, "len too big");
|
|
ulong flaglen = len << 32 | flags;
|
|
int ret = syscall(SA(fd), SA(buf), SA(flaglen), SA(sa),
|
|
SA(salen), SYS_RECVFROM);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
ssize_t
|
|
recvmsg(int fd, struct msghdr *msg, int flags)
|
|
{
|
|
ssize_t ret = syscall(SA(fd), SA(msg), SA(flags), 0, 0, SYS_RECVMSG);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
rename(const char *old, const char *new)
|
|
{
|
|
int ret = syscall(SA(old), SA(new), 0, 0, 0, SYS_RENAME);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
ssize_t
|
|
send(int fd, const void *buf, size_t len, int flags)
|
|
{
|
|
return sendto(fd, buf, len, flags, NULL, 0);
|
|
}
|
|
|
|
ssize_t
|
|
sendto(int fd, const void *buf, size_t len, int flags,
|
|
const struct sockaddr *sa, socklen_t slen)
|
|
{
|
|
if (len >= (1ULL << 32))
|
|
errx(-1, "len too big");
|
|
ulong flaglen = len << 32 | flags;
|
|
int ret = syscall(SA(fd), SA(buf), SA(flaglen), SA(sa), SA(slen),
|
|
SYS_SENDTO);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
ssize_t
|
|
sendmsg(int fd, struct msghdr *msg, int flags)
|
|
{
|
|
ssize_t ret = syscall(SA(fd), SA(msg), SA(flags), 0, 0, SYS_SENDMSG);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
setrlimit(int res, const struct rlimit *rlp)
|
|
{
|
|
int ret = syscall(SA(res), SA(rlp), 0, 0, 0, SYS_SETRLIMIT);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
pid_t
|
|
setsid(void)
|
|
{
|
|
printf("warning: no sessions or process groups yet\n");
|
|
return getpid();
|
|
}
|
|
|
|
int
|
|
setsockopt(int a, int b, int c, const void *d, socklen_t e)
|
|
{
|
|
static char *on[] = {
|
|
#define F(x) [x] = #x
|
|
F(SO_SNDTIMEO),
|
|
F(SO_ERROR),
|
|
F(SO_TYPE),
|
|
F(SO_REUSEADDR),
|
|
F(SO_KEEPALIVE),
|
|
F(SO_LINGER),
|
|
F(SO_SNDLOWAT),
|
|
F(TCP_NODELAY),
|
|
#undef F
|
|
};
|
|
long ret = 0;
|
|
if (on[c] != NULL) {
|
|
errno = 0;
|
|
fprintf(stderr, "warning: setsockopt no-op for %s\n", on[c]);
|
|
} else {
|
|
ret = syscall(SA(a), SA(b), SA(c), SA(d), SA(e),
|
|
SYS_SETSOCKOPT);
|
|
ERRNO_NEG(ret);
|
|
}
|
|
return (int)ret;
|
|
}
|
|
|
|
int
|
|
sigaction(int sig, const struct sigaction *act, struct sigaction *oact)
|
|
{
|
|
printf("warning: no signals yet\n");
|
|
return 0;
|
|
//int ret = syscall(SA(sig), SA(act), SA(oact), 0, 0, SYS_SIGACTION);
|
|
//ERRNO_NEG(ret);
|
|
//return ret;
|
|
}
|
|
|
|
ssize_t
|
|
readv(int fd, const struct iovec *iovs, int niovs)
|
|
{
|
|
ssize_t ret = syscall(SA(fd), SA(iovs), SA(niovs), 0, 0, SYS_READV);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
(*signal(int sig, void (*f)(int)))(int)
|
|
{
|
|
struct sigaction sa, oa;
|
|
memset(&sa, 0, sizeof(struct sigaction));
|
|
sa.sa_handler = f;
|
|
sigaction(sig, &sa, &oa);
|
|
return oa.sa_handler;
|
|
}
|
|
|
|
int
|
|
shutdown(int fd, int how)
|
|
{
|
|
int ret = syscall(SA(fd), SA(how), 0, 0, 0, SYS_SHUTDOWN);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
socket(int dom, int type, int proto)
|
|
{
|
|
int ret = syscall(SA(dom), SA(type), SA(proto), 0, 0, SYS_SOCKET);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
socketpair(int dom, int type, int proto, int p[2])
|
|
{
|
|
int ret = syscall(SA(dom), SA(type), SA(proto), SA(p), 0,
|
|
SYS_SOCKETPAIR);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
stat(const char *path, struct stat *st)
|
|
{
|
|
int ret = syscall(SA(path), SA(st), 0, 0, 0, SYS_STAT);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
sync(void)
|
|
{
|
|
int ret = syscall(0, 0, 0, 0, 0, SYS_SYNC);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
long
|
|
sys_prof(long ptype, long events, long flags, long intperiod)
|
|
{
|
|
long ret = syscall(ptype, events, flags, intperiod, 0, SYS_PROF);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
long
|
|
sys_info(long n)
|
|
{
|
|
long ret = syscall(n, 0, 0, 0, 0, SYS_INFO);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
truncate(const char *p, off_t newlen)
|
|
{
|
|
int ret = syscall(SA(p), SA(newlen), 0, 0, 0, SYS_TRUNC);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
_unlink(const char *path, int wantdir)
|
|
{
|
|
int ret = syscall(SA(path), SA(wantdir), 0, 0, 0, SYS_UNLINK);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
unlink(const char *path)
|
|
{
|
|
return _unlink(path, 0);
|
|
}
|
|
|
|
int
|
|
rmdir(const char *path)
|
|
{
|
|
return _unlink(path, 1);
|
|
}
|
|
|
|
pid_t
|
|
wait(int *status)
|
|
{
|
|
pid_t ret = syscall(WAIT_ANY, SA(status), 0, 0, 0, SYS_WAIT4);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
pid_t
|
|
waitpid(pid_t pid, int *status, int options)
|
|
{
|
|
return wait4(pid, status, options, NULL);
|
|
}
|
|
|
|
pid_t
|
|
wait4(pid_t pid, int *status, int options, struct rusage *r)
|
|
{
|
|
pid_t ret = syscall(pid, SA(status), SA(options), SA(r), 0,
|
|
SYS_WAIT4);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
pid_t
|
|
wait3(int *status, int options, struct rusage *r)
|
|
{
|
|
pid_t ret = syscall(WAIT_ANY, SA(status), SA(options), SA(r), 0,
|
|
SYS_WAIT4);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
long
|
|
write(int fd, const void *buf, size_t c)
|
|
{
|
|
int ret = syscall(fd, SA(buf), SA(c), 0, 0, SYS_WRITE);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
ssize_t
|
|
writev(int fd, const struct iovec *iovs, int niovs)
|
|
{
|
|
ssize_t ret = syscall(SA(fd), SA(iovs), SA(niovs), 0, 0, SYS_WRITEV);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* thread stuff
|
|
*/
|
|
void
|
|
tfork_done(long status)
|
|
{
|
|
threxit(status);
|
|
errx(-1, "threxit returned");
|
|
}
|
|
|
|
int
|
|
tfork_thread(struct tfork_t *args, long (*fn)(void *), void *fnarg)
|
|
{
|
|
int tid;
|
|
long flags = FORK_THREAD;
|
|
|
|
#if defined(__x86_64__)
|
|
// rbx and rbp are preserved across syscalls. i don't know how to
|
|
// specify rbp as a register contraint.
|
|
register ulong rbp asm("rbp") = (ulong)fn;
|
|
register ulong rbx asm("rbx") = (ulong)fnarg;
|
|
|
|
asm volatile(
|
|
"movq %%rsp, %%r10\n"
|
|
"leaq 2(%%rip), %%r11\n"
|
|
"syscall\n"
|
|
"cmpl $0, %%eax\n"
|
|
// parent or error
|
|
"jne 1f\n"
|
|
// child
|
|
"movq %5, %%rdi\n"
|
|
"call *%4\n"
|
|
"movq %%rax, %%rdi\n"
|
|
"call tfork_done\n"
|
|
"movq $0, 0x0\n"
|
|
"1:\n"
|
|
: "=a"(tid)
|
|
: "D"(args), "S"(flags), "0"(SYS_FORK), "r"(rbp), "r"(rbx)
|
|
: SYSCALL_CLOBBERS);
|
|
#elif defined(__aarch64__)
|
|
// all registers are preserved across syscalls for aarch64.
|
|
register ulong x8 asm("x8") = SYS_FORK;
|
|
register ulong x0 asm("x0") = (ulong)args;
|
|
register ulong x1 asm("x1") = flags;
|
|
|
|
asm volatile(
|
|
"svc 0\n"
|
|
"cmp x0, #0\n"
|
|
// parent or error
|
|
"b.ne 1f\n"
|
|
// child
|
|
"ldr x0, %5\n"
|
|
"ldr x9, %4\n"
|
|
"blr x9\n"
|
|
"bl tfork_done\n"
|
|
"mov x0, #0\n"
|
|
"str xzr, [x0]\n"
|
|
"1:\n"
|
|
: "=r"(tid)
|
|
: "r"(x8), "0"(x0), "r"(x1), "m"(fn), "m"(fnarg)
|
|
: SYSCALL_CLOBBERS);
|
|
#endif
|
|
|
|
return tid;
|
|
}
|
|
|
|
void
|
|
threxit(long status)
|
|
{
|
|
syscall(SA(status), 0, 0, 0, 0, SYS_THREXIT);
|
|
}
|
|
|
|
int
|
|
thrwait(int tid, long *status)
|
|
{
|
|
if (tid <= 0)
|
|
errx(-1, "thrwait: bad tid %d", tid);
|
|
int ret = syscall(SA(tid), SA(status), 0, 0, 1, SYS_WAIT4);
|
|
return ret;
|
|
}
|
|
|
|
static void *
|
|
mkstack(size_t size)
|
|
{
|
|
const size_t pgsize = 1 << 12;
|
|
size += pgsize - 1;
|
|
size &= ~(pgsize - 1);
|
|
char *ret = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
|
MAP_ANON | MAP_PRIVATE, -1, 0);
|
|
if (!ret)
|
|
return NULL;
|
|
return ret + size;
|
|
}
|
|
|
|
struct pcargs_t {
|
|
void* (*fn)(void *);
|
|
void *arg;
|
|
void *stack;
|
|
void *tls;
|
|
ulong stksz;
|
|
};
|
|
|
|
static long
|
|
_pcreate(void *vpcarg)
|
|
{
|
|
struct pcargs_t pcargs = *(struct pcargs_t *)vpcarg;
|
|
free(vpcarg);
|
|
|
|
// use arch_prctl to set $fs manually
|
|
syscall(0x1002, (long)pcargs.tls, 0, 0, 0, 158);
|
|
|
|
long status;
|
|
status = (long)(pcargs.fn(pcargs.arg));
|
|
free(pcargs.tls);
|
|
|
|
#if defined(__x86_64__)
|
|
// rbx and rbp are preserved across syscalls. i don't know how to
|
|
// specify rbp as a register contraint.
|
|
register ulong rbp asm("rbp") = SYS_THREXIT;
|
|
register long rbx asm("rbx") = status;
|
|
|
|
asm volatile(
|
|
"movq %%rsp, %%r10\n"
|
|
"leaq 2(%%rip), %%r11\n"
|
|
"syscall\n"
|
|
"cmpq $0, %%rax\n"
|
|
"je 1f\n"
|
|
"movq $0, 0x0\n"
|
|
"1:\n"
|
|
"movq %3, %%rax\n"
|
|
"movq %4, %%rdi\n"
|
|
"leaq 2(%%rip), %%r11\n"
|
|
"syscall\n"
|
|
"movq $0, 0x1\n"
|
|
:
|
|
: "a"(SYS_MUNMAP), "D"(pcargs.stack), "S"(pcargs.stksz),
|
|
"r"(rbp), "r"(rbx)
|
|
: SYSCALL_CLOBBERS);
|
|
#elif defined(__aarch64__)
|
|
register ulong x8 asm("x8") = SYS_MUNMAP;
|
|
register ulong x0 asm("x0") = (ulong)pcargs.stack;
|
|
register ulong x1 asm("x1") = (ulong)pcargs.stksz;
|
|
|
|
asm volatile(
|
|
"svc 0\n"
|
|
"cmp x0, #0\n"
|
|
"b.eq 1f\n"
|
|
"mov x0, #0\n"
|
|
"str xzr, [x0]\n"
|
|
"1:\n"
|
|
"mov x8, %3\n"
|
|
"ldr x0, %4\n"
|
|
"svc 0\n"
|
|
"mov x0, #1\n"
|
|
"str xzr, [x0]\n"
|
|
:
|
|
: "r"(x8), "r"(x0), "r"(x1),
|
|
"X"(SYS_THREXIT), "m"(status)
|
|
: SYSCALL_CLOBBERS);
|
|
#endif
|
|
|
|
// not reached
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_create(pthread_t *t, pthread_attr_t *attrs, void* (*fn)(void *),
|
|
void *arg)
|
|
{
|
|
// allocate and setup TLS space
|
|
if (!kinfo)
|
|
errx(-1, "nil kinfo");
|
|
// +8 for the tls indirect pointer
|
|
void *newtls = malloc(kinfo->len + 8);
|
|
if (!newtls)
|
|
return ENOMEM;
|
|
char *tlsdata = newtls;
|
|
tlsdata += 8;
|
|
memmove(tlsdata, kinfo->freshtls, kinfo->len);
|
|
|
|
char **tlsptr = newtls;
|
|
*tlsptr = tlsdata + kinfo->len;
|
|
|
|
size_t stksz = _PTHREAD_DEFSTKSZ;
|
|
if (attrs) {
|
|
stksz = attrs->stacksize;
|
|
if (stksz > 1024*1024*128)
|
|
errx(-1, "big fat stack");
|
|
const ulong pgsize = 1ul << 12;
|
|
if ((stksz % pgsize) != 0)
|
|
errx(-1, "stack size not aligned");
|
|
}
|
|
int ret;
|
|
// XXX setup guard page
|
|
void *stack = mkstack(stksz);
|
|
if (!stack) {
|
|
ret = ENOMEM;
|
|
goto tls;
|
|
}
|
|
long tid;
|
|
struct tfork_t tf = {
|
|
.tf_tcb = tlsptr,
|
|
.tf_tid = &tid,
|
|
.tf_stack = stack,
|
|
};
|
|
struct pcargs_t *pca = malloc(sizeof(struct pcargs_t));
|
|
if (!pca) {
|
|
ret = ENOMEM;
|
|
goto errstack;
|
|
}
|
|
|
|
pca->fn = fn;
|
|
pca->arg = arg;
|
|
pca->stack = stack - stksz;
|
|
pca->tls = newtls;
|
|
pca->stksz = stksz;
|
|
ret = tfork_thread(&tf, _pcreate, pca);
|
|
if (ret < 0) {
|
|
ret = -ret;
|
|
goto both;
|
|
}
|
|
*t = tid;
|
|
return 0;
|
|
both:
|
|
free(pca);
|
|
errstack:
|
|
munmap(stack - stksz, stksz);
|
|
tls:
|
|
free(newtls);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
pthread_join(pthread_t t, void **retval)
|
|
{
|
|
int ret = thrwait(t, (long *)retval);
|
|
if (ret < 0)
|
|
return -ret;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_cancel(pthread_t t)
|
|
{
|
|
errx(-1, "no imp");
|
|
}
|
|
|
|
int
|
|
pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *attr)
|
|
{
|
|
if (attr)
|
|
errx(-1, "mutex attributes not supported");
|
|
m->locks = 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_mutex_lock(pthread_mutex_t *m)
|
|
{
|
|
if (!dolock)
|
|
return 0;
|
|
// fast path
|
|
uint * const p = &m->locks;
|
|
uint old;
|
|
if ((old = __sync_val_compare_and_swap(p, 0, 1)) == 0)
|
|
return 0;
|
|
|
|
while (1) {
|
|
// upgrade lock to "has waiters"
|
|
old = __sync_lock_test_and_set(p, 2);
|
|
// did we get lucky?
|
|
if (old == 0)
|
|
return 0;
|
|
|
|
// sleep and acquire
|
|
int ret;
|
|
ret = futex(FUTEX_SLEEP, &m->locks, NULL, 2, NULL);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
int
|
|
pthread_mutex_unlock(pthread_mutex_t *m)
|
|
{
|
|
uint old;
|
|
old = __sync_lock_test_and_set(&m->locks, 0);
|
|
|
|
int ret = 0;
|
|
if (old == 2)
|
|
ret = futex(FUTEX_WAKE, &m->locks, NULL, 1, NULL);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
pthread_mutex_destroy(pthread_mutex_t *mutex)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_once(pthread_once_t *octl, void (*fn)(void))
|
|
{
|
|
errx(-1, "pthread_once: no imp");
|
|
return 0;
|
|
}
|
|
|
|
pthread_t
|
|
pthread_self(void)
|
|
{
|
|
return gettid();
|
|
}
|
|
|
|
// XXX use fancy new futexes
|
|
int
|
|
pthread_barrier_init(pthread_barrier_t *b, pthread_barrierattr_t *attr, uint c)
|
|
{
|
|
if (attr)
|
|
errx(-1, "barrier attributes not supported");
|
|
b->target = c;
|
|
b->current = 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_barrier_destroy(pthread_barrier_t *b)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_barrier_wait(pthread_barrier_t *b)
|
|
{
|
|
uint m = 1ull << 31;
|
|
uint c;
|
|
while (1) {
|
|
uint o = b->current;
|
|
uint n = o + 1;
|
|
if ((o & m) != 0) {
|
|
#if defined(__x86_64__)
|
|
asm volatile("pause":::"memory");
|
|
#elif defined(__aarch64__)
|
|
asm volatile("yield":::"memory");
|
|
#endif
|
|
continue;
|
|
}
|
|
c = n;
|
|
if (__sync_bool_compare_and_swap(&b->current, o, n))
|
|
break;
|
|
}
|
|
|
|
if (c == b->target) {
|
|
while (1) {
|
|
uint o = b->current;
|
|
uint n = (b->current - 1) | m;
|
|
if (__sync_bool_compare_and_swap(&b->current, o, n))
|
|
break;
|
|
}
|
|
return PTHREAD_BARRIER_SERIAL_THREAD;
|
|
}
|
|
|
|
while ((b->current & m) == 0)
|
|
#if defined(__x86_64__)
|
|
asm volatile("pause":::"memory");
|
|
#elif defined(__aarch64__)
|
|
asm volatile("yield":::"memory");
|
|
#endif
|
|
|
|
c = __sync_add_and_fetch(&b->current, -1);
|
|
if (c == m)
|
|
b->current = 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_cond_broadcast(pthread_cond_t *c)
|
|
{
|
|
__sync_add_and_fetch(&c->gen, 1);
|
|
return futex(FUTEX_WAKE, &c->gen, NULL, -1, NULL);
|
|
}
|
|
|
|
int
|
|
pthread_cond_destroy(pthread_cond_t *c)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *ca)
|
|
{
|
|
if (ca)
|
|
errx(-1, "cond attritbutes not supported");
|
|
c->gen = 0;
|
|
c->bcast = 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m,
|
|
const struct timespec *t)
|
|
{
|
|
uint last = c->gen;
|
|
asm volatile("":::"memory");
|
|
int ret;
|
|
if ((ret = pthread_mutex_unlock(m)))
|
|
return ret;
|
|
|
|
int moveq;
|
|
if ((moveq = futex(FUTEX_SLEEP, &c->gen, NULL, last, t)) < 0)
|
|
return moveq;
|
|
|
|
if ((ret = pthread_mutex_lock(m)))
|
|
return ret;
|
|
|
|
// on pthread_cond_broadcast(3), move cond's sleep queue to my mutex
|
|
if (moveq) {
|
|
// make sure threads moved to mutex wait queue are woken up
|
|
c->bcast = 1;
|
|
ret = futex(FUTEX_CNDGIVE, &c->gen, &m->locks, 0, NULL);
|
|
}
|
|
if (c->bcast)
|
|
m->locks = 2;
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m)
|
|
{
|
|
return pthread_cond_timedwait(c, m, NULL);
|
|
}
|
|
|
|
int
|
|
pthread_cond_signal(pthread_cond_t *c)
|
|
{
|
|
__sync_add_and_fetch(&c->gen, 1);
|
|
return futex(FUTEX_WAKE, &c->gen, NULL, 1, NULL);
|
|
}
|
|
|
|
int
|
|
pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
|
|
{
|
|
printf("warning: signals not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_setcancelstate(int state, int *oldstate)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_setcanceltype(int type, int *oldtype)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_attr_destroy(pthread_attr_t *a)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_attr_init(pthread_attr_t *a)
|
|
{
|
|
memset(a, 0, sizeof(pthread_attr_t));
|
|
a->stacksize = _PTHREAD_DEFSTKSZ;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_attr_getstacksize(pthread_attr_t *a, size_t *sz)
|
|
{
|
|
*sz = a->stacksize;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
pthread_attr_setstacksize(pthread_attr_t *a, size_t sz)
|
|
{
|
|
a->stacksize = sz;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* posix
|
|
*/
|
|
|
|
int
|
|
_posix_dups(const posix_spawn_file_actions_t *fa)
|
|
{
|
|
int i;
|
|
for (i = 0; i < fa->dup2slot; i++) {
|
|
int ret;
|
|
int from = fa->dup2s[i].from;
|
|
int to = fa->dup2s[i].to;
|
|
if ((ret = dup2(from, to)) < 0)
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static char *_environ[] = {"", NULL};
|
|
|
|
int
|
|
posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *fa,
|
|
const posix_spawnattr_t *sa, char *const argv[], char *const envp[])
|
|
{
|
|
if (sa)
|
|
errx(-1, "spawnattr not supported");
|
|
if (envp != NULL)
|
|
if (envp != _environ || !envp[0] ||
|
|
envp[0][0] != '\0' || envp[1] != NULL)
|
|
errx(-1, "environ not supported");
|
|
pid_t p = fork();
|
|
if (p < 0)
|
|
return p;
|
|
|
|
int child = !p;
|
|
if (child) {
|
|
if (fa) {
|
|
if (_posix_dups(fa))
|
|
errx(127, "posix_spawn dups failed");
|
|
}
|
|
execv(path, argv);
|
|
errx(127, "posix_spawn exec failed");
|
|
}
|
|
|
|
if (pid)
|
|
*pid = p;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, int ofd, int newfd)
|
|
{
|
|
if (ofd < 0 || newfd < 0)
|
|
return -EINVAL;
|
|
|
|
size_t nelms = sizeof(fa->dup2s)/sizeof(fa->dup2s[0]);
|
|
int myslot = fa->dup2slot++;
|
|
if (myslot < 0 || myslot >= nelms)
|
|
errx(-1, "bad dup2slot: %d", myslot);
|
|
|
|
fa->dup2s[myslot].from = ofd;
|
|
fa->dup2s[myslot].to = newfd;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
posix_spawn_file_actions_init(posix_spawn_file_actions_t *fa)
|
|
{
|
|
memset(fa, 0, sizeof(posix_spawn_file_actions_t));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* libc
|
|
*/
|
|
void
|
|
abort(void)
|
|
{
|
|
errx(-1, "abort");
|
|
}
|
|
|
|
int
|
|
atoi(const char *n)
|
|
{
|
|
return (int)strtol(n, NULL, 10);
|
|
}
|
|
|
|
double
|
|
ceil(double x)
|
|
{
|
|
if (x == trunc(x))
|
|
return x;
|
|
return trunc(x + 1);
|
|
}
|
|
|
|
int
|
|
creat(const char *p, mode_t m)
|
|
{
|
|
return open(p, O_WRONLY | O_CREAT | O_TRUNC, m);
|
|
}
|
|
|
|
char *
|
|
ctime(const time_t *t)
|
|
{
|
|
static char cbuf[64];
|
|
return ctime_r(t, cbuf);
|
|
}
|
|
|
|
char *
|
|
ctime_r(const time_t *t, char *buf)
|
|
{
|
|
printf("warning: ctime_r not implemented\n");
|
|
char *msg = "i dont wish to write this";
|
|
memmove(buf, msg, strlen(msg) + 1);
|
|
return buf;
|
|
}
|
|
|
|
void
|
|
err(int eval, const char *fmt, ...)
|
|
{
|
|
dolock = 0;
|
|
fprintf(stderr, "%s: ", __progname);
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
// errno is dumb
|
|
int neval = errno;
|
|
char p[NL_TEXTMAX];
|
|
strerror_r(neval, p, sizeof(p));
|
|
fprintf(stderr, ": %s\n", p);
|
|
exit(eval);
|
|
}
|
|
|
|
void
|
|
errx(int eval, const char *fmt, ...)
|
|
{
|
|
dolock = 0;
|
|
fprintf(stderr, "%s: ", __progname);
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
fprintf(stderr, "\n");
|
|
exit(eval);
|
|
}
|
|
|
|
void
|
|
exit(int ret)
|
|
{
|
|
if (fflush(NULL))
|
|
fprintf(stderr, "warning: failed to fflush\n");
|
|
_exit(ret);
|
|
}
|
|
|
|
int
|
|
fprintf(FILE *f, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
int ret;
|
|
ret = vfprintf(f, fmt, ap);
|
|
va_end(ap);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
_ferror(FILE *f)
|
|
{
|
|
return f->error;
|
|
}
|
|
|
|
int
|
|
ferror(FILE *f)
|
|
{
|
|
pthread_mutex_lock(&f->mut);
|
|
int ret = _ferror(f);
|
|
pthread_mutex_unlock(&f->mut);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
fileno(FILE *f)
|
|
{
|
|
pthread_mutex_lock(&f->mut);
|
|
int ret = f->fd;
|
|
pthread_mutex_unlock(&f->mut);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
_feof(FILE *f)
|
|
{
|
|
return f->eof;
|
|
}
|
|
|
|
int
|
|
feof(FILE *f)
|
|
{
|
|
pthread_mutex_lock(&f->mut);
|
|
int ret = _feof(f);
|
|
pthread_mutex_unlock(&f->mut);
|
|
return ret;
|
|
}
|
|
|
|
static struct {
|
|
FILE *head;
|
|
pthread_mutex_t mut;
|
|
} _allfiles;
|
|
|
|
static FILE _stdin, _stdout, _stderr;
|
|
FILE *stdin = &_stdin, *stdout = &_stdout, *stderr = &_stderr;
|
|
|
|
static void fdinit(void)
|
|
{
|
|
#define FDINIT(f, fdn, bt) do { \
|
|
f.fd = fdn; \
|
|
f.p = &f.buf[0]; \
|
|
f.end = f.p; \
|
|
f.lnext = _allfiles.head; \
|
|
f.lprev = NULL; \
|
|
f.btype = bt; \
|
|
if (pthread_mutex_init(&f.mut, NULL)) \
|
|
pmsg("mutex init", 9); \
|
|
if (_allfiles.head) \
|
|
_allfiles.head->lprev = &f; \
|
|
_allfiles.head = &f; \
|
|
} while (0)
|
|
|
|
FDINIT(_stdin, 0, _IONBF);
|
|
FDINIT(_stdout, 1, _IONBF);
|
|
FDINIT(_stderr, 2, _IONBF);
|
|
#undef FDINIT
|
|
pthread_mutex_init(&_allfiles.mut, NULL);
|
|
}
|
|
|
|
static int
|
|
_fflush(FILE *f)
|
|
{
|
|
if (!f->writing)
|
|
return 0;
|
|
char * const bst = &f->buf[0];
|
|
size_t len = f->p - bst;
|
|
ssize_t r;
|
|
if ((r = write(f->fd, bst, len)) < 0) {
|
|
f->error = errno;
|
|
return EOF;
|
|
} else if (r != len) {
|
|
printf("asked: %zu, actual: %zu\n", len, r);
|
|
return EOF;
|
|
}
|
|
f->p = f->end = bst;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fflush(FILE *f)
|
|
{
|
|
if (f == NULL) {
|
|
int ret = 0;
|
|
pthread_mutex_lock(&_allfiles.mut);
|
|
FILE *f = _allfiles.head;
|
|
while (f != NULL) {
|
|
ret |= fflush(f);
|
|
f = f->lnext;
|
|
}
|
|
pthread_mutex_unlock(&_allfiles.mut);
|
|
return ret;
|
|
}
|
|
|
|
pthread_mutex_lock(&f->mut);
|
|
int ret = _fflush(f);
|
|
pthread_mutex_unlock(&f->mut);
|
|
return ret;
|
|
}
|
|
|
|
static FILE *
|
|
_fopen(const char *path, int gfd, const char *mode)
|
|
{
|
|
int flags = -1;
|
|
int plus = strchr(mode, '+') != NULL;
|
|
int x = strchr(mode, 'x') != NULL;
|
|
switch (mode[0]) {
|
|
case 'r':
|
|
if (plus)
|
|
flags = O_RDWR;
|
|
else
|
|
flags = O_RDONLY;
|
|
break;
|
|
case 'w':
|
|
if (plus)
|
|
flags = O_RDWR | O_CREAT | O_TRUNC;
|
|
else
|
|
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
|
if (x)
|
|
flags |= O_EXCL;
|
|
break;
|
|
case 'a':
|
|
if (plus)
|
|
flags = O_RDWR | O_CREAT | O_APPEND;
|
|
else
|
|
flags = O_WRONLY | O_CREAT | O_APPEND;
|
|
if (x)
|
|
flags |= O_EXCL;
|
|
break;
|
|
default:
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
if (strchr(mode, 'e') != NULL)
|
|
flags |= O_CLOEXEC;
|
|
|
|
FILE *ret = malloc(sizeof(FILE));
|
|
if (ret == NULL) {
|
|
errno = ENOMEM;
|
|
return NULL;
|
|
}
|
|
memset(ret, 0, sizeof(FILE));
|
|
|
|
int fd = gfd;
|
|
if (path)
|
|
fd = open(path, flags);
|
|
else
|
|
if (fd < 0)
|
|
errno = EBADF;
|
|
if (fd < 0) {
|
|
free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
pthread_mutex_init(&ret->mut, NULL);
|
|
ret->fd = fd;
|
|
ret->p = &ret->buf[0];
|
|
ret->end = ret->p;
|
|
// POSIX: fully-buffered iff is not an interactive device
|
|
ret->btype = _IONBF;
|
|
struct stat st;
|
|
if (fstat(ret->fd, &st) == 0 && S_ISREG(st.st_mode))
|
|
ret->btype = _IOFBF;
|
|
|
|
// insert into global list
|
|
pthread_mutex_lock(&_allfiles.mut);
|
|
ret->lnext = _allfiles.head;
|
|
if (_allfiles.head)
|
|
_allfiles.head->lprev = ret;
|
|
_allfiles.head = ret;
|
|
pthread_mutex_unlock(&_allfiles.mut);
|
|
return ret;
|
|
}
|
|
|
|
FILE *
|
|
fdopen(int fd, const char *mode)
|
|
{
|
|
return _fopen(NULL, fd, mode);
|
|
}
|
|
|
|
FILE *
|
|
fopen(const char *path, const char *mode)
|
|
{
|
|
return _fopen(path, -1, mode);
|
|
}
|
|
|
|
int
|
|
fclose(FILE *f)
|
|
{
|
|
// remove from global list
|
|
pthread_mutex_lock(&_allfiles.mut);
|
|
if (f->lnext)
|
|
f->lnext->lprev = f->lprev;
|
|
if (f->lprev)
|
|
f->lprev->lnext = f->lnext;
|
|
if (_allfiles.head == f)
|
|
_allfiles.head = f->lnext;
|
|
pthread_mutex_unlock(&_allfiles.mut);
|
|
|
|
pthread_mutex_lock(&f->mut);
|
|
_fflush(f);
|
|
|
|
// try to crash buggy programs that use f after us
|
|
f->fd = -1;
|
|
f->p = (void *)1;
|
|
f->end = NULL;
|
|
pthread_mutex_unlock(&f->mut);
|
|
|
|
pthread_mutex_destroy(&f->mut);
|
|
free(f);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static size_t
|
|
_fread1(void *dst, size_t left, FILE *f)
|
|
{
|
|
if (!(f->btype == _IOFBF || f->btype == _IONBF))
|
|
errx(-1, "noimp");
|
|
// unbuffered stream
|
|
if (f->btype == _IONBF) {
|
|
ssize_t r = read(f->fd, dst, left);
|
|
if (r < 0) {
|
|
f->error = errno;
|
|
return 0;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// switch to read mode
|
|
if (_fflush(f))
|
|
return 0;
|
|
f->writing = 0;
|
|
|
|
// fill buffer?
|
|
if (f->p == f->end && !_feof(f) && !_ferror(f)) {
|
|
char * const bst = &f->buf[0];
|
|
size_t r;
|
|
if ((r = read(f->fd, bst, sizeof(f->buf))) < 0) {
|
|
f->error = errno;
|
|
return 0;
|
|
} else if (r == 0) {
|
|
f->eof = 1;
|
|
return 0;
|
|
}
|
|
f->p = bst;
|
|
f->end = bst + r;
|
|
}
|
|
|
|
size_t ub = MIN(left, f->end - f->p);
|
|
memmove(dst, f->p, ub);
|
|
f->p += ub;
|
|
|
|
return ub;
|
|
}
|
|
|
|
size_t
|
|
fread(void *dst, size_t sz, size_t mem, FILE *f)
|
|
{
|
|
if (sz == 0)
|
|
return 0;
|
|
if (mem > ULONG_MAX / sz) {
|
|
errno = -EOVERFLOW;
|
|
return 0;
|
|
}
|
|
|
|
pthread_mutex_lock(&f->mut);
|
|
|
|
size_t ret = 0;
|
|
size_t left = mem * sz;
|
|
while (left > 0 && !_feof(f) && !_ferror(f)) {
|
|
size_t c = _fread1(dst, left, f);
|
|
//if (c == 0)
|
|
// break;
|
|
dst += c;
|
|
left -= c;
|
|
ret += c;
|
|
}
|
|
|
|
pthread_mutex_unlock(&f->mut);
|
|
return ret/sz;
|
|
}
|
|
|
|
static size_t
|
|
_fwrite1(const void *src, size_t left, FILE *f)
|
|
{
|
|
if (!(f->btype == _IOFBF || f->btype == _IONBF))
|
|
errx(-1, "noimp");
|
|
// unbuffered stream
|
|
if (f->btype == _IONBF) {
|
|
ssize_t r = write(f->fd, src, left);
|
|
if (r < 0) {
|
|
f->error = errno;
|
|
return 0;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
char * const bst = &f->buf[0];
|
|
char * const bend = bst + sizeof(f->buf);
|
|
|
|
// switch to write mode (discarding unread data and using undefined
|
|
// offset)
|
|
if (!f->writing) {
|
|
f->writing = 1;
|
|
f->p = f->end = bst;
|
|
}
|
|
|
|
// buffer full?
|
|
if (f->p == bend) {
|
|
if (_fflush(f))
|
|
return 0;
|
|
f->p = f->end = bst;
|
|
}
|
|
size_t put = MIN(bend - f->p, left);
|
|
memmove(f->p, src, put);
|
|
f->p += put;
|
|
f->end = MAX(f->p, f->end);
|
|
return put;
|
|
}
|
|
|
|
size_t
|
|
fwrite(const void *src, size_t sz, size_t mem, FILE *f)
|
|
{
|
|
if (sz == 0)
|
|
return 0;
|
|
if (mem > ULONG_MAX / sz) {
|
|
errno = -EOVERFLOW;
|
|
return 0;
|
|
}
|
|
|
|
pthread_mutex_lock(&f->mut);
|
|
|
|
size_t ret = 0;
|
|
size_t left = mem * sz;
|
|
while (left > 0 && !_ferror(f)) {
|
|
size_t c = _fwrite1(src, left, f);
|
|
src += c;
|
|
left -= c;
|
|
ret += c;
|
|
}
|
|
|
|
pthread_mutex_unlock(&f->mut);
|
|
return ret/sz;
|
|
}
|
|
|
|
static int
|
|
_fgetc(FILE *f)
|
|
{
|
|
char buf;
|
|
if (_fread1(&buf, 1, f) != 1)
|
|
return EOF;
|
|
return buf;
|
|
}
|
|
|
|
int
|
|
fgetc(FILE *f)
|
|
{
|
|
pthread_mutex_lock(&f->mut);
|
|
int ret = _fgetc(f);
|
|
pthread_mutex_unlock(&f->mut);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
_ungetc(int c, FILE *f)
|
|
{
|
|
const char * const bst = &f->buf[0];
|
|
if (f->p == bst)
|
|
return EOF;
|
|
unsigned char put = (unsigned char)c;
|
|
f->p--;
|
|
*f->p = put;
|
|
f->eof = 0;
|
|
return put;
|
|
}
|
|
|
|
int
|
|
ungetc(int c, FILE *f)
|
|
{
|
|
if (c == EOF)
|
|
return c;
|
|
|
|
pthread_mutex_lock(&f->mut);
|
|
int ret = _ungetc(c, f);
|
|
pthread_mutex_unlock(&f->mut);
|
|
return ret;
|
|
}
|
|
|
|
char *
|
|
fgets(char *buf, int size, FILE *f)
|
|
{
|
|
if (size == 0)
|
|
return NULL;
|
|
// get room for nul
|
|
size--;
|
|
pthread_mutex_lock(&f->mut);
|
|
|
|
char *ret = NULL;
|
|
size_t c = 0;
|
|
while (c < size) {
|
|
int ch = _fgetc(f);
|
|
if (ch == EOF) {
|
|
if (c > 0)
|
|
ret = buf;
|
|
goto out;
|
|
}
|
|
buf[c++] = ch;
|
|
if (ch == '\n')
|
|
break;
|
|
}
|
|
ret = buf;
|
|
out:
|
|
buf[c] = '\0';
|
|
pthread_mutex_unlock(&f->mut);
|
|
return ret;
|
|
}
|
|
|
|
off_t
|
|
ftello(FILE *f)
|
|
{
|
|
return lseek(f->fd, 0, SEEK_CUR);
|
|
}
|
|
|
|
int
|
|
ftruncate(int fd, off_t newlen)
|
|
{
|
|
int ret = syscall(SA(fd), SA(newlen), 0, 0, 0, SYS_FTRUNC);
|
|
ERRNO_NZ(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
futex(const int op, void *fut, void *fut2, int aux, const struct timespec *ts)
|
|
{
|
|
int ret = syscall(SA(op), SA(fut), SA(fut2), SA(aux), SA(ts),
|
|
SYS_FUTEX);
|
|
ERRNO_NEG(ret);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
fsync(int fd)
|
|
{
|
|
sync();
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
_gcfrac(struct gcfrac_t *r)
|
|
{
|
|
struct timeval tv;
|
|
if (gettimeofday(&tv, NULL))
|
|
err(-1, "gettimeofday");
|
|
long nowms = tv.tv_sec*1000 + tv.tv_usec/1000;
|
|
long gcwork = sys_info(SINFO_GCMS);
|
|
long markt = sys_info(SINFO_GCMARKTIME);
|
|
long sweept = sys_info(SINFO_GCSWEEPTIME);
|
|
long wbtime = sys_info(SINFO_GCWBARTIME);
|
|
if (gcwork == -1 || markt == -1 || sweept == -1 || wbtime == -1)
|
|
err(-1, "sysinfo");
|
|
|
|
r->startms = nowms;
|
|
r->gcworkms = gcwork;
|
|
r->details.wbms = wbtime;
|
|
r->details.bgsweepms = sweept;
|
|
r->details.markms = markt;
|
|
}
|
|
|
|
struct gcfrac_t
|
|
gcfracst(void)
|
|
{
|
|
struct gcfrac_t ret;
|
|
_gcfrac(&ret);
|
|
return ret;
|
|
}
|
|
|
|
double
|
|
gcfracend(struct gcfrac_t *of, long *marke, long *sweepe,
|
|
long *wbe)
|
|
{
|
|
struct gcfrac_t nf;
|
|
_gcfrac(&nf);
|
|
if (marke)
|
|
*marke = nf.details.markms - of->details.markms;
|
|
if (sweepe)
|
|
*sweepe = nf.details.bgsweepms - of->details.bgsweepms;
|
|
if (wbe)
|
|
*wbe = nf.details.wbms - of->details.wbms;
|
|
//return (double)(nf.gcworkms - of->gcworkms)/(nf.startms - of->startms);
|
|
return NAN;
|
|
}
|
|
|
|
char *optarg;
|
|
int optind = 1;
|
|
|
|
int
|
|
getopt(int argc, char * const *argv, const char *optstring)
|
|
{
|
|
optarg = NULL;
|
|
for (; optind < argc && !argv[optind]; optind++)
|
|
;
|
|
if (optind >= argc)
|
|
return -1;
|
|
char *ca = argv[optind];
|
|
if (ca[0] != '-')
|
|
return -1;
|
|
optind++;
|
|
const char wut = '?';
|
|
char *o = strchr(optstring, ca[1]);
|
|
if (!o)
|
|
return wut;
|
|
int needarg = o[1] == ':';
|
|
if (!needarg)
|
|
return ca[1];
|
|
if (ca[2] != '\0') {
|
|
optarg = &ca[2];
|
|
return ca[1];
|
|
}
|
|
const char argwut = optstring[0] == ':' ? ':' : wut;
|
|
if (optind >= argc || argv[optind][0] == '-')
|
|
return argwut;
|
|
optarg = argv[optind];
|
|
optind++;
|
|
return ca[1];
|
|
}
|
|
|
|
int
|
|
islower(int c)
|
|
{
|
|
return c >= 'a' && c <= 'z';
|
|
}
|
|
|
|
int
|
|
isupper(int c)
|
|
{
|
|
return c >= 'A' && c <= 'Z';
|
|
}
|
|
|
|
int
|
|
isalpha(int c)
|
|
{
|
|
return isupper(c) || islower(c);
|
|
}
|
|
|
|
int
|
|
isdigit(int c)
|
|
{
|
|
return c >= '0' && c <= '9';
|
|
}
|
|
|
|
int
|
|
isprint(int c)
|
|
{
|
|
return isalpha(c) || isdigit(c) || ispunct(c);
|
|
}
|
|
|
|
static char _punct[255] = {
|
|
['!'] = 1, ['"'] = 1, ['#'] = 1, ['$'] = 1, ['%'] = 1, ['&'] = 1,
|
|
['\'']= 1, ['('] = 1, [')'] = 1, ['*'] = 1, ['+'] = 1, [','] = 1,
|
|
['-'] = 1, ['.'] = 1, ['/'] = 1, [':'] = 1, [';'] = 1, ['<'] = 1,
|
|
['='] = 1, ['>'] = 1, ['?'] = 1, ['@'] = 1, ['['] = 1, ['\\']= 1,
|
|
[']'] = 1, ['^'] = 1, ['_'] = 1, ['`'] = 1, ['{'] = 1, ['|'] = 1,
|
|
['}'] = 1, ['~'] = 1,
|
|
};
|
|
|
|
int
|
|
ispunct(int c)
|
|
{
|
|
return _punct[(unsigned char)c];
|
|
}
|
|
|
|
int
|
|
isspace(int c)
|
|
{
|
|
return c == ' ' || c == '\f' || c == '\n' || c == '\t' || c == '\v' ||
|
|
c == '\r';
|
|
}
|
|
|
|
int
|
|
isxdigit(int c)
|
|
{
|
|
int d = tolower(c);
|
|
return isdigit(c) || (d >= 'a' && d <= 'f');
|
|
}
|
|
|
|
double
|
|
log(double x)
|
|
{
|
|
if (x < 1)
|
|
errx(-1, "no imp");
|
|
const double ln2 = 0.693147;
|
|
double ret = 0;
|
|
while (x >= 2) {
|
|
x /= 2;
|
|
ret += ln2;
|
|
}
|
|
if (x > 1)
|
|
ret += x - 1;
|
|
return ret;
|
|
}
|
|
|
|
dev_t
|
|
makedev(uint maj, uint min)
|
|
{
|
|
return (ulong)maj << 32 | min;
|
|
}
|
|
|
|
int
|
|
memcmp(const void *q, const void *p, size_t sz)
|
|
{
|
|
const long *e1 = (long *)q;
|
|
const long *e2 = (long *)p;
|
|
while (sz >= 8 && *e1 == *e2)
|
|
sz -= 8, e1++, e2++;
|
|
|
|
const char *s1 = (char *)e1;
|
|
const char *s2 = (char *)e2;
|
|
while (sz && *s1 == *s2)
|
|
sz--, s1++, s2++;
|
|
if (!sz)
|
|
return 0;
|
|
return *s1 - *s2;
|
|
}
|
|
|
|
void *
|
|
memcpy(void *dst, const void *src, size_t n)
|
|
{
|
|
return memmove(dst, src, n);
|
|
}
|
|
|
|
void *
|
|
memmove(void *dst, const void *src, size_t n)
|
|
{
|
|
char *d = dst;
|
|
const char *s = src;
|
|
if (d == s || n == 0)
|
|
return d;
|
|
if (d > s && d <= s + n) {
|
|
// copy backwards
|
|
s += n;
|
|
d += n;
|
|
if (n >= 8) {
|
|
long *dst8 = (long *)d;
|
|
const long *src8 = (const long *)s;
|
|
for (; n >= 8; n -= 8)
|
|
*--dst8 = *--src8;
|
|
d = (char *)dst8;
|
|
s = (const char *)src8;
|
|
}
|
|
while (n--)
|
|
*--d = *--s;
|
|
return d;
|
|
}
|
|
if (n >= 8) {
|
|
long *dst8 = dst;
|
|
const long *src8 = src;
|
|
for (; n >= 8; n -= 8)
|
|
*dst8++ = *src8++;
|
|
d = (char *)dst8;
|
|
s = (const char *)src8;
|
|
}
|
|
while (n--)
|
|
*d++ = *s++;
|
|
return dst;
|
|
}
|
|
|
|
void *
|
|
memset(void *d, int c, size_t n)
|
|
{
|
|
if (n == 0)
|
|
return d;
|
|
long *pl = d;
|
|
if (n >= 8) {
|
|
ulong c8 = (uchar)c;
|
|
c8 = c8 | c8 << 8;
|
|
c8 |= c8 << 16;
|
|
c8 |= c8 << 32;
|
|
for (; n >= 8; n -= 8)
|
|
*pl++ = c8;
|
|
}
|
|
char *pc = (char *)pl;
|
|
char v = (char)c;
|
|
while (n--)
|
|
*pc++ = v;
|
|
return d;
|
|
}
|
|
|
|
int
|
|
mkstemp(char *t)
|
|
{
|
|
// XXX
|
|
static uint seed;
|
|
if (seed == 0)
|
|
seed = (uint)time(NULL);
|
|
size_t l = strlen(t);
|
|
if (l < 6)
|
|
goto inval;
|
|
char *oe = t + l - 6;
|
|
if (strncmp(oe, "XXXXXX", 6) != 0)
|
|
goto inval;
|
|
int fails = 0;
|
|
for (;;) {
|
|
char *e = oe;
|
|
int i;
|
|
for (i = 0; i < 6; i++) {
|
|
uint n = rand_r(&seed) % 26*2;
|
|
if (n >= 26)
|
|
*e = 'A' + n - 26;
|
|
else
|
|
*e = 'a' + n;
|
|
e++;
|
|
}
|
|
int fd = open(t, O_RDWR | O_CREAT | O_EXCL, 0600);
|
|
if (fd != -1)
|
|
return fd;
|
|
else if (errno != EEXIST)
|
|
return -1;
|
|
else if (fails++ == 10)
|
|
return -1;
|
|
}
|
|
inval:
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
DIR *
|
|
fdopendir(int fd)
|
|
{
|
|
#define BSIZE 4096
|
|
|
|
struct stat st;
|
|
if (fstat(fd, &st) == -1)
|
|
return NULL;
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
errno = ENOTDIR;
|
|
return NULL;
|
|
}
|
|
// allocate enough space for all dirents; count them.
|
|
if (lseek(fd, 0, SEEK_SET) == -1)
|
|
return NULL;
|
|
// on disk format of directory entries
|
|
struct __attribute__((packed)) _dirent64_t {
|
|
ulong inode;
|
|
ulong offset;
|
|
ushort reclen;
|
|
u8 type;
|
|
char name[_POSIX_NAME_MAX];
|
|
};
|
|
size_t c = 0;
|
|
char buf[BSIZE];
|
|
ssize_t r;
|
|
while ((r = getdents64(fd, buf, sizeof(buf))) > 0) {
|
|
int i = 0;
|
|
while (i < r) {
|
|
struct _dirent64_t *de = (struct _dirent64_t *)(buf + i);
|
|
if (de->name[0] != '\0')
|
|
c++;
|
|
|
|
i += de->reclen;
|
|
}
|
|
}
|
|
if (r == -1)
|
|
return NULL;
|
|
if (c > (ULONG_MAX - sizeof(DIR))/sizeof(struct dirent))
|
|
errx(-1, "very fat dir");
|
|
DIR *ret = malloc(sizeof(DIR) + c*sizeof(struct dirent));
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
// copy dirents into ret
|
|
ret->fd = fd;
|
|
ret->nent = (int)c;
|
|
ret->cent = 0;
|
|
if (lseek(fd, 0, SEEK_SET) == -1)
|
|
goto out;
|
|
struct dirent *p = &ret->dents[0];
|
|
while ((r = getdents64(fd, buf, sizeof(buf))) > 0) {
|
|
int i = 0;
|
|
while (i < r) {
|
|
struct _dirent64_t *de = (struct _dirent64_t *)(buf + i);
|
|
if (de->name[0] != '\0') {
|
|
p->d_ino = de->inode;
|
|
strncpy(p->d_name, de->name,
|
|
sizeof(p->d_name));
|
|
p++;
|
|
}
|
|
i += de->reclen;
|
|
}
|
|
}
|
|
if (r == -1)
|
|
goto out;
|
|
return ret;
|
|
out:
|
|
free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
DIR *
|
|
opendir(const char *path)
|
|
{
|
|
int fd = open(path, O_RDONLY | O_DIRECTORY);
|
|
if (fd == -1)
|
|
return NULL;
|
|
return fdopendir(fd);
|
|
}
|
|
|
|
int
|
|
closedir(DIR *d)
|
|
{
|
|
int fd = d->fd;
|
|
free(d);
|
|
return close(fd);
|
|
}
|
|
|
|
struct dirent *
|
|
readdir(DIR *d)
|
|
{
|
|
static struct dirent _ret;
|
|
struct dirent *ret;
|
|
readdir_r(d, &_ret, &ret);
|
|
return ret;
|
|
}
|
|
|
|
// POSIX: returns error number, not -1
|
|
int
|
|
readdir_r(DIR *d, struct dirent *entry, struct dirent **ret)
|
|
{
|
|
if (d->cent == d->nent) {
|
|
*ret = NULL;
|
|
return 0;
|
|
}
|
|
struct dirent *src = &d->dents[d->cent++];
|
|
// avoid sse instructions generated
|
|
memcpy(entry, src, sizeof(struct dirent));
|
|
*ret = entry;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
rewinddir(DIR *d)
|
|
{
|
|
d->cent = 0;
|
|
}
|
|
|
|
struct {
|
|
const char *prefix;
|
|
int pid;
|
|
int ndelay;
|
|
int fac;
|
|
ulong mask;
|
|
} _slogopts = {.fac = LOG_USER, .mask = -1};
|
|
|
|
void
|
|
openlog(const char *ident, int logopt, int fac)
|
|
{
|
|
_slogopts.prefix = ident;
|
|
_slogopts.fac = fac;
|
|
if (logopt & LOG_PID)
|
|
_slogopts.pid = 1;
|
|
if (logopt & LOG_NDELAY)
|
|
_slogopts.ndelay = 1;
|
|
}
|
|
|
|
size_t
|
|
strlen(const char *msg)
|
|
{
|
|
size_t ret = 0;
|
|
while (*msg++)
|
|
ret++;
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
strcmp(const char *s1, const char *s2)
|
|
{
|
|
while (*s1 && *s1 == *s2)
|
|
s1++, s2++;
|
|
return *s1 - *s2;
|
|
}
|
|
|
|
int
|
|
strcoll(const char *s1, const char *s2)
|
|
{
|
|
return strcmp(s1, s2);
|
|
}
|
|
|
|
int
|
|
strncmp(const char *s1, const char *s2, size_t n)
|
|
{
|
|
while (n && *s1 && *s1 == *s2)
|
|
n--, s1++, s2++;
|
|
if (n == 0)
|
|
return 0;
|
|
return *s1 - *s2;
|
|
}
|
|
|
|
static void
|
|
pmsg(char *msg, long sz)
|
|
{
|
|
write(1, msg, sz);
|
|
}
|
|
|
|
static int
|
|
wc(char *p, char *end, char c)
|
|
{
|
|
if (p < end) {
|
|
*p = c;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static char
|
|
numtoch(char n)
|
|
{
|
|
char c = n;
|
|
if (n < 10)
|
|
c += '0';
|
|
else
|
|
c += 'a' - 10;
|
|
return c;
|
|
}
|
|
|
|
static int
|
|
putn(char *p, char *end, ulong n, int base)
|
|
{
|
|
if (n == 0) {
|
|
wc(p, end, '0');
|
|
return 1;
|
|
}
|
|
char buf[21];
|
|
int i = 0;
|
|
while (n) {
|
|
int left = n % base;
|
|
buf[i++] = numtoch(left);
|
|
n /= base;
|
|
}
|
|
int ret = i;
|
|
while (i--)
|
|
p += wc(p, end, buf[i]);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
_vprintf(const char *fmt, va_list ap, char *dst, char *end)
|
|
{
|
|
const char *start = dst;
|
|
char c;
|
|
|
|
c = *fmt;
|
|
while (c && dst < end) {
|
|
if (c != '%') {
|
|
dst += wc(dst, end, c);
|
|
fmt++;
|
|
c = *fmt;
|
|
continue;
|
|
}
|
|
|
|
fmt++;
|
|
int prehex = 0;
|
|
int done = 0;
|
|
int longmode = 0;
|
|
int sig = 1;
|
|
int precmode = 0;
|
|
int prec = 6;
|
|
while (!done && *fmt && !isspace(*fmt)) {
|
|
char t = *fmt;
|
|
fmt++;
|
|
if (isdigit(t) && precmode) {
|
|
char *nend;
|
|
long newprec = strtol(fmt - 1, &nend, 10);
|
|
if (newprec > 0 || t == '0') {
|
|
prec = newprec;
|
|
fmt = nend;
|
|
continue;
|
|
}
|
|
}
|
|
switch (t) {
|
|
case '%':
|
|
dst += wc(dst, end, '%');
|
|
done = 1;
|
|
break;
|
|
case '#':
|
|
prehex = 1;
|
|
break;
|
|
case '.':
|
|
precmode = 1;
|
|
break;
|
|
case 'z':
|
|
case 'l':
|
|
longmode = 1;
|
|
break;
|
|
case 'u':
|
|
sig = 0;
|
|
case 'd':
|
|
{
|
|
ulong n;
|
|
if (longmode)
|
|
n = va_arg(ap, ulong);
|
|
else
|
|
n = (ulong)(long)va_arg(ap, int);
|
|
if (sig && (long)n < 0) {
|
|
dst += wc(dst, end, '-');
|
|
n = -n;
|
|
}
|
|
dst += putn(dst, end, n, 10);
|
|
done = 1;
|
|
break;
|
|
}
|
|
case 'g':
|
|
case 'f':
|
|
{
|
|
double n;
|
|
// floats are promoted to double when used for
|
|
// a ... argument
|
|
n = va_arg(ap, double);
|
|
if (isnan(n)) {
|
|
dst += wc(dst, end, 'n');
|
|
dst += wc(dst, end, 'a');
|
|
dst += wc(dst, end, 'n');
|
|
done = 1;
|
|
break;
|
|
}
|
|
if (isinf(n)) {
|
|
dst += wc(dst, end, 'i');
|
|
dst += wc(dst, end, 'n');
|
|
dst += wc(dst, end, 'f');
|
|
done = 1;
|
|
break;
|
|
}
|
|
if (n < 0) {
|
|
dst += wc(dst, end, '-');
|
|
n = -n;
|
|
}
|
|
dst += putn(dst, end, (ulong)n, 10);
|
|
if (prec == 0) {
|
|
done = 1;
|
|
break;
|
|
}
|
|
dst += wc(dst, end, '.');
|
|
n -= (ulong)n;
|
|
for (; prec > 0; prec--) {
|
|
n *= 10;
|
|
dst += putn(dst, end, (ulong)n, 10);
|
|
n -= (ulong)n;
|
|
}
|
|
done = 1;
|
|
break;
|
|
}
|
|
case 'p':
|
|
longmode = 1;
|
|
prehex = 1;
|
|
case 'x':
|
|
{
|
|
if (prehex) {
|
|
dst += wc(dst, end, '0');
|
|
dst += wc(dst, end, 'x');
|
|
}
|
|
ulong n;
|
|
if (longmode)
|
|
n = va_arg(ap, ulong);
|
|
else
|
|
n = (ulong)(uint)va_arg(ap, int);
|
|
dst += putn(dst, end, n, 16);
|
|
done = 1;
|
|
break;
|
|
}
|
|
case 'c':
|
|
dst += wc(dst, end, (char)va_arg(ap, int));
|
|
done = 1;
|
|
break;
|
|
case 's':
|
|
{
|
|
char *s = va_arg(ap, char *);
|
|
if (!s) {
|
|
dst += wc(dst, end, '(');
|
|
dst += wc(dst, end, 'n');
|
|
dst += wc(dst, end, 'i');
|
|
dst += wc(dst, end, 'l');
|
|
dst += wc(dst, end, ')');
|
|
done = 1;
|
|
break;
|
|
}
|
|
while (*s && (!precmode || prec-- > 0))
|
|
dst += wc(dst, end, *s++);
|
|
done = 1;
|
|
break;
|
|
}
|
|
default:
|
|
//errx(-1, "unsupported printf format: %c\n", t);
|
|
dst += wc(dst, end, '?');
|
|
break;
|
|
}
|
|
}
|
|
c = *fmt;
|
|
prehex = 0;
|
|
}
|
|
|
|
if (dst > end)
|
|
dst = end - 1;
|
|
*dst = '\0';
|
|
return dst - start;
|
|
}
|
|
|
|
int
|
|
vsnprintf(char *dst, size_t sz, const char *fmt, va_list ap)
|
|
{
|
|
return _vprintf(fmt, ap, dst, dst + sz);
|
|
}
|
|
|
|
static int
|
|
_vscanf(const char *src, const char * const end, const char *fmt, va_list ap)
|
|
{
|
|
int ret = 0;
|
|
while (*fmt && src < end) {
|
|
if (isspace(*fmt)) {
|
|
fmt++;
|
|
while (isspace(*src))
|
|
src++;
|
|
continue;
|
|
}
|
|
if (*fmt != '%') {
|
|
if (*src != *fmt)
|
|
return ret;
|
|
fmt++;
|
|
src++;
|
|
continue;
|
|
}
|
|
fmt++;
|
|
int done = 0;
|
|
int longmode = 0;
|
|
int hexmode = 0;
|
|
while (!done) {
|
|
switch (*fmt) {
|
|
case '%':
|
|
if (*src != '%')
|
|
return ret;
|
|
src++;
|
|
done = 1;
|
|
break;
|
|
case 'l':
|
|
longmode = 1;
|
|
break;
|
|
case 'x':
|
|
hexmode = 1;
|
|
case 'u':
|
|
case 'd':
|
|
{
|
|
char *eptr;
|
|
long read = strtol(src, &eptr, hexmode ? 16 : 10);
|
|
if (src == eptr)
|
|
return ret;
|
|
if (longmode) {
|
|
long *p = va_arg(ap, long *);
|
|
*p = read;
|
|
} else {
|
|
int *p = va_arg(ap, int *);
|
|
*p = (int)read;
|
|
}
|
|
src = eptr;
|
|
done = 1;
|
|
ret++;
|
|
}
|
|
break;
|
|
case 'g':
|
|
case 'f':
|
|
{
|
|
char *eptr;
|
|
long double read = strtold(src, &eptr);
|
|
if (src == eptr)
|
|
return ret;
|
|
if (longmode) {
|
|
double *p = va_arg(ap, double *);
|
|
*p = (double)read;
|
|
} else {
|
|
float *p = va_arg(ap, float *);
|
|
*p = (float)read;
|
|
}
|
|
src = eptr;
|
|
done = 1;
|
|
ret++;
|
|
}
|
|
break;
|
|
default:
|
|
errx(-1, "unsupported scanf format: %c", *fmt);
|
|
}
|
|
fmt++;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
vsscanf(const char *src, const char *fmt, va_list ap)
|
|
{
|
|
const char * const end = strchr(src, '\0');
|
|
if (!end)
|
|
errx(-1, "no null");
|
|
return _vscanf(src, end, fmt, ap);
|
|
}
|
|
|
|
int
|
|
printf(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int ret;
|
|
|
|
va_start(ap, fmt);
|
|
ret = vfprintf(stdout, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
double
|
|
pow(double x, double y)
|
|
{
|
|
long exp = (long)y;
|
|
if (y - exp != 0)
|
|
errx(-1, "shitty pow(3) only accepts integer exponents");
|
|
if (exp == 0)
|
|
return 1;
|
|
int inv = 0;
|
|
if (exp < 0) {
|
|
exp = -exp;
|
|
inv = 1;
|
|
}
|
|
double ret = 1;
|
|
while (exp--)
|
|
ret *= x;
|
|
if (inv)
|
|
return 1.0/ret;
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
perror(const char *str)
|
|
{
|
|
char *e = strerror(errno);
|
|
if (str)
|
|
fprintf(stderr, "%s: %s\n", str, e);
|
|
else
|
|
fprintf(stderr, "%s\n", e);
|
|
}
|
|
|
|
static void _qsort(void *_base, size_t elms, size_t esz,
|
|
int (*cmp)(const void *, const void *), char *scratch, char *pivot)
|
|
{
|
|
if (elms <= 1)
|
|
return;
|
|
char *base = _base;
|
|
if (elms == 2) {
|
|
if (cmp(base, base+esz) > 0) {
|
|
memmove(pivot, base, esz);
|
|
memmove(base, base+esz, esz);
|
|
memmove(base+esz, pivot, esz);
|
|
}
|
|
return;
|
|
}
|
|
// the first element is the pivot
|
|
memmove(pivot, base, esz);
|
|
// sort to scratch
|
|
char *lh, *hh;
|
|
size_t l, h;
|
|
l = h = 0;
|
|
lh = scratch;
|
|
hh = (char *)scratch + (elms - 1)*esz;
|
|
size_t i;
|
|
char *cur = (char *)base + esz;
|
|
for (i = 1; i < elms; i++, cur += esz) {
|
|
int c = cmp(cur, pivot);
|
|
if (c < 0) {
|
|
memmove(lh, cur, esz);
|
|
lh += esz;
|
|
l++;
|
|
} else {
|
|
memmove(hh, cur, esz);
|
|
hh -= esz;
|
|
h++;
|
|
}
|
|
}
|
|
//copy scratch back to base
|
|
void *plow = base;
|
|
memmove(plow, scratch, l*esz);
|
|
|
|
void *piv = base + l*esz;
|
|
memmove(piv, pivot, esz);
|
|
|
|
void *phigh = piv + esz;
|
|
size_t tail = elms - h;
|
|
memmove(phigh, scratch + tail*esz, h*esz);
|
|
|
|
_qsort(plow, l, esz, cmp, scratch, pivot);
|
|
_qsort(phigh, h, esz, cmp, scratch, pivot);
|
|
}
|
|
|
|
void
|
|
qsort(void *base, size_t elms, size_t esz,
|
|
int (*cmp)(const void *, const void *))
|
|
{
|
|
if (elms < 0)
|
|
return;
|
|
// +1 for pivot
|
|
char *scratch = calloc(elms + 1, esz);
|
|
if (scratch == NULL)
|
|
errx(-1, "qsort calloc");
|
|
char *pivot = (char *)scratch + elms*esz;
|
|
_qsort(base, elms, esz, cmp, scratch, pivot);
|
|
free(scratch);
|
|
}
|
|
|
|
uint _seed1;
|
|
|
|
int
|
|
rand(void)
|
|
{
|
|
return rand_r(&_seed1);
|
|
}
|
|
|
|
int
|
|
rand_r(uint *seed)
|
|
{
|
|
*seed = *seed * 1103515245 + 12345;
|
|
return *seed & RAND_MAX;
|
|
}
|
|
|
|
void
|
|
srand(uint seed)
|
|
{
|
|
_seed1 = seed;
|
|
}
|
|
|
|
uint _seed2;
|
|
|
|
// POSIX requires that random(3)'s range is [0, 2**31)
|
|
long
|
|
random(void)
|
|
{
|
|
return rand_r(&_seed2);
|
|
}
|
|
|
|
void
|
|
srandom(uint seed)
|
|
{
|
|
_seed2 = seed;
|
|
}
|
|
|
|
int
|
|
sscanf(const char *src, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
int ret = vsscanf(src, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
ulong
|
|
rdtsc(void)
|
|
{
|
|
#if defined(__x86_64__)
|
|
ulong low, hi;
|
|
asm volatile(
|
|
"rdtsc\n"
|
|
: "=a"(low), "=d"(hi)
|
|
:
|
|
:);
|
|
return hi << 32 | low;
|
|
#else
|
|
// TODO: aarch64
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static char readlineb[256];
|
|
|
|
char *
|
|
readline(const char *prompt)
|
|
{
|
|
if (prompt)
|
|
printf("%s", prompt);
|
|
int ret;
|
|
int i = 0;
|
|
char c = 0x41;
|
|
// XXX
|
|
while ((ret = read(0, &c, 1)) > 0) {
|
|
if (c == '\n')
|
|
break;
|
|
if (c == '\b') {
|
|
if (--i < 0)
|
|
i = 0;
|
|
continue;
|
|
}
|
|
if (i < sizeof(readlineb) - 1)
|
|
readlineb[i++] = c;
|
|
}
|
|
if (ret < 0)
|
|
printf("readline: read failed: %d\n", ret);
|
|
if (ret == 0)
|
|
return NULL;
|
|
readlineb[i] = 0;
|
|
return readlineb;
|
|
}
|
|
|
|
int
|
|
setenv(const char *k, const char *v, int overwrite)
|
|
{
|
|
printf("setenv: no environment yet\n");
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static inline int _fdisset(int fd, fd_set *fds)
|
|
{
|
|
if (fds)
|
|
return FD_ISSET(fd, fds);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
select(int maxfd, fd_set *rfds, fd_set *wfds, fd_set *efds,
|
|
struct timeval *timeout)
|
|
{
|
|
int nfds = 0;
|
|
int i;
|
|
for (i = 0; i < maxfd; i++)
|
|
if (_fdisset(i, rfds) || _fdisset(i, wfds) ||
|
|
_fdisset(i, efds))
|
|
nfds++;
|
|
struct pollfd pfds[nfds];
|
|
int curp = 0;
|
|
for (i = 0; i < maxfd; i++) {
|
|
if ((_fdisset(i, rfds) || _fdisset(i, wfds) ||
|
|
_fdisset(i, efds)) == 0)
|
|
continue;
|
|
pfds[curp].fd = i;
|
|
pfds[curp].events = 0;
|
|
if (_fdisset(i, rfds))
|
|
pfds[curp].events |= POLLIN;
|
|
if (_fdisset(i, wfds))
|
|
pfds[curp].events |= POLLOUT;
|
|
if (_fdisset(i, efds))
|
|
pfds[curp].events |= POLLERR;
|
|
curp++;
|
|
}
|
|
int toms = -1;
|
|
if (timeout)
|
|
toms = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
|
|
|
|
// account for differences between select(2) and poll(2):
|
|
// 1. poll's HUP counts as a read-ready fd for select
|
|
// 2. an invalid fd is an error for select
|
|
// 3. select returns the number of bits set in the fdsets, not the
|
|
// number of ready fds.
|
|
if (rfds)
|
|
FD_ZERO(rfds);
|
|
if (wfds)
|
|
FD_ZERO(wfds);
|
|
if (efds)
|
|
FD_ZERO(efds);
|
|
int ret = poll(pfds, nfds, toms);
|
|
if (ret <= 0)
|
|
return ret;
|
|
|
|
int selret = 0;
|
|
for (i = 0; i < nfds; i++) {
|
|
if (pfds[i].events & POLLNVAL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
#define _PTOS(pf, fds) do { \
|
|
if ((pfds[i].revents & pf) && \
|
|
pfds[i].events & pf) { \
|
|
FD_SET(pfds[i].fd, fds); \
|
|
selret++; \
|
|
} \
|
|
} while (0)
|
|
|
|
_PTOS(POLLIN, rfds);
|
|
_PTOS(POLLHUP, rfds);
|
|
_PTOS(POLLOUT, wfds);
|
|
_PTOS(POLLERR, efds);
|
|
#undef _PTOS
|
|
}
|
|
return selret;
|
|
}
|
|
|
|
char *
|
|
setlocale(int cat, const char *loc)
|
|
{
|
|
// null loc to fetch current locale
|
|
if (!loc || strcmp(loc, "") == 0 || strcmp(loc, "C") == 0 ||
|
|
strcmp(loc, "POSIX") == 0)
|
|
return "C";
|
|
errx(-1, "no fancy locale support");
|
|
}
|
|
|
|
uint
|
|
sleep(uint s)
|
|
{
|
|
struct timespec t = {s, 0};
|
|
struct timespec r = {0, 0};
|
|
int ret = nanosleep(&t, &r);
|
|
if (ret)
|
|
return (uint)r.tv_sec;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
snprintf(char *dst, size_t sz, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int ret;
|
|
|
|
va_start(ap, fmt);
|
|
ret = vsnprintf(dst, sz, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
sprintf(char *dst, const char *fmt, ...)
|
|
{
|
|
size_t dur = (uintptr_t)-1 - (uintptr_t)dst;
|
|
|
|
va_list ap;
|
|
int ret;
|
|
|
|
va_start(ap, fmt);
|
|
ret = vsnprintf(dst, dur, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
strcasecmp(const char *s1, const char *s2)
|
|
{
|
|
return strncasecmp(s1, s2, ULONG_MAX);
|
|
}
|
|
|
|
int
|
|
strncasecmp(const char *s1, const char *s2, size_t n)
|
|
{
|
|
uint a = tolower(*s1++);
|
|
uint b = tolower(*s2++);
|
|
while (n && a && a == b) {
|
|
n--;
|
|
a = tolower(*s1++);
|
|
b = tolower(*s2++);
|
|
}
|
|
if (n == 0)
|
|
return 0;
|
|
return a - b;
|
|
}
|
|
|
|
char *
|
|
strchr(const char *big, int l)
|
|
{
|
|
for (; *big; big++)
|
|
if (*big == l)
|
|
return (char *)big;
|
|
if (l == '\0')
|
|
return (char *)big;
|
|
return NULL;
|
|
}
|
|
|
|
char *
|
|
strdup(const char *orig)
|
|
{
|
|
size_t l = strlen(orig) + 1;
|
|
char *ret = malloc(l);
|
|
if (!l) {
|
|
errno = ENOMEM;
|
|
return NULL;
|
|
}
|
|
memmove(ret, orig, l);
|
|
return ret;
|
|
}
|
|
|
|
static const char * const _errstr[] = {
|
|
[EPERM] = "Operation not permitted",
|
|
[ENOENT] = "No such file or directory",
|
|
[ESRCH] = "No such process",
|
|
[EINTR] = "Interrupted system call",
|
|
[EIO] = "Input/output error",
|
|
[E2BIG] = "Argument list too long",
|
|
[EBADF] = "Bad file descriptor",
|
|
[EAGAIN] = "Resource temporarily unavailable",
|
|
[ECHILD] = "No child processes",
|
|
[ENOMEM] = "Out of memory",
|
|
[EACCES] = "Permission denied",
|
|
[EFAULT] = "Bad address",
|
|
[EBUSY] = "Device busy",
|
|
[EEXIST] = "File exists",
|
|
[EXDEV] = "Cross device link",
|
|
[ENODEV] = "Operation not supported by device",
|
|
[ENOTDIR] = "Not a directory",
|
|
[EISDIR] = "Is a directory",
|
|
[EINVAL] = "Invalid argument",
|
|
[ENFILE] = "Too many open files in system",
|
|
[EMFILE] = "Too many open files",
|
|
[ENOSPC] = "No space left on device",
|
|
[ESPIPE] = "Illegal seek",
|
|
[EPIPE] = "Broken pipe",
|
|
[ERANGE] = "Result too large",
|
|
[ENAMETOOLONG] = "File name too long",
|
|
[ENOSYS] = "Function not implemented",
|
|
[ENOTEMPTY] = "Directory not empty",
|
|
[EDESTADDRREQ] = "Destination address required",
|
|
[EAFNOSUPPORT] = "Protocol family not supported",
|
|
[EADDRINUSE] = "Address already in use",
|
|
[EADDRNOTAVAIL] = "Can't assign requested address",
|
|
[ENETDOWN] = "Network is down",
|
|
[ENETUNREACH] = "Network is unreachable",
|
|
[ECONNABORTED] = "Software caused connection abort",
|
|
[ELOOP] = "Too many levels of symbolic links",
|
|
[EHOSTDOWN] = "Host is down",
|
|
[EHOSTUNREACH] = "No route to host",
|
|
[EOVERFLOW] = "Value too large to be stored in data type",
|
|
[ENOTSOCK] = "Socket operation on non-socket",
|
|
[EOPNOTSUPP] = "Operation not supported",
|
|
[EISCONN] = "Socket is already connected",
|
|
[ENOTCONN] = "Socket is not connected",
|
|
[ETIMEDOUT] = "Operation timed out",
|
|
[ECONNRESET] = "Connection reset by peer",
|
|
[ECONNREFUSED] = "Connection refused",
|
|
[EINPROGRESS] = "Operation now in progress",
|
|
};
|
|
|
|
char *
|
|
strerror(int errnum)
|
|
{
|
|
int nents = sizeof(_errstr)/sizeof(_errstr[0]);
|
|
const char *p = "Unknown error";
|
|
if (errnum >= 0 && errnum < nents && _errstr[errnum] != NULL)
|
|
p = _errstr[errnum];
|
|
return (char *)p;
|
|
}
|
|
|
|
int
|
|
strerror_r(int errnum, char *dst, size_t dstlen)
|
|
{
|
|
int nents = sizeof(_errstr)/sizeof(_errstr[0]);
|
|
const char *p = "Unknown error";
|
|
if (errnum >= 0 && errnum < nents && _errstr[errnum] != NULL)
|
|
p = _errstr[errnum];
|
|
strncpy(dst, p, dstlen);
|
|
return 0;
|
|
}
|
|
|
|
char *
|
|
strncpy(char *dst, const char *src, size_t sz)
|
|
{
|
|
snprintf(dst, sz, "%s", src);
|
|
return dst;
|
|
}
|
|
|
|
char *
|
|
strstr(const char *big, const char *little)
|
|
{
|
|
while (*big) {
|
|
if (*big == *little) {
|
|
const char *guess = big;
|
|
const char *l = little;
|
|
while (*big) {
|
|
if (*l == 0)
|
|
return (char *)guess;
|
|
if (*big != *l)
|
|
break;
|
|
big++;
|
|
l++;
|
|
}
|
|
if (*big == 0 && *l == 0)
|
|
return (char *)guess;
|
|
} else
|
|
big++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
syslog(int priority, const char *msg, ...)
|
|
{
|
|
if (((priority & LOG_ALL) & _slogopts.mask) == 0)
|
|
return;
|
|
va_list ap;
|
|
va_start(ap, msg);
|
|
|
|
const char *pref = _slogopts.prefix ? _slogopts.prefix : "";
|
|
char lbuf[2048];
|
|
if (_slogopts.pid)
|
|
snprintf(lbuf, sizeof(lbuf), "syslog (pid %ld): %s: %s",
|
|
getpid(), pref, msg);
|
|
else
|
|
snprintf(lbuf, sizeof(lbuf), "syslog: %s: %s", pref, msg);
|
|
vfprintf(stderr, lbuf, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
static int _isoct(int c)
|
|
{
|
|
return c >= '0' && c <= '7';
|
|
}
|
|
|
|
long
|
|
strtol(const char *n, char **endptr, int base)
|
|
{
|
|
// gobble spaces, then "+" or "-", and finally "0x" or "0"
|
|
while (isspace(*n))
|
|
n++;
|
|
int sign = 1;
|
|
if (*n == '+')
|
|
n++;
|
|
else if (*n == '-') {
|
|
sign = -1;
|
|
n++;
|
|
}
|
|
|
|
int haspre = strcmp(n, "0x") == 0;
|
|
int (*matcher)(int);
|
|
if (base == 16 || (base == 0 && haspre)) {
|
|
base = 16;
|
|
if (haspre)
|
|
n += 2;
|
|
matcher = isxdigit;
|
|
} else if (base == 8 || (base == 0 && *n == '0')) {
|
|
base = 8;
|
|
if (*n == '0')
|
|
n++;
|
|
matcher = _isoct;
|
|
} else if (base == 0 || base == 10) {
|
|
base = 10;
|
|
matcher = isdigit;
|
|
} else {
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
long tot = 0;
|
|
while (matcher(*n)) {
|
|
int c = tolower(*n++);
|
|
tot = tot*base;
|
|
if (isalpha(c))
|
|
tot += c - 'a' + 10;
|
|
else
|
|
tot += c - '0';
|
|
}
|
|
if (endptr)
|
|
*endptr = (char *)n;
|
|
return tot*sign;
|
|
}
|
|
|
|
double
|
|
strtod(const char *s, char **end)
|
|
{
|
|
return (double)strtold(s, end);
|
|
}
|
|
|
|
long double
|
|
strtold(const char *s, char **end)
|
|
{
|
|
char *tend;
|
|
long t = strtol(s, &tend, 10);
|
|
// tend points to '.' or [Ee]
|
|
long double ret = (long double)t;
|
|
if (*tend == '.') {
|
|
tend++;
|
|
char *fend;
|
|
long frac = strtol(tend, &fend, 10);
|
|
long digs = fend - tend;
|
|
if (digs) {
|
|
long double addend = (long double)frac/(pow(10, digs));
|
|
if (ret < 0)
|
|
ret -= addend;
|
|
else
|
|
ret += addend;
|
|
tend = fend;
|
|
}
|
|
}
|
|
if (tolower(*tend) == 'e') {
|
|
tend++;
|
|
char *eend;
|
|
long exp = strtol(tend, &eend, 10);
|
|
if (eend - tend > 0) {
|
|
ret *= pow(10, exp);
|
|
tend = eend;
|
|
}
|
|
}
|
|
if (end)
|
|
*end = tend;
|
|
return ret;
|
|
}
|
|
|
|
long long
|
|
strtoll(const char *n, char **endptr, int base)
|
|
{
|
|
return strtol(n, endptr, base);
|
|
}
|
|
|
|
ulong
|
|
strtoul(const char *n, char **endptr, int base)
|
|
{
|
|
long ret = strtol(n, endptr, base);
|
|
return (ulong)ret;
|
|
}
|
|
|
|
unsigned long long
|
|
strtoull(const char *n, char **endptr, int base)
|
|
{
|
|
return strtoull(n, endptr, base);
|
|
}
|
|
|
|
time_t
|
|
time(time_t *tloc)
|
|
{
|
|
struct timeval tv;
|
|
if (gettimeofday(&tv, NULL))
|
|
return -1;
|
|
if (tloc)
|
|
*tloc = tv.tv_sec;
|
|
return tv.tv_sec;
|
|
}
|
|
|
|
int
|
|
tolower(int c)
|
|
{
|
|
if (!isalpha(c))
|
|
return c;
|
|
return c | 0x20;
|
|
}
|
|
|
|
int
|
|
toupper(int c)
|
|
{
|
|
if (!isalpha(c))
|
|
return c;
|
|
return c & ~0x20;
|
|
}
|
|
|
|
double
|
|
trunc(double x)
|
|
{
|
|
return (long)x;
|
|
}
|
|
|
|
int
|
|
vprintf(const char *fmt, va_list ap)
|
|
{
|
|
char lbuf[2048];
|
|
|
|
if (strlen(fmt) >= sizeof(lbuf))
|
|
printf("warning: fmt too long\n");
|
|
|
|
int ret;
|
|
ret = vsnprintf(lbuf, sizeof(lbuf), fmt, ap);
|
|
pmsg(lbuf, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct utsname _unamed = {
|
|
.sysname = "BiscuitOS",
|
|
.nodename = "localhost",
|
|
.release = BISCUIT_RELEASE,
|
|
.version = BISCUIT_VERSION,
|
|
.machine = "amd64",
|
|
};
|
|
|
|
int
|
|
uname(struct utsname *name)
|
|
{
|
|
memmove(name, &_unamed, sizeof(struct utsname));
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
usleep(uint us)
|
|
{
|
|
long ns = (us % 1000000)*1000;
|
|
struct timespec t = {us/1000000, ns};
|
|
return nanosleep(&t, NULL);
|
|
}
|
|
|
|
int
|
|
vfprintf(FILE *f, const char *fmt, va_list ap)
|
|
{
|
|
char lbuf[2048];
|
|
|
|
if (strlen(fmt) >= sizeof(lbuf))
|
|
printf("warning: fmt too long\n");
|
|
|
|
int ret;
|
|
ret = vsnprintf(lbuf, sizeof(lbuf), fmt, ap);
|
|
int wrote = fwrite(lbuf, ret, 1, f);
|
|
|
|
return wrote;
|
|
}
|
|
|
|
#define _DLMALLOC 0
|
|
|
|
#if _DLMALLOC
|
|
|
|
#include <../dlmalloc.c>
|
|
|
|
#else
|
|
|
|
static volatile long biglock;
|
|
static void acquire(void)
|
|
{
|
|
if (!dolock)
|
|
return;
|
|
while (__sync_lock_test_and_set(&biglock, 1) != 0)
|
|
;
|
|
}
|
|
|
|
static void release(void)
|
|
{
|
|
asm volatile("":::"memory");
|
|
biglock = 0;
|
|
}
|
|
|
|
struct header_t {
|
|
char *start;
|
|
char *end;
|
|
ulong objs;
|
|
size_t maxsz;
|
|
struct header_t *next;
|
|
struct header_t *prev;
|
|
};
|
|
|
|
static struct header_t *allh;
|
|
static struct header_t *curh;
|
|
static char *bump;
|
|
|
|
static void
|
|
_free_header(struct header_t *ch)
|
|
{
|
|
if (ch->prev)
|
|
ch->prev->next = ch->next;
|
|
else
|
|
allh = ch->next;
|
|
if (ch->next)
|
|
ch->next->prev = ch->prev;
|
|
int ret;
|
|
if ((ret = munmap(ch->start, ch->end - ch->start)) < 0)
|
|
err(ret, "munmap");
|
|
}
|
|
|
|
static void *
|
|
_malloc(size_t sz)
|
|
{
|
|
// Minimum allocation size = 8 bytes.
|
|
sz = (sz + 7) & ~7;
|
|
int newhdr = 0;
|
|
if (curh) {
|
|
size_t left = curh->end - bump;
|
|
newhdr = sz > left;
|
|
} else {
|
|
newhdr = 1;
|
|
}
|
|
if (newhdr) {
|
|
if (curh && curh->objs == 0) {
|
|
_free_header(curh);
|
|
curh = NULL;
|
|
}
|
|
const size_t pgsize = 1 << 12;
|
|
// Also account for the header that we embed within the
|
|
// allocated space.
|
|
size_t mmapsz = (sz + sizeof(struct header_t) + pgsize - 1) &
|
|
~(pgsize - 1);
|
|
if (mmapsz < sz) {
|
|
errno = EOVERFLOW;
|
|
return NULL;
|
|
}
|
|
struct header_t *nh = mmap(NULL, mmapsz, PROT_READ | PROT_WRITE,
|
|
MAP_ANON | MAP_PRIVATE, -1, 0);
|
|
if (nh == MAP_FAILED) {
|
|
errno = ENOMEM;
|
|
return NULL;
|
|
}
|
|
nh->start = (char *)nh;
|
|
nh->end = nh->start + mmapsz;
|
|
nh->objs = 0;
|
|
nh->next = allh;
|
|
nh->prev = NULL;
|
|
if (nh->next)
|
|
nh->next->prev = nh;
|
|
nh->maxsz = 0;
|
|
allh = nh;
|
|
|
|
curh = nh;
|
|
bump = curh->start + sizeof(struct header_t);
|
|
}
|
|
curh->objs++;
|
|
char *ret = bump;
|
|
bump += sz;
|
|
if (sz > curh->maxsz)
|
|
curh->maxsz = sz;
|
|
return ret;
|
|
}
|
|
|
|
#define ZEROPTR ((void *)1)
|
|
|
|
void *
|
|
malloc(size_t sz)
|
|
{
|
|
// try to catch accesses to zero-length objects. is this ok?
|
|
if (sz == 0)
|
|
return ZEROPTR;
|
|
acquire();
|
|
void *ret = _malloc(sz);
|
|
release();
|
|
|
|
return ret;
|
|
}
|
|
|
|
void *
|
|
calloc(size_t n, size_t sz)
|
|
{
|
|
if (n == 0 || sz == 0)
|
|
return ZEROPTR;
|
|
if (n > ULONG_MAX / sz) {
|
|
errno = EOVERFLOW;
|
|
return NULL;
|
|
}
|
|
size_t big = sz*n;
|
|
void *ret = malloc(big);
|
|
if (!ret)
|
|
return NULL;
|
|
memset(ret, 0, big);
|
|
return ret;
|
|
}
|
|
|
|
static struct header_t *
|
|
_findseg(void *pp)
|
|
{
|
|
char *p = (char *)pp;
|
|
struct header_t *ch;
|
|
for (ch = allh; ch; ch = ch->next)
|
|
if (ch->start <= p && ch->end > p)
|
|
break;
|
|
return ch;
|
|
}
|
|
|
|
static void
|
|
_free(void *pp)
|
|
{
|
|
// find containing seg
|
|
struct header_t *ch;
|
|
ch = _findseg(pp);
|
|
if (!ch)
|
|
errx(-1, "free: bad pointer");
|
|
ch->objs--;
|
|
if (ch->objs == 0) {
|
|
if (ch == curh)
|
|
return;
|
|
_free_header(ch);
|
|
}
|
|
}
|
|
|
|
void
|
|
free(void *pp)
|
|
{
|
|
acquire();
|
|
_free(pp);
|
|
release();
|
|
}
|
|
|
|
void *
|
|
realloc(void *vold, size_t nsz)
|
|
{
|
|
char *old = vold;
|
|
if (!old)
|
|
return malloc(nsz);
|
|
|
|
acquire();
|
|
|
|
void *ret = old;
|
|
struct header_t *ch = _findseg(old);
|
|
// we don't know the exact size of the object, but its size is bounded
|
|
// as follows
|
|
size_t oldsz = MIN(ch->maxsz, ch->end - old);
|
|
ret = _malloc(nsz);
|
|
if (ret == NULL)
|
|
goto out;
|
|
memmove(ret, old, MIN(oldsz, nsz));
|
|
_free(old);
|
|
out:
|
|
release();
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
char __progname[64];
|
|
char **environ = _environ;
|
|
|
|
void
|
|
__start(int argc, char **argv, struct kinfo_t *k)
|
|
{
|
|
kinfo = k;
|
|
|
|
if (argc)
|
|
strncpy(__progname, argv[0], sizeof(__progname));
|
|
fdinit();
|
|
int main(int, char **);
|
|
int ret = main(argc, argv);
|
|
exit(ret);
|
|
}
|
|
|
|
void
|
|
_start(void)
|
|
{
|
|
#if defined(__x86_64__)
|
|
// make sure that the stack is 16-byte aligned, as gcc assumes, after
|
|
// _start's function prologue. gcc emits SSE instructions that require
|
|
// 16-byte alignment (misalignment generates #GP).
|
|
asm(
|
|
"movq (%%rsp), %%rdi\n" // argc
|
|
"leaq 8(%%rsp), %%rsi\n" // argv
|
|
"andq $0xfffffffffffffff0, %%rsp\n"
|
|
"subq $8, %%rsp\n"
|
|
"movabs $__start, %%rax\n"
|
|
"jmpq *%%rax\n"
|
|
::: "memory", "cc");
|
|
#elif defined(__aarch64__)
|
|
asm(
|
|
"ldr x0, [sp]\n" // argc
|
|
"add x1, sp, #8\n" // argv
|
|
"bl __start\n"
|
|
::: "memory", "cc");
|
|
#endif
|
|
}
|
|
|
|
/* NGINX STUFF */
|
|
long timezone;
|
|
|
|
#define FAIL errx(-1, "%s -- no imp", __FUNCTION__)
|
|
#define HACK(x) do { \
|
|
fprintf(stderr, "warning: %s is a no-op\n", __FUNCTION__); \
|
|
return x; \
|
|
} while (0)
|
|
|
|
char *
|
|
getenv(char *name)
|
|
{
|
|
HACK(NULL);
|
|
}
|
|
|
|
uid_t
|
|
geteuid(void)
|
|
{
|
|
HACK(666);
|
|
}
|
|
|
|
struct passwd *
|
|
getpwnam(const char *a)
|
|
{
|
|
if (strcmp(a, "nobody") != 0)
|
|
errx(-1, "unexpected getpwnam");
|
|
|
|
static struct passwd dur = {"nobody", 666, 667, "/tmp",
|
|
"/bin/cmd.exe"};
|
|
HACK(&dur);
|
|
}
|
|
|
|
struct group *
|
|
getgrnam(const char *a)
|
|
{
|
|
if (strcmp(a, "nogroup") != 0)
|
|
errx(-1, "unexpected getpwnam");
|
|
|
|
static struct group dur = {"nogroup", 667, NULL};
|
|
HACK(&dur);
|
|
}
|
|
|
|
struct hostent *
|
|
gethostbyname(const char *a)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
int
|
|
chown(const char *a, uid_t b, gid_t c)
|
|
{
|
|
if (b != 666 || c != -1)
|
|
errx(-1, "unexpected chown");
|
|
errno = 0;
|
|
HACK(0);
|
|
}
|
|
|
|
time_t
|
|
mktime(struct tm *a)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
int
|
|
gethostname(char *a, size_t b)
|
|
{
|
|
snprintf(a, b, "localhost");
|
|
HACK(0);
|
|
}
|
|
|
|
char *
|
|
strpbrk(const char *a, const char *bb)
|
|
{
|
|
while (*a) {
|
|
const char *b = bb;
|
|
while (*b)
|
|
if (*a == *b++)
|
|
return (char *)a;
|
|
a++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
setitimer(int a, struct itimerval *b, struct itimerval *c)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
struct tm *
|
|
localtime(const time_t *a)
|
|
{
|
|
static struct tm dur;
|
|
struct timeval tv;
|
|
if (gettimeofday(&tv, NULL) == -1)
|
|
err(-1, "localtime/gettimeofday");
|
|
dur.tm_hour = tv.tv_sec / (60*60);
|
|
dur.tm_min = tv.tv_sec / 60;
|
|
dur.tm_sec = tv.tv_sec % 60;
|
|
return &dur;
|
|
}
|
|
|
|
struct tm *
|
|
gmtime(const time_t *a)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
int
|
|
utimes(const char *a, const struct timeval b[2])
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
int
|
|
glob(const char *a, int b, int (*c)(const char *, int), glob_t *d)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
void
|
|
globfree(glob_t *a)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
int
|
|
ioctl(int fd, ulong req, ...)
|
|
{
|
|
if (req != FIOASYNC)
|
|
errx(-1, "nyet");
|
|
HACK(0);
|
|
}
|
|
|
|
int
|
|
raise(int a)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
mode_t
|
|
umask(mode_t a)
|
|
{
|
|
static mode_t dur = 0022;
|
|
mode_t old = dur;
|
|
dur = a;
|
|
HACK(old);
|
|
}
|
|
|
|
int
|
|
getpagesize(void)
|
|
{
|
|
return 1 << 12;
|
|
}
|
|
|
|
int
|
|
sigprocmask(int a, sigset_t *b, sigset_t *c)
|
|
{
|
|
if (c)
|
|
errx(-1, "no old set");
|
|
HACK(0);
|
|
}
|
|
|
|
int
|
|
sigsuspend(const sigset_t *a)
|
|
{
|
|
fprintf(stderr, "warning: no signals, thus sleep forever...\n");
|
|
pause();
|
|
errx(-1, "sigsuspend no imp");
|
|
}
|
|
|
|
int
|
|
setpriority(int a, int b, int c)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
uid_t
|
|
getuid(void)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
int
|
|
setuid(uid_t a)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
int
|
|
setgid(gid_t a)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
int
|
|
initgroups(const char *a, gid_t b)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
char *
|
|
realpath(const char *a, char *b)
|
|
{
|
|
FAIL;
|
|
}
|
|
|
|
int
|
|
lstat(const char *a, struct stat *b)
|
|
{
|
|
// will some applications break because they expect lstat to be a
|
|
// system call?
|
|
// XXX yes, if it has maximum open fds already
|
|
int fd = open(a, O_RDONLY);
|
|
if (fd == -1)
|
|
return -1;
|
|
int ret = fstat(fd, b);
|
|
if (close(fd) == -1)
|
|
fprintf(stderr, "lstat close failed\n");
|
|
return ret;
|
|
}
|
|
|
|
/* LMBENCH STUFF */
|
|
unsigned int
|
|
alarm(unsigned int sec)
|
|
{
|
|
HACK(0);
|
|
}
|
|
|
|
#if 0
|
|
#include <../bloat.c>
|
|
#endif
|