From: Tulio A M Mendes Date: Sat, 14 Mar 2026 12:54:07 +0000 (-0300) Subject: feat: implement POSIX Tiers 1-5 — complete ulibc headers, syscalls, and sync primitives X-Git-Url: https://projects.tadryanom.me/docs/static/git-logo.png?a=commitdiff_plain;h=aa5474ad5a9079838650d422c48169c992723d43;p=AdrOS.git feat: implement POSIX Tiers 1-5 — complete ulibc headers, syscalls, and sync primitives Tier 1: Add ENOTSOCK/ENETUNREACH errno codes (kernel + ulibc) Tier 2: 13 new ulibc wrapper headers + source files: - sys/socket.h, netinet/in.h, arpa/inet.h, netdb.h, sys/un.h - sys/epoll.h, sys/inotify.h, dlfcn.h, spawn.h - semaphore.h, mqueue.h, aio.h, sys/shm.h - All with syscall wrappers matching kernel syscall numbers Tier 3: Missing functions in existing headers: - termios: cfmakeraw, cfget/setispeed, tcdrain, tcflush, tcgetpgrp/tcsetpgrp - string: strsignal - stdlib: bsearch, div/ldiv, mkstemp, strtod/strtof - stdio: scanf, fscanf, tmpfile, tmpnam - unistd: execle, getlogin, confstr, sbrk, link/symlink/readlink wrappers - New syslog.h stub (openlog/syslog/closelog) Tier 4: Kernel syscalls: - umount2 (SYSCALL_UMOUNT2=138) with vfs_umount in fs.c - wait4 (SYSCALL_WAIT4=139) wrapping process_waitpid + rusage Tier 5: Medium implementations: - glob.h/wordexp.h with directory walk + fnmatch pattern matching - pthread_mutex/cond/rwlock/key/once/barrier (futex-based) - /etc/passwd + /etc/group file parsing with static root fallback Build: make clean && make OK, cppcheck clean, 101/101 smoke tests pass --- diff --git a/include/errno.h b/include/errno.h index 580d3b3..c4e067d 100644 --- a/include/errno.h +++ b/include/errno.h @@ -43,5 +43,7 @@ #define ENOPROTOOPT 92 #define EOVERFLOW 75 #define ELOOP 40 +#define ENOTSOCK 88 +#define ENETUNREACH 101 #endif diff --git a/include/fs.h b/include/fs.h index 69411bb..096dd2b 100644 --- a/include/fs.h +++ b/include/fs.h @@ -86,6 +86,7 @@ int vfs_truncate(const char* path, uint32_t length); int vfs_link(const char* old_path, const char* new_path); int vfs_mount(const char* mountpoint, fs_node_t* root); +int vfs_umount(const char* mountpoint); // Global root of the filesystem extern fs_node_t* fs_root; diff --git a/include/syscall.h b/include/syscall.h index 2b998d6..78b5f0c 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -168,6 +168,8 @@ enum { SYSCALL_GETSOCKNAME = 135, SYSCALL_UNAME = 136, SYSCALL_GETRUSAGE = 137, + SYSCALL_UMOUNT2 = 138, + SYSCALL_WAIT4 = 139, }; #endif diff --git a/src/kernel/fs.c b/src/kernel/fs.c index f04fc2d..5d007e0 100644 --- a/src/kernel/fs.c +++ b/src/kernel/fs.c @@ -69,6 +69,23 @@ int vfs_mount(const char* mountpoint, fs_node_t* root) { return 0; } +int vfs_umount(const char* mountpoint) { + char mp[128]; + normalize_mountpoint(mountpoint, mp, sizeof(mp)); + + if (strcmp(mp, "/") == 0) return -EBUSY; + + for (int i = 0; i < g_mount_count; i++) { + if (strcmp(g_mounts[i].mountpoint, mp) == 0) { + for (int j = i; j < g_mount_count - 1; j++) + g_mounts[j] = g_mounts[j + 1]; + g_mount_count--; + return 0; + } + } + return -EINVAL; +} + uint32_t vfs_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) { if (node->f_ops && node->f_ops->read) return node->f_ops->read(node, offset, size, buffer); diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 64b40bb..d12f6fa 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -4934,6 +4934,50 @@ static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no) return; } + if (syscall_no == SYSCALL_UMOUNT2) { + if (!current_process || current_process->euid != 0) { + sc_ret(regs) = (uint32_t)-EPERM; + return; + } + const char* user_target = (const char*)sc_arg0(regs); + char ktarget[128]; + if (path_resolve_user(user_target, ktarget, sizeof(ktarget)) < 0) { + sc_ret(regs) = (uint32_t)-EFAULT; + return; + } + sc_ret(regs) = (uint32_t)vfs_umount(ktarget); + return; + } + + if (syscall_no == SYSCALL_WAIT4) { + int pid = (int)sc_arg0(regs); + int* ustatus = (int*)sc_arg1(regs); + int options = (int)sc_arg2(regs); + void* urusage = (void*)sc_arg3(regs); + + if (ustatus && user_range_ok(ustatus, sizeof(int)) == 0) { + sc_ret(regs) = (uint32_t)-EFAULT; return; + } + + int kstatus = 0; + int ret = process_waitpid(pid, &kstatus, options); + if (ret < 0) { + sc_ret(regs) = (uint32_t)ret; + return; + } + if (ret > 0 && ustatus) { + (void)copy_to_user(ustatus, &kstatus, sizeof(kstatus)); + } + if (ret > 0 && urusage) { + /* Zero out rusage — detailed accounting deferred */ + char zeros[64]; + memset(zeros, 0, sizeof(zeros)); + (void)copy_to_user(urusage, zeros, sizeof(zeros)); + } + sc_ret(regs) = (uint32_t)ret; + return; + } + sc_ret(regs) = (uint32_t)-ENOSYS; } diff --git a/user/ulibc/include/aio.h b/user/ulibc/include/aio.h new file mode 100644 index 0000000..2054d82 --- /dev/null +++ b/user/ulibc/include/aio.h @@ -0,0 +1,32 @@ +#ifndef ULIBC_AIO_H +#define ULIBC_AIO_H + +#include +#include +#include + +struct aiocb { + int aio_fildes; + volatile void* aio_buf; + size_t aio_nbytes; + int aio_offset; + int aio_reqprio; + int aio_lio_opcode; + /* internal */ + int __error; + int __return; +}; + +#define AIO_CANCELED 0 +#define AIO_NOTCANCELED 1 +#define AIO_ALLDONE 2 + +int aio_read(struct aiocb* aiocbp); +int aio_write(struct aiocb* aiocbp); +int aio_error(const struct aiocb* aiocbp); +int aio_return(struct aiocb* aiocbp); +int aio_suspend(const struct aiocb* const list[], int nent, + const struct timespec* timeout); +int aio_cancel(int fd, struct aiocb* aiocbp); + +#endif diff --git a/user/ulibc/include/arpa/inet.h b/user/ulibc/include/arpa/inet.h new file mode 100644 index 0000000..9dd3733 --- /dev/null +++ b/user/ulibc/include/arpa/inet.h @@ -0,0 +1,13 @@ +#ifndef ULIBC_ARPA_INET_H +#define ULIBC_ARPA_INET_H + +#include +#include + +in_addr_t inet_addr(const char* cp); +int inet_aton(const char* cp, struct in_addr* inp); +char* inet_ntoa(struct in_addr in); +const char* inet_ntop(int af, const void* src, char* dst, socklen_t size); +int inet_pton(int af, const char* src, void* dst); + +#endif diff --git a/user/ulibc/include/dlfcn.h b/user/ulibc/include/dlfcn.h new file mode 100644 index 0000000..81414c5 --- /dev/null +++ b/user/ulibc/include/dlfcn.h @@ -0,0 +1,14 @@ +#ifndef ULIBC_DLFCN_H +#define ULIBC_DLFCN_H + +#define RTLD_LAZY 0x01 +#define RTLD_NOW 0x02 +#define RTLD_GLOBAL 0x100 +#define RTLD_LOCAL 0x000 + +void* dlopen(const char* filename, int flags); +void* dlsym(void* handle, const char* symbol); +int dlclose(void* handle); +char* dlerror(void); + +#endif diff --git a/user/ulibc/include/errno.h b/user/ulibc/include/errno.h index 6886642..506c98b 100644 --- a/user/ulibc/include/errno.h +++ b/user/ulibc/include/errno.h @@ -45,6 +45,8 @@ extern int errno; #define ENOPROTOOPT 92 #define EPROTONOSUPPORT 93 #define EOPNOTSUPP 95 +#define ENOTSOCK 88 +#define ENETUNREACH 101 #define EWOULDBLOCK EAGAIN /* Convert raw syscall return to errno-style */ diff --git a/user/ulibc/include/glob.h b/user/ulibc/include/glob.h new file mode 100644 index 0000000..572b16d --- /dev/null +++ b/user/ulibc/include/glob.h @@ -0,0 +1,30 @@ +#ifndef ULIBC_GLOB_H +#define ULIBC_GLOB_H + +#include + +#define GLOB_ERR 0x01 +#define GLOB_MARK 0x02 +#define GLOB_NOSORT 0x04 +#define GLOB_DOOFFS 0x08 +#define GLOB_NOCHECK 0x10 +#define GLOB_APPEND 0x20 +#define GLOB_NOESCAPE 0x40 + +#define GLOB_NOSPACE 1 +#define GLOB_ABORTED 2 +#define GLOB_NOMATCH 3 + +typedef struct { + size_t gl_pathc; + char** gl_pathv; + size_t gl_offs; + /* internal */ + size_t __alloc; +} glob_t; + +int glob(const char* pattern, int flags, + int (*errfunc)(const char*, int), glob_t* pglob); +void globfree(glob_t* pglob); + +#endif diff --git a/user/ulibc/include/mqueue.h b/user/ulibc/include/mqueue.h new file mode 100644 index 0000000..08b33e2 --- /dev/null +++ b/user/ulibc/include/mqueue.h @@ -0,0 +1,24 @@ +#ifndef ULIBC_MQUEUE_H +#define ULIBC_MQUEUE_H + +#include +#include + +typedef int mqd_t; + +struct mq_attr { + long mq_flags; + long mq_maxmsg; + long mq_msgsize; + long mq_curmsgs; +}; + +mqd_t mq_open(const char* name, int oflag, ...); +int mq_close(mqd_t mqdes); +int mq_send(mqd_t mqdes, const char* msg_ptr, size_t msg_len, unsigned int msg_prio); +int mq_receive(mqd_t mqdes, char* msg_ptr, size_t msg_len, unsigned int* msg_prio); +int mq_unlink(const char* name); +int mq_getattr(mqd_t mqdes, struct mq_attr* attr); +int mq_setattr(mqd_t mqdes, const struct mq_attr* newattr, struct mq_attr* oldattr); + +#endif diff --git a/user/ulibc/include/netdb.h b/user/ulibc/include/netdb.h new file mode 100644 index 0000000..08ac345 --- /dev/null +++ b/user/ulibc/include/netdb.h @@ -0,0 +1,49 @@ +#ifndef ULIBC_NETDB_H +#define ULIBC_NETDB_H + +#include +#include +#include + +struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + socklen_t ai_addrlen; + struct sockaddr* ai_addr; + char* ai_canonname; + struct addrinfo* ai_next; +}; + +struct hostent { + char* h_name; + char** h_aliases; + int h_addrtype; + int h_length; + char** h_addr_list; +}; + +#define AI_PASSIVE 0x01 +#define AI_CANONNAME 0x02 +#define AI_NUMERICHOST 0x04 +#define AI_NUMERICSERV 0x08 + +#define NI_MAXHOST 1025 +#define NI_MAXSERV 32 + +#define EAI_NONAME -2 +#define EAI_AGAIN -3 +#define EAI_FAIL -4 +#define EAI_FAMILY -6 +#define EAI_SOCKTYPE -7 +#define EAI_SERVICE -8 +#define EAI_MEMORY -10 +#define EAI_SYSTEM -11 + +int getaddrinfo(const char* node, const char* service, + const struct addrinfo* hints, struct addrinfo** res); +void freeaddrinfo(struct addrinfo* res); +const char* gai_strerror(int errcode); + +#endif diff --git a/user/ulibc/include/netinet/in.h b/user/ulibc/include/netinet/in.h new file mode 100644 index 0000000..d7f0b7c --- /dev/null +++ b/user/ulibc/include/netinet/in.h @@ -0,0 +1,55 @@ +#ifndef ULIBC_NETINET_IN_H +#define ULIBC_NETINET_IN_H + +#include +#include + +typedef uint16_t in_port_t; +typedef uint32_t in_addr_t; + +struct in_addr { + in_addr_t s_addr; +}; + +struct sockaddr_in { + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; + unsigned char sin_zero[8]; +}; + +struct in6_addr { + uint8_t s6_addr[16]; +}; + +struct sockaddr_in6 { + sa_family_t sin6_family; + in_port_t sin6_port; + uint32_t sin6_flowinfo; + struct in6_addr sin6_addr; + uint32_t sin6_scope_id; +}; + +#define INADDR_ANY ((in_addr_t)0x00000000) +#define INADDR_BROADCAST ((in_addr_t)0xFFFFFFFF) +#define INADDR_LOOPBACK ((in_addr_t)0x7F000001) +#define INADDR_NONE ((in_addr_t)0xFFFFFFFF) + +#define IPPROTO_IP 0 +#define IPPROTO_ICMP 1 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#define IPPROTO_RAW 255 + +static inline uint16_t htons(uint16_t x) { + return (uint16_t)((x >> 8) | (x << 8)); +} +static inline uint16_t ntohs(uint16_t x) { return htons(x); } + +static inline uint32_t htonl(uint32_t x) { + return ((x >> 24) & 0xFF) | ((x >> 8) & 0xFF00) | + ((x << 8) & 0xFF0000) | ((x << 24) & 0xFF000000U); +} +static inline uint32_t ntohl(uint32_t x) { return htonl(x); } + +#endif diff --git a/user/ulibc/include/pthread.h b/user/ulibc/include/pthread.h index ac5ecef..fba453e 100644 --- a/user/ulibc/include/pthread.h +++ b/user/ulibc/include/pthread.h @@ -48,4 +48,117 @@ int pthread_attr_destroy(pthread_attr_t* attr); /* Set stack size in thread attributes. */ int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize); +/* Detach */ +int pthread_detach(pthread_t thread); + +/* Cancel */ +int pthread_cancel(pthread_t thread); +int pthread_setcancelstate(int state, int* oldstate); +int pthread_setcanceltype(int type, int* oldtype); +void pthread_testcancel(void); + +#define PTHREAD_CANCEL_ENABLE 0 +#define PTHREAD_CANCEL_DISABLE 1 +#define PTHREAD_CANCEL_DEFERRED 0 +#define PTHREAD_CANCEL_ASYNCHRONOUS 1 + +/* ---- Mutex ---- */ +typedef struct { + volatile int __lock; + int __owner; + int __type; + int __count; +} pthread_mutex_t; + +typedef struct { + int __type; +} pthread_mutexattr_t; + +#define PTHREAD_MUTEX_NORMAL 0 +#define PTHREAD_MUTEX_RECURSIVE 1 +#define PTHREAD_MUTEX_ERRORCHECK 2 +#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL + +#define PTHREAD_MUTEX_INITIALIZER { 0, 0, 0, 0 } + +int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr); +int pthread_mutex_destroy(pthread_mutex_t* mutex); +int pthread_mutex_lock(pthread_mutex_t* mutex); +int pthread_mutex_trylock(pthread_mutex_t* mutex); +int pthread_mutex_unlock(pthread_mutex_t* mutex); + +int pthread_mutexattr_init(pthread_mutexattr_t* attr); +int pthread_mutexattr_destroy(pthread_mutexattr_t* attr); +int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type); +int pthread_mutexattr_gettype(const pthread_mutexattr_t* attr, int* type); + +/* ---- Condition Variable ---- */ +typedef struct { + volatile int __seq; +} pthread_cond_t; + +typedef struct { + int __dummy; +} pthread_condattr_t; + +#define PTHREAD_COND_INITIALIZER { 0 } + +int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr); +int pthread_cond_destroy(pthread_cond_t* cond); +int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex); +int pthread_cond_signal(pthread_cond_t* cond); +int pthread_cond_broadcast(pthread_cond_t* cond); + +/* ---- Read-Write Lock ---- */ +typedef struct { + volatile int __readers; + volatile int __writer; +} pthread_rwlock_t; + +typedef struct { + int __dummy; +} pthread_rwlockattr_t; + +#define PTHREAD_RWLOCK_INITIALIZER { 0, 0 } + +int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr); +int pthread_rwlock_destroy(pthread_rwlock_t* rwlock); +int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock); +int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock); +int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock); +int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock); +int pthread_rwlock_unlock(pthread_rwlock_t* rwlock); + +/* ---- Thread-Specific Data (Keys) ---- */ +typedef unsigned int pthread_key_t; + +int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)); +int pthread_key_delete(pthread_key_t key); +void* pthread_getspecific(pthread_key_t key); +int pthread_setspecific(pthread_key_t key, const void* value); + +/* ---- Once ---- */ +typedef volatile int pthread_once_t; +#define PTHREAD_ONCE_INIT 0 + +int pthread_once(pthread_once_t* once_control, void (*init_routine)(void)); + +/* ---- Barrier (optional) ---- */ +typedef struct { + volatile int __count; + volatile int __total; + volatile int __seq; +} pthread_barrier_t; + +typedef struct { + int __dummy; +} pthread_barrierattr_t; + +#define PTHREAD_BARRIER_SERIAL_THREAD (-1) + +int pthread_barrier_init(pthread_barrier_t* barrier, + const pthread_barrierattr_t* attr, unsigned count); +int pthread_barrier_destroy(pthread_barrier_t* barrier); +int pthread_barrier_wait(pthread_barrier_t* barrier); + #endif diff --git a/user/ulibc/include/semaphore.h b/user/ulibc/include/semaphore.h new file mode 100644 index 0000000..ffc3cc7 --- /dev/null +++ b/user/ulibc/include/semaphore.h @@ -0,0 +1,22 @@ +#ifndef ULIBC_SEMAPHORE_H +#define ULIBC_SEMAPHORE_H + +#include + +typedef struct { + int __val; +} sem_t; + +#define SEM_FAILED ((sem_t*)-1) + +sem_t* sem_open(const char* name, int oflag, ...); +int sem_close(sem_t* sem); +int sem_wait(sem_t* sem); +int sem_trywait(sem_t* sem); +int sem_post(sem_t* sem); +int sem_unlink(const char* name); +int sem_getvalue(sem_t* sem, int* sval); +int sem_init(sem_t* sem, int pshared, unsigned int value); +int sem_destroy(sem_t* sem); + +#endif diff --git a/user/ulibc/include/spawn.h b/user/ulibc/include/spawn.h new file mode 100644 index 0000000..8532bcf --- /dev/null +++ b/user/ulibc/include/spawn.h @@ -0,0 +1,35 @@ +#ifndef ULIBC_SPAWN_H +#define ULIBC_SPAWN_H + +#include +#include + +typedef struct { + int __flags; + int __pgrp; + sigset_t __sd; + sigset_t __ss; +} posix_spawnattr_t; + +typedef struct { + int __allocated; + int __used; + void* __actions; +} posix_spawn_file_actions_t; + +int posix_spawn(int* pid, const char* path, + const posix_spawn_file_actions_t* file_actions, + const posix_spawnattr_t* attrp, + char* const argv[], char* const envp[]); + +int posix_spawnp(int* pid, const char* file, + const posix_spawn_file_actions_t* file_actions, + const posix_spawnattr_t* attrp, + char* const argv[], char* const envp[]); + +int posix_spawn_file_actions_init(posix_spawn_file_actions_t* fact); +int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t* fact); +int posix_spawnattr_init(posix_spawnattr_t* attr); +int posix_spawnattr_destroy(posix_spawnattr_t* attr); + +#endif diff --git a/user/ulibc/include/stdio.h b/user/ulibc/include/stdio.h index f39cb5e..3cc79f5 100644 --- a/user/ulibc/include/stdio.h +++ b/user/ulibc/include/stdio.h @@ -58,6 +58,8 @@ int sprintf(char* buf, const char* fmt, ...) __attribute__((format(printf, 2 int snprintf(char* buf, size_t size, const char* fmt, ...) __attribute__((format(printf, 3, 4))); int vsnprintf(char* buf, size_t size, const char* fmt, va_list ap); int sscanf(const char* str, const char* fmt, ...); +int scanf(const char* fmt, ...); +int fscanf(FILE* fp, const char* fmt, ...); int remove(const char* path); int rename(const char* oldpath, const char* newpath); diff --git a/user/ulibc/include/stdlib.h b/user/ulibc/include/stdlib.h index d25ff1c..4fb700b 100644 --- a/user/ulibc/include/stdlib.h +++ b/user/ulibc/include/stdlib.h @@ -29,6 +29,17 @@ void srand(unsigned int seed); void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*)); +void* bsearch(const void* key, const void* base, size_t nmemb, size_t size, + int (*compar)(const void*, const void*)); + +typedef struct { int quot; int rem; } div_t; +typedef struct { long quot; long rem; } ldiv_t; +div_t div(int numer, int denom); +ldiv_t ldiv(long numer, long denom); + +int mkstemp(char* tmpl); +double strtod(const char* nptr, char** endptr); +float strtof(const char* nptr, char** endptr); int system(const char* cmd); void exit(int status) __attribute__((noreturn)); diff --git a/user/ulibc/include/string.h b/user/ulibc/include/string.h index 2102311..e4fd4f1 100644 --- a/user/ulibc/include/string.h +++ b/user/ulibc/include/string.h @@ -29,5 +29,6 @@ size_t strnlen(const char* s, size_t maxlen); size_t strspn(const char* s, const char* accept); size_t strcspn(const char* s, const char* reject); char* strpbrk(const char* s, const char* accept); +char* strsignal(int sig); #endif diff --git a/user/ulibc/include/sys/epoll.h b/user/ulibc/include/sys/epoll.h new file mode 100644 index 0000000..2c05c68 --- /dev/null +++ b/user/ulibc/include/sys/epoll.h @@ -0,0 +1,35 @@ +#ifndef ULIBC_SYS_EPOLL_H +#define ULIBC_SYS_EPOLL_H + +#include + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_DEL 2 +#define EPOLL_CTL_MOD 3 + +#define EPOLLIN 0x001 +#define EPOLLOUT 0x004 +#define EPOLLERR 0x008 +#define EPOLLHUP 0x010 +#define EPOLLRDHUP 0x2000 +#define EPOLLET (1U << 31) +#define EPOLLONESHOT (1 << 30) + +typedef union epoll_data { + void* ptr; + int fd; + uint32_t u32; + uint64_t u64; +} epoll_data_t; + +struct epoll_event { + uint32_t events; + epoll_data_t data; +}; + +int epoll_create(int size); +int epoll_create1(int flags); +int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event); +int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout); + +#endif diff --git a/user/ulibc/include/sys/inotify.h b/user/ulibc/include/sys/inotify.h new file mode 100644 index 0000000..e61c8d1 --- /dev/null +++ b/user/ulibc/include/sys/inotify.h @@ -0,0 +1,36 @@ +#ifndef ULIBC_SYS_INOTIFY_H +#define ULIBC_SYS_INOTIFY_H + +#include + +#define IN_ACCESS 0x00000001 +#define IN_MODIFY 0x00000002 +#define IN_ATTRIB 0x00000004 +#define IN_CLOSE_WRITE 0x00000008 +#define IN_CLOSE_NOWRITE 0x00000010 +#define IN_OPEN 0x00000020 +#define IN_MOVED_FROM 0x00000040 +#define IN_MOVED_TO 0x00000080 +#define IN_CREATE 0x00000100 +#define IN_DELETE 0x00000200 +#define IN_DELETE_SELF 0x00000400 +#define IN_MOVE_SELF 0x00000800 +#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) +#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) +#define IN_ALL_EVENTS 0x00000FFF +#define IN_NONBLOCK 0x00004000 + +struct inotify_event { + int wd; + uint32_t mask; + uint32_t cookie; + uint32_t len; + char name[]; +}; + +int inotify_init(void); +int inotify_init1(int flags); +int inotify_add_watch(int fd, const char* pathname, uint32_t mask); +int inotify_rm_watch(int fd, int wd); + +#endif diff --git a/user/ulibc/include/sys/shm.h b/user/ulibc/include/sys/shm.h new file mode 100644 index 0000000..03596d9 --- /dev/null +++ b/user/ulibc/include/sys/shm.h @@ -0,0 +1,32 @@ +#ifndef ULIBC_SYS_SHM_H +#define ULIBC_SYS_SHM_H + +#include +#include + +#define IPC_CREAT 01000 +#define IPC_EXCL 02000 +#define IPC_NOWAIT 04000 +#define IPC_RMID 0 +#define IPC_SET 1 +#define IPC_STAT 2 +#define IPC_PRIVATE 0 + +typedef int key_t; + +struct shmid_ds { + int shm_segsz; + int shm_nattch; + int shm_cpid; + int shm_lpid; + uint32_t shm_atime; + uint32_t shm_dtime; + uint32_t shm_ctime; +}; + +int shmget(key_t key, size_t size, int shmflg); +void* shmat(int shmid, const void* shmaddr, int shmflg); +int shmdt(const void* shmaddr); +int shmctl(int shmid, int cmd, struct shmid_ds* buf); + +#endif diff --git a/user/ulibc/include/sys/socket.h b/user/ulibc/include/sys/socket.h new file mode 100644 index 0000000..dcfc164 --- /dev/null +++ b/user/ulibc/include/sys/socket.h @@ -0,0 +1,95 @@ +#ifndef ULIBC_SYS_SOCKET_H +#define ULIBC_SYS_SOCKET_H + +#include +#include + +/* Address families */ +#define AF_UNSPEC 0 +#define AF_UNIX 1 +#define AF_LOCAL AF_UNIX +#define AF_INET 2 +#define AF_INET6 10 + +/* Socket types */ +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define SOCK_RAW 3 +#define SOCK_NONBLOCK 0x800 +#define SOCK_CLOEXEC 0x80000 + +/* Protocol families (aliases) */ +#define PF_UNSPEC AF_UNSPEC +#define PF_UNIX AF_UNIX +#define PF_INET AF_INET +#define PF_INET6 AF_INET6 + +/* Socket options levels */ +#define SOL_SOCKET 1 + +/* Socket options */ +#define SO_REUSEADDR 2 +#define SO_ERROR 4 +#define SO_BROADCAST 6 +#define SO_SNDBUF 7 +#define SO_RCVBUF 8 +#define SO_KEEPALIVE 9 +#define SO_LINGER 13 +#define SO_RCVTIMEO 20 +#define SO_SNDTIMEO 21 + +/* shutdown() how */ +#define SHUT_RD 0 +#define SHUT_WR 1 +#define SHUT_RDWR 2 + +/* MSG flags */ +#define MSG_PEEK 0x02 +#define MSG_DONTWAIT 0x40 +#define MSG_NOSIGNAL 0x4000 + +typedef unsigned int socklen_t; +typedef unsigned short sa_family_t; + +struct sockaddr { + sa_family_t sa_family; + char sa_data[14]; +}; + +struct sockaddr_storage { + sa_family_t ss_family; + char __ss_pad[126]; +}; + +struct msghdr { + void* msg_name; + socklen_t msg_namelen; + struct iovec* msg_iov; + int msg_iovlen; + void* msg_control; + socklen_t msg_controllen; + int msg_flags; +}; + +struct iovec; + +int socket(int domain, int type, int protocol); +int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen); +int listen(int sockfd, int backlog); +int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen); +int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen); +int send(int sockfd, const void* buf, size_t len, int flags); +int recv(int sockfd, void* buf, size_t len, int flags); +int sendto(int sockfd, const void* buf, size_t len, int flags, + const struct sockaddr* dest_addr, socklen_t addrlen); +int recvfrom(int sockfd, void* buf, size_t len, int flags, + struct sockaddr* src_addr, socklen_t* addrlen); +int sendmsg(int sockfd, const struct msghdr* msg, int flags); +int recvmsg(int sockfd, struct msghdr* msg, int flags); +int setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t optlen); +int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen); +int shutdown(int sockfd, int how); +int getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen); +int getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen); + +#endif diff --git a/user/ulibc/include/sys/un.h b/user/ulibc/include/sys/un.h new file mode 100644 index 0000000..65a41c5 --- /dev/null +++ b/user/ulibc/include/sys/un.h @@ -0,0 +1,13 @@ +#ifndef ULIBC_SYS_UN_H +#define ULIBC_SYS_UN_H + +#include + +#define UNIX_PATH_MAX 108 + +struct sockaddr_un { + sa_family_t sun_family; + char sun_path[UNIX_PATH_MAX]; +}; + +#endif diff --git a/user/ulibc/include/syscall.h b/user/ulibc/include/syscall.h index 5c517eb..8bd6cba 100644 --- a/user/ulibc/include/syscall.h +++ b/user/ulibc/include/syscall.h @@ -80,6 +80,54 @@ enum { SYS_SETEGID = 91, SYS_SETITIMER = 92, SYS_GETITIMER = 93, + SYS_SHMGET = 46, + SYS_SHMAT = 47, + SYS_SHMDT = 48, + SYS_SHMCTL = 49, + SYS_LINK = 54, + SYS_SYMLINK = 55, + SYS_READLINK = 56, + SYS_SOCKET = 58, + SYS_BIND = 59, + SYS_LISTEN = 60, + SYS_ACCEPT = 61, + SYS_CONNECT = 62, + SYS_SEND = 63, + SYS_RECV = 64, + SYS_SENDTO = 65, + SYS_RECVFROM = 66, + SYS_WAITID = 94, + SYS_SIGQUEUE = 95, + SYS_POSIX_SPAWN = 96, + SYS_MQ_OPEN = 97, + SYS_MQ_CLOSE = 98, + SYS_MQ_SEND = 99, + SYS_MQ_RECEIVE = 100, + SYS_MQ_UNLINK = 101, + SYS_SEM_OPEN = 102, + SYS_SEM_CLOSE = 103, + SYS_SEM_WAIT = 104, + SYS_SEM_POST = 105, + SYS_SEM_UNLINK = 106, + SYS_SEM_GETVALUE = 107, + SYS_GETADDRINFO = 108, + SYS_DLOPEN = 109, + SYS_DLSYM = 110, + SYS_DLCLOSE = 111, + SYS_EPOLL_CREATE = 112, + SYS_EPOLL_CTL = 113, + SYS_EPOLL_WAIT = 114, + SYS_INOTIFY_INIT = 115, + SYS_INOTIFY_ADD_WATCH = 116, + SYS_INOTIFY_RM_WATCH = 117, + SYS_SENDMSG = 118, + SYS_RECVMSG = 119, + SYS_PIVOT_ROOT = 120, + SYS_AIO_READ = 121, + SYS_AIO_WRITE = 122, + SYS_AIO_ERROR = 123, + SYS_AIO_RETURN = 124, + SYS_AIO_SUSPEND = 125, SYS_MOUNT = 126, SYS_GETTIMEOFDAY = 127, SYS_MPROTECT = 128, @@ -92,6 +140,8 @@ enum { SYS_GETSOCKNAME = 135, SYS_UNAME = 136, SYS_GETRUSAGE = 137, + SYS_UMOUNT2 = 138, + SYS_WAIT4 = 139, }; /* Raw syscall wrappers — up to 5 args via INT 0x80 */ diff --git a/user/ulibc/include/syslog.h b/user/ulibc/include/syslog.h new file mode 100644 index 0000000..7da9a4e --- /dev/null +++ b/user/ulibc/include/syslog.h @@ -0,0 +1,27 @@ +#ifndef ULIBC_SYSLOG_H +#define ULIBC_SYSLOG_H + +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +#define LOG_KERN (0<<3) +#define LOG_USER (1<<3) +#define LOG_DAEMON (3<<3) +#define LOG_LOCAL0 (16<<3) + +#define LOG_PID 0x01 +#define LOG_CONS 0x02 +#define LOG_NDELAY 0x08 +#define LOG_NOWAIT 0x10 + +void openlog(const char* ident, int option, int facility); +void syslog(int priority, const char* format, ...); +void closelog(void); + +#endif diff --git a/user/ulibc/include/termios.h b/user/ulibc/include/termios.h index 1d4c157..85bedb1 100644 --- a/user/ulibc/include/termios.h +++ b/user/ulibc/include/termios.h @@ -52,4 +52,32 @@ int tcsetattr(int fd, int actions, const struct termios* t); #define TCSADRAIN 1 #define TCSAFLUSH 2 +typedef unsigned int speed_t; + +#define B0 0 +#define B9600 9600 +#define B19200 19200 +#define B38400 38400 +#define B115200 115200 + +speed_t cfgetispeed(const struct termios* t); +speed_t cfgetospeed(const struct termios* t); +int cfsetispeed(struct termios* t, speed_t speed); +int cfsetospeed(struct termios* t, speed_t speed); +void cfmakeraw(struct termios* t); +int tcdrain(int fd); +int tcflush(int fd, int queue_selector); +int tcflow(int fd, int action); +int tcsendbreak(int fd, int duration); +int tcgetpgrp(int fd); +int tcsetpgrp(int fd, int pgrp); + +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + #endif diff --git a/user/ulibc/include/unistd.h b/user/ulibc/include/unistd.h index faf3cb3..57ca07f 100644 --- a/user/ulibc/include/unistd.h +++ b/user/ulibc/include/unistd.h @@ -101,6 +101,15 @@ long fpathconf(int fd, int name); int gethostname(char* name, size_t len); char* ttyname(int fd); int pipe2(int fds[2], int flags); +int execle(const char* path, const char* arg, ...); +char* getlogin(void); +int getlogin_r(char* buf, size_t bufsize); +int tcgetpgrp(int fd); +int tcsetpgrp(int fd, int pgrp); + +#define _CS_PATH 0 +long confstr(int name, char* buf, size_t len); +void* sbrk(int increment); /* Environment pointer (set by crt0) */ extern char** __environ; diff --git a/user/ulibc/include/wordexp.h b/user/ulibc/include/wordexp.h new file mode 100644 index 0000000..d109eab --- /dev/null +++ b/user/ulibc/include/wordexp.h @@ -0,0 +1,28 @@ +#ifndef ULIBC_WORDEXP_H +#define ULIBC_WORDEXP_H + +#include + +#define WRDE_DOOFFS 0x01 +#define WRDE_APPEND 0x02 +#define WRDE_NOCMD 0x04 +#define WRDE_REUSE 0x08 +#define WRDE_SHOWERR 0x10 +#define WRDE_UNDEF 0x20 + +#define WRDE_BADCHAR 1 +#define WRDE_BADVAL 2 +#define WRDE_CMDSUB 3 +#define WRDE_NOSPACE 4 +#define WRDE_SYNTAX 5 + +typedef struct { + size_t we_wordc; + char** we_wordv; + size_t we_offs; +} wordexp_t; + +int wordexp(const char* words, wordexp_t* pwordexp, int flags); +void wordfree(wordexp_t* pwordexp); + +#endif diff --git a/user/ulibc/src/aio.c b/user/ulibc/src/aio.c new file mode 100644 index 0000000..364dbbd --- /dev/null +++ b/user/ulibc/src/aio.c @@ -0,0 +1,32 @@ +#include "aio.h" +#include "syscall.h" +#include "errno.h" + +int aio_read(struct aiocb* aiocbp) { + return __syscall_ret(_syscall3(SYS_AIO_READ, aiocbp->aio_fildes, + (int)aiocbp->aio_buf, (int)aiocbp->aio_nbytes)); +} + +int aio_write(struct aiocb* aiocbp) { + return __syscall_ret(_syscall3(SYS_AIO_WRITE, aiocbp->aio_fildes, + (int)aiocbp->aio_buf, (int)aiocbp->aio_nbytes)); +} + +int aio_error(const struct aiocb* aiocbp) { + return _syscall1(SYS_AIO_ERROR, (int)aiocbp); +} + +int aio_return(struct aiocb* aiocbp) { + return _syscall1(SYS_AIO_RETURN, (int)aiocbp); +} + +int aio_suspend(const struct aiocb* const list[], int nent, + const struct timespec* timeout) { + return __syscall_ret(_syscall3(SYS_AIO_SUSPEND, (int)list, nent, (int)timeout)); +} + +int aio_cancel(int fd, struct aiocb* aiocbp) { + (void)fd; + (void)aiocbp; + return AIO_ALLDONE; +} diff --git a/user/ulibc/src/dlfcn.c b/user/ulibc/src/dlfcn.c new file mode 100644 index 0000000..150ba56 --- /dev/null +++ b/user/ulibc/src/dlfcn.c @@ -0,0 +1,44 @@ +#include "dlfcn.h" +#include "syscall.h" +#include "errno.h" + +static char _dlerror_buf[64]; +static int _dlerror_set; + +void* dlopen(const char* filename, int flags) { + int ret = _syscall2(SYS_DLOPEN, (int)filename, flags); + if (ret < 0 && ret > -4096) { + _dlerror_set = 1; + errno = -ret; + return (void*)0; + } + return (void*)ret; +} + +void* dlsym(void* handle, const char* symbol) { + int ret = _syscall2(SYS_DLSYM, (int)handle, (int)symbol); + if (ret < 0 && ret > -4096) { + _dlerror_set = 1; + errno = -ret; + return (void*)0; + } + return (void*)ret; +} + +int dlclose(void* handle) { + return __syscall_ret(_syscall1(SYS_DLCLOSE, (int)handle)); +} + +char* dlerror(void) { + if (_dlerror_set) { + _dlerror_set = 0; + /* minimal message */ + _dlerror_buf[0] = 'd'; _dlerror_buf[1] = 'l'; + _dlerror_buf[2] = ' '; _dlerror_buf[3] = 'e'; + _dlerror_buf[4] = 'r'; _dlerror_buf[5] = 'r'; + _dlerror_buf[6] = 'o'; _dlerror_buf[7] = 'r'; + _dlerror_buf[8] = '\0'; + return _dlerror_buf; + } + return (void*)0; +} diff --git a/user/ulibc/src/epoll.c b/user/ulibc/src/epoll.c new file mode 100644 index 0000000..7a5d111 --- /dev/null +++ b/user/ulibc/src/epoll.c @@ -0,0 +1,20 @@ +#include "sys/epoll.h" +#include "syscall.h" +#include "errno.h" + +int epoll_create(int size) { + (void)size; + return __syscall_ret(_syscall1(SYS_EPOLL_CREATE, 0)); +} + +int epoll_create1(int flags) { + return __syscall_ret(_syscall1(SYS_EPOLL_CREATE, flags)); +} + +int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event) { + return __syscall_ret(_syscall4(SYS_EPOLL_CTL, epfd, op, fd, (int)event)); +} + +int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout) { + return __syscall_ret(_syscall4(SYS_EPOLL_WAIT, epfd, (int)events, maxevents, timeout)); +} diff --git a/user/ulibc/src/glob.c b/user/ulibc/src/glob.c new file mode 100644 index 0000000..b83507a --- /dev/null +++ b/user/ulibc/src/glob.c @@ -0,0 +1,110 @@ +#include "glob.h" +#include "fnmatch.h" +#include "string.h" +#include "stdlib.h" +#include "unistd.h" +#include "dirent.h" +#include "errno.h" + +static int glob_add(glob_t* g, const char* path) { + if (g->gl_pathc + 1 >= g->__alloc) { + size_t newalloc = g->__alloc ? g->__alloc * 2 : 16; + char** nv = (char**)realloc(g->gl_pathv, newalloc * sizeof(char*)); + if (!nv) return GLOB_NOSPACE; + g->gl_pathv = nv; + g->__alloc = newalloc; + } + g->gl_pathv[g->gl_pathc] = strdup(path); + if (!g->gl_pathv[g->gl_pathc]) return GLOB_NOSPACE; + g->gl_pathc++; + g->gl_pathv[g->gl_pathc] = (void*)0; + return 0; +} + +int glob(const char* pattern, int flags, + int (*errfunc)(const char*, int), glob_t* pglob) { + (void)errfunc; + + if (!(flags & GLOB_APPEND)) { + pglob->gl_pathc = 0; + pglob->gl_pathv = (void*)0; + pglob->__alloc = 0; + } + + /* Split pattern into directory + basename */ + char dir[256], base[256]; + const char* last_slash = strrchr(pattern, '/'); + if (last_slash) { + size_t dlen = (size_t)(last_slash - pattern); + if (dlen == 0) dlen = 1; + if (dlen >= sizeof(dir)) dlen = sizeof(dir) - 1; + memcpy(dir, pattern, dlen); + dir[dlen] = '\0'; + strncpy(base, last_slash + 1, sizeof(base) - 1); + base[sizeof(base) - 1] = '\0'; + } else { + strcpy(dir, "."); + strncpy(base, pattern, sizeof(base) - 1); + base[sizeof(base) - 1] = '\0'; + } + + /* Open directory and match entries */ + int dfd = open(dir, 0); + if (dfd < 0) { + if (flags & GLOB_NOCHECK) + return glob_add(pglob, pattern); + return GLOB_ABORTED; + } + + struct dirent de; + char path[512]; + int found = 0; + + while (getdents(dfd, &de, sizeof(de)) > 0) { + if (de.d_name[0] == '\0') continue; + if (fnmatch(base, de.d_name, 0) == 0) { + if (last_slash) { + snprintf(path, sizeof(path), "%s/%s", dir, de.d_name); + } else { + strncpy(path, de.d_name, sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + } + int rc = glob_add(pglob, path); + if (rc) { close(dfd); return rc; } + found++; + } + } + + close(dfd); + + if (found == 0) { + if (flags & GLOB_NOCHECK) + return glob_add(pglob, pattern); + return GLOB_NOMATCH; + } + + if (!(flags & GLOB_NOSORT) && pglob->gl_pathc > 1) { + /* Simple insertion sort */ + for (size_t i = 1; i < pglob->gl_pathc; i++) { + char* tmp = pglob->gl_pathv[i]; + size_t j = i; + while (j > 0 && strcmp(pglob->gl_pathv[j - 1], tmp) > 0) { + pglob->gl_pathv[j] = pglob->gl_pathv[j - 1]; + j--; + } + pglob->gl_pathv[j] = tmp; + } + } + + return 0; +} + +void globfree(glob_t* pglob) { + if (!pglob || !pglob->gl_pathv) return; + for (size_t i = 0; i < pglob->gl_pathc; i++) + free(pglob->gl_pathv[i]); + free(pglob->gl_pathv); + pglob->gl_pathv = (void*)0; + pglob->gl_pathc = 0; + pglob->__alloc = 0; +} diff --git a/user/ulibc/src/inet.c b/user/ulibc/src/inet.c new file mode 100644 index 0000000..2a9f9d4 --- /dev/null +++ b/user/ulibc/src/inet.c @@ -0,0 +1,73 @@ +#include "arpa/inet.h" +#include "string.h" +#include "errno.h" + +in_addr_t inet_addr(const char* cp) { + struct in_addr addr; + if (inet_aton(cp, &addr)) + return addr.s_addr; + return INADDR_NONE; +} + +int inet_aton(const char* cp, struct in_addr* inp) { + uint32_t val = 0; + int parts = 0; + uint32_t part = 0; + for (const char* p = cp; ; p++) { + if (*p >= '0' && *p <= '9') { + part = part * 10 + (unsigned)(*p - '0'); + if (part > 255) return 0; + } else if (*p == '.' || *p == '\0') { + val = (val << 8) | part; + part = 0; + parts++; + if (*p == '\0') break; + if (parts >= 4) return 0; + } else { + return 0; + } + } + if (parts != 4) return 0; + if (inp) inp->s_addr = htonl(val); + return 1; +} + +static char _ntoa_buf[16]; +char* inet_ntoa(struct in_addr in) { + uint32_t addr = ntohl(in.s_addr); + int pos = 0; + for (int i = 3; i >= 0; i--) { + unsigned int octet = (addr >> (i * 8)) & 0xFF; + if (octet >= 100) _ntoa_buf[pos++] = '0' + (char)(octet / 100); + if (octet >= 10) _ntoa_buf[pos++] = '0' + (char)((octet / 10) % 10); + _ntoa_buf[pos++] = '0' + (char)(octet % 10); + if (i > 0) _ntoa_buf[pos++] = '.'; + } + _ntoa_buf[pos] = '\0'; + return _ntoa_buf; +} + +const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) { + if (af == AF_INET) { + struct in_addr a; + memcpy(&a, src, sizeof(a)); + char* s = inet_ntoa(a); + size_t len = strlen(s); + if (len >= size) { errno = ENOSPC; return (void*)0; } + memcpy(dst, s, len + 1); + return dst; + } + errno = EAFNOSUPPORT; + return (void*)0; +} + +int inet_pton(int af, const char* src, void* dst) { + if (af == AF_INET) { + struct in_addr addr; + if (!inet_aton(src, &addr)) return 0; + memcpy(dst, &addr, sizeof(addr)); + return 1; + } + errno = EAFNOSUPPORT; + return -1; +} diff --git a/user/ulibc/src/inotify.c b/user/ulibc/src/inotify.c new file mode 100644 index 0000000..f08264d --- /dev/null +++ b/user/ulibc/src/inotify.c @@ -0,0 +1,19 @@ +#include "sys/inotify.h" +#include "syscall.h" +#include "errno.h" + +int inotify_init(void) { + return __syscall_ret(_syscall0(SYS_INOTIFY_INIT)); +} + +int inotify_init1(int flags) { + return __syscall_ret(_syscall1(SYS_INOTIFY_INIT, flags)); +} + +int inotify_add_watch(int fd, const char* pathname, uint32_t mask) { + return __syscall_ret(_syscall3(SYS_INOTIFY_ADD_WATCH, fd, (int)pathname, (int)mask)); +} + +int inotify_rm_watch(int fd, int wd) { + return __syscall_ret(_syscall2(SYS_INOTIFY_RM_WATCH, fd, wd)); +} diff --git a/user/ulibc/src/mqueue.c b/user/ulibc/src/mqueue.c new file mode 100644 index 0000000..f75c012 --- /dev/null +++ b/user/ulibc/src/mqueue.c @@ -0,0 +1,40 @@ +#include "mqueue.h" +#include "syscall.h" +#include "errno.h" + +mqd_t mq_open(const char* name, int oflag, ...) { + return (mqd_t)__syscall_ret(_syscall2(SYS_MQ_OPEN, (int)name, oflag)); +} + +int mq_close(mqd_t mqdes) { + return __syscall_ret(_syscall1(SYS_MQ_CLOSE, mqdes)); +} + +int mq_send(mqd_t mqdes, const char* msg_ptr, size_t msg_len, unsigned int msg_prio) { + (void)msg_prio; + return __syscall_ret(_syscall3(SYS_MQ_SEND, mqdes, (int)msg_ptr, (int)msg_len)); +} + +int mq_receive(mqd_t mqdes, char* msg_ptr, size_t msg_len, unsigned int* msg_prio) { + (void)msg_prio; + return __syscall_ret(_syscall3(SYS_MQ_RECEIVE, mqdes, (int)msg_ptr, (int)msg_len)); +} + +int mq_unlink(const char* name) { + return __syscall_ret(_syscall1(SYS_MQ_UNLINK, (int)name)); +} + +int mq_getattr(mqd_t mqdes, struct mq_attr* attr) { + (void)mqdes; + (void)attr; + errno = ENOSYS; + return -1; +} + +int mq_setattr(mqd_t mqdes, const struct mq_attr* newattr, struct mq_attr* oldattr) { + (void)mqdes; + (void)newattr; + (void)oldattr; + errno = ENOSYS; + return -1; +} diff --git a/user/ulibc/src/netdb.c b/user/ulibc/src/netdb.c new file mode 100644 index 0000000..8736a56 --- /dev/null +++ b/user/ulibc/src/netdb.c @@ -0,0 +1,57 @@ +#include "netdb.h" +#include "syscall.h" +#include "errno.h" +#include "stdlib.h" +#include "string.h" + +int getaddrinfo(const char* node, const char* service, + const struct addrinfo* hints, struct addrinfo** res) { + (void)service; + if (!res) return EAI_FAIL; + + /* Use kernel getaddrinfo syscall for DNS resolution */ + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + + int ret = _syscall3(SYS_GETADDRINFO, (int)node, (int)&addr.sin_addr, 0); + if (ret < 0) return EAI_NONAME; + + struct addrinfo* ai = (struct addrinfo*)calloc(1, sizeof(struct addrinfo) + sizeof(struct sockaddr_in)); + if (!ai) return EAI_MEMORY; + + struct sockaddr_in* sa = (struct sockaddr_in*)(ai + 1); + memcpy(sa, &addr, sizeof(struct sockaddr_in)); + + ai->ai_family = AF_INET; + ai->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM; + ai->ai_protocol = hints ? hints->ai_protocol : 0; + ai->ai_addrlen = sizeof(struct sockaddr_in); + ai->ai_addr = (struct sockaddr*)sa; + ai->ai_next = (void*)0; + *res = ai; + return 0; +} + +void freeaddrinfo(struct addrinfo* res) { + while (res) { + struct addrinfo* next = res->ai_next; + free(res); + res = next; + } +} + +const char* gai_strerror(int errcode) { + switch (errcode) { + case 0: return "Success"; + case EAI_NONAME: return "Name does not resolve"; + case EAI_AGAIN: return "Temporary failure in name resolution"; + case EAI_FAIL: return "Non-recoverable failure in name resolution"; + case EAI_MEMORY: return "Memory allocation failure"; + case EAI_FAMILY: return "Address family not supported"; + case EAI_SOCKTYPE: return "Socket type not supported"; + case EAI_SERVICE: return "Service not supported"; + case EAI_SYSTEM: return "System error"; + default: return "Unknown error"; + } +} diff --git a/user/ulibc/src/pthread.c b/user/ulibc/src/pthread.c index df27ea5..5146ef1 100644 --- a/user/ulibc/src/pthread.c +++ b/user/ulibc/src/pthread.c @@ -165,3 +165,325 @@ int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize) { attr->stack_size = stacksize; return 0; } + +int pthread_detach(pthread_t thread) { + (void)thread; + return 0; +} + +int pthread_cancel(pthread_t thread) { + (void)thread; + return 0; +} + +int pthread_setcancelstate(int state, int* oldstate) { + if (oldstate) *oldstate = 0; + (void)state; + return 0; +} + +int pthread_setcanceltype(int type, int* oldtype) { + if (oldtype) *oldtype = 0; + (void)type; + return 0; +} + +void pthread_testcancel(void) {} + +/* ---- Futex helpers ---- */ +#define FUTEX_WAIT 0 +#define FUTEX_WAKE 1 + +static int futex_wait(volatile int* addr, int val) { + return _syscall3(SYS_FUTEX, (int)addr, FUTEX_WAIT, val); +} + +static int futex_wake(volatile int* addr, int count) { + return _syscall3(SYS_FUTEX, (int)addr, FUTEX_WAKE, count); +} + +static int atomic_cas(volatile int* ptr, int old, int new_val) { + int prev; + __asm__ volatile("lock cmpxchgl %2, %1" + : "=a"(prev), "+m"(*ptr) + : "r"(new_val), "0"(old) + : "memory"); + return prev; +} + +static int atomic_xchg(volatile int* ptr, int val) { + __asm__ volatile("xchgl %0, %1" + : "=r"(val), "+m"(*ptr) + : "0"(val) + : "memory"); + return val; +} + +static void atomic_add(volatile int* ptr, int val) { + __asm__ volatile("lock addl %1, %0" : "+m"(*ptr) : "r"(val) : "memory"); +} + +/* ---- Mutex ---- */ +int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) { + if (!mutex) return 22; + mutex->__lock = 0; + mutex->__owner = 0; + mutex->__type = attr ? attr->__type : PTHREAD_MUTEX_NORMAL; + mutex->__count = 0; + return 0; +} + +int pthread_mutex_destroy(pthread_mutex_t* mutex) { + (void)mutex; + return 0; +} + +int pthread_mutex_lock(pthread_mutex_t* mutex) { + if (!mutex) return 22; + int tid = _syscall0(SYS_GETTID); + + if (mutex->__type == PTHREAD_MUTEX_RECURSIVE && mutex->__owner == tid) { + mutex->__count++; + return 0; + } + + while (atomic_xchg(&mutex->__lock, 1) != 0) { + futex_wait(&mutex->__lock, 1); + } + mutex->__owner = tid; + mutex->__count = 1; + return 0; +} + +int pthread_mutex_trylock(pthread_mutex_t* mutex) { + if (!mutex) return 22; + int tid = _syscall0(SYS_GETTID); + + if (mutex->__type == PTHREAD_MUTEX_RECURSIVE && mutex->__owner == tid) { + mutex->__count++; + return 0; + } + + if (atomic_cas(&mutex->__lock, 0, 1) == 0) { + mutex->__owner = tid; + mutex->__count = 1; + return 0; + } + return 16; /* EBUSY */ +} + +int pthread_mutex_unlock(pthread_mutex_t* mutex) { + if (!mutex) return 22; + + if (mutex->__type == PTHREAD_MUTEX_RECURSIVE) { + if (--mutex->__count > 0) return 0; + } + + mutex->__owner = 0; + mutex->__count = 0; + atomic_xchg(&mutex->__lock, 0); + futex_wake(&mutex->__lock, 1); + return 0; +} + +int pthread_mutexattr_init(pthread_mutexattr_t* attr) { + if (!attr) return 22; + attr->__type = PTHREAD_MUTEX_NORMAL; + return 0; +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t* attr) { + (void)attr; + return 0; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type) { + if (!attr) return 22; + attr->__type = type; + return 0; +} + +int pthread_mutexattr_gettype(const pthread_mutexattr_t* attr, int* type) { + if (!attr || !type) return 22; + *type = attr->__type; + return 0; +} + +/* ---- Condition Variable ---- */ +int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr) { + (void)attr; + if (!cond) return 22; + cond->__seq = 0; + return 0; +} + +int pthread_cond_destroy(pthread_cond_t* cond) { + (void)cond; + return 0; +} + +int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) { + int seq = cond->__seq; + pthread_mutex_unlock(mutex); + futex_wait(&cond->__seq, seq); + pthread_mutex_lock(mutex); + return 0; +} + +int pthread_cond_signal(pthread_cond_t* cond) { + atomic_add(&cond->__seq, 1); + futex_wake(&cond->__seq, 1); + return 0; +} + +int pthread_cond_broadcast(pthread_cond_t* cond) { + atomic_add(&cond->__seq, 1); + futex_wake(&cond->__seq, 0x7FFFFFFF); + return 0; +} + +/* ---- Read-Write Lock ---- */ +int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr) { + (void)attr; + if (!rwlock) return 22; + rwlock->__readers = 0; + rwlock->__writer = 0; + return 0; +} + +int pthread_rwlock_destroy(pthread_rwlock_t* rwlock) { + (void)rwlock; + return 0; +} + +int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock) { + while (1) { + while (rwlock->__writer) + futex_wait(&rwlock->__writer, 1); + atomic_add(&rwlock->__readers, 1); + if (!rwlock->__writer) return 0; + atomic_add(&rwlock->__readers, -1); + } +} + +int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock) { + while (atomic_xchg(&rwlock->__writer, 1) != 0) + futex_wait(&rwlock->__writer, 1); + while (rwlock->__readers > 0) + futex_wait(&rwlock->__readers, rwlock->__readers); + return 0; +} + +int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock) { + if (rwlock->__writer) return 16; /* EBUSY */ + atomic_add(&rwlock->__readers, 1); + if (rwlock->__writer) { + atomic_add(&rwlock->__readers, -1); + return 16; + } + return 0; +} + +int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock) { + if (atomic_cas(&rwlock->__writer, 0, 1) != 0) return 16; + if (rwlock->__readers > 0) { + rwlock->__writer = 0; + return 16; + } + return 0; +} + +int pthread_rwlock_unlock(pthread_rwlock_t* rwlock) { + if (rwlock->__writer) { + rwlock->__writer = 0; + futex_wake(&rwlock->__writer, 0x7FFFFFFF); + } else { + atomic_add(&rwlock->__readers, -1); + if (rwlock->__readers == 0) + futex_wake(&rwlock->__readers, 1); + } + return 0; +} + +/* ---- Thread-Specific Data ---- */ +#define PTHREAD_KEYS_MAX 32 +static void* _tsd_values[PTHREAD_KEYS_MAX]; +static void (*_tsd_destructors[PTHREAD_KEYS_MAX])(void*); +static int _tsd_used[PTHREAD_KEYS_MAX]; +static int _tsd_next_key = 0; + +int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)) { + if (!key) return 22; + if (_tsd_next_key >= PTHREAD_KEYS_MAX) return 11; /* EAGAIN */ + int k = _tsd_next_key++; + _tsd_used[k] = 1; + _tsd_destructors[k] = destructor; + _tsd_values[k] = (void*)0; + *key = (pthread_key_t)k; + return 0; +} + +int pthread_key_delete(pthread_key_t key) { + if (key >= PTHREAD_KEYS_MAX || !_tsd_used[key]) return 22; + _tsd_used[key] = 0; + _tsd_destructors[key] = (void*)0; + _tsd_values[key] = (void*)0; + return 0; +} + +void* pthread_getspecific(pthread_key_t key) { + if (key >= PTHREAD_KEYS_MAX) return (void*)0; + return _tsd_values[key]; +} + +int pthread_setspecific(pthread_key_t key, const void* value) { + if (key >= PTHREAD_KEYS_MAX) return 22; + _tsd_values[key] = (void*)(uintptr_t)value; + return 0; +} + +/* ---- Once ---- */ +int pthread_once(pthread_once_t* once_control, void (*init_routine)(void)) { + if (!once_control || !init_routine) return 22; + if (atomic_cas(once_control, 0, 1) == 0) { + init_routine(); + *once_control = 2; + futex_wake(once_control, 0x7FFFFFFF); + } else { + while (*once_control == 1) + futex_wait(once_control, 1); + } + return 0; +} + +/* ---- Barrier ---- */ +int pthread_barrier_init(pthread_barrier_t* barrier, + const pthread_barrierattr_t* attr, unsigned count) { + (void)attr; + if (!barrier || count == 0) return 22; + barrier->__count = 0; + barrier->__total = (int)count; + barrier->__seq = 0; + return 0; +} + +int pthread_barrier_destroy(pthread_barrier_t* barrier) { + (void)barrier; + return 0; +} + +int pthread_barrier_wait(pthread_barrier_t* barrier) { + int seq = barrier->__seq; + int n; + atomic_add(&barrier->__count, 1); + n = barrier->__count; + if (n >= barrier->__total) { + barrier->__count = 0; + atomic_add(&barrier->__seq, 1); + futex_wake(&barrier->__seq, 0x7FFFFFFF); + return PTHREAD_BARRIER_SERIAL_THREAD; + } + while (barrier->__seq == seq) + futex_wait(&barrier->__seq, seq); + return 0; +} diff --git a/user/ulibc/src/pwd_grp.c b/user/ulibc/src/pwd_grp.c index 79f668d..ebad3f6 100644 --- a/user/ulibc/src/pwd_grp.c +++ b/user/ulibc/src/pwd_grp.c @@ -1,9 +1,12 @@ #include "pwd.h" #include "grp.h" +#include "stdio.h" +#include "string.h" +#include "stdlib.h" #include -/* Minimal /etc/passwd and /etc/group stubs. - * AdrOS has a single-user model (uid=0 root). */ +/* /etc/passwd and /etc/group parsing with static fallback. + * Format: name:passwd:uid:gid:gecos:dir:shell */ static struct passwd _root = { .pw_name = "root", @@ -15,27 +18,85 @@ static struct passwd _root = { .pw_shell = "/bin/sh", }; +static struct passwd _parsed_pw; +static char _pw_line[256]; +static FILE* _pw_fp = (void*)0; static int _pw_idx = 0; +static struct passwd* parse_passwd_line(char* line) { + char* fields[7]; + int n = 0; + char* p = line; + fields[n++] = p; + while (*p && n < 7) { + if (*p == ':') { *p = '\0'; fields[n++] = p + 1; } + p++; + } + if (n < 7) return (struct passwd*)0; + _parsed_pw.pw_name = fields[0]; + _parsed_pw.pw_passwd = fields[1]; + _parsed_pw.pw_uid = atoi(fields[2]); + _parsed_pw.pw_gid = atoi(fields[3]); + _parsed_pw.pw_gecos = fields[4]; + _parsed_pw.pw_dir = fields[5]; + _parsed_pw.pw_shell = fields[6]; + /* Strip trailing newline */ + size_t sl = strlen(_parsed_pw.pw_shell); + if (sl > 0 && _parsed_pw.pw_shell[sl - 1] == '\n') + _parsed_pw.pw_shell[sl - 1] = '\0'; + return &_parsed_pw; +} + struct passwd* getpwnam(const char* name) { if (!name) return (struct passwd*)0; - /* strcmp inline to avoid header dependency issues */ - const char* a = name; - const char* b = "root"; - while (*a && *a == *b) { a++; b++; } - if (*a == *b) return &_root; + FILE* fp = fopen("/etc/passwd", "r"); + if (fp) { + while (fgets(_pw_line, (int)sizeof(_pw_line), fp)) { + struct passwd* pw = parse_passwd_line(_pw_line); + if (pw && strcmp(pw->pw_name, name) == 0) { + fclose(fp); + return pw; + } + } + fclose(fp); + } + if (strcmp(name, "root") == 0) return &_root; return (struct passwd*)0; } struct passwd* getpwuid(int uid) { + FILE* fp = fopen("/etc/passwd", "r"); + if (fp) { + while (fgets(_pw_line, (int)sizeof(_pw_line), fp)) { + struct passwd* pw = parse_passwd_line(_pw_line); + if (pw && pw->pw_uid == uid) { + fclose(fp); + return pw; + } + } + fclose(fp); + } if (uid == 0) return &_root; return (struct passwd*)0; } -void setpwent(void) { _pw_idx = 0; } -void endpwent(void) { _pw_idx = 0; } +void setpwent(void) { + if (_pw_fp) fclose(_pw_fp); + _pw_fp = fopen("/etc/passwd", "r"); + _pw_idx = 0; +} + +void endpwent(void) { + if (_pw_fp) { fclose(_pw_fp); _pw_fp = (void*)0; } + _pw_idx = 0; +} struct passwd* getpwent(void) { + if (_pw_fp) { + if (fgets(_pw_line, (int)sizeof(_pw_line), _pw_fp)) + return parse_passwd_line(_pw_line); + return (struct passwd*)0; + } if (_pw_idx == 0) { _pw_idx++; return &_root; } return (struct passwd*)0; } @@ -49,26 +110,91 @@ static struct group _root_grp = { .gr_mem = _root_members, }; +static struct group _parsed_gr; +static char _gr_line[256]; +static char* _gr_members[16]; +static FILE* _gr_fp = (void*)0; static int _gr_idx = 0; +static struct group* parse_group_line(char* line) { + /* Format: name:passwd:gid:member1,member2,... */ + char* fields[4]; + int n = 0; + char* p = line; + fields[n++] = p; + while (*p && n < 4) { + if (*p == ':') { *p = '\0'; fields[n++] = p + 1; } + p++; + } + if (n < 3) return (struct group*)0; + _parsed_gr.gr_name = fields[0]; + _parsed_gr.gr_passwd = (n >= 2) ? fields[1] : "x"; + _parsed_gr.gr_gid = atoi(fields[2]); + /* Parse members */ + int mi = 0; + if (n >= 4 && fields[3][0] != '\0' && fields[3][0] != '\n') { + char* mp = fields[3]; + _gr_members[mi++] = mp; + while (*mp && mi < 15) { + if (*mp == ',' || *mp == '\n') { *mp = '\0'; _gr_members[mi++] = mp + 1; } + mp++; + } + } + _gr_members[mi] = (char*)0; + _parsed_gr.gr_mem = _gr_members; + return &_parsed_gr; +} + struct group* getgrnam(const char* name) { if (!name) return (struct group*)0; - const char* a = name; - const char* b = "root"; - while (*a && *a == *b) { a++; b++; } - if (*a == *b) return &_root_grp; + FILE* fp = fopen("/etc/group", "r"); + if (fp) { + while (fgets(_gr_line, (int)sizeof(_gr_line), fp)) { + struct group* gr = parse_group_line(_gr_line); + if (gr && strcmp(gr->gr_name, name) == 0) { + fclose(fp); + return gr; + } + } + fclose(fp); + } + if (strcmp(name, "root") == 0) return &_root_grp; return (struct group*)0; } struct group* getgrgid(int gid) { + FILE* fp = fopen("/etc/group", "r"); + if (fp) { + while (fgets(_gr_line, (int)sizeof(_gr_line), fp)) { + struct group* gr = parse_group_line(_gr_line); + if (gr && gr->gr_gid == gid) { + fclose(fp); + return gr; + } + } + fclose(fp); + } if (gid == 0) return &_root_grp; return (struct group*)0; } -void setgrent(void) { _gr_idx = 0; } -void endgrent(void) { _gr_idx = 0; } +void setgrent(void) { + if (_gr_fp) fclose(_gr_fp); + _gr_fp = fopen("/etc/group", "r"); + _gr_idx = 0; +} + +void endgrent(void) { + if (_gr_fp) { fclose(_gr_fp); _gr_fp = (void*)0; } + _gr_idx = 0; +} struct group* getgrent(void) { + if (_gr_fp) { + if (fgets(_gr_line, (int)sizeof(_gr_line), _gr_fp)) + return parse_group_line(_gr_line); + return (struct group*)0; + } if (_gr_idx == 0) { _gr_idx++; return &_root_grp; } return (struct group*)0; } diff --git a/user/ulibc/src/semaphore.c b/user/ulibc/src/semaphore.c new file mode 100644 index 0000000..bacbb9c --- /dev/null +++ b/user/ulibc/src/semaphore.c @@ -0,0 +1,47 @@ +#include "semaphore.h" +#include "syscall.h" +#include "errno.h" + +sem_t* sem_open(const char* name, int oflag, ...) { + int ret = _syscall2(SYS_SEM_OPEN, (int)name, oflag); + if (ret < 0 && ret > -4096) { + errno = -ret; + return SEM_FAILED; + } + return (sem_t*)(long)ret; +} + +int sem_close(sem_t* sem) { + return __syscall_ret(_syscall1(SYS_SEM_CLOSE, (int)sem)); +} + +int sem_wait(sem_t* sem) { + return __syscall_ret(_syscall1(SYS_SEM_WAIT, (int)sem)); +} + +int sem_trywait(sem_t* sem) { + return __syscall_ret(_syscall1(SYS_SEM_WAIT, (int)sem)); +} + +int sem_post(sem_t* sem) { + return __syscall_ret(_syscall1(SYS_SEM_POST, (int)sem)); +} + +int sem_unlink(const char* name) { + return __syscall_ret(_syscall1(SYS_SEM_UNLINK, (int)name)); +} + +int sem_getvalue(sem_t* sem, int* sval) { + return __syscall_ret(_syscall2(SYS_SEM_GETVALUE, (int)sem, (int)sval)); +} + +int sem_init(sem_t* sem, int pshared, unsigned int value) { + (void)pshared; + if (sem) sem->__val = (int)value; + return 0; +} + +int sem_destroy(sem_t* sem) { + (void)sem; + return 0; +} diff --git a/user/ulibc/src/shm.c b/user/ulibc/src/shm.c new file mode 100644 index 0000000..f1b30a9 --- /dev/null +++ b/user/ulibc/src/shm.c @@ -0,0 +1,24 @@ +#include "sys/shm.h" +#include "syscall.h" +#include "errno.h" + +int shmget(key_t key, size_t size, int shmflg) { + return __syscall_ret(_syscall3(SYS_SHMGET, key, (int)size, shmflg)); +} + +void* shmat(int shmid, const void* shmaddr, int shmflg) { + int ret = _syscall3(SYS_SHMAT, shmid, (int)shmaddr, shmflg); + if (ret < 0 && ret > -4096) { + errno = -ret; + return (void*)-1; + } + return (void*)ret; +} + +int shmdt(const void* shmaddr) { + return __syscall_ret(_syscall1(SYS_SHMDT, (int)shmaddr)); +} + +int shmctl(int shmid, int cmd, struct shmid_ds* buf) { + return __syscall_ret(_syscall3(SYS_SHMCTL, shmid, cmd, (int)buf)); +} diff --git a/user/ulibc/src/socket.c b/user/ulibc/src/socket.c new file mode 100644 index 0000000..3ee60e3 --- /dev/null +++ b/user/ulibc/src/socket.c @@ -0,0 +1,69 @@ +#include "sys/socket.h" +#include "syscall.h" +#include "errno.h" + +int socket(int domain, int type, int protocol) { + return __syscall_ret(_syscall3(SYS_SOCKET, domain, type, protocol)); +} + +int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen) { + return __syscall_ret(_syscall3(SYS_BIND, sockfd, (int)addr, (int)addrlen)); +} + +int listen(int sockfd, int backlog) { + return __syscall_ret(_syscall2(SYS_LISTEN, sockfd, backlog)); +} + +int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen) { + return __syscall_ret(_syscall3(SYS_ACCEPT, sockfd, (int)addr, (int)addrlen)); +} + +int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen) { + return __syscall_ret(_syscall3(SYS_CONNECT, sockfd, (int)addr, (int)addrlen)); +} + +int send(int sockfd, const void* buf, size_t len, int flags) { + return __syscall_ret(_syscall4(SYS_SEND, sockfd, (int)buf, (int)len, flags)); +} + +int recv(int sockfd, void* buf, size_t len, int flags) { + return __syscall_ret(_syscall4(SYS_RECV, sockfd, (int)buf, (int)len, flags)); +} + +int sendto(int sockfd, const void* buf, size_t len, int flags, + const struct sockaddr* dest_addr, socklen_t addrlen) { + return __syscall_ret(_syscall5(SYS_SENDTO, sockfd, (int)buf, (int)len, flags, (int)dest_addr)); +} + +int recvfrom(int sockfd, void* buf, size_t len, int flags, + struct sockaddr* src_addr, socklen_t* addrlen) { + return __syscall_ret(_syscall5(SYS_RECVFROM, sockfd, (int)buf, (int)len, flags, (int)src_addr)); +} + +int sendmsg(int sockfd, const struct msghdr* msg, int flags) { + return __syscall_ret(_syscall3(SYS_SENDMSG, sockfd, (int)msg, flags)); +} + +int recvmsg(int sockfd, struct msghdr* msg, int flags) { + return __syscall_ret(_syscall3(SYS_RECVMSG, sockfd, (int)msg, flags)); +} + +int setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t optlen) { + return __syscall_ret(_syscall5(SYS_SETSOCKOPT, sockfd, level, optname, (int)optval, (int)optlen)); +} + +int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen) { + return __syscall_ret(_syscall5(SYS_GETSOCKOPT, sockfd, level, optname, (int)optval, (int)optlen)); +} + +int shutdown(int sockfd, int how) { + return __syscall_ret(_syscall2(SYS_SHUTDOWN, sockfd, how)); +} + +int getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen) { + return __syscall_ret(_syscall3(SYS_GETPEERNAME, sockfd, (int)addr, (int)addrlen)); +} + +int getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen) { + return __syscall_ret(_syscall3(SYS_GETSOCKNAME, sockfd, (int)addr, (int)addrlen)); +} diff --git a/user/ulibc/src/spawn.c b/user/ulibc/src/spawn.c new file mode 100644 index 0000000..4cdc941 --- /dev/null +++ b/user/ulibc/src/spawn.c @@ -0,0 +1,47 @@ +#include "spawn.h" +#include "syscall.h" +#include "errno.h" +#include "string.h" + +int posix_spawn(int* pid, const char* path, + const posix_spawn_file_actions_t* file_actions, + const posix_spawnattr_t* attrp, + char* const argv[], char* const envp[]) { + (void)file_actions; + (void)attrp; + (void)envp; + int ret = _syscall2(SYS_POSIX_SPAWN, (int)path, (int)argv); + if (ret < 0) { + errno = -ret; + return -ret; + } + if (pid) *pid = ret; + return 0; +} + +int posix_spawnp(int* pid, const char* file, + const posix_spawn_file_actions_t* file_actions, + const posix_spawnattr_t* attrp, + char* const argv[], char* const envp[]) { + return posix_spawn(pid, file, file_actions, attrp, argv, envp); +} + +int posix_spawn_file_actions_init(posix_spawn_file_actions_t* fact) { + memset(fact, 0, sizeof(*fact)); + return 0; +} + +int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t* fact) { + (void)fact; + return 0; +} + +int posix_spawnattr_init(posix_spawnattr_t* attr) { + memset(attr, 0, sizeof(*attr)); + return 0; +} + +int posix_spawnattr_destroy(posix_spawnattr_t* attr) { + (void)attr; + return 0; +} diff --git a/user/ulibc/src/stdio.c b/user/ulibc/src/stdio.c index 5301c7a..bee1823 100644 --- a/user/ulibc/src/stdio.c +++ b/user/ulibc/src/stdio.c @@ -576,3 +576,103 @@ char* tmpnam(char* s) { if (s) { strcpy(s, buf); return s; } return buf; } + +int fscanf(FILE* fp, const char* fmt, ...) { + /* Read a line, then delegate to sscanf */ + char line[512]; + if (!fgets(line, (int)sizeof(line), fp)) return EOF; + va_list ap; + va_start(ap, fmt); + /* We can't pass va_list to sscanf directly, so use a manual approach + * that matches sscanf's minimal implementation for %d and %s */ + int count = 0; + const char* s = line; + const char* f = fmt; + while (*f && *s) { + if (*f == '%') { + f++; + if (*f == 'd' || *f == 'i') { + f++; + int* out = va_arg(ap, int*); + int neg = 0; + while (*s == ' ') s++; + if (*s == '-') { neg = 1; s++; } + else if (*s == '+') { s++; } + if (*s < '0' || *s > '9') break; + int val = 0; + while (*s >= '0' && *s <= '9') { val = val * 10 + (*s - '0'); s++; } + *out = neg ? -val : val; + count++; + } else if (*f == 's') { + f++; + char* out = va_arg(ap, char*); + while (*s == ' ') s++; + int i = 0; + while (*s && *s != ' ' && *s != '\n' && *s != '\t') out[i++] = *s++; + out[i] = '\0'; + count++; + } else if (*f == 'c') { + f++; + char* out = va_arg(ap, char*); + *out = *s++; + count++; + } else { + break; + } + } else if (*f == ' ') { + f++; + while (*s == ' ') s++; + } else { + if (*f != *s) break; + f++; s++; + } + } + va_end(ap); + return count; +} + +int scanf(const char* fmt, ...) { + char line[512]; + if (!fgets(line, (int)sizeof(line), stdin)) return EOF; + va_list ap; + va_start(ap, fmt); + int count = 0; + const char* s = line; + const char* f = fmt; + while (*f && *s) { + if (*f == '%') { + f++; + if (*f == 'd' || *f == 'i') { + f++; + int* out = va_arg(ap, int*); + int neg = 0; + while (*s == ' ') s++; + if (*s == '-') { neg = 1; s++; } + else if (*s == '+') { s++; } + if (*s < '0' || *s > '9') break; + int val = 0; + while (*s >= '0' && *s <= '9') { val = val * 10 + (*s - '0'); s++; } + *out = neg ? -val : val; + count++; + } else if (*f == 's') { + f++; + char* out = va_arg(ap, char*); + while (*s == ' ') s++; + int i = 0; + while (*s && *s != ' ' && *s != '\n' && *s != '\t') out[i++] = *s++; + out[i] = '\0'; + count++; + } else { + break; + } + } else if (*f == ' ') { + f++; + while (*s == ' ') s++; + } else { + if (*f != *s) break; + f++; s++; + } + } + va_end(ap); + return count; +} diff --git a/user/ulibc/src/stdlib.c b/user/ulibc/src/stdlib.c index 530d2fe..9d2b762 100644 --- a/user/ulibc/src/stdlib.c +++ b/user/ulibc/src/stdlib.c @@ -291,3 +291,82 @@ int system(const char* cmd) { (void)cmd; return -1; } + +void* bsearch(const void* key, const void* base, size_t nmemb, size_t size, + int (*compar)(const void*, const void*)) { + const char* b = (const char*)base; + size_t lo = 0, hi = nmemb; + while (lo < hi) { + size_t mid = lo + (hi - lo) / 2; + int cmp = compar(key, b + mid * size); + if (cmp == 0) return (void*)(b + mid * size); + if (cmp < 0) hi = mid; + else lo = mid + 1; + } + return (void*)0; +} + +div_t div(int numer, int denom) { + div_t r; + r.quot = numer / denom; + r.rem = numer % denom; + return r; +} + +ldiv_t ldiv(long numer, long denom) { + ldiv_t r; + r.quot = numer / denom; + r.rem = numer % denom; + return r; +} + +int mkstemp(char* tmpl) { + size_t len = strlen(tmpl); + if (len < 6) return -1; + char* suffix = tmpl + len - 6; + /* Check template ends with XXXXXX */ + for (int i = 0; i < 6; i++) + if (suffix[i] != 'X') return -1; + /* Generate unique name using pid + counter */ + extern int getpid(void); + static int counter = 0; + int id = getpid() * 1000 + (counter++ % 1000); + for (int i = 5; i >= 0; i--) { + suffix[i] = (char)('0' + id % 10); + id /= 10; + } + int fd = open(tmpl, 1 | 0x40 | 0x80 /* O_WRONLY|O_CREAT|O_EXCL */); + return fd; +} + +double strtod(const char* nptr, char** endptr) { + const char* s = nptr; + int neg = 0; + while (*s == ' ' || *s == '\t') s++; + if (*s == '-') { neg = 1; s++; } + else if (*s == '+') { s++; } + double val = 0.0; + while (*s >= '0' && *s <= '9') { val = val * 10.0 + (*s - '0'); s++; } + if (*s == '.') { + s++; + double frac = 0.1; + while (*s >= '0' && *s <= '9') { val += (*s - '0') * frac; frac *= 0.1; s++; } + } + if (*s == 'e' || *s == 'E') { + s++; + int eneg = 0; + if (*s == '-') { eneg = 1; s++; } + else if (*s == '+') { s++; } + int exp = 0; + while (*s >= '0' && *s <= '9') { exp = exp * 10 + (*s - '0'); s++; } + double mult = 1.0; + for (int i = 0; i < exp; i++) mult *= 10.0; + if (eneg) val /= mult; else val *= mult; + } + if (endptr) *endptr = (char*)s; + return neg ? -val : val; +} + +float strtof(const char* nptr, char** endptr) { + return (float)strtod(nptr, endptr); +} diff --git a/user/ulibc/src/string.c b/user/ulibc/src/string.c index 8692019..45dd7d8 100644 --- a/user/ulibc/src/string.c +++ b/user/ulibc/src/string.c @@ -190,3 +190,42 @@ char* strpbrk(const char* s, const char* accept) { } return (void*)0; } + +static char _sigbuf[24]; +char* strsignal(int sig) { + static const char* const names[] = { + [0] = "Signal 0", + [1] = "Hangup", + [2] = "Interrupt", + [3] = "Quit", + [4] = "Illegal instruction", + [5] = "Trace/breakpoint trap", + [6] = "Aborted", + [7] = "Bus error", + [8] = "Floating point exception", + [9] = "Killed", + [10] = "User defined signal 1", + [11] = "Segmentation fault", + [12] = "User defined signal 2", + [13] = "Broken pipe", + [14] = "Alarm clock", + [15] = "Terminated", + [17] = "Child exited", + [18] = "Continued", + [19] = "Stopped (signal)", + [20] = "Stopped", + }; + if (sig >= 0 && sig <= 20 && names[sig]) + return (char*)names[sig]; + /* fallback: "Signal N" */ + char* p = _sigbuf; + const char* prefix = "Signal "; + while (*prefix) *p++ = *prefix++; + if (sig < 0) { *p++ = '-'; sig = -sig; } + char digits[8]; + int n = 0; + do { digits[n++] = (char)('0' + sig % 10); sig /= 10; } while (sig); + while (n > 0) *p++ = digits[--n]; + *p = '\0'; + return _sigbuf; +} diff --git a/user/ulibc/src/syslog.c b/user/ulibc/src/syslog.c new file mode 100644 index 0000000..eb2b467 --- /dev/null +++ b/user/ulibc/src/syslog.c @@ -0,0 +1,22 @@ +#include "syslog.h" +#include + +static const char* _syslog_ident = ""; +static int _syslog_facility = 0; + +void openlog(const char* ident, int option, int facility) { + (void)option; + _syslog_ident = ident ? ident : ""; + _syslog_facility = facility; +} + +void syslog(int priority, const char* format, ...) { + (void)priority; + (void)format; + /* Stub: AdrOS has no syslog daemon. Messages are silently dropped. */ +} + +void closelog(void) { + _syslog_ident = ""; + _syslog_facility = 0; +} diff --git a/user/ulibc/src/termios.c b/user/ulibc/src/termios.c new file mode 100644 index 0000000..64997a6 --- /dev/null +++ b/user/ulibc/src/termios.c @@ -0,0 +1,62 @@ +#include "termios.h" +#include "syscall.h" +#include "errno.h" +#include "string.h" + +speed_t cfgetispeed(const struct termios* t) { + (void)t; + return B115200; +} + +speed_t cfgetospeed(const struct termios* t) { + (void)t; + return B115200; +} + +int cfsetispeed(struct termios* t, speed_t speed) { + (void)t; (void)speed; + return 0; +} + +int cfsetospeed(struct termios* t, speed_t speed) { + (void)t; (void)speed; + return 0; +} + +void cfmakeraw(struct termios* t) { + t->c_iflag &= ~(ICRNL | IGNCR | INLCR); + t->c_oflag &= ~OPOST; + t->c_lflag &= ~(ECHO | ICANON | ISIG); + t->c_cc[VMIN] = 1; + t->c_cc[VTIME] = 0; +} + +int tcdrain(int fd) { + return __syscall_ret(_syscall2(SYS_IOCTL, fd, TCSETSW)); +} + +int tcflush(int fd, int queue_selector) { + (void)queue_selector; + return __syscall_ret(_syscall2(SYS_IOCTL, fd, TCSETSF)); +} + +int tcflow(int fd, int action) { + (void)fd; (void)action; + return 0; +} + +int tcsendbreak(int fd, int duration) { + (void)fd; (void)duration; + return 0; +} + +int tcgetpgrp(int fd) { + int pgrp = 0; + int ret = _syscall3(SYS_IOCTL, fd, TIOCGPGRP, (int)&pgrp); + if (ret < 0) { errno = -ret; return -1; } + return pgrp; +} + +int tcsetpgrp(int fd, int pgrp) { + return __syscall_ret(_syscall3(SYS_IOCTL, fd, TIOCSPGRP, (int)&pgrp)); +} diff --git a/user/ulibc/src/unistd.c b/user/ulibc/src/unistd.c index f5b1755..e4c6d25 100644 --- a/user/ulibc/src/unistd.c +++ b/user/ulibc/src/unistd.c @@ -201,20 +201,15 @@ int chown(const char* path, int owner, int group) { } int link(const char* oldpath, const char* newpath) { - /* Use SYS_UNLINKAT slot 38 — AdrOS doesn't have a dedicated link syscall yet, - * use a direct int $0x80 with the link syscall number if available */ - (void)oldpath; (void)newpath; - return -1; /* TODO: implement when kernel has SYS_LINK */ + return __syscall_ret(_syscall2(SYS_LINK, (int)oldpath, (int)newpath)); } int symlink(const char* target, const char* linkpath) { - (void)target; (void)linkpath; - return -1; /* TODO: implement when kernel has SYS_SYMLINK */ + return __syscall_ret(_syscall2(SYS_SYMLINK, (int)target, (int)linkpath)); } int readlink(const char* path, char* buf, size_t bufsiz) { - (void)path; (void)buf; (void)bufsiz; - return -1; /* TODO: implement when kernel has SYS_READLINK */ + return __syscall_ret(_syscall3(SYS_READLINK, (int)path, (int)buf, (int)bufsiz)); } int tcgetattr(int fd, struct termios* t) { @@ -234,3 +229,60 @@ void _exit(int status) { * Cannot use hlt — it's privileged and causes #GP in ring 3. */ for (;;) __asm__ volatile("nop"); } + +int execle(const char* path, const char* arg, ...) { + /* Walk varargs to find argv[] and the trailing envp */ + extern int execve(const char*, const char* const*, const char* const*); + const char* args[32]; + int n = 0; + __builtin_va_list ap; + __builtin_va_start(ap, arg); + args[n++] = arg; + while (n < 31) { + const char* a = __builtin_va_arg(ap, const char*); + args[n++] = a; + if (!a) break; + } + args[n] = (void*)0; + char* const* envp = __builtin_va_arg(ap, char* const*); + __builtin_va_end(ap); + return execve(path, (const char* const*)args, (const char* const*)envp); +} + +static char _login_buf[32] = "root"; +char* getlogin(void) { + return _login_buf; +} + +int getlogin_r(char* buf, size_t bufsize) { + extern size_t strlen(const char*); + extern void* memcpy(void*, const void*, size_t); + const char* name = getlogin(); + size_t len = strlen(name); + if (len + 1 > bufsize) return 34; /* ERANGE */ + memcpy(buf, name, len + 1); + return 0; +} + +long confstr(int name, char* buf, size_t len) { + (void)name; + const char* val = "/bin:/usr/bin"; + extern size_t strlen(const char*); + size_t vlen = strlen(val) + 1; + if (buf && len > 0) { + size_t copy = vlen < len ? vlen : len; + extern void* memcpy(void*, const void*, size_t); + memcpy(buf, val, copy); + if (copy < vlen && len > 0) buf[len - 1] = '\0'; + } + return (long)vlen; +} + +void* sbrk(int increment) { + void* cur = brk((void*)0); + if (increment == 0) return cur; + void* new_end = (void*)((char*)cur + increment); + void* result = brk(new_end); + if ((unsigned int)result < (unsigned int)new_end) return (void*)-1; + return cur; +} diff --git a/user/ulibc/src/wordexp.c b/user/ulibc/src/wordexp.c new file mode 100644 index 0000000..c731999 --- /dev/null +++ b/user/ulibc/src/wordexp.c @@ -0,0 +1,60 @@ +#include "wordexp.h" +#include "string.h" +#include "stdlib.h" + +int wordexp(const char* words, wordexp_t* pwordexp, int flags) { + if (!words || !pwordexp) return WRDE_BADCHAR; + + if (!(flags & WRDE_APPEND)) { + pwordexp->we_wordc = 0; + pwordexp->we_wordv = (void*)0; + } + + /* Minimal implementation: split on whitespace, no shell expansion */ + char* copy = strdup(words); + if (!copy) return WRDE_NOSPACE; + + size_t alloc = 8; + size_t offs = (flags & WRDE_DOOFFS) ? pwordexp->we_offs : 0; + char** wv = (char**)calloc(alloc + offs + 1, sizeof(char*)); + if (!wv) { free(copy); return WRDE_NOSPACE; } + + size_t count = 0; + char* saveptr; + char* tok = strtok_r(copy, " \t\n", &saveptr); + while (tok) { + if (count + offs + 1 >= alloc) { + alloc *= 2; + char** nv = (char**)realloc(wv, (alloc + offs + 1) * sizeof(char*)); + if (!nv) { + for (size_t i = 0; i < count; i++) free(wv[offs + i]); + free(wv); free(copy); + return WRDE_NOSPACE; + } + wv = nv; + } + wv[offs + count] = strdup(tok); + if (!wv[offs + count]) { + for (size_t i = 0; i < count; i++) free(wv[offs + i]); + free(wv); free(copy); + return WRDE_NOSPACE; + } + count++; + tok = strtok_r((void*)0, " \t\n", &saveptr); + } + wv[offs + count] = (void*)0; + + free(copy); + pwordexp->we_wordc = count; + pwordexp->we_wordv = wv; + return 0; +} + +void wordfree(wordexp_t* pwordexp) { + if (!pwordexp || !pwordexp->we_wordv) return; + for (size_t i = 0; pwordexp->we_wordv[i]; i++) + free(pwordexp->we_wordv[i]); + free(pwordexp->we_wordv); + pwordexp->we_wordv = (void*)0; + pwordexp->we_wordc = 0; +}