From 582eb39df61f7814c7854f2107891bf379a0ff94 Mon Sep 17 00:00:00 2001 From: Mike Mestnik Date: Sun, 17 May 2015 21:57:12 -0500 Subject: [PATCH] atratus-0.4.tar.gz --- ANNOUNCE | 8 + etc/passwd | 1 + include/linux-defines.h | 152 ++++- lib/Makefile | 2 + lib/c.c | 1084 ++++++++++++++++++++++++++++++----- lib/loader.c | 114 ++-- lib/loader.h | 2 + server/Makefile | 1 + server/atratus.c | 497 ++++++++++++---- server/debug.h | 6 + server/dev.c | 7 +- server/filp.h | 47 +- server/pipe.c | 55 +- server/process.h | 3 + server/tty.c | 716 +++++------------------ server/tty.h | 27 +- server/vt100.c | 709 +++++++++++++++++++++++ server/vt100.h | 6 + server/winfs.c | 74 ++- tests/dynamic/.gitignore | 8 + tests/dynamic/Makefile | 10 +- tests/dynamic/args.c | 7 +- tests/dynamic/ctype.c | 111 ++++ tests/dynamic/dir.c | 9 +- tests/dynamic/fork.c | 29 + tests/dynamic/jmpbuf.c | 40 ++ tests/dynamic/pipe.c | 36 ++ tests/dynamic/qsort.c | 37 ++ tests/dynamic/read.c | 74 +++ tests/dynamic/stat.c | 21 + tests/dynamic/stdio.c | 24 + tests/dynamic/string.c | 62 +- tests/dynamic/vtplay.c | 39 ++ tests/static/.gitignore | 4 + tests/static/Makefile | 4 + tests/static/dir.c | 37 +- tests/static/gettimeofday.c | 80 +++ tests/static/select.c | 179 ++++++ tests/static/stat.c | 163 ++++++ tests/static/stat64.c | 184 ++++++ 40 files changed, 3703 insertions(+), 966 deletions(-) create mode 100644 etc/passwd create mode 100644 server/vt100.c create mode 100644 server/vt100.h create mode 100644 tests/dynamic/ctype.c create mode 100644 tests/dynamic/fork.c create mode 100644 tests/dynamic/jmpbuf.c create mode 100644 tests/dynamic/pipe.c create mode 100644 tests/dynamic/qsort.c create mode 100644 tests/dynamic/read.c create mode 100644 tests/dynamic/stat.c create mode 100644 tests/dynamic/vtplay.c create mode 100644 tests/static/gettimeofday.c create mode 100644 tests/static/select.c create mode 100644 tests/static/stat.c create mode 100644 tests/static/stat64.c diff --git a/ANNOUNCE b/ANNOUNCE index 0794dc9..4ad2fc6 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -1,5 +1,13 @@ Announcement & Release notes +Fri Dec 14 09:27:28 EST 2012 +0.4 - December release + + - vitetris gameplay works + - busybox's vi editing work + - busybox's ash can start commands + - dash shows prompt + Sun Nov 18 08:22:23 EST 2012 0.3 - November release diff --git a/etc/passwd b/etc/passwd new file mode 100644 index 0000000..14a2d4d --- /dev/null +++ b/etc/passwd @@ -0,0 +1 @@ +atratus:x:1000:1000:Atratus,,,:/:/bin/sh diff --git a/include/linux-defines.h b/include/linux-defines.h index 9d6b769..51056d8 100644 --- a/include/linux-defines.h +++ b/include/linux-defines.h @@ -1,6 +1,15 @@ +#ifndef __LINUX_ABI_H__ +#define __LINUX_ABI_H__ + +#ifndef PACKED +#define PACKED __attribute__((__packed__)) +#endif + /* _l_ prefix is for Linux kernel ABI stuff */ #define _l_MAP_FAILED ((void*)-1) +#define _l_FD_SETSIZE 1024 + #define _l_MAP_SHARED 1 #define _l_MAP_PRIVATE 2 #define _l_MAP_FIXED 0x10 @@ -20,12 +29,149 @@ #define _l_SIGCHLD (17) -#define _l_POLLIN 1 -#define _l_POLLOUT 4 -#define _l_POLLERR 8 +#define _l_POLLIN (1 << 0) +#define _l_POLLPRI (1 << 1) +#define _l_POLLOUT (1 << 2) +#define _l_POLLERR (1 << 3) +#define _l_POLLHUP (1 << 4) #define _l_F_DUPFD 0 #define _l_F_GETFD 1 #define _l_F_SETFD 2 #define _l_SIGABRT 6 + +#define _l_DT_DIR 4 +#define _l_DT_REG 8 +#define _l_DT_LNK 10 + +#define TIOCGPGRP 0x540F +#define TIOCSPGRP 0x5410 +#define TIOCGWINSZ 0x5413 + +#define TIOCG 0x5401 +#define TIOCS 0x5402 + +#define VERASE 2 +#define VEOF 4 + +#define IGNBRK (1 << 0) +#define BRKINT (1 << 1) +#define IGNPAR (1 << 2) +#define PARMRK (1 << 3) +#define INPCK (1 << 4) +#define ISTRIP (1 << 5) +#define INLCR (1 << 6) +#define ICRNL (1 << 8) +#define IUCLC (1 << 9) +#define IXON (1 << 10) +#define IXANY (1 << 11) +#define IXOFF (1 << 12) +#define IMAXBEL (1 << 13) +#define IUTF8 (1 << 14) + +#define ISIG (1 << 0) +#define ICANON (1 << 1) +#define XCASE (1 << 2) +#define ECHO (1 << 3) +#define ECHOE (1 << 4) +#define ECHOK (1 << 5) +#define ECHONL (1 << 6) +#define NOFLSH (1 << 7) + +#define OPOST (1 << 0) +#define OLCUC (1 << 1) +#define ONLCR (1 << 2) +#define OCRNL (1 << 3) +#define ONOCR (1 << 4) +#define ONLRET (1 << 5) + +#define NCCS 32 + +struct termios +{ + unsigned int c_iflag; + unsigned int c_oflag; + unsigned int c_cflag; + unsigned int c_lflag; + unsigned char c_line; + unsigned char c_cc[NCCS]; + unsigned int c_ispeed; + unsigned int c_ospeed; +}; + +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +struct fdset +{ + unsigned long fds_bits[1024/32]; +}; + +struct stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned long st_rdev; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long st_atime_nsec; + unsigned long st_mtime; + unsigned long st_mtime_nsec; + unsigned long st_ctime; + unsigned long st_ctime_nsec; + //unsigned long __unused1; + //unsigned long __unused2; +}; + +/* be aware! + * mingw32 packs stat64 differently to gcc on Linux without + */ +struct stat64 { + unsigned long long st_dev; + int32_t __pad0; + unsigned long st_ino; + unsigned int st_mode; + unsigned int st_nlink; + unsigned long st_uid; + unsigned long st_gid; + unsigned long long st_rdev; + int32_t __pad1; + long long st_size; + unsigned long st_blksize; + unsigned long long st_blocks; + int st_atime; + unsigned int st_atime_nsec; + int st_mtime; + unsigned int st_mtime_nsec; + int st_ctime; + unsigned int st_ctime_nsec; + //unsigned int __unused1; + //unsigned int __unused2; +} PACKED; + +struct linux_dirent { + unsigned long d_ino; + unsigned long d_off; + unsigned short d_reclen; + char d_name[]; +}; + +struct linux_dirent64 { + unsigned long long d_ino; + long long d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[]; +}; + +#endif diff --git a/lib/Makefile b/lib/Makefile index ba268f9..901a4de 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -13,6 +13,8 @@ LDFLAGS += -fpic -g TARGET = ld-linux.so.2 +all: $(TARGET) + OBJECTS = \ c.o \ loader.o \ diff --git a/lib/c.c b/lib/c.c index aa4ef58..f3c0cf1 100644 --- a/lib/c.c +++ b/lib/c.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "loader.h" #include "string.h" @@ -38,18 +39,17 @@ #define NULL ((void *)0) -#define O_RDONLY 0 - /* * errno should be per thread, * leave as global until we can load an ELF binary */ static int errno; int verbose = 0; -extern char **ld_environment; #define EXPORT __attribute__((visibility("default"))) +EXPORT char **environ; + EXPORT void abort(void); static inline int set_errno(int r) @@ -62,12 +62,13 @@ static inline int set_errno(int r) return r; } -EXPORT void exit(int status) +EXPORT void _exit(int status) { while (1) { __asm__ __volatile__ ( "\tpushl %%ebx\n" + "\tmovl %%eax, %%ebx\n" "\tmov $1, %%eax\n" "\tint $0x80\n" "\tpopl %%ebx\n" @@ -75,6 +76,46 @@ EXPORT void exit(int status) } } +struct exit_fn +{ + void (*func) (void *); + void *arg; + void *dso_handle; + struct exit_fn *next; +}; + +static struct exit_fn *first_exit_fn; + +EXPORT void __cxa_atexit(void (*func) (void *), void * arg, void *dso_handle) +{ + struct exit_fn *efn; + + efn = malloc(sizeof *efn); + if (!efn) + abort(); + efn->func = func; + efn->arg = arg; + efn->dso_handle = dso_handle; + + efn->next = first_exit_fn; + first_exit_fn = efn; +} + +EXPORT void exit(int status) +{ + struct exit_fn *efn; + + /* call atexit functions */ + while ((efn = first_exit_fn)) + { + efn->func(efn->arg); + first_exit_fn = efn->next; + free(efn); + } + + _exit(status); +} + EXPORT int ioctl(int fd, int request, int value) { int r; @@ -120,7 +161,7 @@ EXPORT int write(int fd, const void *buffer, size_t length) return dl_write(fd, buffer, length); } -EXPORT int open(const char *filename, int flags) +EXPORT int open(const char *filename, int flags, int mode) { int r; __asm__ __volatile__ ( @@ -129,7 +170,8 @@ EXPORT int open(const char *filename, int flags) "\tmov $5, %%eax\n" "\tint $0x80\n" "\tpopl %%ebx\n" - :"=a"(r): "a"(filename), "c"(flags) : "memory"); + :"=a"(r): "a"(filename), "c"(flags), "d"(mode) + : "memory"); return set_errno(r); } @@ -138,7 +180,7 @@ EXPORT int open(const char *filename, int flags) EXPORT int open64(const char *filename, int flags) { - return open(filename, flags | O_LARGEFILE); + return open(filename, flags | O_LARGEFILE, 0); } EXPORT off_t lseek(int fd, off_t offset, int whence) @@ -188,6 +230,35 @@ EXPORT int close(int fd) return set_errno(r); } +EXPORT int fork(void) +{ + int r; + __asm__ __volatile__ ( + "\tint $0x80\n" + : "=a"(r) + : "a"(2) + : "memory"); + return set_errno(r); +} + +EXPORT int execve(const char *filename, + char **argv, + char **env) +{ + int r; + __asm__ __volatile__ ( + "\tpushl %%ebx\n" + "\tmovl %%eax, %%ebx\n" + "\tmov $11, %%eax\n" + "\tint $0x80\n" + "\tpopl %%ebx\n" + : "=a"(r) + : "a"(filename), "c"(argv), "d"(env) + : "memory"); + + return set_errno(r); +} + EXPORT int kill(pid_t pid, int signal) { int r; @@ -201,7 +272,7 @@ EXPORT int kill(pid_t pid, int signal) return set_errno(r); } -EXPORT char *getcwd(char *buf, size_t size) +static int sys_getcwd(char *buf, size_t size) { int r; __asm__ __volatile__ ( @@ -212,12 +283,59 @@ EXPORT char *getcwd(char *buf, size_t size) "\tpopl %%ebx\n" :"=a"(r): "a"(buf), "c"(size) : "memory"); - if (r < 0) + return set_errno(r); +} + +EXPORT char *getcwd(char *buf, size_t size) +{ + int r; + if (buf) { - set_errno(r); + r = sys_getcwd(buf, size); + if (r < 0) + { + set_errno(r); + return NULL; + } + return buf; + } + + if (size) + { + set_errno(-_L(EINVAL)); return NULL; } + size = 0x1000; + buf = malloc(size); + if (!buf) + return NULL; + while (1) + { + char *p; + r = sys_getcwd(buf, size); + if (r >= 0) + break; + + if (r != -_L(ERANGE)) + { + free(buf); + buf = NULL; + break; + } + + size *= 2; + p = realloc(buf, size); + if (!p) + { + free(buf); + buf = NULL; + break; + } + + buf = p; + } + return buf; } @@ -236,6 +354,36 @@ EXPORT int chdir(const char *path) return set_errno(r); } +EXPORT int getdents(int fd, void *de, int len) +{ + int r; + __asm__ __volatile__( + "\tpushl %%ebx\n" + "\tmovl %%eax, %%ebx\n" + "\tmov $141, %%eax\n" + "\tint $0x80\n" + "\tpopl %%ebx\n" + :"=a"(r) + : "a"(fd), "c"(de), "d"(len) + : "memory"); + return r; +} + +EXPORT int getdents64(int fd, void *de, int len) +{ + int r; + __asm__ __volatile__( + "\tpushl %%ebx\n" + "\tmovl %%eax, %%ebx\n" + "\tmov $220, %%eax\n" + "\tint $0x80\n" + "\tpopl %%ebx\n" + :"=a"(r) + : "a"(fd), "c"(de), "d"(len) + : "memory"); + return r; +} + EXPORT int setuid(uid_t uid) { int r; @@ -327,6 +475,51 @@ EXPORT pid_t getppid(void) return set_errno(r); } +EXPORT int setreuid(int uid, int euid) +{ + int r; + __asm__ __volatile__ ( + "\tpushl %%ebx\n" + "\tmovl %%eax, %%ebx\n" + "\tmov $70, %%eax\n" + "\tint $0x80\n" + "\tpopl %%ebx\n" + : "=a"(r) + : "a"(uid), "c"(euid) + : "memory"); + return set_errno(r); +} + +EXPORT int setregid(int gid, int egid) +{ + int r; + __asm__ __volatile__ ( + "\tpushl %%ebx\n" + "\tmovl %%eax, %%ebx\n" + "\tmov $71, %%eax\n" + "\tint $0x80\n" + "\tpopl %%ebx\n" + : "=a"(r) + : "a"(gid), "c"(egid) + : "memory"); + return set_errno(r); +} + +EXPORT int pipe(int *fds) +{ + int r; + __asm__ __volatile__ ( + "\tpushl %%ebx\n" + "\tmovl %%eax, %%ebx\n" + "\tmov $42, %%eax\n" + "\tint $0x80\n" + "\tpopl %%ebx\n" + : "=a"(r) + : "a"(fds) + : "memory"); + return set_errno(r); +} + EXPORT int waitpid(int pid, int *status, int options) { int r; @@ -342,6 +535,31 @@ EXPORT int waitpid(int pid, int *status, int options) return set_errno(r); } +struct rusage; + +EXPORT int wait3(int *status, int options, struct rusage *rusage) +{ + if (rusage) + warn("wait3(): non-zero rusage\n"); + return waitpid(-1, status, options); +} + +struct stat; + +EXPORT int __xstat(int ver, const char *path, struct stat *st) +{ + int r; + + __asm__ __volatile__ ( + "\tpushl %%ebx\n" + "\tmovl %%eax, %%ebx\n" + "\tmov $106, %%eax\n" + "\tint $0x80\n" + "\tpopl %%ebx\n" + :"=a"(r): "a"(path), "c"(st) : "memory"); + return set_errno(r); +} + struct stat64; EXPORT int __xstat64(int ver, const char *path, struct stat64 *st) @@ -413,6 +631,25 @@ EXPORT time_t time(time_t *t) return r; } +struct timezone +{ + int tz_minuteswest; + int tz_dsttime; +}; + +EXPORT int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + int r; + __asm__ __volatile__ ( + "\tpushl %%ebx\n" + "\tmov %%eax, %%ebx\n" + "\tmov $78, %%eax\n" + "\tint $0x80\n" + "\tpopl %%ebx\n" + :"=a"(r): "a"(tv), "c"(tz) : "memory"); + return r; +} + void* sys_brk(void *addr) { int r; @@ -480,6 +717,33 @@ EXPORT int poll(struct pollfd *pfds, int nfds, int timeout) return set_errno(r); } +int sys_select(void *args) +{ + int r; + __asm__ __volatile__ ( + "\tmov %%eax, %%ebx\n" + "\tmov $82, %%eax\n" + "\tint $0x80\n" + : "=a"(r) + : "a"(args) + : "memory"); + + return set_errno(r); +} + +EXPORT int select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout) +{ + struct { + int nfds; + fd_set *rfds; + fd_set *wfds; + fd_set *efds; + struct timeval *tv; + } args = { nfds, readfds, writefds, exceptfds, timeout }; + return sys_select(&args); +} + struct utsname { char sysname[65]; char nodename[65]; @@ -519,6 +783,140 @@ EXPORT int nanosleep(const struct timespec *req, struct timespec *rem) return set_errno(r); } +EXPORT int usleep(unsigned int usec) +{ + struct timeval tv; + + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + + return select(0, NULL, NULL, NULL, &tv); +} + +#define CTYPE_UPPER (1 << 8) +#define CTYPE_LOWER (1 << 9) +#define CTYPE_ALPHA (1 << 10) +#define CTYPE_NUMERIC (1 << 11) +#define CTYPE_HEX (1 << 12) +#define CTYPE_SPACE (1 << 13) +#define CTYPE_PRINT (1 << 14) +#define CTYPE_GRAPH (1 << 15) +#define CTYPE_BLANK (1 << 0) +#define CTYPE_CONTROL (1 << 1) +#define CTYPE_PUNCT (1 << 2) +#define CTYPE_ALNUM (1 << 3) + +EXPORT int32_t ** __ctype_toupper_loc(void) +{ + static int a[128 + 256]; + static int *pa; + + if (!pa) + { + int i; + pa = &a[128]; + for (i = 0; i < 256; i++) + { + int val = i; + if (val >= 'a' && val <= 'z') + val &= ~0x20; + a[128 + i] = val; + if (i >= 128) + a[256 - i] = val; + } + } + return &pa; +} + +EXPORT int32_t ** __ctype_tolower_loc(void) +{ + static int a[128 + 256]; + static int *pa; + + if (!pa) + { + int i; + pa = &a[128]; + for (i = 0; i < 256; i++) + { + int val = i; + if (val >= 'a' && val <= 'z') + val |= 0x20; + a[128 + i] = val; + if (i >= 128) + a[256 - i] = val; + } + } + return &pa; +} + +EXPORT unsigned short ** __ctype_b_loc(void) +{ + static unsigned short a[128 + 256]; + static unsigned short *pa; + + if (!pa) + { + int i; + pa = &a[128]; + for (i = 0; i < 256; i++) + { + unsigned short val = 0; + if (i >= 'A' && i <= 'Z') + val |= (CTYPE_UPPER | CTYPE_ALPHA | CTYPE_ALNUM); + if (i >= 'a' && i <= 'z') + val |= (CTYPE_LOWER | CTYPE_ALPHA | CTYPE_ALNUM); + if (i >= '0' && i <= '9') + val |= (CTYPE_NUMERIC | CTYPE_ALNUM); + if ((i >= '0' && i <= '9') || + (i >= 'a' && i <= 'f') || + (i >= 'A' && i <= 'F')) + val |= CTYPE_HEX; + if (i == ' ' || i == '\t' || i == '\r' || i == '\n') + val |= CTYPE_SPACE; + if (i >= 0x20 && i <= 0x7f) + val |= CTYPE_SPACE; + + a[128 + i] = val; + if (i >= 128) + a[256 - i] = val; + } + } + + return &pa; +} + +unsigned int random_value; + +EXPORT void srand(unsigned int seed) +{ + random_value = seed; +} + +EXPORT int rand_r(unsigned int *seed) +{ + *seed ^= 0x0f00baa0; + *seed += 0xfeedface; + *seed = (*seed << 13) | (*seed >> 19); + return *seed % INT_MAX; +} + +EXPORT int rand(void) +{ + return rand_r(&random_value); +} + +/* TODO: srandom/random use a better PRNG */ +EXPORT void srandom(unsigned int seed) +{ + srand(seed); +} + +EXPORT long int random() +{ + return rand(); +} + EXPORT size_t strlen(const char *x) { size_t n = 0; @@ -545,6 +943,11 @@ EXPORT int strcmp(const char *a, const char *b) return 0; } +EXPORT int strcoll(const char *a, const char *b) +{ + return strcmp(a, b); +} + EXPORT int strncmp(const char *a, const char *b, size_t n) { int i; @@ -638,6 +1041,12 @@ EXPORT FILE *stdin = &__stdin_file; EXPORT FILE *stdout = &__stdout_file; EXPORT FILE *stderr = &__stderr_file; +/* checks an fd is within the maximum fd set size */ +EXPORT unsigned long int __fdelt_chk(unsigned long int fd) +{ + return fd; +} + EXPORT int fputs_unlocked(const char *str, FILE *stream) { /* FIXME: use stream */ @@ -645,10 +1054,45 @@ EXPORT int fputs_unlocked(const char *str, FILE *stream) size_t len = strlen(str); if (len != dl_write(fd, str, len)) return EOF; - dl_write(1, "\n", 1); return len; } +EXPORT char *fgets_unlocked(char *s, int n, FILE *stream) +{ + int count = 0; + + if (n <= 0) + return NULL; + + while (count < (n - 1)) + { + if (stream->read_avail == 0 && !stdio_read_to_buffer(stream)) + break; + + if (!stream->read_avail) + break; + + s[count] = stream->read_buffer[stream->read_start]; + stream->read_start++; + stream->read_avail--; + if (s[count] == '\n') + break; + count++; + } + + if (count == 0) + return NULL; + + s[count] = 0; + + return s; +} + +EXPORT char *fgets(char *s, int n, FILE *stream) +{ + return fgets_unlocked(s, n, stream); +} + EXPORT int fputs(const char *str, FILE *stream) { return fputs_unlocked(str, stream); @@ -661,13 +1105,19 @@ EXPORT FILE *fopen(const char *path, const char *mode) if (mode[0] == 'r' && mode[1] != '+') { - fd = open(path, O_RDONLY); + fd = open(path, _L(O_RDONLY), 0); + if (fd < 0) + return NULL; + } + else if (mode[0] == 'w') + { + fd = open(path, _L(O_RDWR) | _L(O_CREAT), 0666); if (fd < 0) return NULL; } else { - dprintf("unknown mode %s\n", mode); + warn("unknown mode: fopen(%s)\n", mode); return NULL; } @@ -721,11 +1171,6 @@ EXPORT size_t fread(void *ptr, size_t size, size_t nmemb, FILE *f) return 0; } -EXPORT int puts(const char *str) -{ - return fputs(str, stdout); -} - EXPORT int fputc(int c, FILE *stream) { char ch = c; @@ -742,6 +1187,13 @@ EXPORT int putc_unlocked(int c, FILE *stream) return c; } +EXPORT int puts(const char *str) +{ + int r = fputs_unlocked(str, stdout); + putc_unlocked('\n', stdout); + return r + 1; +} + EXPORT int putchar_unlocked(int c) { return putc_unlocked(c, stdout); @@ -752,6 +1204,11 @@ EXPORT int putchar(int c) return fputc(c, stdout); } +EXPORT int _IO_putc(int c, FILE *stream) +{ + return fputc(c, stream); +} + EXPORT int getc_unlocked(FILE *stream) { unsigned char ch; @@ -775,6 +1232,21 @@ EXPORT int getc_unlocked(FILE *stream) return ch; } +EXPORT int getc(FILE *stream) +{ + return getc_unlocked(stream); +} + +EXPORT int getchar(void) +{ + return getc(stdin); +} + +EXPORT void clearerr(FILE *f) +{ + f->flags &= ~(STDIO_READ_ERROR | STDIO_READ_EOF); +} + EXPORT int ferror(FILE *f) { return f->flags & STDIO_READ_ERROR; @@ -799,6 +1271,119 @@ EXPORT int fclose(FILE *f) return -1; } +#define DIR_MAGIC 0xd1aad1ab + +typedef struct +{ + int magic; + int fd; + int available; + int used; + unsigned char buffer[0x1000]; +} DIR; + +EXPORT DIR *opendir(const char *name) +{ + int fd; + DIR *dir; + + fd = open(name, _L(O_RDONLY), 0); + if (fd < 0) + return NULL; + + dir = malloc(sizeof *dir); + if (dir) + { + dir->magic = DIR_MAGIC; + dir->fd = fd; + dir->available = 0; + dir->used = 0; + } + else + close(fd); + + return dir; +} + +EXPORT struct dirent *readdir(DIR *dir) +{ + warn("readdir()\n"); + return NULL; +} + +EXPORT struct linux_dirent64 *readdir64(DIR *dir) +{ + struct linux_dirent64 *de; + int r; + + if (!dir || dir->magic != DIR_MAGIC) + return NULL; + + if (dir->available == dir->used) + { + r = getdents64(dir->fd, dir->buffer, sizeof dir->buffer); + if (r <= 0) + return NULL; + + dir->available = r; + dir->used = 0; + + de = (void*) dir->buffer; + } + else + { + de = (void*) (&dir->buffer[dir->used]); + dir->used += de->d_reclen; + de = (void*) (&dir->buffer[dir->used]); + } + + return de; +} + +EXPORT int closedir(DIR *dir) +{ + if (!dir || dir->magic != DIR_MAGIC) + return -1; + + close(dir->fd); + dir->magic = 0; + free(dir); + + return 0; +} + +EXPORT void perror(const char *s) +{ + __printf_chk(0, "%s: errno=%d\n", s, errno); +} + +EXPORT char* setlocale(int category, const char *locale) +{ + warn("setlocale(%d,%s)\n", category, locale); + return "en_US.UTF-8"; +} + +EXPORT char *bindtextdomain(const char *domain, + const char *dirname) +{ + warn("bindtextdomain(%s,%s)\n", domain, dirname); + return NULL; +} + +EXPORT char *textdomain(const char *domain) +{ + warn("textdomain(%s)\n", domain); + return NULL; +} + +EXPORT char *dcgettext(const char *domain, + const char *msgid, + int category) +{ + /* TODO: implement properly */ + return (char*) msgid; +} + /* ignore leap seconds... */ EXPORT struct tm *gmtime_r(const time_t *timep, struct tm *result) { @@ -924,6 +1509,17 @@ EXPORT char *asctime_r(const struct tm *tm, char *out) return out; } +EXPORT char *asctime(struct tm *tm) +{ + static char out[64]; + return asctime_r(tm, out); +} + +EXPORT char *ctime(const time_t *timep) +{ + return asctime(localtime(timep)); +} + EXPORT size_t strftime(char *s, size_t max, const char *format, const struct tm *tm) @@ -1025,6 +1621,8 @@ struct printf_output { int (*fn)(struct printf_output *pfo, const char *str, size_t len); int width; + int maxwidth; + int left_justify; char pad; char *buffer; size_t max; @@ -1032,6 +1630,23 @@ struct printf_output FILE *f; }; +static void pf_output(struct printf_output *pfo, + const char *buf, size_t len) +{ + int i; + + /* add characters for left or right justification */ + if (!pfo->left_justify) + for (i = 0; (i + len) < pfo->width; i++) + pfo->fn(pfo, &pfo->pad, 1); + + pfo->fn(pfo, buf, len); + + if (pfo->left_justify) + for (i = 0; (i + len) < pfo->width; i++) + pfo->fn(pfo, &pfo->pad, 1); +} + static void pf_decimal(int value, struct printf_output *pfo) { char buf[32]; @@ -1051,10 +1666,8 @@ static void pf_decimal(int value, struct printf_output *pfo) buf[--n] = '0'; if (sign < 0) buf[--n] = '-'; - while (sizeof buf > n && sizeof buf - n < pfo->width) - buf[--n] = pfo->pad; - pfo->fn(pfo, &buf[n], sizeof buf - n); + pf_output(pfo, &buf[n], sizeof buf - n); } static void pf_unsigned(unsigned int value, struct printf_output *pfo) @@ -1070,10 +1683,8 @@ static void pf_unsigned(unsigned int value, struct printf_output *pfo) if (n == sizeof buf) buf[--n] = '0'; - while (sizeof buf > n && sizeof buf - n < pfo->width) - buf[--n] = pfo->pad; - pfo->fn(pfo, &buf[n], sizeof buf - n); + pf_output(pfo, &buf[n], sizeof buf - n); } static void pf_octal(int value, struct printf_output *pfo) @@ -1089,10 +1700,8 @@ static void pf_octal(int value, struct printf_output *pfo) if (n == sizeof buf) buf[--n] = '0'; - while (sizeof buf > n && sizeof buf - n < pfo->width) - buf[--n] = pfo->pad; - pfo->fn(pfo, &buf[n], sizeof buf - n); + pf_output(pfo, &buf[n], sizeof buf - n); } static char to_hex(int value) @@ -1115,12 +1724,7 @@ static void pf_hex(unsigned int value, struct printf_output *pfo) value /= 16; } - if (n == sizeof buf) - buf[--n] = '0'; - while (sizeof buf > n && sizeof buf - n < pfo->width) - buf[--n] = pfo->pad; - - pfo->fn(pfo, &buf[n], sizeof buf - n); + pf_output(pfo, &buf[n], sizeof buf - n); } static void pf_pointer(void *value, struct printf_output *pfo) @@ -1135,27 +1739,26 @@ static void pf_pointer(void *value, struct printf_output *pfo) for (i = 0; i < 8; i++) buf[2 + i] = to_hex((x >> (28 - i * 4)) & 0x0f); - pfo->fn(pfo, buf, sizeof buf); + pf_output(pfo, buf, sizeof buf); } static void pf_string(const char *value, struct printf_output *pfo) { size_t len; - size_t i; if (!value) value = "(null)"; len = strlen(value); - for (i = len; i < pfo->width; i++) - pfo->fn(pfo, " ", 1); + if (len > pfo->maxwidth) + len = pfo->maxwidth; - pfo->fn(pfo, value, len); + pf_output(pfo, value, len); } static void pf_char(char value, struct printf_output *pfo) { - pfo->fn(pfo, &value, 1); + pf_output(pfo, &value, 1); } static int internal_vsprintf(int flags, const char *str, va_list va, @@ -1183,6 +1786,14 @@ static int internal_vsprintf(int flags, const char *str, va_list va, p++; pfo->width = 0; + if (*p == '-') + { + pfo->left_justify = 1; + p++; + } + else + pfo->left_justify = 0; + if (*p == '0') { pfo->pad = '0'; @@ -1202,6 +1813,25 @@ static int internal_vsprintf(int flags, const char *str, va_list va, p++; } + if (*p == '.') + { + pfo->maxwidth = 0; + p++; + if (*p == '*') + { + pfo->maxwidth = va_arg(va, int); + p++; + } + while (*p >= '0' && *p <= '9') + { + pfo->maxwidth *= 10; + pfo->maxwidth += (*p - '0'); + p++; + } + } + else + pfo->maxwidth = INT_MAX; + /* long */ lng = 0; if (*p == 'l') @@ -1270,7 +1900,7 @@ static int internal_vsprintf(int flags, const char *str, va_list va, p++; break; default: - dprintf("printf(): %c unhandled\n", *p); + warn("printf(): %c unhandled\n", *p); return 0; } } @@ -1297,6 +1927,11 @@ EXPORT int vprintf(const char *str, va_list va) return internal_vsprintf(0, str, va, &pfo); } +EXPORT int __vprintf_chk(int flag, const char *str, va_list va) +{ + return vprintf(str, va); +} + EXPORT int __printf_chk(int flag, const char *str, ...) { va_list va; @@ -1304,7 +1939,7 @@ EXPORT int __printf_chk(int flag, const char *str, ...) va_start(va, str); - r = vprintf(str, va); + r = __vprintf_chk(flag, str, va); va_end(va); @@ -1325,9 +1960,8 @@ static int snprintf_chk_pfo(struct printf_output *pfo, const char *str, size_t l return 1; } -EXPORT int __sprintf_chk(char *out, int flag, size_t maxlen, const char *str, ...) +EXPORT int __vsprintf_chk(char *out, int flag, size_t maxlen, const char *str, va_list va) { - va_list va; int r; struct printf_output pfo = { @@ -1337,21 +1971,28 @@ EXPORT int __sprintf_chk(char *out, int flag, size_t maxlen, const char *str, .. .max = maxlen, }; - va_start(va, str); - r = internal_vsprintf(flag, str, va, &pfo); pfo.buffer[pfo.out_size] = 0; + return r; +} + +EXPORT int __sprintf_chk(char *out, int flag, size_t maxlen, const char *str, ...) +{ + va_list va; + int r; + + va_start(va, str); + r = __vsprintf_chk(out, flag, maxlen, str, va); va_end(va); return r; } -EXPORT int __snprintf_chk(char *out, size_t maxlen, int flag, - size_t strlen, const char *str, ...) +EXPORT int __vsnprintf_chk(char *out, size_t maxlen, int flag, + size_t slen, const char *str, va_list va) { - va_list va; int r; struct printf_output pfo = { @@ -1361,14 +2002,8 @@ EXPORT int __snprintf_chk(char *out, size_t maxlen, int flag, .max = maxlen ? maxlen - 1 : 0, }; - if (strlen < maxlen) - { - verbose = 1; - dprintf("snprintf overflow\n"); - abort(); - } - - va_start(va, str); + if (slen < maxlen) + die("snprintf overflow\n"); r = internal_vsprintf(flag, str, va, &pfo); @@ -1380,6 +2015,23 @@ EXPORT int __snprintf_chk(char *out, size_t maxlen, int flag, pfo.buffer[pfo.max] = 0; } + return r; +} + +EXPORT int vsnprintf(char *out, size_t maxlen, + const char *str, va_list va) +{ + return __vsnprintf_chk(out, maxlen, 0, INT_MAX, str, va); +} + +EXPORT int __snprintf_chk(char *out, size_t maxlen, int flag, + size_t slen, const char *str, ...) +{ + va_list va; + int r; + + va_start(va, str); + r = __vsnprintf_chk(out, maxlen, flag, slen, str, va); va_end(va); return r; @@ -1467,6 +2119,15 @@ EXPORT int __asprintf_chk(char **strp, int flags, const char *fmt, ...) return r; } +void warn(const char *str, ...) +{ + va_list va; + + va_start(va, str); + vprintf(str, va); + va_end(va); +} + void dprintf(const char *str, ...) { va_list va; @@ -1478,6 +2139,18 @@ void dprintf(const char *str, ...) va_end(va); } +void die(const char *str, ...) +{ + va_list va; + + va_start(va, str); + vprintf(str, va); + va_end(va); + + abort(); +} + + #define STRTOX_IMPL \ int sign = 1; \ \ @@ -1653,21 +2326,15 @@ static void heap_compress(void) { if (p->magic != HEAP_MAGIC) { - verbose = 1; - dprintf("heap magic wrong! %p %08x\n", p, p->magic); - abort(); + die("heap magic wrong! %p %08x\n", p, p->magic); } if (p->next && p->next <= p) { - verbose = 1; - dprintf("heap not linear! %p -> %p\n", p, p->next); - abort(); + die("heap not linear! %p -> %p\n", p, p->next); } if (p->sz < sizeof (*p)) { - verbose = 1; - dprintf("heap block too small! %p\n", p); - abort(); + die("heap block too small! %p\n", p); } if (!p->used) { @@ -1700,11 +2367,7 @@ EXPORT void *malloc(size_t sz) for (p = &first_block; *p; p = &((*p)->next)) { if ((*p)->magic != HEAP_MAGIC) - { - verbose = 1; - dprintf("malloc(): corrupt heap %p\n", *p); - abort(); - } + die("malloc(): corrupt heap %p\n", *p); if ((*p)->used) continue; if (heap_block_sz(*p) >= sz) @@ -1758,18 +2421,10 @@ EXPORT void *realloc(void *ptr, size_t mem) p--; if (p->magic != HEAP_MAGIC) - { - verbose = 1; - dprintf("realloc(): corrupt heap %p\n", ptr); - abort(); - } + die("realloc(): corrupt heap %p\n", ptr); if (!p->used) - { - verbose = 1; - dprintf("realloc(): memory not allocated %p\n", ptr); - abort(); - } + die("realloc(): memory not allocated %p\n", ptr); old_size = heap_block_sz(p); if (old_size >= mem) @@ -1817,18 +2472,10 @@ EXPORT void free(void *ptr) p--; if (p->magic != HEAP_MAGIC) - { - verbose = 1; - dprintf("free(): corrupt heap %p\n", ptr); - abort(); - } + die("free(): corrupt heap %p\n", ptr); if (!p->used) - { - verbose = 1; - dprintf("realloc(): memory not allocated %p\n", ptr); - abort(); - } + die("realloc(): memory not allocated %p\n", ptr); heap_free_block(p); } @@ -1855,6 +2502,19 @@ EXPORT void *memcpy(void *dest, const void *src, size_t n) return dest; } +EXPORT void *mempcpy(void *dest, const void *src, size_t n) +{ + memcpy(dest, src, n); + return (char*) dest + n; +} + +EXPORT void *__memcpy_chk(void *dest, const void *src, size_t n, size_t destsize) +{ + if (destsize < n) + die("bad memcpy"); + return memcpy(dest, src, n); +} + EXPORT void *memmove(void *dest, const void *src, size_t n) { const unsigned char *sc = src; @@ -1962,16 +2622,22 @@ EXPORT char *strcpy(char *dest, const char *s) return dest; } +EXPORT char *strncpy(char *dest, const char *s, size_t n) +{ + size_t i; + for (i = 0; i < n && s[n]; i++) + dest[n] = s[n]; + for ( ; i < n; i++) + dest[n] = 0; + return dest; +} + EXPORT char *__strcpy_chk(char *dest, const char *s, size_t destlen) { size_t len = strlen(s) + 1; if (len > destlen) - { - verbose = 1; - dprintf("strcpy() overrun\n"); - abort(); - } + die("strcpy() overrun\n"); memcpy(dest, s, len); return dest; @@ -1986,7 +2652,8 @@ EXPORT char *stpcpy(char *d, const char *s) EXPORT char *strcat(char *dest, const char *src) { - return strcpy(dest + strlen(dest), src); + strcpy(dest + strlen(dest), src); + return dest; } EXPORT char *__strcat_chk(char *dest, const char *src, size_t destlen) @@ -1994,11 +2661,8 @@ EXPORT char *__strcat_chk(char *dest, const char *src, size_t destlen) size_t sl = strlen(src); size_t dl = strlen(dest); if (sl + dl >= destlen) - { - verbose = 1; - dprintf("strcat(): overrun\n"); - abort(); - } + die("strcat(): overrun\n"); + memcpy(&dest[dl], src, sl + 1); return dest; } @@ -2016,11 +2680,8 @@ EXPORT char *__strncat_chk(char *dest, const char *src, sl = n; if (sl + dl >= destlen) - { - verbose = 1; - dprintf("strcat(): overrun\n"); - abort(); - } + die("strcat(): overrun\n"); + memcpy(&dest[dl], src, sl); dest[dl + sl] = 0; return dest; @@ -2079,6 +2740,21 @@ EXPORT size_t strcspn(const char *str, const char *reject) return r; } +EXPORT char *strpbrk(const char *s, const char *accept) +{ + unsigned char ok[256] = {0}; + int i; + + for (i = 0; accept[i]; i++) + ok[(unsigned char)accept[i]] = 1; + + for (i = 0; s[i]; i++) + if (ok[(unsigned char)s[i]]) + return (char*) &s[i]; + + return NULL; +} + typedef int regoff_t; typedef struct @@ -2095,7 +2771,7 @@ typedef struct EXPORT int regcomp(regex_t *preg, const char *regex, int cflags) { - dprintf("regcomp(%p,%s,%08x)\n", preg, regex, cflags); + warn("regcomp(%p,%s,%08x)\n", preg, regex, cflags); return 0; } @@ -2104,7 +2780,7 @@ EXPORT int regexec(const regex_t *preg, const char *string, size_t nmatch, { int i; - dprintf("regexec(%p,...)\n", preg); + warn("regexec(%p,...)\n", preg); for (i = 0; i < nmatch; i++) { @@ -2147,13 +2823,54 @@ EXPORT void *bsearch(const void *key, const void *base, } } +/* not quicksort, but anyway */ +typedef int (*fn_compare)(const void *, const void *); +EXPORT void qsort(void *base, size_t nmemb, size_t size, fn_compare fn) +{ + size_t mid; + unsigned char *a1, *a2, *end; + + if (nmemb <= 1) + return; + + mid = nmemb/2; + a1 = (unsigned char *)base; + a2 = base + size * mid; + end = base + size * nmemb; + + /* sort halves */ + if (size > 2) + { + qsort(a1, mid, size, fn); + qsort(a2, nmemb - mid, size, fn); + } + + while (a1 < a2 && a2 < end) + { + int r = fn(a1, a2); + if (r < 0) + { + a1 += size; + } + else + { + unsigned char tmp[size]; + memcpy(tmp, a2, size); + memmove(a1 + size, a1, a2 - a1); + memcpy(a1, tmp, size); + a1 += size; + a2 += size; + } + } +} + EXPORT char **__environ; EXPORT char *getenv(const char *name) { char **p; size_t len = strlen(name); - for (p = ld_environment; + for (p = environ; *p; p++) { @@ -2406,27 +3123,57 @@ EXPORT int getopt(int argc, const char **argv, return getopt_long(argc, argv, optstring, NULL, NULL); } -struct termios +struct passwd { - unsigned int c_iflag; - unsigned int c_oflag; - unsigned int c_cflag; - unsigned int c_lflag; - unsigned char c_line; - unsigned char c_cc[8]; + char *pw_name; + char *pw_passwd; + uid_t pw_uid; + gid_t pw_gid; + char *pw_gecos; + char *pw_dir; + char *pw_shell; }; -#define TCGETS 0x5401 -#define TCSETS 0x5402 +EXPORT struct passwd *getpwuid(uid_t uid) +{ + struct passwd *pw; + const char str[] = "atratus\0*\0Atratus\0/\0/bin/sh"; + + /* FIXME: read from /etc/passwd when ready */ + pw = malloc(sizeof *pw + sizeof str); + memcpy(&pw[1], str, sizeof str); + pw->pw_name = (char*) (&pw[1]); + pw->pw_passwd = pw->pw_name + strlen(pw->pw_name) + 1; + pw->pw_uid = uid; + pw->pw_gid = uid; + pw->pw_gecos = pw->pw_passwd + strlen(pw->pw_passwd) + 1; + pw->pw_dir = pw->pw_gecos + strlen(pw->pw_gecos) + 1; + pw->pw_shell = pw->pw_dir + strlen(pw->pw_dir) + 1; + + return pw; +} + +/*typedef uint64_t dev_t;*/ + +EXPORT unsigned int gnu_dev_major(dev_t devid) +{ + return (devid >> 16); +} + +EXPORT unsigned int gnu_dev_minor(dev_t devid) +{ + return (devid & 0xffff); +} EXPORT int tcgetattr(int fd, struct termios *tios) { - return ioctl(fd, TCGETS, (int) tios); + return ioctl(fd, TIOCG, (int) tios); } -EXPORT int tcsetattr(int fd, struct termios *tios) +EXPORT int tcsetattr(int fd, int optional_actions, struct termios *tios) { - return ioctl(fd, TCSETS, (int) tios); + /* TODO: do something with optional_actions */ + return ioctl(fd, TIOCS, (int) tios); } EXPORT pid_t tcgetpgrp(int fd) @@ -2476,15 +3223,90 @@ EXPORT int ttyname_r(int fd, char *buf, size_t buflen) return 0; } -struct jmp_buf { -}; - -EXPORT int _setjmp(struct jmp_buf *buf) +EXPORT char *ttyname(int fd) { - dprintf("setjmp(%p)\n", buf); - return 0; + static char buf[0x1000]; + int r; + r = ttyname_r(fd, buf, sizeof buf - 1); + if (r < 0) + return NULL; + return buf; } +/* jmpbuf is 156 bytes in size */ +struct jmp_buf; + +EXPORT int _setjmp(struct jmp_buf *buf); +__asm__ ( + "\n" +".text\n" +".globl _setjmp\n" +".type _setjmp, @function\n" +"_setjmp:\n" + "\tpush %ebx\n" + "\tmovl 8(%esp), %eax\n" /* jmpbuf */ + "\tmovl %ebx, (%eax)\n" + "\tmovl %ecx, 4(%eax)\n" + "\tmovl %edx, 8(%eax)\n" + "\tmovl %esi, 12(%eax)\n" + "\tmovl %edi, 16(%eax)\n" + "\tmovl %ebp, 20(%eax)\n" + "\tmovl %esp, 24(%eax)\n" + "\tmovl 4(%esp), %ebx\n" /* save the return address */ + "\tmovl %ebx, 28(%eax)\n" + /* TODO: save floating point, debug registers, etc */ + "\txor %eax, %eax\n" /* return 0 */ + "\tpopl %ebx\n" + "\tret\n" + "\t.size _setjmp, .-_setjmp\n" +); + +EXPORT void __longjmp_chk(struct jmp_buf *buf, int val); +__asm__ ( + "\n" +".text\n" +".globl __longjmp_chk\n" +".type __longjmp_chk, @function\n" +"__longjmp_chk:\n" + "\tmov 4(%esp), %eax\n" /* eax hold pointer to jmpbuf */ + + /* fetch old EIP, save it on the return stack */ + "\tmov 28(%eax), %ebx\n" /* ebx the return EIP */ + "\tmov 24(%eax), %ecx\n" /* ecx holds pointer to return stack */ + "\tmov %ebx, 4(%ecx)\n" /* save return address on the return stack */ + + /* fetch return value, save it on the "return" stack */ + "\tmov 8(%esp), %ebx\n" /* val */ + "\tcmp $1, %ebx\n" /* change 0 to 1 */ + "\tadc $0, %ebx\n" + "\tmov %ebx, (%ecx)\n" /* save the return value on the return stack */ + + /* restore registers */ + "\tmov (%eax), %ebx\n" + "\tmov 4(%eax), %ecx\n" + "\tmov 8(%eax), %edx\n" + "\tmov 12(%eax), %esi\n" + "\tmov 16(%eax), %edi\n" + "\tmov 20(%eax), %ebp\n" + "\tmov 24(%eax), %esp\n" + + /* restore EAX and EIP */ + "\tpop %eax\n" + "\tret\n" + "\t.size __longjmp_chk, .-__longjmp_chk\n" +); + +EXPORT void longjmp(struct jmp_buf *buf, int val); +__asm__ ( + "\n" +".text\n" +".globl longjmp\n" +".type longjmp, @function\n" +"longjmp:\n" + "\tjmp __longjmp_chk\n" + "\t.size longjmp, .-longjmp\n" +); + #define SIG_DFL 0 #define SIG_IGN 1 @@ -2560,6 +3382,8 @@ EXPORT int __libc_start_main(fn_main pmain, fn_rtld_fini prtld_fini, void (* stack_end)) { + int r; + dprintf("%s called\n", __FUNCTION__); dprintf("main %p\n", pmain); dprintf("argc %d\n", argc); @@ -2572,7 +3396,7 @@ EXPORT int __libc_start_main(fn_main pmain, dprintf("init() done\n"); - pmain(argc, ubp_av, NULL); - dprintf("main() done\n"); - exit(0); + r = pmain(argc, ubp_av, environ); + dprintf("main() returned %d\n", r); + exit(r); } diff --git a/lib/loader.c b/lib/loader.c index 7ca209c..a35fdb6 100644 --- a/lib/loader.c +++ b/lib/loader.c @@ -35,10 +35,9 @@ #define NULL ((void *)0) -extern int verbose; static struct module_info loader_module; static struct module_info main_module; -char **ld_environment; +extern char **environ; /* * errno should be per thread, @@ -166,11 +165,8 @@ Elf32_Sym *elf_hash_lookup(struct module_info *m, count--; } if (!count) - { - verbose = 1; - dprintf("circular chain in ELF32 hash\n"); - exit(1); - } + die("circular chain in ELF32 hash\n"); + return 0; } @@ -227,7 +223,7 @@ Elf32_Sym *elf_gnu_hash_lookup(struct module_info *m, index++; } - if (verbose && !r) + if (!r) dprintf("symbol %s not found in %s\n", symbol_name, m->name); @@ -296,11 +292,8 @@ void *__ld_dynamic_resolve(void *arg, unsigned int entry, void *callee) Elf32_Sym *st; void **got_entry; - if (verbose) - { - dprintf("%s arg=%p plt_entry=%08x callee=%p\n", - __FUNCTION__, arg, entry, callee); - } + dprintf("%s arg=%p plt_entry=%08x callee=%p\n", + __FUNCTION__, arg, entry, callee); if (m->dt.pltrel != DT_REL) dprintf("not DT_REL\n"); @@ -311,46 +304,33 @@ void *__ld_dynamic_resolve(void *arg, unsigned int entry, void *callee) if (symtype != R_386_JMP_SLOT) return 0; - if (verbose) - { - dprintf("dt.symtab = %08x\n", m->dt.symtab); - dprintf("syminfo = %08x\n", syminfo); - } + dprintf("dt.symtab = %08x\n", m->dt.symtab); + dprintf("syminfo = %08x\n", syminfo); st = (Elf32_Sym*) m->dt.symtab; st += syminfo; - if (verbose) - dprintf("st_name = %d offset = %08x\n", st->st_name, rel->r_offset); + dprintf("st_name = %d offset = %08x\n", st->st_name, rel->r_offset); symbol_name = (const char*) m->dt.strtab + st->st_name; - if (verbose) - dprintf("symbol_name = %s\n", symbol_name); + dprintf("symbol_name = %s\n", symbol_name); target = ld_get_symbol_address(symbol_name); if (!target) { // FIXME: handle weak symbols - verbose = 1; - dprintf("no such symbol (%s)\n", symbol_name); - exit(1); - return 0; + die("no such symbol (%s)\n", symbol_name); } - if (verbose) - dprintf("dynamic resolve, symbol -> %s\n", symbol_name); - r = (void*) target; - if (verbose) - dprintf("dynamic resolve: %08x -> %p\n", entry, r); + dprintf("dynamic resolve: %08x -> %p\n", entry, r); /* patch the correct value into the GOT */ got_entry = (void**)((char*)m->delta + rel->r_offset); /* patch the resolved address into the GOT */ *got_entry = r; - if (verbose) - dprintf("patched GOT at %p\n", got_entry); + dprintf("patched GOT at %p\n", got_entry); return r; } @@ -376,11 +356,8 @@ void patch_got(struct module_info *m) /* TODO: apply delta between actual and intended load address */ unsigned int *got = (void*) + m->dt.pltgot; - if (verbose) - { - dprintf("PLTGOT: %08x\n", m->dt.pltgot); - dprintf("PLTRELSZ: %08x\n", m->dt.pltrelsz); - } + dprintf("PLTGOT: %08x\n", m->dt.pltgot); + dprintf("PLTRELSZ: %08x\n", m->dt.pltrelsz); /* skip the first 3 entries, they're special */ i = 1; @@ -405,9 +382,7 @@ void elf_apply_reloc_glob_dat(struct module_info *m, int offset) syminfo = ELF32_R_SYM(rel[offset].r_info); - if (verbose) - dprintf("%08x %06x R_386_GLOB_DAT\n", - rel->r_offset, syminfo); + dprintf("%08x %06x R_386_GLOB_DAT\n", rel->r_offset, syminfo); st = (Elf32_Sym*) m->dt.symtab; st += syminfo; @@ -421,21 +396,18 @@ void elf_apply_reloc_glob_dat(struct module_info *m, int offset) /* has a value and size, so is defined */ value = (uint32_t) (m->delta + st->st_value); - if (verbose) - dprintf("R_386_GLOB_DAT: definition " - "of %s (in %s) @%p -> %08x\n", - symbol_name, m->name, p, value); + dprintf("R_386_GLOB_DAT: definition " + "of %s (in %s) @%p -> %08x\n", + symbol_name, m->name, p, value); } else { - if (verbose) - dprintf("%s used in %s\n", symbol_name, m->name); + dprintf("%s used in %s\n", symbol_name, m->name); value = ld_get_symbol_address(symbol_name); - if (verbose) - dprintf("R_386_GLOB_DAT: reference " - "to %s (in %s) @%p -> %08x\n", - symbol_name, m->name, p, value); + dprintf("R_386_GLOB_DAT: reference " + "to %s (in %s) @%p -> %08x\n", + symbol_name, m->name, p, value); } *p = (uint32_t) value; @@ -465,10 +437,7 @@ void ld_apply_reloc_copy(struct module_info *m, Elf32_Rel *rel) memcpy(dest, (void*)src, st->st_size); - if (verbose) - { - dprintf("R_386_COPY (%s) %p -> %p\n", symbol_name, src, dest); - } + dprintf("R_386_COPY (%s) %p -> %p\n", symbol_name, src, dest); } static void ld_apply_reloc_32(struct module_info *m, Elf32_Rel *rel) @@ -484,18 +453,13 @@ static void ld_apply_reloc_32(struct module_info *m, Elf32_Rel *rel) value = ld_get_symbol_address(symbol_name); if (!value) - { - verbose = 1; - dprintf("symbol '%s' not found\n", symbol_name); - exit(1); - } + die("symbol '%s' not found\n", symbol_name); + value += main_module.delta; - if (verbose) - { - dprintf("R_386_32 reloc applied %s -> %08x\n", - symbol_name, value); - } + dprintf("R_386_32 reloc applied %s -> %08x\n", + symbol_name, value); + dest = (void*)(m->delta + rel->r_offset); *dest = value; } @@ -504,8 +468,7 @@ int ld_apply_relocations(struct module_info *m) { int i; - if (verbose) - dprintf("Applying relocs for %s\n", m->name); + dprintf("Applying relocs for %s\n", m->name); for (i = 0; i < m->dt.relsz/sizeof (Elf32_Rel); i++) { @@ -535,8 +498,7 @@ static int ld_apply_loader_relocations(struct module_info *m) { int i; - if (verbose) - dprintf("Applying relocs for %s\n", m->name); + dprintf("Applying relocs for %s\n", m->name); for (i = 0; i < m->dt.relsz/sizeof (Elf32_Rel); i++) { @@ -552,11 +514,9 @@ static int ld_apply_loader_relocations(struct module_info *m) ld_apply_reloc_32(m, &rel[i]); break; default: - verbose = 1; - dprintf("%08x %06x " + die("%08x %06x " "(symbol type %d not supported)\n", rel[i].r_offset, syminfo, symtype); - exit(1); } } @@ -659,8 +619,6 @@ void *ld_main(int argc, char **argv, char **env, Elf32_Aux *auxv) ld_setup_gs(); - ld_environment = env; - /* read the loader's dynamic section */ loader_module.name = "ld.so"; @@ -688,17 +646,17 @@ void *ld_main(int argc, char **argv, char **env, Elf32_Aux *auxv) ld_read_dynamic_section(&main_module, dynamic->p_vaddr); - if (verbose) - dprintf("patching GOT\n"); + dprintf("patching GOT\n"); patch_got(&main_module); - if (verbose) - dprintf("done returning to %p\n", entry); + dprintf("done returning to %p\n", entry); ld_apply_loader_relocations(&loader_module); ld_apply_relocations(&main_module); + environ = env; + return entry; error: return 0; diff --git a/lib/loader.h b/lib/loader.h index 13c773b..8d522a6 100644 --- a/lib/loader.h +++ b/lib/loader.h @@ -3,5 +3,7 @@ extern void* ld_main(int argc, char **argv, char **env, Elf32_Aux *auxv); extern void dprintf(const char *str, ...) __attribute__((format(printf,1,2))); +extern void die(const char *str, ...); +extern void warn(const char *str, ...); #endif /* __LOADER_H__ */ diff --git a/server/Makefile b/server/Makefile index 4f04d2a..2efeb35 100644 --- a/server/Makefile +++ b/server/Makefile @@ -16,6 +16,7 @@ ATRATUS_SRCS = \ dev.c \ pipe.c \ tty.c \ + vt100.c \ winfs.c STUB_SRCS = \ diff --git a/server/atratus.c b/server/atratus.c index 8714e47..0218fc6 100644 --- a/server/atratus.c +++ b/server/atratus.c @@ -32,13 +32,14 @@ #include "linux-errno.h" #include "linux-defines.h" #include "filp.h" -#include "tty.h" #include "pipe.h" #include "sys/elf32.h" #include "elf-av.h" #include "process.h" +#include "tty.h" +#include "vt100.h" struct _l_pollfd { int fd; @@ -80,7 +81,7 @@ int nt_process_memcpy_from(void *local_addr, const void *client_addr, size_t siz local_addr, size, &bytesRead); if (r != STATUS_SUCCESS) return -_L(EFAULT); - return bytesRead; + return 0; } int nt_process_memcpy_to(void *client_addr, const void *local_addr, size_t size) @@ -92,7 +93,7 @@ int nt_process_memcpy_to(void *client_addr, const void *local_addr, size_t size) local_addr, size, &bytesWritten); if (r != STATUS_SUCCESS) return -_L(EFAULT); - return bytesWritten; + return 0; } struct process_ops nt_process_ops = @@ -1239,6 +1240,7 @@ int sys_fork(void) context->sibling = current->child; current->child = context; context->umask = current->umask; + context->tty = current->tty; dprintf("fork() good!\n"); @@ -1259,7 +1261,8 @@ int sys_exit(int exit_code) process_signal(current->parent, _L(SIGCHLD)); - unlink_process(current); + if (!current->parent) + unlink_process(current); return 0; } @@ -1318,7 +1321,7 @@ static char *get_path(const char *file) if (file[0] == '/') return strdup(file); - path = malloc(strlen(file) + strlen(current->cwd) + 1); + path = malloc(strlen(file) + strlen(current->cwd) + 2); strcpy(path, current->cwd); strcat(path, "/"); @@ -1358,28 +1361,33 @@ static int do_open(const char *file, int flags, int mode) return r; } -int add_dirent(void *ptr, WCHAR* entry, USHORT entrylen, int avail) +int add_dirent(void *ptr, const char* entry, size_t name_len, + int avail, unsigned long next_offset, char type) { - int name_len = WideCharToMultiByte(CP_UTF8, 0, entry, entrylen, NULL, 0, NULL, NULL); struct linux_dirent *de; - - /* add a trailing NUL and round up to multiple of 4 */ int len = (name_len + sizeof *de + 4) & ~3; + int r; + char *t; de = alloca(len); - if (len > avail) return -1; de->d_ino = 0; - de->d_off = 0; + de->d_off = next_offset; de->d_reclen = len; - WideCharToMultiByte(CP_UTF8, 0, entry, entrylen, de->d_name, avail, NULL, NULL); + memcpy(de->d_name, entry, name_len); de->d_name[name_len] = 0; + t = (char*) de; + t[len - 1] = type; dprintf("adding %s\n", de->d_name); - return current->ops->memcpy_to(ptr, de, len); + r = current->ops->memcpy_to(ptr, de, len); + if (r < 0) + return r; + + return len; } int sys_getdents(int fd, struct linux_dirent *de, unsigned int count) @@ -1398,26 +1406,37 @@ int sys_getdents(int fd, struct linux_dirent *de, unsigned int count) return fp->ops->fn_getdents(fp, de, count, &add_dirent); } -int add_dirent64(void *ptr, WCHAR* entry, USHORT entrylen, int avail) +int add_dirent64(void *ptr, const char* entry, size_t name_len, + int avail, unsigned long next_offset, char type) { - int name_len = WideCharToMultiByte(CP_UTF8, 0, entry, entrylen, NULL, 0, NULL, NULL); - struct linux_dirent64 *de = ptr; + struct linux_dirent64 *de; + int r; /* add a trailing NUL and round up to multiple of 4 */ - int len = (name_len + sizeof *de + 4) & ~3; + int len = (name_len + sizeof *de + 5) & ~3; + char *t; if (len > avail) return -1; + de = alloca(len); + de->d_ino = 0; - de->d_off = 0; + de->d_off = next_offset; de->d_type = 0; de->d_reclen = len; - WideCharToMultiByte(CP_UTF8, 0, entry, entrylen, de->d_name, avail, NULL, NULL); + memcpy(de->d_name, entry, name_len); de->d_name[name_len] = 0; + de->d_name[name_len+1] = type; + t = (char*) de; + t[len - 1] = type; dprintf("added %s\n", de->d_name); + r = current->ops->memcpy_to(ptr, de, len); + if (r < 0) + return r; + return len; } @@ -1587,9 +1606,9 @@ int sys_close(int fd) if (!fp) return -_L(EBADF); - if (fp->handle) - CloseHandle(fp->handle); - current->handles[fd] = 0; + if (fp->ops->fn_close) + fp->ops->fn_close(fp); + current->handles[fd] = NULL; return 0; } @@ -1626,31 +1645,37 @@ int sys_llseek(unsigned int fd, unsigned long offset_high, #define SECSPERDAY 86400 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY) -int sys_time(time_t *tloc) +static void gettimeval(struct timeval *tv) { SYSTEMTIME st; FILETIME ft; ULONGLONG seconds; - time_t t; - - dprintf("sys_time(%p)\n", tloc); GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); seconds = (((ULONGLONG)ft.dwHighDateTime) << 32) + ft.dwLowDateTime; - seconds /= 10000000LL; + seconds /= 10LL; + tv->tv_usec = seconds % 1000000LL; + seconds /= 1000000LL; seconds -= SECS_1601_TO_1970; - t = (time_t)(seconds&0xffffffff); + tv->tv_sec = (time_t)(seconds&0xffffffff); +} + +int sys_time(time_t *tloc) +{ + struct timeval tv; + + dprintf("sys_time(%p)\n", tloc); + + timeout_now(&tv); if (tloc) { // TODO: How is an error returned here? - ULONG sz; - NtWriteVirtualMemory(current->process, tloc, - &t, sizeof t, &sz); + current->ops->memcpy_to(tloc, &tv.tv_sec, sizeof tv.tv_sec); } - return (seconds&0xffffffff); + return tv.tv_sec; } int sys_exec(const char *fn, const char **ap, const char **ep) @@ -1743,12 +1768,16 @@ int sys_getcwd(char *buf, unsigned long size) { const char *dir = current->cwd; size_t len; + int r; dprintf("getcwd(%p,%lu)\n", buf, size); len = strlen(dir) + 1; if (len > size) return -_L(ERANGE); - return current->ops->memcpy_to(buf, dir, len); + r = current->ops->memcpy_to(buf, dir, len); + if (r < 0) + return r; + return len; } int do_chdir(const char *dir) @@ -1972,12 +2001,20 @@ void* sys_mmap(void *addr, ULONG len, int prot, int flags, int fd, off_t offset) return addr; } -int sys_select(int maxfd, void *readfds, void *writefds, - void *exceptfds, void *tv) +int sys_gettimeofday(void *arg) { - dprintf("select(%d,%p,%p,%p,%p)\n", - maxfd, readfds, writefds, exceptfds, tv); - return -_L(ENOSYS); + struct timeval tv; + int r; + + dprintf("sys_gettimeofday(%p)\n", arg); + + timeout_now(&tv); + + r = current->ops->memcpy_to(arg, &tv, sizeof tv); + if (r < 0) + return r; + + return 0; } int sys_munmap(void *addr, size_t len) @@ -2050,6 +2087,18 @@ int sys_setgid(int gid) return 0; } +int sys_setreuid(int uid, int euid) +{ + dprintf("setreuid(%d, %d)\n", uid, euid); + return 0; +} + +int sys_setregid(int gid, int egid) +{ + dprintf("setregid(%d, %d)\n", gid, egid); + return 0; +} + int sys_getgid(void) { dprintf("getgid()\n"); @@ -2090,6 +2139,57 @@ static int do_stat64(const char *file, struct stat64 *statbuf, BOOL follow_links return r; } +static void stat_from_stat64(struct stat *st, const struct stat64 *st64) +{ + memset(st, 0, sizeof *st); +#define X(field) st->st_##field = st64->st_##field; + X(dev) + X(ino) + X(mode) + X(nlink) + X(uid) + X(gid) + X(rdev) + X(size) + X(blksize) + X(blocks) + X(atime) + X(atime_nsec) + X(mtime) + X(mtime_nsec) + X(ctime) + X(ctime_nsec) +#undef X +} + +int sys_stat(const char *ptr, struct stat *statbuf) +{ + struct stat64 st64; + int r; + char *path = NULL; + + r = read_string(ptr, &path); + if (r < 0) + { + dprintf("stat64(,%p)\n", statbuf); + return r; + } + + dprintf("stat(\"%s\",%p)\n", path, statbuf); + + r = do_stat64(path, &st64, TRUE); + if (r == 0) + { + struct stat st; + stat_from_stat64(&st, &st64); + r = current->ops->memcpy_to(statbuf, &st, sizeof st); + } + + free(path); + + return r; +} + int sys_stat64(const char *ptr, struct stat64 *statbuf) { struct stat64 st; @@ -2316,7 +2416,7 @@ int sys_pipe(int *ptr) sys_close(fds[1]); } - return r; + return 0; } static struct process *find_zombie(struct process *parent) @@ -2330,16 +2430,6 @@ static struct process *find_zombie(struct process *parent) return NULL; } - -static int process_reap_zombie(struct process *p) -{ - int exit_code = p->exit_code; - - process_free(p); - - return exit_code; -} - int sys_waitpid(int pid, int *stat_addr, int options) { struct process *p = NULL; @@ -2373,9 +2463,13 @@ int sys_waitpid(int pid, int *stat_addr, int options) if (p) { /* TODO: handle WIFSIGNALED(), etc */ - exit_code = process_reap_zombie(p); - r = current->ops->memcpy_to(stat_addr, &exit_code, + int status = (p->exit_code & 0xff) << 8; + r = current->ops->memcpy_to(stat_addr, &status, sizeof exit_code); + if (r < 0) + return r; + r = (ULONG)p->id.UniqueProcess; + process_free(p); } dprintf("waitpid() -> %d\n", r); @@ -2475,24 +2569,13 @@ static int poll_check(filp **fps, struct _l_pollfd *fds, int nfds) return ready; } -int sys_poll(struct _l_pollfd *ptr, int nfds, int timeout) +int do_poll(int nfds, struct _l_pollfd *fds, struct timeval *tv) { - int r; - int ready = -_L(EBADF); - int i; struct wait_entry *wait_list; - struct _l_pollfd *fds; filp **fps; struct poll_timeout pt; - - dprintf("poll(%p,%d,%d)\n", ptr, nfds, timeout); - - if (nfds < 0) - return -_L(EINVAL); - - /* stack memory, no free needed */ - fds = alloca(nfds * sizeof fds[0]); - memset(fds, 0, nfds * sizeof fds[0]); + int ready; + int i; wait_list = alloca(nfds * sizeof wait_list[0]); memset(wait_list, 0, nfds * sizeof wait_list[0]); @@ -2507,18 +2590,19 @@ int sys_poll(struct _l_pollfd *ptr, int nfds, int timeout) } ready = poll_check(fps, fds, nfds); - if (ready || !timeout) - goto end; + if (ready) + return ready; pt.t.fn = &poll_timeout_waker; pt.p = current; pt.timed_out = 0; - if (timeout) - timeout_add_ms(&pt.t, timeout); + if (tv) + timeout_add_tv(&pt.t, tv); for (i = 0; i < nfds; i++) { + wait_list[i].p = current; fps[i]->ops->fn_poll_add(fps[i], &wait_list[i]); } @@ -2535,26 +2619,182 @@ int sys_poll(struct _l_pollfd *ptr, int nfds, int timeout) for (i = 0; i < nfds; i++) { - fps[i]->ops->fn_poll_del(fps[i], - &wait_list[i]); + fps[i]->ops->fn_poll_del(fps[i], &wait_list[i]); } - if (timeout) + if (tv) timeout_remove(&pt.t); -end: - /* copy back */ - r = current->ops->memcpy_to(ptr, fds, nfds * sizeof fds[0]); + return ready; +} + +int sys_poll(struct _l_pollfd *ptr, int nfds, int timeout) +{ + int r; + int ready; + struct _l_pollfd *fds; + struct timeval tv = {0, 0}; + struct timeval *ptv = NULL; + + dprintf("poll(%p,%d,%d)\n", ptr, nfds, timeout); + + if (nfds < 0) + return -_L(EINVAL); + + /* stack memory, no free needed */ + fds = alloca(nfds * sizeof fds[0]); + memset(fds, 0, nfds * sizeof fds[0]); + + r = current->ops->memcpy_from(fds, ptr, nfds * sizeof fds[0]); if (r < 0) return r; + if (timeout >= 0) + { + tv_from_ms(&tv, timeout); + ptv = &tv; + } + + ready = do_poll(nfds, fds, ptv); + if (ready >= 0) + { + /* copy back */ + r = current->ops->memcpy_to(ptr, fds, nfds * sizeof fds[0]); + if (r < 0) + return r; + } + + return ready; +} + +static inline int fdset_has(struct fdset *fds, int fd) +{ + int n = sizeof fds->fds_bits[0] * 8; + unsigned long int mask = 1 << (fd % n); + return (fds->fds_bits[fd / n] & mask); +} + +static inline void fdset_set(struct fdset *fds, int fd) +{ + int n = sizeof fds->fds_bits[0] * 8; + unsigned long int mask = 1 << (fd % n); + fds->fds_bits[fd / n] |= mask; +} + +int do_select(int maxfd, void *rfds, void *wfds, void *efds, void *tvptr) +{ + struct fdset rdset, wrset, exset; + int i, r, nfds, ready; + struct _l_pollfd fds[_L(FD_SETSIZE)]; + struct timeval tv = {0, 0}; + + dprintf("select(%d,%p,%p,%p,%p)\n", + maxfd, rfds, wfds, efds, tvptr); + + if (maxfd < 0 || maxfd > _L(FD_SETSIZE)) + return -_L(EINVAL); + + if (tvptr) + { + r = current->ops->memcpy_from(&tv, tvptr, sizeof tv); + if (r < 0) + return -_L(EFAULT); + } + + memset(&rdset, 0, sizeof rdset); + memset(&wrset, 0, sizeof wrset); + memset(&exset, 0, sizeof exset); + + if (rfds) + { + r = current->ops->memcpy_from(&rdset, rfds, sizeof rdset); + if (r < 0) + return -_L(EFAULT); + } + + if (wfds) + { + r = current->ops->memcpy_from(&wrset, wfds, sizeof wrset); + if (r < 0) + return -_L(EFAULT); + } + + if (efds) + { + r = current->ops->memcpy_from(&exset, efds, sizeof exset); + if (r < 0) + return -_L(EFAULT); + } + + /* convert to an array of pollfds */ + nfds = 0; + for (i = 0; i < _L(FD_SETSIZE); i++) + { + int events = 0; + + if (fdset_has(&rdset, i)) + events |= _l_POLLIN; + if (fdset_has(&wrset, i)) + events |= _l_POLLOUT; + if (fdset_has(&exset, i)) + events |= (_l_POLLERR | _l_POLLHUP); + + if (events) + { + fds[nfds].fd = i; + fds[nfds].events = events; + fds[nfds].revents = 0; + nfds++; + } + } + + ready = do_poll(nfds, fds, tvptr ? &tv : NULL); + if (ready >= 0) + { + memset(&rdset, 0, sizeof rdset); + memset(&wrset, 0, sizeof wrset); + memset(&exset, 0, sizeof exset); + for (i = 0; i < nfds; i++) + { + if (fds[i].revents & _L(POLLIN)) + fdset_set(&rdset, i); + if (fds[i].revents & _L(POLLOUT)) + fdset_set(&wrset, i); + if (fds[i].revents & (_L(POLLERR)|_L(POLLHUP))) + fdset_set(&exset, i); + } + if (rfds) + current->ops->memcpy_to(rfds, &rdset, sizeof rdset); + if (wfds) + current->ops->memcpy_to(wfds, &wrset, sizeof wrset); + if (efds) + current->ops->memcpy_to(efds, &exset, sizeof exset); + } + return ready; } +int sys_select(void *select_args) +{ + struct { + int nfds; + struct fdset *rfds; + struct fdset *wfds; + struct fdset *efds; + struct timeval *tv; + } a; + int r; + + r = current->ops->memcpy_from(&a, select_args, sizeof a); + if (r < 0) + return -_L(EFAULT); + + return do_select(a.nfds, a.rfds, a.wfds, a.efds, a.tv); +} + int sys_newuname(struct _l_new_utsname *ptr) { struct _l_new_utsname un; - int r; dprintf("newuname(%p)\n", ptr); strcpy(un.sysname, "Linux"); @@ -2563,10 +2803,8 @@ int sys_newuname(struct _l_new_utsname *ptr) strcpy(un.version, "atratus v0.1"); strcpy(un.machine, "i686"); strcpy(un.domainname, "(none)"); - r = current->ops->memcpy_to(ptr, &un, sizeof un); - if (r < 0) - return r; - return 0; + + return current->ops->memcpy_to(ptr, &un, sizeof un); } static inline void *ptr(int x) @@ -2668,12 +2906,24 @@ int do_syscall(int n, int a1, int a2, int a3, int a4, int a5, int a6) r = sys_getpgrp(); break; #endif + case 70: + r = sys_setreuid(a1, a2); + break; + case 71: + r = sys_setregid(a1, a2); + break; + case 78: + r = sys_gettimeofday(ptr(a1)); + break; case 82: - r = sys_select(a1, ptr(a2), ptr(a3), ptr(a4), ptr(a5)); + r = sys_select(ptr(a1)); break; case 91: r = sys_munmap(ptr(a1), a2); break; + case 106: + r = sys_stat(ptr(a1), ptr(a2)); + break; case 122: r = sys_newuname(ptr(a1)); break; @@ -3465,7 +3715,9 @@ static NTSTATUS ReadDebugPort(HANDLE debugObject) context = context_from_client_id(&event.ClientId); if (!context) { - printf("received event for unknown process\n"); + printf("received event for unknown process %08x:%08x\n", + event.ClientId.UniqueProcess, + event.ClientId.UniqueThread); return STATUS_UNSUCCESSFUL; } @@ -3738,16 +3990,7 @@ static struct timeout *timeout_head; void timeout_now(struct timeval *tv) { - SYSTEMTIME st; - FILETIME ft; - ULONGLONG seconds; - - GetSystemTime(&st); - SystemTimeToFileTime(&st, &ft); - - seconds = (((ULONGLONG)ft.dwHighDateTime) << 32) + ft.dwLowDateTime; - tv->tv_sec = seconds / 10000000LL; - tv->tv_usec = (ft.dwLowDateTime / 10) % 1000000; + gettimeval(tv); } static void timeout_dprint_timeval(struct timeval *tv, const char *what) @@ -3781,24 +4024,24 @@ void timeout_add_tv(struct timeout *t, struct timeval *ts) timeout_now(&now); t->tv.tv_usec = now.tv_usec + ts->tv_usec; - t->tv.tv_sec = now.tv_sec + ts->tv_usec; + t->tv.tv_sec = now.tv_sec + ts->tv_sec; t->tv.tv_sec += t->tv.tv_usec / (1000 * 1000); t->tv.tv_usec %= (1000 * 1000); timeout_add(t); } -void timeout_add_ms(struct timeout *t, int ms) +void tv_from_ms(struct timeval *tv, int ms) { - struct timeval now; - - timeout_now(&now); - - t->tv.tv_usec = now.tv_usec + (ms%1000) * 1000; - t->tv.tv_sec = now.tv_sec + (ms / 1000) + now.tv_usec / (1000 * 1000); - t->tv.tv_usec %= (1000 * 1000); + tv->tv_usec = (ms % 1000) * 1000; + tv->tv_sec = (ms / 1000); +} - timeout_add(t); +void timeout_add_ms(struct timeout *t, int ms) +{ + struct timeval tv; + tv_from_ms(&tv, ms); + timeout_add_tv(t, &tv); } void timeout_add(struct timeout *t) @@ -3858,8 +4101,10 @@ int main(int argc, char **argv) HANDLE debugObject = 0; NTSTATUS r; struct process *context = alloc_process(); - filp *tty = NULL; int n = 1; + HANDLE console_handle; + DWORD console_mode = 0; + BOOL backtrace_on_ctrl_c = 0; context->cwd = strdup("/"); context->uid = 1000; @@ -3878,6 +4123,7 @@ int main(int argc, char **argv) char *env[] = { "TERM=vt100", + "PS1=$ ", "PATH=/usr/local/bin:/usr/bin:/bin", NULL, }; @@ -3903,10 +4149,23 @@ int main(int argc, char **argv) return 1; } - if (n < argc && !strcmp(argv[n], "-d")) + while (n < argc) { - verbose = 1; - n++; + if (!strcmp(argv[n], "-d")) + { + verbose = 1; + n++; + continue; + } + + if (!strcmp(argv[n], "-c")) + { + backtrace_on_ctrl_c = 1; + n++; + continue; + } + + break; } if (n >= argc) @@ -3941,11 +4200,18 @@ int main(int argc, char **argv) goto out; } - /* allocate a console for the first process only */ - tty = get_console(); - context->handles[0] = tty; - context->handles[1] = tty; - context->handles[2] = tty; + /* + * check if stdout is actually a console or not and + * allocate a console for the first process only + */ + console_handle = GetStdHandle(STD_OUTPUT_HANDLE); + if (!GetConsoleMode(console_handle, &console_mode)) + fprintf(stderr, "output not a console...\n"); + + current->tty = get_vt100_console(console_handle); + context->handles[0] = current->tty; + context->handles[1] = current->tty; + context->handles[2] = current->tty; /* move exec into fiber */ r = do_exec(argv[n], argv + n, env); @@ -4000,6 +4266,15 @@ int main(int argc, char **argv) if (r == WAIT_OBJECT_0) { fprintf(stderr,"User abort!\n"); + if (backtrace_on_ctrl_c) + { + SuspendThread(first_process->thread); + context = first_process; + read_process_registers(context); + dump_regs(current); + dump_stack(current); + backtrace(current); + } break; } else if (r == (WAIT_OBJECT_0 + 1)) diff --git a/server/debug.h b/server/debug.h index af8e6c8..2c8e1dd 100644 --- a/server/debug.h +++ b/server/debug.h @@ -1,6 +1,12 @@ #ifndef __ATRATUS_DEBUG_H__ #define __ATRATUS_DEBUG_H__ +#define STATIC_ASSERT(expr) \ + do { \ + char _sa[(expr) ? 1 : -1]; \ + (void) _sa; \ + } while(0) + int dprintf(const char *fmt, ...); #endif /* __ATRATUS_DEBUG_H__ */ diff --git a/server/dev.c b/server/dev.c index db26563..3ba8983 100644 --- a/server/dev.c +++ b/server/dev.c @@ -9,7 +9,7 @@ #include "tty.h" static int dev_stat64(struct fs *fs, const char *path, - struct stat64 *statbuf, BOOL follow_links) + struct stat64 *statbuf, bool follow_links) { return -_L(ENOENT); } @@ -25,7 +25,10 @@ static int dev_open(struct fs *fs, const char *file, int flags, int mode) file++; if (!strcmp(file, "tty")) - fp = get_console(); + fp = current->tty; + + if (!fp) + return -_L(ENOENT); fd = alloc_fd(); if (fd < 0) diff --git a/server/filp.h b/server/filp.h index 7f29ede..60e4c6a 100644 --- a/server/filp.h +++ b/server/filp.h @@ -2,51 +2,17 @@ #define __FILP_H__ #include +#include + +#include "linux-defines.h" typedef int64_t loff_t; typedef struct _filp filp; typedef struct _poll_list poll_list; -struct stat64 { - unsigned long long st_dev; - unsigned long long st_ino; - unsigned int st_mode; - unsigned int st_nlink; - unsigned int st_uid; - unsigned int st_gid; - unsigned long long st_rdev; - unsigned long long __pad1; - long long st_size; - int st_blksize; - int __pad2; - long long st_blocks; - int atime; - unsigned int atime_nsec; - int mtime; - unsigned int mtime_nsec; - int ctime; - unsigned int ctime_nsec; - unsigned int __unused1; - unsigned int __unused2; -}; - -struct linux_dirent { - unsigned long d_ino; - unsigned long d_off; - unsigned short d_reclen; - char d_name[]; -}; - -struct linux_dirent64 { - unsigned long long d_ino; - long long d_off; - unsigned short d_reclen; - unsigned char d_type; - char d_name[]; -}; - -typedef int (*fn_add_dirent)(void *, WCHAR* entry, USHORT entrylen, int avail); +typedef int (*fn_add_dirent)(void *, const char* entry, size_t entrylen, + int avail, unsigned long dirofs, char type); typedef void (*fn_wake)(filp *f, void *arg); struct wait_entry; @@ -60,6 +26,7 @@ struct filp_ops { int (*fn_poll)(filp *f); void (*fn_poll_add)(filp *f, struct wait_entry *we); void (*fn_poll_del)(filp *f, struct wait_entry *we); + void (*fn_close)(filp *f); }; struct _filp { @@ -84,7 +51,7 @@ struct fs struct fs *next; int (*open)(struct fs *fs, const char *subpath, int flags, int mode); int (*stat64)(struct fs *fs, const char *path, - struct stat64 *statbuf, BOOL follow_links); + struct stat64 *statbuf, bool follow_links); }; extern int alloc_fd(void); diff --git a/server/pipe.c b/server/pipe.c index 64d1b4c..8b60025 100644 --- a/server/pipe.c +++ b/server/pipe.c @@ -1,8 +1,59 @@ +#include #include "pipe.h" +#include "filp.h" +#include "process.h" #include "linux-errno.h" +#include "debug.h" -int do_pipe(int *fds) +static int pipe_read(filp *f, void *buf, size_t size, loff_t *off) +{ + return 0; +} + +static int pipe_write(filp *f, const void *buf, size_t size, loff_t *off) { - return -_L(ENOSYS); + return 0; } +static const struct filp_ops pipe_ops = { + .fn_read = &pipe_read, + .fn_write = &pipe_write, +}; + +int do_pipe(int *fds) +{ + filp *fp; + + fp = malloc(sizeof (*fp)); + if (!fp) + return -_L(ENOMEM); + + memset(fp, 0, sizeof *fp); + fp->ops = &pipe_ops; + fp->pgid = 0; + fp->handle = INVALID_HANDLE_VALUE; + fp->offset = 0; + + fds[0] = alloc_fd(); + if (fds[0] < 0) + { + free(fp); + return -_L(ENOMEM); + } + + current->handles[fds[0]] = fp; + + fds[1] = alloc_fd(); + if (fds[1] < 0) + { + /* FIXME: leaks fd0 */ + free(fp); + return -_L(ENOMEM); + } + + current->handles[fds[1]] = fp; + + dprintf("fds[] -> %d, %d\n", fds[0], fds[1]); + + return 0; +} diff --git a/server/process.h b/server/process.h index 01f1072..06ea646 100644 --- a/server/process.h +++ b/server/process.h @@ -61,6 +61,7 @@ struct process int exit_code; PVOID fiber; char *cwd; + filp *tty; }; extern struct process *current; @@ -138,9 +139,11 @@ struct timeout }; extern void timeout_add(struct timeout *t); +extern void tv_from_ms(struct timeval *t, int ms); extern void timeout_add_ms(struct timeout *t, int ms); extern void timeout_add_tv(struct timeout *t, struct timeval *ts); extern void timeout_remove(struct timeout *t); +extern void timeout_now(struct timeval *tv); struct signal_waiter { diff --git a/server/tty.c b/server/tty.c index 346b2b1..b68ce6a 100644 --- a/server/tty.c +++ b/server/tty.c @@ -30,77 +30,72 @@ #include "linux-defines.h" #include "debug.h" -typedef struct _con_filp con_filp; - -typedef void *(*con_callback_fn)(void *param); - -struct con_callback { - con_callback_fn fn; - void *param; - struct con_callback *next; -}; - -#define MAX_VT100_PARAMS 3 - -struct termios -{ - unsigned int c_iflag; - unsigned int c_oflag; - unsigned int c_cflag; - unsigned int c_lflag; - unsigned char c_line; - unsigned char c_cc[8]; -}; - -#define ICANON 2 - -struct _con_filp { - filp fp; - int state; - int num[MAX_VT100_PARAMS]; - int num_count; - int fg_color; - int bg_color; - int brightness; - int ready_count; - struct termios tios; - int eof; - unsigned char ready_data[20]; - struct wait_list wl; - HANDLE thread; - CRITICAL_SECTION cs; -}; - -/* vt100 emulation */ - -void con_poll_add(filp *f, struct wait_entry *we); -void con_poll_del(filp *f, struct wait_entry *we); +static void con_poll_add(filp *f, struct wait_entry *we); +static void con_poll_del(filp *f, struct wait_entry *we); static int con_is_canonical(con_filp *con) { return con->tios.c_lflag & ICANON; } -int con_input_available(con_filp *con) +/* + * Process input characters into an input string + * return number of characters ready to read + */ +static int con_process_input(con_filp *con) { - int i; - int r = 0; + int i, pos, end; if (!con_is_canonical(con)) - r = (con->ready_count > 0); - else + return con->ready_count; + + /* remove VERASE characters, find line feed */ + pos = 0; + end = -1; + for (i = 0; i < con->ready_count; i++) { - for (i = 0; r == 0 && i < con->ready_count; i++) - if (con->ready_data[i] == '\r') - r = 1; + char ch = con->ready_data[i]; + + /* once we find a line feed character, just copy */ + if (end < 0 && !con->eof) + { + if (pos && ch == con->tios.c_cc[VERASE]) + { + pos--; + continue; + } + + if ((con->tios.c_iflag & INLCR) && ch == '\n') + ch = '\r'; + else if ((con->tios.c_iflag & ICRNL) && ch == '\r') + ch = '\n'; + + if ((con->tios.c_iflag & IUCLC) && ch >= 'A' && ch <= 'Z') + ch += ('a' - 'A'); + + if (ch == '\n') + { + end = pos; + con->ready_data[pos] = ch; + } + else if (ch == con->tios.c_cc[VEOF]) + { + end = pos - 1; + con->eof = 1; + } + } + + if (pos != i) + con->ready_data[pos] = ch; + pos++; } - dprintf("input available = %d\n", r); + con->ready_count -= (i - pos); - return r; + return end + 1; } -int con_read(filp *f, void *buf, size_t size, loff_t *ofs) +static int con_read(filp *f, void *buf, size_t size, loff_t *ofs) { con_filp *con = (con_filp*) f; int ret = 0; @@ -113,43 +108,37 @@ int con_read(filp *f, void *buf, size_t size, loff_t *ofs) while (ret < size && !con->eof && !done) { BOOL wait = 0; + size_t len; - EnterCriticalSection(&con->cs); + con->ops->fn_lock(con); assert(con->ready_count >= 0); - if (con_input_available(con)) + len = con_process_input(con); + if (len) { - BYTE ch = con->ready_data[0]; + if (len > size) + len = size; - /* FIXME: use a ring buffer */ - con->ready_count--; - memmove(con->ready_data, con->ready_data+1, con->ready_count); - - dprintf("tty read '%c'\n", ch); - - /* eof? */ - if (ch == 0x04) - con->eof = 1; - else + int r; + r = current->ops->memcpy_to(buf, con->ready_data, len); + if (r < 0) { - int r; - r = current->ops->memcpy_to(buf, &ch, 1); - if (r < 0) - { - if (ret > 0) - break; - return -_L(EFAULT); - } - buf = (char*)buf + 1; - ret++; + if (ret > 0) + break; + return -_L(EFAULT); } - if (con_is_canonical(con) && ch == '\r') - done = 1; + buf = (char*)buf + len; + ret += len; + + /* move everything to the front */ + con->ready_count -= len; + memmove(con->ready_data, con->ready_data+len, con->ready_count); + done = 1; } else { wait = 1; } - LeaveCriticalSection(&con->cs); + con->ops->fn_unlock(con); if (wait) { @@ -161,399 +150,74 @@ int con_read(filp *f, void *buf, size_t size, loff_t *ofs) con_poll_del(&con->fp, &we); - dprintf("r = %d\n", ret); + dprintf("con_read r = %d/%d\n", ret, size); return ret; } void tty_input_add_char(con_filp *con, char ch) { - EnterCriticalSection(&con->cs); - - if (con_is_canonical(con)) - { - // backspace - if (con->ready_count > 0 && ch == 9) - { - con->ready_count--; - } - } - - /* notify data is present */ - if (!con_input_available(con)) - { - struct wait_entry *we; + struct wait_entry *we; - for (we = con->wl.head; we; we = we->next) - { - ready_list_add(we->p); - } - } + con->ops->fn_lock(con); /* add data to buffer */ if (con->ready_count < sizeof con->ready_data) con->ready_data[con->ready_count++] = ch; - dprintf("added '%c' to buffer\n", ch); - LeaveCriticalSection(&con->cs); -} - -void tty_input_add_string(con_filp *con, const char *string) -{ - int n = 0; - - EnterCriticalSection(&con->cs); - while (string[n]) - { - tty_input_add_char(con, string[n]); - n++; - } - LeaveCriticalSection(&con->cs); -} + /* TODO: send only to the controlling terminal */ + for (we = con->wl.head; we; we = we->next) + ready_list_add(we->p); -int tty_set_cursor_pos(con_filp *con, int x, int y) -{ - COORD coord; - CONSOLE_SCREEN_BUFFER_INFO info; - HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD height, width; - BOOL r; - - dprintf("cursor to %d,%d\n", x, y); - - GetConsoleScreenBufferInfo(handle, &info); - dprintf("dwSize %d,%d\n", info.dwSize.X, info.dwSize.Y); - dprintf("srWindow %d,%d-%d,%d\n", - info.srWindow.Left, info.srWindow.Top, - info.srWindow.Right, info.srWindow.Bottom); - height = info.srWindow.Bottom - info.srWindow.Top + 1; - width = info.srWindow.Right - info.srWindow.Left + 1; - if (x >= width) - x = width; - if (y >= height) - y = height; - if (y < 1) - y = 1; - if (x < 1) - x = 1; - coord.X = x - 1; - coord.Y = y - 1; - dprintf("Set %d,%d\n", coord.X, coord.Y); - r = SetConsoleCursorPosition(handle, coord); - if (!r) - dprintf("failed to set cursor\n"); - return 0; + con->ops->fn_unlock(con); } -static DWORD tty_get_attributes(con_filp *con) +void tty_input_add_string(con_filp *con, const char *string) { - DWORD dwAttribute = 0; - - if (con->brightness) - dwAttribute |= FOREGROUND_INTENSITY; - - switch (con->fg_color) - { - case 30: /* Black */ - break; - case 31: /* Red */ - dwAttribute |= FOREGROUND_RED; - break; - case 32: /* Green */ - dwAttribute |= FOREGROUND_GREEN; - break; - case 33: /* Yellow */ - dwAttribute |= FOREGROUND_RED | FOREGROUND_GREEN; - break; - case 34: /* Blue */ - dwAttribute |= FOREGROUND_BLUE; - break; - case 35: /* Magenta */ - dwAttribute |= FOREGROUND_RED | FOREGROUND_BLUE; - break; - case 36: /* Cyan */ - dwAttribute |= FOREGROUND_GREEN | FOREGROUND_BLUE; - break; - case 37: /* White */ - dwAttribute |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; - break; - } + struct wait_entry *we; + int len = strlen(string); - switch (con->bg_color) + con->ops->fn_lock(con); + if ((con->ready_count + len) <= sizeof con->ready_data) { - case 40: /* Black */ - break; - case 41: /* Red */ - dwAttribute |= BACKGROUND_RED; - break; - case 42: /* Green */ - dwAttribute |= BACKGROUND_GREEN; - break; - case 43: /* Yellow */ - dwAttribute |= BACKGROUND_RED | BACKGROUND_GREEN; - break; - case 44: /* Blue */ - dwAttribute |= BACKGROUND_BLUE; - break; - case 45: /* Magenta */ - dwAttribute |= BACKGROUND_RED | BACKGROUND_BLUE; - break; - case 46: /* Cyan */ - dwAttribute |= BACKGROUND_GREEN | BACKGROUND_BLUE; - break; - case 47: /* White */ - dwAttribute |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED; - break; + memcpy(&con->ready_data[con->ready_count], string, len); + con->ready_count += len; } - return dwAttribute; -} - -int tty_set_color(con_filp *con, int code) -{ - HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD dwAttribute; - - if (code >= 30 && code <= 37) - con->fg_color = code; - if (code >= 40 && code <= 47) - con->bg_color = code; - if (code >= 0 && code <= 1) - con->brightness = code; - - dwAttribute = tty_get_attributes(con); - - SetConsoleTextAttribute(handle, dwAttribute); - return 0; -} + for (we = con->wl.head; we; we = we->next) + ready_list_add(we->p); -void tty_do_cr(con_filp *con) -{ - HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); - CONSOLE_SCREEN_BUFFER_INFO info; - COORD coord; - - GetConsoleScreenBufferInfo(handle, &info); - coord = info.dwCursorPosition; - coord.X = 0; - SetConsoleCursorPosition(handle, coord); -} - -void tty_do_lf(con_filp *con) -{ - HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); - CONSOLE_SCREEN_BUFFER_INFO info; - COORD coord; - - GetConsoleScreenBufferInfo(handle, &info); - coord = info.dwCursorPosition; - if (1) - coord.X = 0; - if (coord.Y >= info.srWindow.Bottom) - { - SMALL_RECT rect; - COORD topleft; - CHAR_INFO ci; - - topleft.X = info.srWindow.Left; - topleft.Y = info.srWindow.Top - 1; - rect = info.srWindow; - ci.Char.AsciiChar = ' '; - ci.Attributes = 0; - ScrollConsoleScreenBuffer(handle, &rect, NULL, topleft, &ci); - } - else - coord.Y++; - SetConsoleCursorPosition(handle, coord); + con->ops->fn_unlock(con); } -int tty_write_normal(con_filp *con, unsigned char ch) +/* write to the terminal, observing termios settings */ +static int con_write_output(con_filp *con, unsigned char ch) { - HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD write_count = 0; - BOOL r; - switch (ch) { - case 0x1b: - con->state = 1; - break; - case '\r': - tty_do_cr(con); - break; -#if 0 - case '\n': - tty_do_lf(con); - break; -#endif - default: - r = WriteConsole(handle, &ch, 1, &write_count, NULL); - if (!r) - return -_L(EIO); + case 0x0d: + if (con->tios.c_oflag & ONLRET) + return 0; + if (con->tios.c_oflag & OCRNL) + ch = 0x0d; + break; + case 0x0a: + if (con->tios.c_oflag & ONLCR) + con->ops->fn_write(con, 0x0d); + return con->ops->fn_write(con, 0x0a); } - return 0; -} - -int tty_write_wait_lbracket(con_filp *con, unsigned char ch) -{ - if (ch == '[') + /* map lower case to upper case */ + if (con->tios.c_oflag & OLCUC) { - int i; - - con->state = 2; - for (i=0; inum[i] = 0; - con->num_count = 0; + if (ch >= 'a' && ch <= 'z') + ch = ch - 'a' + 'A'; } - else - con->state = 0; - return 0; + return con->ops->fn_write(con, ch); } -void tty_device_status(con_filp *con, int req) -{ - char response[16]; - CONSOLE_SCREEN_BUFFER_INFO info; - HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); - - switch (req) - { - case 6: /* query cursor position */ - GetConsoleScreenBufferInfo(handle, &info); - sprintf(response, "\x1b[%d;%dR", - info.dwCursorPosition.Y+1, - info.dwCursorPosition.X+1); - dprintf("response = %s\n", response+1); - tty_input_add_string(con, response); - break; - case 5: /* query device status */ - default: - dprintf("unknown request %d\n", req); - } -} - -void tty_erase_to_end_of_line(con_filp *con) -{ - CONSOLE_SCREEN_BUFFER_INFO info; - HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); - COORD pos; - DWORD count; - - GetConsoleScreenBufferInfo(handle, &info); - - pos = info.dwCursorPosition; - - dprintf("erasing at %d,%d\n", pos.X, pos.Y); - - FillConsoleOutputCharacter(handle, ' ', - info.dwSize.X - pos.X, pos, &count); - - SetConsoleCursorPosition(handle, pos); -} - -void tty_erase_to_end_of_screen(con_filp *con) -{ - CONSOLE_SCREEN_BUFFER_INFO info; - HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); - COORD pos; - DWORD count; - - GetConsoleScreenBufferInfo(handle, &info); - - pos = info.dwCursorPosition; - - FillConsoleOutputCharacter(handle, ' ', - (info.dwSize.Y - pos.Y) * info.dwSize.X + - info.dwSize.X - pos.X, pos, &count); - - SetConsoleCursorPosition(handle, pos); -} - -void tty_move_cursor(con_filp *con, int delta_x, int delta_y) -{ - CONSOLE_SCREEN_BUFFER_INFO info; - HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); - COORD pos; - - GetConsoleScreenBufferInfo(handle, &info); - - pos = info.dwCursorPosition; - - if (pos.X > 0 && pos.X < info.dwSize.X) - pos.X += delta_x; - if (pos.Y > 0 && pos.X < info.dwSize.Y) - pos.Y += delta_y; - - SetConsoleCursorPosition(handle, pos); -} - -int tty_write_wait_number(con_filp *con, unsigned char ch) -{ - switch (ch) - { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - con->num[con->num_count] *= 10; - con->num[con->num_count] += (ch - '0'); - break; - case ';': - con->num_count++; - if (con->num_count >= MAX_VT100_PARAMS) - { - dprintf("too many tty params\n", ch); - con->state = 0; - } - break; - case 'm': - tty_set_color(con, con->num[0]); - con->state = 0; - break; - case 'n': - tty_device_status(con, con->num[0]); - con->state = 0; - break; - case 'A': - tty_move_cursor(con, 0, -con->num[0]); - break; - case 'B': - tty_move_cursor(con, 0, con->num[0]); - break; - case 'C': - tty_move_cursor(con, -con->num[0], 0); - break; - case 'D': - tty_move_cursor(con, con->num[0], 0); - break; - case 'K': - tty_erase_to_end_of_line(con); - con->state = 0; - break; - case 'J': - tty_erase_to_end_of_screen(con); - con->state = 0; - break; - case 'H': - tty_set_cursor_pos(con, con->num[1], con->num[0]); - con->state = 0; - break; - default: - dprintf("unknown escape code %c (num1)\n", ch); - con->state = 0; - } - return 0; -} - -typedef int (*tty_state_fn)(con_filp *con, unsigned char ch); - -tty_state_fn tty_state_list[] = { - tty_write_normal, - tty_write_wait_lbracket, - tty_write_wait_number, -}; - -int con_write(filp *f, const void *buf, size_t size, loff_t *off) +static int con_write(filp *f, const void *buf, size_t size, loff_t *off) { con_filp *con = (con_filp*) f; DWORD written = 0; @@ -577,13 +241,14 @@ int con_write(filp *f, const void *buf, size_t size, loff_t *off) break; return r; } - buf_remaining = r; + dprintf("tty out -> '%*s'\n", sz, buffer); + buf_remaining = sz; buf = (char*)buf + buf_remaining; p = buffer; } if (buf_remaining == 0) break; - r = tty_state_list[con->state](con, *p); + r = con_write_output(con, *p); if (r < 0) break; written++; @@ -598,8 +263,25 @@ int con_write(filp *f, const void *buf, size_t size, loff_t *off) static int con_set_termios(con_filp *con, void *p) { - dprintf("set termios(%p)\n", p); - return current->ops->memcpy_from(&con->tios, p, sizeof con->tios); + int r; + /* + * There's two or three different termios structures. + * TODO: use the kernel structure, not the libc one + */ + STATIC_ASSERT(sizeof con->tios == 60); + + r = current->ops->memcpy_from(&con->tios, p, sizeof con->tios); + if (r < 0) + return r; + dprintf("tios: %s %s %s %s %s\n", + (con->tios.c_oflag & ONLRET) ? "ONLRET" : "~ONLRET", + (con->tios.c_oflag & OCRNL) ? "OCRNL" : "~OCRNL", + (con->tios.c_oflag & ONLCR) ? "ONLCR" : "~ONLCR", + (con->tios.c_oflag & OLCUC) ? "OLCUC" : "~OLCUC", + (con->tios.c_oflag & OPOST) ? "OPOST" : "~POST" + ); + + return 0; } static int con_get_termios(con_filp *con, void *p) @@ -608,44 +290,15 @@ static int con_get_termios(con_filp *con, void *p) return current->ops->memcpy_to(p, &con->tios, sizeof con->tios); } -#define TIOCGPGRP 0x540F -#define TIOCSPGRP 0x5410 -#define TIOCGWINSZ 0x5413 - -#define TIOCG 0x5401 -#define TIOCS 0x5402 - -struct winsize { - unsigned short ws_row; - unsigned short ws_col; - unsigned short ws_xpixel; - unsigned short ws_ypixel; -}; - -static int con_get_winsize(void *ptr) +static int con_get_winsize(con_filp *con, void *ptr) { - CONSOLE_SCREEN_BUFFER_INFO info; - HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); struct winsize ws; - int r; - - GetConsoleScreenBufferInfo(handle, &info); - - ws.ws_col = info.srWindow.Right - info.srWindow.Left + 1; - ws.ws_row = info.srWindow.Bottom - info.srWindow.Top + 1; - ws.ws_xpixel = 0; - ws.ws_ypixel = 0; - dprintf("winsize -> %d,%d\n", ws.ws_col, ws.ws_row); - - r = current->ops->memcpy_to(ptr, &ws, sizeof ws); - if (r < 0) - return r; - - return 0; + con->ops->fn_get_winsize(con, &ws); + return current->ops->memcpy_to(ptr, &ws, sizeof ws); } -int con_ioctl(filp *f, int cmd, unsigned long arg) +static int con_ioctl(filp *f, int cmd, unsigned long arg) { con_filp *con = (con_filp*) f; int *pgrp; @@ -665,7 +318,7 @@ int con_ioctl(filp *f, int cmd, unsigned long arg) case TIOCG: return con_get_termios(con, (void*)arg); case TIOCGWINSZ: - return con_get_winsize((void*)arg); + return con_get_winsize(con, (void*)arg); default: dprintf("unknown tty ioctl(%08x, %08x)\n", cmd, arg); r = -_L(EINVAL); @@ -678,76 +331,30 @@ static int con_poll(filp *f) { int events = 0; con_filp *con = (con_filp*) f; - EnterCriticalSection(&con->cs); - if (con_input_available(con)) + con->ops->fn_lock(con); + if (0 != con_process_input(con)) events |= _l_POLLIN; events |= _l_POLLOUT; - LeaveCriticalSection(&con->cs); + con->ops->fn_unlock(con); return events; } -void con_poll_add(filp *f, struct wait_entry *we) +static void con_poll_add(filp *f, struct wait_entry *we) { con_filp *con = (con_filp*) f; - EnterCriticalSection(&con->cs); + con->ops->fn_lock(con); wait_entry_append(&con->wl, we); - LeaveCriticalSection(&con->cs); + con->ops->fn_unlock(con); } -void con_poll_del(filp *f, struct wait_entry *we) +static void con_poll_del(filp *f, struct wait_entry *we) { con_filp *con = (con_filp*) f; - EnterCriticalSection(&con->cs); + con->ops->fn_lock(con); wait_entry_remove(&con->wl, we); - LeaveCriticalSection(&con->cs); -} - -DWORD WINAPI con_input_thread(LPVOID param) -{ - HANDLE in = GetStdHandle(STD_INPUT_HANDLE); - con_filp *con = param; - unsigned char ch = 0; - DWORD count = 0; - BOOL r; - INPUT_RECORD ir; - - while (1) - { - r = ReadConsoleInput(in, &ir, 1, &count); - if (!r) - { - fprintf(stderr, "\nReadConsoleInput failed\n"); - break; - } - if (!count) - break; - - if (ir.EventType != KEY_EVENT) - continue; - - if (!ir.Event.KeyEvent.bKeyDown) - continue; - - switch (ir.Event.KeyEvent.wVirtualKeyCode) - { - case VK_RETURN: - ch = '\r'; - break; - default: - ch = ir.Event.KeyEvent.uChar.AsciiChar; - } - - if (!ch) - continue; - - tty_input_add_char(con, ch); - } - - fprintf(stderr, "\ninput thread died\n"); - - return 0; + con->ops->fn_unlock(con); } static const struct filp_ops con_file_ops = { @@ -759,45 +366,18 @@ static const struct filp_ops con_file_ops = { .fn_poll_del = &con_poll_del, }; -static filp* alloc_console(void) +void tty_init(con_filp *con) { - HANDLE in = GetStdHandle(STD_INPUT_HANDLE); - DWORD id = 0; - con_filp *con; - - con = malloc(sizeof *con); - - memset(con, 0, sizeof *con); - con->fp.ops = &con_file_ops; con->fp.handle = NULL; con->fp.pgid = 0; con->fp.poll_first = NULL; con->fp.pgid = 0; - con->state = 0; - con->num_count = 0; - con->fg_color = 37; - con->bg_color = 40; - con->brightness = 0; - con->ready_count = 0; + con->tios.c_lflag = ICANON | ECHO; + con->tios.c_cc[VERASE] = 8; + con->tios.c_cc[VEOF] = 4; + con->tios.c_oflag = ONLCR | OPOST; con->eof = 0; - con->tios.c_lflag |= ICANON; - - SetConsoleMode(in, ENABLE_PROCESSED_INPUT); - - InitializeCriticalSection(&con->cs); - - con->thread = CreateThread(NULL, 0, &con_input_thread, con, 0, &id); - - return &con->fp; -} - -static filp *condev; - -filp* get_console(void) -{ - if (!condev) - condev = alloc_console(); - return condev; + con->ready_count = 0; } diff --git a/server/tty.h b/server/tty.h index ee7e425..051ef09 100644 --- a/server/tty.h +++ b/server/tty.h @@ -1,6 +1,31 @@ #ifndef __TTY_H__ #define __TTY_H__ -filp* get_console(void); +#include "linux-defines.h" + +struct _con_filp; + +struct con_ops { + void (*fn_lock)(struct _con_filp *con); + void (*fn_unlock)(struct _con_filp *con); + int (*fn_write)(struct _con_filp *con, unsigned char ch); + void (*fn_get_winsize)(struct _con_filp *con, struct winsize *ws); +}; + +struct _con_filp { + filp fp; + struct con_ops *ops; + int eof; + unsigned char ready_data[20]; + int ready_count; + struct termios tios; + struct wait_list wl; +}; + +typedef struct _con_filp con_filp; + +void tty_input_add_string(con_filp *con, const char *string); +void tty_input_add_char(con_filp *con, char ch); +void tty_init(con_filp* con); #endif /* __TTY_H__ */ diff --git a/server/vt100.c b/server/vt100.c new file mode 100644 index 0000000..3b9abb9 --- /dev/null +++ b/server/vt100.c @@ -0,0 +1,709 @@ +/* + * vt100 terminal emulation in a windows console + * + * Copyright (C) 2006-2012 Mike McCormack + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include "filp.h" +#include "process.h" +#include "tty.h" +#include "linux-errno.h" +#include "linux-defines.h" +#include "debug.h" +#include "tty.h" +#include "vt100.h" + +#define MAX_VT100_PARAMS 3 + +struct _vt100_filp +{ + con_filp con; + int state; + int num[MAX_VT100_PARAMS]; + int num_count; + int fg_color; + int bg_color; + int brightness; + int cursor_key_mode; + HANDLE thread; + HANDLE handle; + CRITICAL_SECTION cs; +}; + +typedef struct _vt100_filp vt100_filp; + +static int vt100_set_cursor_pos(vt100_filp *vt, int x, int y) +{ + COORD coord; + CONSOLE_SCREEN_BUFFER_INFO info; + DWORD height, width; + BOOL r; + + dprintf("cursor to %d,%d\n", x, y); + + GetConsoleScreenBufferInfo(vt->handle, &info); + dprintf("dwSize %d,%d\n", info.dwSize.X, info.dwSize.Y); + dprintf("srWindow %d,%d-%d,%d\n", + info.srWindow.Left, info.srWindow.Top, + info.srWindow.Right, info.srWindow.Bottom); + height = info.srWindow.Bottom - info.srWindow.Top + 1; + width = info.srWindow.Right - info.srWindow.Left + 1; + if (x >= width) + x = width; + if (y >= height) + y = height; + if (y < 1) + y = 1; + if (x < 1) + x = 1; + coord.X = x - 1; + coord.Y = y - 1; + dprintf("Set %d,%d\n", coord.X, coord.Y); + r = SetConsoleCursorPosition(vt->handle, coord); + if (!r) + dprintf("failed to set cursor\n"); + return 0; +} + +static DWORD vt100_get_attributes(vt100_filp *vt) +{ + DWORD dwAttribute = 0; + + if (vt->brightness) + dwAttribute |= FOREGROUND_INTENSITY; + + switch (vt->fg_color) + { + case 30: /* Black */ + break; + case 31: /* Red */ + dwAttribute |= FOREGROUND_RED; + break; + case 32: /* Green */ + dwAttribute |= FOREGROUND_GREEN; + break; + case 33: /* Yellow */ + dwAttribute |= FOREGROUND_RED | FOREGROUND_GREEN; + break; + case 34: /* Blue */ + dwAttribute |= FOREGROUND_BLUE; + break; + case 35: /* Magenta */ + dwAttribute |= FOREGROUND_RED | FOREGROUND_BLUE; + break; + case 36: /* Cyan */ + dwAttribute |= FOREGROUND_GREEN | FOREGROUND_BLUE; + break; + case 37: /* White */ + dwAttribute |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; + break; + } + + switch (vt->bg_color) + { + case 40: /* Black */ + break; + case 41: /* Red */ + dwAttribute |= BACKGROUND_RED; + break; + case 42: /* Green */ + dwAttribute |= BACKGROUND_GREEN; + break; + case 43: /* Yellow */ + dwAttribute |= BACKGROUND_RED | BACKGROUND_GREEN; + break; + case 44: /* Blue */ + dwAttribute |= BACKGROUND_BLUE; + break; + case 45: /* Magenta */ + dwAttribute |= BACKGROUND_RED | BACKGROUND_BLUE; + break; + case 46: /* Cyan */ + dwAttribute |= BACKGROUND_GREEN | BACKGROUND_BLUE; + break; + case 47: /* White */ + dwAttribute |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED; + break; + } + + return dwAttribute; +} + +static int vt100_set_color(vt100_filp *vt) +{ + DWORD dwAttribute; + int i; + + for (i = 0; i <= vt->num_count; i++) + { + int code = vt->num[i]; + + if (code >= 30 && code <= 37) + vt->fg_color = code; + else if (code >= 40 && code <= 47) + vt->bg_color = code; + else if (code == 0) + { + vt->fg_color = 37; + vt->bg_color = 40; + vt->brightness = 0; + } + else if (code == 1) + vt->brightness = code; + else + dprintf("Unhandled color code %d\n", code); + } + + dwAttribute = vt100_get_attributes(vt); + + SetConsoleTextAttribute(vt->handle, dwAttribute); + return 0; +} + +/* carriage return. Move the cursor to the start of the line */ +static void vt100_do_cr(vt100_filp *vt) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + COORD coord; + + GetConsoleScreenBufferInfo(vt->handle, &info); + coord = info.dwCursorPosition; + coord.X = 0; + SetConsoleCursorPosition(vt->handle, coord); +} + +/* line feed. Go down one line, but not to the start */ +static void vt100_do_lf(vt100_filp *vt) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + COORD coord; + + GetConsoleScreenBufferInfo(vt->handle, &info); + coord = info.dwCursorPosition; + if (coord.Y >= info.srWindow.Bottom) + { + SMALL_RECT rect; + COORD topleft; + CHAR_INFO ci; + + topleft.X = info.srWindow.Left; + topleft.Y = info.srWindow.Top - 1; + rect = info.srWindow; + ci.Char.AsciiChar = ' '; + ci.Attributes = 0; + ScrollConsoleScreenBuffer(vt->handle, &rect, NULL, topleft, &ci); + dprintf("scrolled\n"); + } + else + coord.Y++; + SetConsoleCursorPosition(vt->handle, coord); +} + +static int vt100_write_normal(vt100_filp *vt, unsigned char ch) +{ + DWORD write_count = 0; + BOOL r; + + switch (ch) + { + case 0x1b: + vt->state = 1; + break; + case 0x0d: + vt100_do_cr(vt); + break; + case 0x0a: + vt100_do_lf(vt); + break; + default: + r = WriteConsole(vt->handle, &ch, 1, &write_count, NULL); + if (!r) + return -_L(EIO); + } + + return 0; +} + +static int vt100_write_wait_first_char(vt100_filp *vt, unsigned char ch) +{ + int i; + + for (i = 0; i < MAX_VT100_PARAMS; i++) + vt->num[i] = 0; + vt->num_count = 0; + + switch (ch) + { + case '[': + vt->state = 2; + break; + case '#': + vt->state = 3; + break; + case '(': + vt->state = 6; + break; + case ')': + vt->state = 7; + break; + default: + vt->state = 0; + } + + return 0; +} + +static void vt100_device_status(vt100_filp *vt, int req) +{ + char response[16]; + CONSOLE_SCREEN_BUFFER_INFO info; + + switch (req) + { + case 6: /* query cursor position */ + GetConsoleScreenBufferInfo(vt->handle, &info); + sprintf(response, "\x1b[%d;%dR", + info.dwCursorPosition.Y+1, + info.dwCursorPosition.X+1); + dprintf("response = %s\n", response+1); + tty_input_add_string(&vt->con, response); + break; + case 5: /* query device status */ + default: + dprintf("unknown request %d\n", req); + } +} + +static void vt100_erase_to_end_of_line(vt100_filp *vt) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + COORD pos; + DWORD count = 0; + + memset(&info, 0, sizeof info); + + if (!GetConsoleScreenBufferInfo(vt->handle, &info)) + { + /* this happens if the handle is a file */ + dprintf("GetConsoleScreenBufferInfo() failed\n"); + return; + } + + pos = info.dwCursorPosition; + + FillConsoleOutputCharacter(vt->handle, ' ', + info.dwSize.X - pos.X, pos, &count); + + dprintf("erased %d at %hd,%hd\n", count, pos.X, pos.Y); + + SetConsoleCursorPosition(vt->handle, pos); +} + +static void vt100_erase_screen(vt100_filp *vt, int n) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + COORD pos, top; + DWORD count; + + GetConsoleScreenBufferInfo(vt->handle, &info); + + pos = info.dwCursorPosition; + top.X = 0; + top.Y = 0; + + switch (n) + { + case 0: + /* erase from cursor to end */ + FillConsoleOutputCharacter(vt->handle, ' ', + (info.dwSize.Y - pos.Y) * info.dwSize.X + + info.dwSize.X - pos.X, pos, &count); + break; + case 1: + /* erase from top to cursor */ + FillConsoleOutputCharacter(vt->handle, ' ', + pos.Y * info.dwSize.X + pos.X, + top, &count); + break; + case 2: + /* erase entire screen */ + SetConsoleCursorPosition(vt->handle, top); + FillConsoleOutputCharacter(vt->handle, ' ', + info.dwSize.Y * info.dwSize.X, + top, &count); + break; + } + + SetConsoleCursorPosition(vt->handle, pos); +} + +static void vt100_move_cursor(vt100_filp *vt, int delta_x, int delta_y) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + COORD pos; + + GetConsoleScreenBufferInfo(vt->handle, &info); + + pos = info.dwCursorPosition; + + pos.X += delta_x; + pos.Y += delta_y; + + if (pos.X < 0) + pos.X = 0; + if (pos.X >= info.dwSize.X) + pos.X = info.dwSize.X - 1; + if (pos.Y < 0) + pos.Y = 0; + if (pos.Y >= info.dwSize.Y) + pos.Y = info.dwSize.Y - 1; + + SetConsoleCursorPosition(vt->handle, pos); +} + + +/* + * wait for a double height/double width command + * (These are probably not possible on a Windows console) + */ +static int vt100_write_wait_dhdw_mode(vt100_filp *vt, unsigned char ch) +{ + switch (ch) + { + case '8': /* fill upper area of screen with E */ + case '3': /* double height line, top half */ + case '4': /* double height line, bottom half */ + case '5': /* single line mode */ + case '6': /* double line mode */ + break; + } + vt->state = 0; + return 0; +} + +static void vt_hide_cursor(vt100_filp *vt, int enable) +{ + CONSOLE_CURSOR_INFO cci; + + GetConsoleCursorInfo(vt->handle, &cci); + cci.bVisible = enable; + SetConsoleCursorInfo(vt->handle, &cci); +} + +/* + * ESC [ ? N h - enable mode N + * ESC [ ? N l - disable mode N + */ +static int vt100_write_wait_mode_switch_num(vt100_filp *vt, unsigned char ch) +{ + int enable = 0; + if (ch >= '0' && ch <= '9') + { + vt->num[0] *= 10; + vt->num[0] += ch - '0'; + return 0; + } + else if (ch == 'h') + enable = 1; + else if (ch == 'l') + enable = 0; + else + { + vt->state = 0; + return 0; + } + + dprintf("switch mode %d %s\n", vt->num[0], enable ? "on" : "off"); + + switch (vt->num[0]) + { + case 1: /* cursor key mode */ + vt->cursor_key_mode = enable; + break; + case 2: /* vt52/ANSI mode */ + break; + case 3: /* 132 column mode: 132/80 */ + break; + case 4: /* scrolling mode: smooth/jump */ + break; + case 5: /* screen: reverse/normal */ + break; + case 6: /* origin relative/absolute */ + break; + case 7: /* wraparound on/off */ + break; + case 8: /* autorepeat on/off */ + break; + case 9: /* interlace on/off */ + break; + case 25: + vt_hide_cursor(vt, enable); + break; + default: + break; + } + vt->state = 0; + return 0; +} + +static int vt100_write_wait_char_set_g0(vt100_filp *vt, unsigned char ch) +{ + dprintf("switch to g0 %c - unsupported\n", ch); + vt->state = 0; + return 0; +} + +static int vt100_write_wait_char_set_g1(vt100_filp *vt, unsigned char ch) +{ + dprintf("switch to g1 %c - unsupported\n", ch); + vt->state = 0; + return 0; +} + +static int vt100_write_wait_number(vt100_filp *vt, unsigned char ch) +{ + switch (ch) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + vt->num[vt->num_count] *= 10; + vt->num[vt->num_count] += (ch - '0'); + break; + case ';': + vt->num_count++; + if (vt->num_count >= MAX_VT100_PARAMS) + { + dprintf("too many tty params\n", ch); + vt->state = 0; + } + break; + case 'm': + vt100_set_color(vt); + vt->state = 0; + break; + case 'n': + vt100_device_status(vt, vt->num[0]); + vt->state = 0; + break; + case 'A': + if (!vt->num[0]) + vt->num[0]++; + vt100_move_cursor(vt, 0, -vt->num[0]); + vt->state = 0; + break; + case 'B': + if (!vt->num[0]) + vt->num[0]++; + vt100_move_cursor(vt, 0, vt->num[0]); + vt->state = 0; + break; + case 'C': + if (!vt->num[0]) + vt->num[0]++; + vt100_move_cursor(vt, vt->num[0], 0); + vt->state = 0; + break; + case 'D': + if (!vt->num[0]) + vt->num[0]++; + vt100_move_cursor(vt, -vt->num[0], 0); + vt->state = 0; + break; + case 'K': + vt100_erase_to_end_of_line(vt); + vt->state = 0; + break; + case 'J': + vt100_erase_screen(vt, vt->num[0]); + vt->state = 0; + break; + case 'H': + vt100_set_cursor_pos(vt, vt->num[1], vt->num[0]); + vt->state = 0; + break; + case '?': + vt->state = 4; + break; + default: + dprintf("unknown escape code %c (num1)\n", ch); + vt->state = 0; + } + return 0; +} + +typedef int (*vt100_state_fn)(vt100_filp *vt, unsigned char ch); + +static vt100_state_fn vt100_state_list[] = { + vt100_write_normal, + vt100_write_wait_first_char, + vt100_write_wait_number, + vt100_write_wait_dhdw_mode, + vt100_write_wait_mode_switch_num, + NULL, + vt100_write_wait_char_set_g0, + vt100_write_wait_char_set_g1, +}; + +static int vt100_write(con_filp *con, unsigned char ch) +{ + vt100_filp *vt = (void*) con; + return vt100_state_list[vt->state](vt, ch); +} + +static void vt100_send_cursor_code(vt100_filp *vt, char ch) +{ + char cursor[4] = { '\033', vt->cursor_key_mode ? '[' : 'O', ch, 0 }; + tty_input_add_string(&vt->con, cursor); +} + +static DWORD WINAPI vt100_input_thread(LPVOID param) +{ + HANDLE in = GetStdHandle(STD_INPUT_HANDLE); + vt100_filp *vt = param; + unsigned char ch = 0; + DWORD count = 0; + BOOL r; + INPUT_RECORD ir; + + while (1) + { + r = ReadConsoleInput(in, &ir, 1, &count); + if (!r) + { + fprintf(stderr, "\nReadConsoleInput failed\n"); + break; + } + if (!count) + break; + + if (ir.EventType != KEY_EVENT) + continue; + + if (!ir.Event.KeyEvent.bKeyDown) + continue; + + switch (ir.Event.KeyEvent.wVirtualKeyCode) + { + case VK_RETURN: + tty_input_add_char(&vt->con, '\n'); + break; + case VK_UP: + vt100_send_cursor_code(vt, 'A'); + break; + case VK_DOWN: + vt100_send_cursor_code(vt, 'B'); + break; + case VK_RIGHT: + vt100_send_cursor_code(vt, 'C'); + break; + case VK_LEFT: + vt100_send_cursor_code(vt, 'D'); + break; + default: + ch = ir.Event.KeyEvent.uChar.AsciiChar; + if (ch) + tty_input_add_char(&vt->con, ch); + break; + } + + } + + fprintf(stderr, "\ninput thread died\n"); + + return 0; +} + +static void vt100_get_winsize(con_filp *con, struct winsize *ws) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + vt100_filp *vt= (void*) con; + + GetConsoleScreenBufferInfo(vt->handle, &info); + + ws->ws_col = info.srWindow.Right - info.srWindow.Left + 1; + ws->ws_row = info.srWindow.Bottom - info.srWindow.Top + 1; + ws->ws_xpixel = 0; + ws->ws_ypixel = 0; + + dprintf("winsize -> %d,%d\n", ws->ws_col, ws->ws_row); +} + +static void vt100_lock(con_filp *con) +{ + vt100_filp *vt= (void*) con; + EnterCriticalSection(&vt->cs); +} + +static void vt100_unlock(con_filp *con) +{ + vt100_filp *vt= (void*) con; + LeaveCriticalSection(&vt->cs); +} + +struct con_ops vt100_ops = { + .fn_lock = vt100_lock, + .fn_unlock = vt100_unlock, + .fn_write = vt100_write, + .fn_get_winsize = vt100_get_winsize, +}; + +static filp* alloc_vt100_console(HANDLE handle) +{ + vt100_filp *vt; + con_filp *con; + DWORD id; + + vt = malloc(sizeof *vt); + if (!vt) + return NULL; + memset(vt, 0, sizeof *vt); + con = &vt->con; + + con->ops = &vt100_ops; + + tty_init(con); + + vt->state = 0; + vt->num_count = 0; + vt->fg_color = 37; + vt->bg_color = 40; + vt->brightness = 0; + vt->handle = handle; + vt->cursor_key_mode = 1; + + SetConsoleMode(handle, ENABLE_PROCESSED_INPUT); + + InitializeCriticalSection(&vt->cs); + + vt->thread = CreateThread(NULL, 0, &vt100_input_thread, con, 0, &id); + + return &con->fp; +} + +static filp *condev; + +filp* get_vt100_console(HANDLE console) +{ + if (!condev) + condev = alloc_vt100_console(console); + return condev; +} diff --git a/server/vt100.h b/server/vt100.h new file mode 100644 index 0000000..4df0344 --- /dev/null +++ b/server/vt100.h @@ -0,0 +1,6 @@ +#ifndef __ATRATUS_VT100_H__ +#define __ATRATUS_VT100_H__ + +filp* get_vt100_console(HANDLE console); + +#endif // __ATRATUS_VT100_H__ diff --git a/server/winfs.c b/server/winfs.c index fc746dd..5e3c094 100644 --- a/server/winfs.c +++ b/server/winfs.c @@ -6,6 +6,7 @@ #include "linux-defines.h" #include "debug.h" #include "process.h" +#include #define SECSPERDAY 86400 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY) @@ -67,7 +68,7 @@ static char *unix2dos_path(const char *unixpath) } static int winfs_stat64(struct fs *fs, const char *path, - struct stat64 *statbuf, BOOL follow_links) + struct stat64 *statbuf, bool follow_links) { WIN32_FILE_ATTRIBUTE_DATA info; BOOL r; @@ -81,16 +82,18 @@ static int winfs_stat64(struct fs *fs, const char *path, memset(statbuf, 0, sizeof *statbuf); statbuf->st_mode = 0755; + statbuf->st_uid = current->uid; + statbuf->st_gid = current->gid; if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) statbuf->st_mode |= 040000; else statbuf->st_mode |= 0100000; - statbuf->ctime = filetime_to_unixtime(&info.ftCreationTime, - &statbuf->ctime_nsec); - statbuf->mtime = filetime_to_unixtime(&info.ftLastWriteTime, - &statbuf->mtime_nsec); - statbuf->atime = filetime_to_unixtime(&info.ftLastAccessTime, - &statbuf->atime_nsec); + statbuf->st_ctime = filetime_to_unixtime(&info.ftCreationTime, + &statbuf->st_ctime_nsec); + statbuf->st_mtime = filetime_to_unixtime(&info.ftLastWriteTime, + &statbuf->st_mtime_nsec); + statbuf->st_atime = filetime_to_unixtime(&info.ftLastAccessTime, + &statbuf->st_atime_nsec); statbuf->st_size = info.nFileSizeLow; //statbuf->st_size += info.nFileSizeHigh * 0x100000000LL; statbuf->st_blksize = 0x1000; @@ -116,12 +119,12 @@ static int file_stat(filp *f, struct stat64 *statbuf) statbuf->st_mode |= 040000; else statbuf->st_mode |= 0100000; - statbuf->ctime = longlong_to_unixtime(info.ChangeTime.QuadPart, - &statbuf->ctime_nsec); - statbuf->mtime = longlong_to_unixtime(info.LastWriteTime.QuadPart, - &statbuf->mtime_nsec); - statbuf->atime = longlong_to_unixtime(info.LastAccessTime.QuadPart, - &statbuf->atime_nsec); + statbuf->st_ctime = longlong_to_unixtime(info.ChangeTime.QuadPart, + &statbuf->st_ctime_nsec); + statbuf->st_mtime = longlong_to_unixtime(info.LastWriteTime.QuadPart, + &statbuf->st_mtime_nsec); + statbuf->st_atime = longlong_to_unixtime(info.LastAccessTime.QuadPart, + &statbuf->st_atime_nsec); statbuf->st_size = info.AllocationSize.QuadPart; statbuf->st_blksize = 0x1000; @@ -140,7 +143,6 @@ static int file_getdents(filp *fp, void *de, unsigned int count, fn_add_dirent a FILE_DIRECTORY_INFORMATION *info; WCHAR star[] = { '*', '.', '*', 0 }; UNICODE_STRING mask; - struct linux_dirent *prev_de = NULL; ULONG EntryOffset; mask.Length = sizeof star/sizeof (WCHAR); @@ -159,27 +161,40 @@ static int file_getdents(filp *fp, void *de, unsigned int count, fn_add_dirent a dprintf("NtQueryDirectoryFile -> %08lx\n", ret); if (ret != STATUS_SUCCESS) break; - first = FALSE; EntryOffset = 0; do { + size_t len; + info = (FILE_DIRECTORY_INFORMATION*) &buffer[EntryOffset]; + EntryOffset += info->NextEntryOffset; + + len = WideCharToMultiByte(CP_UTF8, 0, + info->FileName, + info->FileNameLength/sizeof (WCHAR), + NULL, 0, NULL, NULL); + char type; + if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + type = _L(DT_DIR); + else + type = _L(DT_REG); + + char name[len + 1]; + WideCharToMultiByte(CP_UTF8, 0, + info->FileName, + info->FileNameLength/sizeof (WCHAR), + name, len + 1, NULL, NULL); - if (prev_de) - { - r = current->ops->memcpy_to(&prev_de->d_off, &ofs, sizeof ofs); - if (r < 0) - break; - } de = (struct linux_dirent*)&p[ofs]; - r = add_de(de, info->FileName, info->FileNameLength/2, count - ofs); + r = add_de(de, name, len, count - ofs, + (first || info->NextEntryOffset) ? EntryOffset : INT_MAX, + type); if (r < 0) break; ofs += r; - prev_de = de; - EntryOffset += info->NextEntryOffset; } while (info->NextEntryOffset); + first = FALSE; } dprintf("%d bytes added\n", ofs); @@ -274,7 +289,7 @@ static int file_write(filp *f, const void *buf, size_t size, loff_t *off) r = WriteFile(f->handle, buf, bytesRead, &bytesWritten, NULL); if (!r) { - fprintf(stderr, "ReadFile %p failed %ld\n", + fprintf(stderr, "WriteFile %p failed %ld\n", f->handle, GetLastError()); return -_L(EIO); } @@ -289,11 +304,18 @@ static int file_write(filp *f, const void *buf, size_t size, loff_t *off) return bytesCopied; } +static void winfs_close(filp *fp) +{ + if (fp->handle) + CloseHandle(fp->handle); +} + static const struct filp_ops disk_file_ops = { .fn_read = &file_read, .fn_write = &file_write, .fn_stat = &file_stat, .fn_getdents = &file_getdents, + .fn_close = &winfs_close, }; static int winfs_open(struct fs *fs, const char *file, int flags, int mode) @@ -325,7 +347,7 @@ static int winfs_open(struct fs *fs, const char *file, int flags, int mode) } if (flags & _L(O_CREAT)) - create = CREATE_NEW; + create = CREATE_ALWAYS; else create = OPEN_EXISTING; diff --git a/tests/dynamic/.gitignore b/tests/dynamic/.gitignore index d4c37b9..bd9d3a0 100644 --- a/tests/dynamic/.gitignore +++ b/tests/dynamic/.gitignore @@ -1,12 +1,20 @@ args bsearch +ctype dir +fork getopt getopt_long hw +jmpbuf malloc +pipe +qsort +read +stat string stdlib stdio ttysize uname +vtplay diff --git a/tests/dynamic/Makefile b/tests/dynamic/Makefile index 5282274..6ad7496 100644 --- a/tests/dynamic/Makefile +++ b/tests/dynamic/Makefile @@ -4,16 +4,24 @@ CC = gcc TARGETS = \ args \ bsearch \ + ctype \ dir \ + fork \ getopt \ getopt_long \ hw \ + jmpbuf \ malloc \ + pipe \ + qsort \ + read \ + stat \ stdio \ stdlib \ string \ ttysize \ - uname + uname \ + vtplay all: $(TARGETS) diff --git a/tests/dynamic/args.c b/tests/dynamic/args.c index f5f4f00..147c6bb 100644 --- a/tests/dynamic/args.c +++ b/tests/dynamic/args.c @@ -1,10 +1,15 @@ #include +#include -int main(int argc, char **argv) +int main(int argc, char **argv, char **env) { int i; for (i = 0; i < argc; i++) puts(argv[i]); + + printf("\nenvironment:\n"); + for (i = 0; env[i]; i++) + printf("%s\n", env[i]); return 0; } diff --git a/tests/dynamic/ctype.c b/tests/dynamic/ctype.c new file mode 100644 index 0000000..ff41ec1 --- /dev/null +++ b/tests/dynamic/ctype.c @@ -0,0 +1,111 @@ +#include +#include + +#define OK(expr) \ + do { \ + if (!(expr)) \ + { \ + printf("expression '%s' untrue at %d\n", \ + #expr, __LINE__); \ + return 0; \ + } \ + } while (0) + +int t_isdigit(char ch) +{ + volatile char xc = ch; + volatile unsigned char xuc = ch; + return isdigit(xc) && isdigit(xuc); +} + +int t_isalpha(char ch) +{ + volatile char xc = ch; + volatile unsigned char xuc = ch; + return isalpha(xc) && isalpha(xuc); +} + +int t_isalnum(char ch) +{ + volatile char xc = ch; + volatile unsigned char xuc = ch; + return isalnum(xc) && isalnum(xuc); +} + +int t_toupper(char ch) +{ + volatile char xc = ch; + return toupper(xc); +} + +int t_tolower(char ch) +{ + volatile char xc = ch; + return tolower(xc); +} + +int test_isnum(void) +{ + OK(t_isdigit('0')); + OK(t_isdigit('9')); + OK(!t_isdigit(' ')); + OK(!t_isdigit('a')); + OK(!t_isdigit('A')); + return 1; +} + +int test_toupper(void) +{ + OK(t_toupper('a') == 'A'); + OK(t_toupper('A') == 'A'); + OK(t_toupper('0') == '0'); + OK(t_toupper('z') == 'Z'); + OK(t_toupper(' ') == ' '); + return 1; +} + +int test_isalpha(void) +{ + OK(t_isalpha('A')); + OK(t_isalpha('Z')); + OK(t_isalpha('a')); + OK(t_isalpha('z')); + OK(!t_isalpha('0')); + OK(!t_isalpha('!')); + OK(!t_isalpha(' ')); + OK(!t_isalpha('/')); + OK(!t_isalpha('\\')); + OK(!t_isalpha('\n')); + return 1; +} + +int test_isalnum(void) +{ + OK(t_isalnum('A')); + OK(t_isalnum('Z')); + OK(t_isalnum('a')); + OK(t_isalnum('z')); + OK(t_isalnum('0')); + OK(t_isalnum('9')); + OK(!t_isalnum('!')); + OK(!t_isalnum(' ')); + OK(!t_isalnum('/')); + OK(!t_isalnum('\\')); + OK(!t_isalnum('\n')); + + return 1; +} + +int main(int argc, char **argv) +{ + if (!test_isnum()) + return 1; + if (!test_toupper()) + return 1; + if (!test_isalpha()) + return 1; + if (!test_isalnum()) + return 1; + printf("OK\n"); + return 0; +} diff --git a/tests/dynamic/dir.c b/tests/dynamic/dir.c index c181055..5f53dbf 100644 --- a/tests/dynamic/dir.c +++ b/tests/dynamic/dir.c @@ -1,6 +1,7 @@ #include #include #include +#include #define OK(expr) \ do { \ @@ -14,11 +15,17 @@ int test_getcwd(void) { - char buf[100]; + char buf[100], *d; OK(0 == chdir("/")); OK(buf == getcwd(buf, sizeof buf)); OK(!strcmp(buf, "/")); + + d = getcwd(NULL, 0); + OK(NULL != d); + OK(!strcmp(d, "/")); + free(d); + return 1; } diff --git a/tests/dynamic/fork.c b/tests/dynamic/fork.c new file mode 100644 index 0000000..9b742d3 --- /dev/null +++ b/tests/dynamic/fork.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + int r; + + r = fork(); + if (r == 0) + { + _exit(0x123); + } + else + { + int child = r; + int status = 0; + r = waitpid(-1, &status, 0); + if (r != child) + return 1; + + if (status != 0x2300) + return 1; + } + printf("ok\n"); + return 0; +} diff --git a/tests/dynamic/jmpbuf.c b/tests/dynamic/jmpbuf.c new file mode 100644 index 0000000..396a308 --- /dev/null +++ b/tests/dynamic/jmpbuf.c @@ -0,0 +1,40 @@ +#include +#include +#include + +#define STATIC_ASSERT(expr) \ + do { \ + char _sa[(expr) ? 1 : -1]; \ + (void) _sa; \ + } while(0) + +int main(int argc, char **argv) +{ + jmp_buf env; + int r; + volatile int x = 0x67895432; + + STATIC_ASSERT(sizeof env == 156); + + r = setjmp(env); + if (r) + { + if (x != 0x67895432) + exit(1); + if (r == 2) + goto again; + goto finish; + } + + longjmp(env, 2); + +again: + longjmp(env, 0); + + printf("fail\n"); + + exit(1); +finish: + printf("ok\n"); + exit(0); +} diff --git a/tests/dynamic/pipe.c b/tests/dynamic/pipe.c new file mode 100644 index 0000000..9b5fa9a --- /dev/null +++ b/tests/dynamic/pipe.c @@ -0,0 +1,36 @@ +#define _GNU_SOURCE + +#include +#include +#include + +#define OK(expr) \ + do { \ + if (!(expr)) \ + { \ + printf("expression '%s' untrue at %d\n", \ + #expr, __LINE__); \ + return 0; \ + } \ + } while (0) + +int test_create_pipe(void) +{ + int fds[2] = {-1, -1}; + + OK(0 == pipe(fds)); + OK(0 == close(fds[0])); + OK(0 == close(fds[1])); + + return 1; +} + +int main(int argc, char **argv) +{ + if (!test_create_pipe()) + return 1; + + printf("OK\n"); + + return 0; +} diff --git a/tests/dynamic/qsort.c b/tests/dynamic/qsort.c new file mode 100644 index 0000000..ab0017f --- /dev/null +++ b/tests/dynamic/qsort.c @@ -0,0 +1,37 @@ +#include +#include +#include + +char *haystack[] = { + "foo", + "master", + "needle", + "noodle", + "stack", + "test", + "a", + "b", + "bar", +}; + +static int cmpfn(const void *a, const void *b) +{ + const char * const * as = a, * const * bs = b; + return strcmp(*as, *bs); +} + +int main(int argc, char **argv) +{ + int nmemb = sizeof haystack/sizeof haystack[0]; + int i; + + qsort(haystack, nmemb, sizeof haystack[0], cmpfn); + + for (i = 1; i < nmemb; i++) + if (0 <= strcmp(haystack[i - 1], haystack[i])) + return 1; + + printf("OK\n"); + + return 0; +} diff --git a/tests/dynamic/read.c b/tests/dynamic/read.c new file mode 100644 index 0000000..f5d9649 --- /dev/null +++ b/tests/dynamic/read.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + char buf[0x100]; + int r, i; + struct termios oldtios; + + printf("sizeof tios = %d\n", sizeof oldtios); + printf("tios.c_cc = %d\n", (int)&((struct termios*)NULL)->c_cc[0]); + + r = tcgetattr(0, &oldtios); + if (r < 0) + return 1; + + printf("tios.c_iflag: (%08x) %s %s %s %s %s\n", oldtios.c_iflag, + (oldtios.c_iflag & ICRNL) ? "ICRNL" : "~ICRNL", + (oldtios.c_iflag & INLCR) ? "INLCR" : "~INLCR", + (oldtios.c_iflag & IUCLC) ? "IUCLC" : "~IUCLC", + (oldtios.c_iflag & IXON) ? "IXON" : "~IXON", + (oldtios.c_iflag & IXON) ? "IXOFF" : "~IXOFF"); + + printf("tios.c_oflag: (%08x) %s %s %s %s %s\n", oldtios.c_oflag, + (oldtios.c_oflag & ONLRET) ? "ONLRET" : "~ONLRET", + (oldtios.c_oflag & OCRNL) ? "OCRNL" : "~OCRNL", + (oldtios.c_oflag & ONLCR) ? "ONLCR" : "~ONLCR", + (oldtios.c_oflag & OLCUC) ? "OLCUC" : "~OLCUC", + (oldtios.c_oflag & OPOST) ? "OPOST" : "~POST"); + + printf("tios.c_lflag (%08x): %s %s\n", oldtios.c_lflag, + (oldtios.c_lflag & ECHO) ? "ECHO" : "~ECHO", + (oldtios.c_lflag & ICANON) ? "ICANON" : "~ICANON"); + + if (argc < 2) + { + printf("Args are (can|raw)\n"); + return 1; + } + + if (!strcmp(argv[1], "can")) + { + printf("Using canonical mode\n"); + } + else if (!strcmp(argv[1], "can")) + { + struct termios tios; + + memset(&tios, 0, sizeof tios); + + r = tcgetattr(0, &tios); + if (r < 0) + return 1; + + tios.c_lflag &= ~ICANON; + r = tcsetattr(0, 0, &tios); + if (r < 0) + return 1; + } + + r = read(0, buf, sizeof buf); + if (r > 0) + r = write(1, buf, r); + printf("read %d characters\n", r); + for (i = 0; i < r; i++) + printf("%02x ", buf[i]); + printf("\n"); + /* restore */ + tcsetattr(0, 0, &oldtios); + return 0; +} diff --git a/tests/dynamic/stat.c b/tests/dynamic/stat.c new file mode 100644 index 0000000..c679d4d --- /dev/null +++ b/tests/dynamic/stat.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + struct stat st; + int r; + + memset(&st, 0, sizeof st); + r = stat(argv[1], &st); + if (r < 0) + return 1; + printf("mode %08x\n", st.st_mode); + printf("size %08lx\n", st.st_size); + + return 0; +} diff --git a/tests/dynamic/stdio.c b/tests/dynamic/stdio.c index ec6da62..286af14 100644 --- a/tests/dynamic/stdio.c +++ b/tests/dynamic/stdio.c @@ -116,6 +116,30 @@ int test_sprintf(void) if (strcmp(" x", out)) return 1; + sprintf(out, "%.1s", "xyz"); + if (strcmp("x", out)) + return 1; + + sprintf(out, "%.*s", 2, "xyz"); + if (strcmp("xy", out)) + return 1; + + sprintf(out, "%-s", "xyz"); + if (strcmp("xyz", out)) + return 1; + + sprintf(out, "%-3s", "xyz"); + if (strcmp("xyz", out)) + return 1; + + sprintf(out, "%-4s", "xyz"); + if (strcmp("xyz ", out)) + return 1; + + sprintf(out, "%-4d", 1); + if (strcmp("1 ", out)) + return 1; + return 0; } diff --git a/tests/dynamic/string.c b/tests/dynamic/string.c index 1b77b8b..51db5db 100644 --- a/tests/dynamic/string.c +++ b/tests/dynamic/string.c @@ -51,8 +51,19 @@ int test_memcpy(void) char dest[10]; memset(dest, 'x', sizeof dest); - memcpy(dest, src, 6); + OK(dest == memcpy(dest, src, 6)); + OK(strcmp(dest, src) == 0); + return 1; +} + +int test_mempcpy(void) +{ + const char *src = "hello"; + char dest[10]; + + memset(dest, 'x', sizeof dest); + OK(&dest[6] == mempcpy(dest, src, 6)); OK(strcmp(dest, src) == 0); return 1; @@ -87,6 +98,33 @@ int test_strcpy(void) return 1; } +int test_strncpy(void) +{ + char dest[10]; + + dest[5] = 'x'; + dest[6] = 'x'; + dest[7] = 0; + OK(dest == strncpy(dest, "hello", 5)); + OK(!strcmp(dest, "helloxx")); + + OK(dest == strncpy(dest, "hello", 6)); + OK(!strcmp(dest, "hello")); + OK(dest[6] == 'x'); + + OK(dest == strncpy(dest, "hello", 7)); + OK(!strcmp(dest, "hello")); + OK(dest[6] == 0); + + OK(dest == strncpy(dest, "", 0)); + OK(!strcmp(dest, "hello")); + + OK(dest == strncpy(dest, "", 1)); + OK(!strcmp(dest, "")); + + return 1; +} + int test_strspn(void) { OK(strspn("", "") == 0); @@ -197,6 +235,19 @@ int test_strcspn(void) return 1; } +int test_strpbrk(void) +{ + char x1[] = "test"; + + OK(strpbrk("", "") == NULL); + OK(strpbrk(x1, "") == NULL); + OK(strpbrk(x1, "x") == NULL); + OK(strpbrk(x1, "t") == x1); + OK(strpbrk(x1, "xyze") == x1+1); + + return 1; +} + int main(int argc, char **argv) { if (!test_strlen()) @@ -211,6 +262,9 @@ int main(int argc, char **argv) if (!test_memcpy()) return 1; + if (!test_mempcpy()) + return 1; + if (!test_memmove()) return 1; @@ -220,6 +274,9 @@ int main(int argc, char **argv) if (!test_strcpy()) return 1; + if (!test_strncpy()) + return 1; + if (!test_strcat_chk()) return 1; @@ -244,6 +301,9 @@ int main(int argc, char **argv) if (!test_strcspn()) return 1; + if (!test_strpbrk()) + return 1; + puts("OK"); return 0; diff --git a/tests/dynamic/vtplay.c b/tests/dynamic/vtplay.c new file mode 100644 index 0000000..4a2e43f --- /dev/null +++ b/tests/dynamic/vtplay.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +/* + * Cat a file slowly to a terminal + * Good for testing terminal with files from: + * + * http://artscene.textfiles.com/vt100 + */ + +int main(int argc, char **argv) +{ + char buf[10]; + int fd, r; + + if (argc != 2) + { + fprintf(stderr, "What, no files?\n"); + return 1; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) + return 1; + while (1) + { + r = read(fd, buf, 1); + if (r != 1) + break; + r = write(1, buf, 1); + if (r != 1) + break; + poll(0, 0, 10); + } + close(fd); + return 0; +} diff --git a/tests/static/.gitignore b/tests/static/.gitignore index 3884623..ebefe52 100644 --- a/tests/static/.gitignore +++ b/tests/static/.gitignore @@ -7,10 +7,14 @@ echo exec exit fork +gettimeofday gs hw mmap pipe +select sleep stack +stat +stat64 uname diff --git a/tests/static/Makefile b/tests/static/Makefile index 9a4f374..b32c98e 100644 --- a/tests/static/Makefile +++ b/tests/static/Makefile @@ -11,12 +11,16 @@ TARGETS = \ exec \ exit \ fork \ + gettimeofday \ gs \ hw \ mmap \ pipe \ + select \ sleep \ stack \ + stat \ + stat64 \ uname all: $(TARGETS) diff --git a/tests/static/dir.c b/tests/static/dir.c index 4fc4b2f..91deb35 100644 --- a/tests/static/dir.c +++ b/tests/static/dir.c @@ -24,7 +24,7 @@ struct linux_dirent { long d_ino; - off_t d_off; + unsigned long d_off; unsigned short d_reclen; char d_name[]; }; @@ -118,6 +118,21 @@ size_t strlen(const char *str) return n; } +static void write_int(unsigned int x) +{ + char ch[8]; + int i; + for (i = 0; i < 8; i++) + { + ch[i] = (x>>((7-i)*4))&0x0f; + if (ch[i] < 10) + ch[i] += '0'; + else + ch[i] += 55; + } + write(1, ch, 8); +} + int main(int argc, char **argv) { unsigned char buf[0x1000]; @@ -139,6 +154,26 @@ int main(int argc, char **argv) { struct linux_dirent *de = (void*) &buf[n]; + write_int(de->d_reclen); + write(1, " ", 1); + write_int(de->d_off); + write(1, " ", 1); + char t = ((char*)de)[de->d_reclen - 1]; + switch (t) + { + case 10: /* link */ + write(1, "l", 1); + break; + case 8: /* file */ + write(1, " ", 1); + break; + case 4: /* dir */ + write(1, "d", 1); + break; + default: + write_int(t); + } + write(1, " ", 1); write(1, de->d_name, strlen(de->d_name)); write(1, "\n", 1); //printf("%s %08lx %d\n", de->d_name, de->d_off, de->d_reclen); diff --git a/tests/static/gettimeofday.c b/tests/static/gettimeofday.c new file mode 100644 index 0000000..328e017 --- /dev/null +++ b/tests/static/gettimeofday.c @@ -0,0 +1,80 @@ +/* + * gettimeofday - show the gettimeofday() syscall output + * + * Copyright (C) 2006-2012 Mike McCormack + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +void exit(int status) +{ + while (1) + { + __asm__ __volatile__ ( + "\tmov $1, %%eax\n" + "\tint $0x80\n" + :: "b"(status) : "memory"); + } +} + +int write(int fd, const void *buffer, size_t length) +{ + int r; + __asm__ __volatile__ ( + "\tmov $4, %%eax\n" + "\tint $0x80\n" + :"=a"(r): "b"(fd), "c"(buffer), "d"(length) : "memory"); + + return r; +} + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + int r; + __asm__ __volatile__ ( + "\tint $0x80\n" + :"=a"(r): "a"(78), "b"(tv), "c"(tz) : "memory"); + return r; +} + +int itoa(unsigned int x, char *buf, int width) +{ + int i; + for (i = width; i > 0; i--) + { + buf[i - 1] = '0' + (x % 10); + x /= 10; + } + return width; +} + +void _start(void) +{ + struct timeval tv; + char buffer[18]; + + if (0 == gettimeofday(&tv, NULL)) + { + int n = itoa(tv.tv_sec, buffer, 10); + buffer[n++] = ':'; + n += itoa(tv.tv_usec, buffer+n, 6); + buffer[n++] = '\n'; + write(1, buffer, n); + } + + exit(0); +} diff --git a/tests/static/select.c b/tests/static/select.c new file mode 100644 index 0000000..22ab26f --- /dev/null +++ b/tests/static/select.c @@ -0,0 +1,179 @@ +/* + * select - test select + * + * Copyright (C) 2006-2012 Mike McCormack + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +typedef unsigned long size_t; + +#define NULL ((void*)0) + +struct timeval +{ + long tv_sec; + long tv_usec; +}; + +struct fdset +{ + unsigned long fds_bits[1024/32]; +}; + +struct pollfd +{ + int fd; + short events; + short revents; +}; + +void __stack_chk_fail(void) +{ +} + +/* + * minwgcc and gcc have different definitions of __thread + * errno should be per thread, + * leave as global until we can load an ELF binary + */ +int errno; + +static inline int set_errno(int r) +{ + if ((r & 0xfffff000) == 0xfffff000) + { + errno = -r; + r = -1; + } + return r; +} + +void exit(int status) +{ + while (1) + { + __asm__ __volatile__ ( + "\tmov $1, %%eax\n" + "\tint $0x80\n" + :: "b"(status) : "memory"); + } +} + +int read(int fd, char *buffer, size_t length) +{ + int r; + __asm__ __volatile__ ( + "\tmov $3, %%eax\n" + "\tint $0x80\n" + :"=a"(r): "b"(fd), "c"(buffer), "d"(length) : "memory"); + return set_errno(r); +} + +int write(int fd, const char *buffer, size_t length) +{ + int r; + __asm__ __volatile__ ( + "\tmov $4, %%eax\n" + "\tint $0x80\n" + :"=a"(r): "b"(fd), "c"(buffer), "d"(length) : "memory"); + + return set_errno(r); +} + +int sys_select(void *args) +{ + int r; + __asm__ __volatile__ ( + "\tint $0x80\n" + : "=a"(r) + : "a"(82), "b"(args) + : "memory"); + + return set_errno(r); +} + +int select(int nfds, struct fdset *readfds, struct fdset *writefds, + struct fdset *exceptfds, struct timeval *timeout) +{ + struct { + int nfds; + struct fdset *rfds; + struct fdset *wfds; + struct fdset *efds; + struct timeval *tv; + } args = { nfds, readfds, writefds, exceptfds, timeout }; + return sys_select(&args); +} + +static inline char tohex(unsigned int val) +{ + if (val <= 9) + return val + '0'; + return val + 'A' - 10; +} + +static inline void itox(unsigned int val, char *buffer) +{ + int i = 8; + while (i > 0) + { + i--; + buffer[i] = tohex(val & 0x0f); + val >>= 4; + } +} + +void *memset(void *s, int c, size_t n) +{ + unsigned char *uc = s; + size_t i; + + for (i = 0; i < n; i++) + uc[i] = c; + return s; +} + +void _start(void) +{ + int r; + struct fdset rfds, wfds, errfds; + struct timeval tv = {0, 0}; + + memset(&rfds, 0, sizeof rfds); + memset(&wfds, 0, sizeof wfds); + memset(&errfds, 0, sizeof errfds); + + if (0) + { + /* this hangs indefinitely */ + r = select(0, 0, 0, 0, 0); + if (r != -1 || errno != 14) + exit(1); + } + + r = select(0, 0, 0, 0, &tv); + if (r != 0) + exit(1); + + r = select(0, &rfds, &wfds, &errfds, &tv); + if (r != 0) + exit(1); + + write(1, "OK\n", 3); + exit(0); +} diff --git a/tests/static/stat.c b/tests/static/stat.c new file mode 100644 index 0000000..a382a57 --- /dev/null +++ b/tests/static/stat.c @@ -0,0 +1,163 @@ +/* + * stat - test the stat syscall + * + * Copyright (C) 2006-2012 Mike McCormack + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +void __stack_chk_fail(void) +{ +} + +/* + * minwgcc and gcc have different definitions of __thread + * errno should be per thread, + * leave as global until we can load an ELF binary + */ +int errno; + +static inline int set_errno(int r) +{ + if ((r & 0xfffff000) == 0xfffff000) + { + errno = -r; + r = -1; + } + return r; +} + +void exit(int status) +{ + while (1) + { + __asm__ __volatile__ ( + "\tmov $1, %%eax\n" + "\tint $0x80\n" + :: "b"(status) : "memory"); + } +} + +size_t strlen(const char *str) +{ + size_t n = 0; + while (str[n]) + n++; + return n; +} + +ssize_t write(int fd, const char *buffer, size_t length) +{ + int r; + __asm__ __volatile__ ( + "\tmov $4, %%eax\n" + "\tint $0x80\n" + :"=a"(r): "b"(fd), "c"(buffer), "d"(length) : "memory"); + + return set_errno(r); +} + +static void write_int(unsigned int x) +{ + char ch[8]; + int i; + for (i = 0; i < 8; i++) + { + ch[i] = (x>>((7-i)*4))&0x0f; + if (ch[i] < 10) + ch[i] += '0'; + else + ch[i] += 55; + } + write(1, ch, 8); +} + +struct stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned long st_rdev; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long st_atime_nsec; + unsigned long st_mtime; + unsigned long st_mtime_nsec; + unsigned long st_ctime; + unsigned long st_ctime_nsec; + unsigned long __unused1; + unsigned long __unused2; +}; + +int stat(const char *path, struct stat *st) +{ + int r; + __asm__ __volatile__ ( + "\tint $0x80\n" + :"=a"(r): "a"(106), "b"(path), "c"(st) : "memory"); + + return set_errno(r); +} + +int main(int argc, char **argv) +{ + int i; + struct stat st; + + for (i = 1; i < argc; i++) + { + stat(argv[i], &st); + write_int(st.st_mode); + write(1, " ", 1); + write_int(st.st_size); + write(1, " ", 1); + write(1, argv[i], strlen(argv[i])); + write(1, "\n", 1); + } + return 0; +} + +__asm__ ( + "\n" +".globl _start\n" +"_start:\n" + "\tmovl 0(%esp), %eax\n" + "\tlea 4(%esp), %ebx\n" + "\tmov %ebx, %ecx\n" +"1:\n" + "\tcmpl $0, 0(%ecx)\n" + "\tlea 4(%ecx), %ecx\n" + "\tjnz 1b\n" + "\tpush %ecx\n" + "\tmov %ecx, %edx\n" +"2:\n" + "\tcmpl $0, 0(%ecx)\n" + "\tlea 4(%ecx), %ecx\n" + "\tjnz 2b\n" + "\tpush %ecx\n" + "\tpush %edx\n" + "\tpush %ebx\n" + "\tpush %eax\n" + "\tcall main\n" + "\tpush %eax\n" + "\tcall exit\n" +); diff --git a/tests/static/stat64.c b/tests/static/stat64.c new file mode 100644 index 0000000..4a07132 --- /dev/null +++ b/tests/static/stat64.c @@ -0,0 +1,184 @@ +/* + * stat64 - test the stat64 syscall + * + * Copyright (C) 2006-2012 Mike McCormack + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +void __stack_chk_fail(void) +{ +} + +/* + * minwgcc and gcc have different definitions of __thread + * errno should be per thread, + * leave as global until we can load an ELF binary + */ +int errno; + +static inline int set_errno(int r) +{ + if ((r & 0xfffff000) == 0xfffff000) + { + errno = -r; + r = -1; + } + return r; +} + +void exit(int status) +{ + while (1) + { + __asm__ __volatile__ ( + "\tmov $1, %%eax\n" + "\tint $0x80\n" + :: "b"(status) : "memory"); + } +} + +size_t strlen(const char *str) +{ + size_t n = 0; + while (str[n]) + n++; + return n; +} + +ssize_t write(int fd, const char *buffer, size_t length) +{ + int r; + __asm__ __volatile__ ( + "\tmov $4, %%eax\n" + "\tint $0x80\n" + :"=a"(r): "b"(fd), "c"(buffer), "d"(length) : "memory"); + + return set_errno(r); +} + +static void write_int(unsigned int x) +{ + char ch[8]; + int i; + for (i = 0; i < 8; i++) + { + ch[i] = (x>>((7-i)*4))&0x0f; + if (ch[i] < 10) + ch[i] += '0'; + else + ch[i] += 55; + } + write(1, ch, 8); +} + +struct stat64 { + unsigned long long st_dev; + int32_t __pad0; + unsigned long st_ino; + unsigned int st_mode; + unsigned int st_nlink; + unsigned long st_uid; + unsigned long st_gid; + unsigned long long st_rdev; + int32_t __pad1; + long long st_size; + unsigned long st_blksize; + unsigned long long st_blocks; + int st_atime; + unsigned int st_atime_nsec; + int st_mtime; + unsigned int st_mtime_nsec; + int st_ctime; + unsigned int st_ctime_nsec; + unsigned int __unused1; + unsigned int __unused2; +}; + +int stat64(const char *path, struct stat64 *st) +{ + int r; + __asm__ __volatile__ ( + "\tint $0x80\n" + :"=a"(r): "a"(195), "b"(path), "c"(st) : "memory"); + + return set_errno(r); +} + +int main(int argc, char **argv) +{ + int i; + unsigned char buf[0x100]; + struct stat64 *st = (void*) buf; + + for (i = 0; i < 0x100; i++) + buf[i] = 'x'; + + for (i = 1; i < argc; i++) + { + int r = stat64(argv[i], st); + if (r != 0) + return 1; + if (buf[sizeof *st] != 'x') + return 1; + write_int(st->st_mode); + write(1, " ", 1); + write_int(st->st_uid); + write(1, " ", 1); + write_int(st->st_gid); + write(1, " ", 1); + write_int(st->st_size); + write(1, " ", 1); + write_int(st->st_atime); + write(1, " ", 1); + write_int(st->st_mtime); + write(1, " ", 1); + write_int(st->st_ctime); + write(1, " ", 1); + write(1, argv[i], strlen(argv[i])); + write(1, "\n", 1); + } + + return 0; +} + +__asm__ ( + "\n" +".globl _start\n" +"_start:\n" + "\tmovl 0(%esp), %eax\n" + "\tlea 4(%esp), %ebx\n" + "\tmov %ebx, %ecx\n" +"1:\n" + "\tcmpl $0, 0(%ecx)\n" + "\tlea 4(%ecx), %ecx\n" + "\tjnz 1b\n" + "\tpush %ecx\n" + "\tmov %ecx, %edx\n" +"2:\n" + "\tcmpl $0, 0(%ecx)\n" + "\tlea 4(%ecx), %ecx\n" + "\tjnz 2b\n" + "\tpush %ecx\n" + "\tpush %edx\n" + "\tpush %ebx\n" + "\tpush %eax\n" + "\tcall main\n" + "\tpush %eax\n" + "\tcall exit\n" +);