diff options
author | Ian Moffett <ian@osmora.org> | 2024-03-07 17:28:00 -0500 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2024-03-07 17:28:32 -0500 |
commit | bd5969fc876a10b18613302db7087ef3c40f18e1 (patch) | |
tree | 7c2b8619afe902abf99570df2873fbdf40a4d1a1 /lib/mlibc/options/posix/generic | |
parent | a95b38b1b92b172e6cc4e8e56a88a30cc65907b0 (diff) |
lib: Add mlibc
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'lib/mlibc/options/posix/generic')
53 files changed, 9828 insertions, 0 deletions
diff --git a/lib/mlibc/options/posix/generic/arpa-inet-stubs.cpp b/lib/mlibc/options/posix/generic/arpa-inet-stubs.cpp new file mode 100644 index 0000000..0bc9727 --- /dev/null +++ b/lib/mlibc/options/posix/generic/arpa-inet-stubs.cpp @@ -0,0 +1,220 @@ + +#include <arpa/inet.h> +#include <bits/ensure.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <mlibc/bitutil.hpp> +#include <mlibc/debug.hpp> + +const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; +const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; + +uint32_t htonl(uint32_t x) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return mlibc::bit_util<uint32_t>::byteswap(x); +#else + return x; +#endif +} +uint16_t htons(uint16_t x) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return mlibc::bit_util<uint16_t>::byteswap(x); +#else + return x; +#endif +} +uint32_t ntohl(uint32_t x) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return mlibc::bit_util<uint32_t>::byteswap(x); +#else + return x; +#endif +} +uint16_t ntohs(uint16_t x) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return mlibc::bit_util<uint16_t>::byteswap(x); +#else + return x; +#endif +} + +// ---------------------------------------------------------------------------- +// IPv4 address manipulation. +// ---------------------------------------------------------------------------- +in_addr_t inet_addr(const char *p) { + struct in_addr a; + if(!inet_aton(p, &a)) + return -1; + return a.s_addr; +} +char *inet_ntoa(struct in_addr addr) { + // string: xxx.yyy.zzz.aaa + // 4 * 3 + 3 + 1 = 12 + 4 = 16 + thread_local static char buffer[16]; + uint32_t proper = htonl(addr.s_addr); + snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d", + (proper >> 24) & 0xff, ((proper >> 16) & 0xff), + (proper >> 8) & 0xff, proper & 0xff); + return buffer; +} +int inet_aton(const char *string, struct in_addr *dest) { + int array[4]; + int i = 0; + char *end; + + for (; i < 4; i++) { + array[i] = strtoul(string, &end, 0); + if (*end && *end != '.') + return 0; + if (!*end) + break; + string = end + 1; + } + + switch (i) { + case 0: + dest->s_addr = htonl(array[0]); + break; + case 1: + if (array[0] > 255 || array[1] > 0xffffff) + return 0; + dest->s_addr = htonl((array[0] << 24) | array[1]); + break; + case 2: + if (array[0] > 255 || array[1] > 255 || + array[2] > 0xffff) + return 0; + dest->s_addr = htonl((array[0] << 24) | (array[1] << 16) | + array[2]); + break; + case 3: + if (array[0] > 255 || array[1] > 255 || + array[2] > 255 || array[3] > 255) + return 0; + dest->s_addr = htonl((array[0] << 24) | (array[1] << 16) | + (array[2] << 8) | array[3]); + break; + } + + return 1; +} + +// ---------------------------------------------------------------------------- +// Generic IP address manipulation. +// ---------------------------------------------------------------------------- +const char *inet_ntop(int af, const void *__restrict src, char *__restrict dst, + socklen_t size) { + switch (af) { + case AF_INET: { + auto source = reinterpret_cast<const struct in_addr*>(src); + if (snprintf(dst, size, "%d.%d.%d.%d", + source->s_addr & 0xff, + (source->s_addr & 0xffff) >> 8, + (source->s_addr & 0xffffff) >> 16, + source->s_addr >> 24) < (int)size) + return dst; + break; + } + case AF_INET6: { + auto source = reinterpret_cast<const struct in6_addr*>(src); + size_t cur_zeroes_off = 0; + size_t cur_zeroes_len = 0; + size_t max_zeroes_off = 0; + size_t max_zeroes_len = 0; + + /* we look for the largest block of zeroed quartet(s) */ + for(size_t i = 0; i < 8; i++) { + auto ptr = source->s6_addr + (i * 2); + if(!ptr[0] && !ptr[1]) { + cur_zeroes_len++; + if(max_zeroes_len < cur_zeroes_len) { + max_zeroes_len = cur_zeroes_len; + max_zeroes_off = cur_zeroes_off; + } + } else { + /* advance the offset to the next quartet to check */ + cur_zeroes_len = 0; + cur_zeroes_off = i + 1; + } + } + + size_t off = 0; + for(size_t i = 0; i < 8; i++) { + auto ptr = source->s6_addr + (i * 2); + + /* if we are at the beginning of the largest block of zeroed quartets, place "::" */ + if(i == max_zeroes_off && max_zeroes_len >= 2) { + if(off < size) { + dst[off++] = ':'; + } + if(off < size) { + dst[off++] = ':'; + } + i += max_zeroes_len - 1; + + continue; + } + + /* place a colon if we're not at the beginning of the string and it is not already there */ + if(off && dst[off - 1] != ':') { + if(off < size) { + dst[off++] = ':'; + } + } + + off += snprintf(dst + off, size - off, "%x", ptr[0] << 8 | ptr[1]); + } + + dst[off] = 0; + + return dst; + } + default: + errno = EAFNOSUPPORT; + return NULL; + } + + errno = ENOSPC; + return NULL; +} +int inet_pton(int af, const char *__restrict src, void *__restrict dst) { + switch (af) { + case AF_INET: { + uint8_t array[4] = {}; + for (int i = 0; i < 4; i++) { + char *end; + long int value = strtol(src, &end, 10); + if (value > 255) + return 0; + if (*end != '\0' && *end != '.') + return 0; + src = end + 1; + array[i] = value; + } + auto addr = reinterpret_cast<struct in_addr*>(dst); + memcpy(&addr->s_addr, array, 4); + break; + } + case AF_INET6: + mlibc::infoLogger() << "inet_pton: ipv6 is not implemented!" << frg::endlog; + /* fallthrough */ + default: + errno = EAFNOSUPPORT; + return -1; + } + + return 1; +} + +struct in_addr inet_makeaddr(in_addr_t, in_addr_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +in_addr_t inet_netof(struct in_addr) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/dirent-stubs.cpp b/lib/mlibc/options/posix/generic/dirent-stubs.cpp new file mode 100644 index 0000000..1352585 --- /dev/null +++ b/lib/mlibc/options/posix/generic/dirent-stubs.cpp @@ -0,0 +1,180 @@ + +#include <errno.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> +#include <stdlib.h> + +#include <bits/ensure.h> +#include <frg/allocation.hpp> +#include <mlibc/allocator.hpp> +#include <mlibc/posix-sysdeps.hpp> +#include <mlibc/debug.hpp> + +// Code taken from musl +int alphasort(const struct dirent **a, const struct dirent **b) { + return strcoll((*a)->d_name, (*b)->d_name); +} + +int closedir(DIR *dir) { + // TODO: Deallocate the dir structure. + close(dir->__handle); + return 0; +} +int dirfd(DIR *dir) { + return dir->__handle; +} +DIR *fdopendir(int fd) { + struct stat st; + + if(fstat(fd, &st) < 0) { + return nullptr; + } + // Musl implements this, but O_PATH is only declared on the linux abi + /*if(fcntl(fd, F_GETFL) & O_PATH) { + errno = EBADF; + return nullptr; + }*/ + if(!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + return nullptr; + } + auto dir = frg::construct<__mlibc_dir_struct>(getAllocator()); + __ensure(dir); + dir->__ent_next = 0; + dir->__ent_limit = 0; + int flags = fcntl(fd, F_GETFD); + fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + dir->__handle = fd; + return dir; +} +DIR *opendir(const char *path) { + auto dir = frg::construct<__mlibc_dir_struct>(getAllocator()); + __ensure(dir); + dir->__ent_next = 0; + dir->__ent_limit = 0; + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_open_dir, nullptr); + if(int e = mlibc::sys_open_dir(path, &dir->__handle); e) { + errno = e; + frg::destruct(getAllocator(), dir); + return nullptr; + }else{ + return dir; + } +} +struct dirent *readdir(DIR *dir) { + __ensure(dir->__ent_next <= dir->__ent_limit); + if(dir->__ent_next == dir->__ent_limit) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_read_entries, nullptr); + if(int e = mlibc::sys_read_entries(dir->__handle, dir->__ent_buffer, 2048, &dir->__ent_limit); e) + __ensure(!"mlibc::sys_read_entries() failed"); + dir->__ent_next = 0; + if(!dir->__ent_limit) + return nullptr; + } + + auto entp = reinterpret_cast<struct dirent *>(dir->__ent_buffer + dir->__ent_next); + // We only copy as many bytes as we need to avoid buffer-overflows. + memcpy(&dir->__current, entp, offsetof(struct dirent, d_name) + strlen(entp->d_name) + 1); + dir->__ent_next += entp->d_reclen; + return &dir->__current; +} +int readdir_r(DIR *dir, struct dirent *entry, struct dirent **result) { + if(!mlibc::sys_read_entries) { + MLIBC_MISSING_SYSDEP(); + return ENOSYS; + } + + __ensure(dir->__ent_next <= dir->__ent_limit); + if(dir->__ent_next == dir->__ent_limit) { + if(int e = mlibc::sys_read_entries(dir->__handle, dir->__ent_buffer, 2048, &dir->__ent_limit); e) + __ensure(!"mlibc::sys_read_entries() failed"); + dir->__ent_next = 0; + if(!dir->__ent_limit) { + *result = NULL; + return 0; + } + } + + auto entp = reinterpret_cast<struct dirent *>(dir->__ent_buffer + dir->__ent_next); + // We only copy as many bytes as we need to avoid buffer-overflows. + memcpy(entry, entp, offsetof(struct dirent, d_name) + strlen(entp->d_name) + 1); + dir->__ent_next += entp->d_reclen; + *result = entry; + return 0; +} + +void rewinddir(DIR *dir) { + lseek(dir->__handle, 0, SEEK_SET); + dir->__ent_next = 0; +} + +int scandir(const char *path, struct dirent ***res, int (*select)(const struct dirent *), + int (*compare)(const struct dirent **, const struct dirent **)) { + DIR *dir = opendir(path); + if (!dir) + return -1; // errno will be set by opendir() + + // we should save the errno + int old_errno = errno; + errno = 0; + + struct dirent *dir_ent; + struct dirent **array = nullptr, **tmp = nullptr; + int length = 0; + int count = 0; + while((dir_ent = readdir(dir)) && !errno) { + if(select && !select(dir_ent)) + continue; + + if(count >= length) { + length = 2*length + 1; + tmp = static_cast<struct dirent**>(realloc(array, + length * sizeof(struct dirent*))); + // we need to check the call actually goes through + // before we overwrite array so that we can + // deallocate the already written entries should realloc() + // have failed + if(!tmp) + break; + array = tmp; + } + array[count] = static_cast<struct dirent*>(malloc(dir_ent->d_reclen)); + if(!array[count]) + break; + + memcpy(array[count], dir_ent, dir_ent->d_reclen); + count++; + } + + if(errno) { + if(array) + while(count-- > 0) + free(array[count]); + free(array); + return -1; + } + + // from here we can set the old errno back + errno = old_errno; + + if(compare) + qsort(array, count, sizeof(struct dirent*), + (int (*)(const void *, const void *)) compare); + *res = array; + return count; +} +void seekdir(DIR *, long) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} +long telldir(DIR *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int versionsort(const struct dirent **a, const struct dirent **b) { + return strverscmp((*a)->d_name, (*b)->d_name); +} diff --git a/lib/mlibc/options/posix/generic/dlfcn-stubs.cpp b/lib/mlibc/options/posix/generic/dlfcn-stubs.cpp new file mode 100644 index 0000000..fc9fd84 --- /dev/null +++ b/lib/mlibc/options/posix/generic/dlfcn-stubs.cpp @@ -0,0 +1,64 @@ + +#include <bits/ensure.h> +#include <dlfcn.h> + +#include <mlibc/debug.hpp> + +struct __dlapi_symbol { + const char *file; + void *base; + const char *symbol; + void *address; +}; + +extern "C" const char *__dlapi_error(); +extern "C" void *__dlapi_open(const char *, int, void *); +extern "C" void *__dlapi_resolve(void *, const char *, void *); +extern "C" int __dlapi_reverse(const void *, __dlapi_symbol *); +extern "C" int __dlapi_close(void *); + +int dlclose(void *handle) { + return __dlapi_close(handle); +} + +char *dlerror(void) { + return const_cast<char *>(__dlapi_error()); +} + +[[gnu::noinline]] +void *dlopen(const char *file, int flags) { + auto ra = __builtin_extract_return_addr(__builtin_return_address(0)); + return __dlapi_open(file, flags, ra); +} + +[[gnu::noinline]] +void *dlsym(void *__restrict handle, const char *__restrict string) { + auto ra = __builtin_extract_return_addr(__builtin_return_address(0)); + return __dlapi_resolve(handle, string, ra); +} + +[[gnu::noinline]] +void *dlvsym(void *__restrict handle, const char *__restrict string, const char *__restrict version) { + mlibc::infoLogger() << "mlibc: dlvsym ignores version " << version << frg::endlog; + auto ra = __builtin_extract_return_addr(__builtin_return_address(0)); + return __dlapi_resolve(handle, string, ra); +} + +//gnu extensions +int dladdr(const void *ptr, Dl_info *out) { + __dlapi_symbol info; + if(__dlapi_reverse(ptr, &info)) + return 0; + + out->dli_fname = info.file; + out->dli_fbase = info.base; + out->dli_sname = info.symbol; + out->dli_saddr = info.address; + return 1; +} + +int dlinfo(void *, int, void *) { + __ensure(!"dlinfo() not implemented"); + __builtin_unreachable(); +} + diff --git a/lib/mlibc/options/posix/generic/fcntl-stubs.cpp b/lib/mlibc/options/posix/generic/fcntl-stubs.cpp new file mode 100644 index 0000000..66e2d12 --- /dev/null +++ b/lib/mlibc/options/posix/generic/fcntl-stubs.cpp @@ -0,0 +1,108 @@ + +#include <errno.h> +#include <bits/ensure.h> +#include <fcntl.h> +#include <stdarg.h> + +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> + +int creat(const char *pathname, mode_t mode) { + return open(pathname, O_CREAT|O_WRONLY|O_TRUNC, mode); +} + +int fallocate(int, int, off_t, off_t) { + mlibc::infoLogger() << "mlibc: fallocate() is a no-op" << frg::endlog; + errno = ENOSYS; + return -1; +} + +int fcntl(int fd, int command, ...) { + va_list args; + va_start(args, command); + int result; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fcntl, -1); + if(int e = mlibc::sys_fcntl(fd, command, args, &result); e) { + errno = e; + return -1; + } + va_end(args); + return result; +} + +int openat(int dirfd, const char *pathname, int flags, ...) { + va_list args; + va_start(args, flags); + mode_t mode = 0; + int fd; + + if((flags & (O_CREAT | O_TMPFILE))) + mode = va_arg(args, mode_t); + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_openat, -1); + if(int e = mlibc::sys_openat(dirfd, pathname, flags, mode, &fd); e) { + errno = e; + return -1; + } + va_end(args); + return fd; +} + +int posix_fadvise(int fd, off_t offset, off_t length, int advice) { + if(!mlibc::sys_fadvise) { + mlibc::infoLogger() << "mlibc: fadvise() ignored due to missing sysdep" << frg::endlog; + return 0; + } + + // posix_fadvise() returns an error instead of setting errno. + return mlibc::sys_fadvise(fd, offset, length, advice); +} + +int posix_fallocate(int fd, off_t offset, off_t size) { + // posix_fallocate() returns an error instead of setting errno. + if(!mlibc::sys_fallocate) { + MLIBC_MISSING_SYSDEP(); + return ENOSYS; + } + return mlibc::sys_fallocate(fd, offset, size); +} + +// This is a linux extension +int name_to_handle_at(int, const char *, struct file_handle *, int *, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int open_by_handle_at(int, struct file_handle *, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +ssize_t splice(int, off_t *, int, off_t *, size_t, unsigned int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +ssize_t vmsplice(int, const struct iovec *, size_t, unsigned int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int open(const char *pathname, int flags, ...) { + mode_t mode = 0; + + if ((flags & O_CREAT) || (flags & O_TMPFILE)) { + va_list args; + va_start(args, flags); + mode = va_arg(args, mode_t); + va_end(args); + } + + int fd; + if(int e = mlibc::sys_open(pathname, flags, mode, &fd); e) { + errno = e; + return -1; + } + return fd; +} + diff --git a/lib/mlibc/options/posix/generic/ftw-stubs.cpp b/lib/mlibc/options/posix/generic/ftw-stubs.cpp new file mode 100644 index 0000000..2d93995 --- /dev/null +++ b/lib/mlibc/options/posix/generic/ftw-stubs.cpp @@ -0,0 +1,18 @@ + +#include <ftw.h> + +#include <bits/ensure.h> + +int ftw(const char *, int (*fn)(const char *, const struct stat *, int), int) { + (void)fn; + __ensure(!"ftw() not implemented"); + __builtin_unreachable(); +} + +int nftw(const char *, int (*fn)(const char *, const struct stat *, int, struct FTW *), + int, int) { + (void)fn; + __ensure(!"nftw() not implemented"); + __builtin_unreachable(); +} + diff --git a/lib/mlibc/options/posix/generic/grp-stubs.cpp b/lib/mlibc/options/posix/generic/grp-stubs.cpp new file mode 100644 index 0000000..f8b2813 --- /dev/null +++ b/lib/mlibc/options/posix/generic/grp-stubs.cpp @@ -0,0 +1,316 @@ + +#include <grp.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <bits/ensure.h> + +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> + +namespace { + FILE *global_file; + + bool open_global_file() { + if(!global_file) { + global_file = fopen("/etc/group", "r"); + if(!global_file) { + errno = EIO; + return false; + } + } + + return true; + } + + void close_global_file() { + if(global_file) { + fclose(global_file); + global_file = nullptr; + } + } + + template<typename F> + void walk_segments(frg::string_view line, char delimiter, F fn) { + size_t s = 0; + while(true) { + size_t d = line.find_first(delimiter, s); + if(d == size_t(-1)) + break; + auto chunk = line.sub_string(s, d - s); + fn(chunk); + s = d + 1; + } + if(line[s]) { + auto chunk = line.sub_string(s, line.size() - s); + + if (chunk.size() > 0) { + // Remove trailing newline + if (chunk[chunk.size() - 1] == '\n') + chunk = chunk.sub_string(0, chunk.size() - 1); + + fn(chunk); + } + } + } + + bool extract_entry(frg::string_view line, group *entry) { + frg::string_view segments[5]; + + // Parse the line into 3 or 4 segments (depending if the group has members or not) + int n = 0; + walk_segments(line, ':', [&] (frg::string_view s) { + __ensure(n < 4); + segments[n++] = s; + }); + + if(n < 3) // n can be 3 when there are no members in the group + return false; + + // TODO: Handle strndup() and malloc() failure. + auto name = strndup(segments[0].data(), segments[0].size()); + __ensure(name); + + auto passwd = strndup(segments[1].data(), segments[1].size()); + + auto gid = segments[2].to_number<int>(); + if(!gid) + return false; + + size_t n_members = 0; + walk_segments(segments[3], ',', [&] (frg::string_view) { + n_members++; + }); + + auto members = reinterpret_cast<char **>(malloc(sizeof(char *) * (n_members + 1))); + __ensure(members); + size_t k = 0; + walk_segments(segments[3], ',', [&] (frg::string_view m) { + members[k] = strndup(m.data(), m.size()); + __ensure(members[k]); + k++; + }); + members[k] = nullptr; + + entry->gr_name = name; + entry->gr_passwd = passwd; + entry->gr_gid = *gid; + entry->gr_mem = members; + return true; + } + + void clear_entry(group *entry) { + free(entry->gr_name); + if(entry->gr_mem) { + for(size_t i = 0; entry->gr_mem[i]; i++) + free(entry->gr_mem[i]); + free(entry->gr_mem); + } + entry->gr_name = nullptr; + entry->gr_mem = nullptr; + } + + template<typename C> + int walk_file(struct group *entry, C cond) { + auto file = fopen("/etc/group", "r"); + if(!file) { + return EIO; + } + + char line[512]; + while(fgets(line, 512, file)) { + if(!extract_entry(line, entry)) + continue; + if(cond(entry)) { + fclose(file); + return 0; + } + } + + int err = ESRCH; + if(ferror(file)) { + err = EIO; + } + + fclose(file); + return err; + } + + int copy_to_buffer(struct group *grp, char *buffer, size_t size) { + // Adjust to correct alignment so that we can put gr_mem first in buffer + uintptr_t mask = sizeof(char *) - 1; + size_t offset = (reinterpret_cast<uintptr_t>(buffer) % sizeof(char *) + mask) & ~mask; + if (size < offset) + return ERANGE; + + buffer += offset; + size -= offset; + + // Calculate the amount of space we need + size_t nmemb, required_size = 0; + for (nmemb = 0; grp->gr_mem[nmemb] != nullptr; nmemb++) { + // One for the string's null terminator and one for the pointer in gr_mem + required_size += strlen(grp->gr_mem[nmemb]) + 1 + sizeof(char *); + } + + // One for null terminator of gr_name, plus sizeof(char *) for nullptr terminator of gr_mem + required_size += strlen(grp->gr_name) + 1 + sizeof(char *); + if (size < required_size) + return ERANGE; + + // Put the gr_mem array first in the buffer as we are guaranteed + // that the pointer is aligned correctly + char *string_data = buffer + (nmemb + 1) * sizeof(char *); + + for (size_t i = 0; i < nmemb; i++) { + reinterpret_cast<char **>(buffer)[i] = string_data; + string_data = stpcpy(string_data, grp->gr_mem[i]) + 1; + free(grp->gr_mem[i]); + } + + reinterpret_cast<char **>(buffer)[nmemb] = nullptr; + free(grp->gr_mem); + grp->gr_mem = reinterpret_cast<char **>(buffer); + + char *gr_name = stpcpy(string_data, grp->gr_name) + 1; + free(grp->gr_name); + grp->gr_name = string_data; + + __ensure(gr_name <= buffer + size); + return 0; + } +} + +void endgrent(void) { + close_global_file(); +} + +struct group *getgrent(void) { + static group entry; + char line[512]; + + if(!open_global_file()) { + return nullptr; + } + + if(fgets(line, 512, global_file)) { + clear_entry(&entry); + if(!extract_entry(line, &entry)) { + errno = EINVAL; + return nullptr; + } + return &entry; + } + + if(ferror(global_file)) { + errno = EIO; + } + + return nullptr; +} + +struct group *getgrgid(gid_t gid) { + static group entry; + + int err = walk_file(&entry, [&] (group *entry) { + return entry->gr_gid == gid; + }); + + if (err) { + errno = err; + return nullptr; + } + + return &entry; +} + +int getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t size, struct group **result) { + *result = nullptr; + int err = walk_file(grp, [&] (group *entry) { + return entry->gr_gid == gid; + }); + + if (err) { + return err; + } + + err = copy_to_buffer(grp, buffer, size); + if (err) { + return err; + } + + *result = grp; + return 0; +} + +struct group *getgrnam(const char *name) { + static group entry; + + int err = walk_file(&entry, [&] (group *entry) { + return !strcmp(entry->gr_name, name); + }); + + if (err) { + errno = err; + return nullptr; + } + + return &entry; +} + +int getgrnam_r(const char *name, struct group *grp, char *buffer, size_t size, struct group **result) { + *result = nullptr; + + int err = walk_file(grp, [&] (group *entry) { + return !strcmp(entry->gr_name, name); + }); + + if (err) { + return err; + } + + err = copy_to_buffer(grp, buffer, size); + if (err) { + return err; + } + + *result = grp; + return 0; +} + +void setgrent(void) { + if(!open_global_file()) { + return; + } + rewind(global_file); +} + +int setgroups(size_t size, const gid_t *list) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setgroups, -1); + if(int e = mlibc::sys_setgroups(size, list); e) { + errno = e; + return -1; + } + return 0; +} + +int initgroups(const char *, gid_t) { + mlibc::infoLogger() << "mlibc: initgroups is a stub" << frg::endlog; + return 0; +} + +int putgrent(const struct group *, FILE *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +struct group *fgetgrent(FILE *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int getgrouplist(const char *, gid_t, gid_t *, int *) { + mlibc::infoLogger() << "mlibc: getgrouplist is a stub" << frg::endlog; + return 0; +} diff --git a/lib/mlibc/options/posix/generic/langinfo-stubs.cpp b/lib/mlibc/options/posix/generic/langinfo-stubs.cpp new file mode 100644 index 0000000..b239cbd --- /dev/null +++ b/lib/mlibc/options/posix/generic/langinfo-stubs.cpp @@ -0,0 +1,15 @@ + +#include <langinfo.h> +#include <bits/ensure.h> +#include <mlibc/debug.hpp> +#include <mlibc/locale.hpp> + +char *nl_langinfo(nl_item item) { + return mlibc::nl_langinfo(item); +} + +char *nl_langinfo_l(nl_item, locale_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + diff --git a/lib/mlibc/options/posix/generic/libgen-stubs.cpp b/lib/mlibc/options/posix/generic/libgen-stubs.cpp new file mode 100644 index 0000000..ff80349 --- /dev/null +++ b/lib/mlibc/options/posix/generic/libgen-stubs.cpp @@ -0,0 +1,51 @@ + +#include <bits/ensure.h> +#include <libgen.h> +#include <string.h> + +#include <mlibc/debug.hpp> + +// Adopted from musl's code. +char *basename(char *s) { + // This empty string behavior is specified by POSIX. + if (!s || !*s) + return const_cast<char *>("."); + + // Delete trailing slashes. + // Note that we do not delete the slash at index zero. + auto i = strlen(s) - 1; + for(; i && s[i] == '/'; i--) + s[i] = 0; + + // Find the last non-trailing slash. + for(; i && s[i - 1] != '/'; i--) + ; + return s + i; +} + +char *dirname(char *s) { + if (!s || !(*s)) + return const_cast<char *>("."); + + auto i = strlen(s) - 1; + + // Skip trailing slashes. + for (; s[i] == '/'; i--) + if(!i) // Path only consists of slashes. + return const_cast<char *>("/"); + + // Skip the last non-slash path component. + for (; s[i] != '/'; i--) + if(!i) // Path only contains a single component. + return const_cast<char *>("."); + + // Skip slashes. + for (; s[i] == '/'; i--) + if(!i) // Path is entry in root directory. + return const_cast<char *>("/"); + + s[i+1] = 0; + + return s; +} + diff --git a/lib/mlibc/options/posix/generic/lookup.cpp b/lib/mlibc/options/posix/generic/lookup.cpp new file mode 100644 index 0000000..f877fe5 --- /dev/null +++ b/lib/mlibc/options/posix/generic/lookup.cpp @@ -0,0 +1,512 @@ +#include <mlibc/lookup.hpp> +#include <mlibc/resolv_conf.hpp> +#include <mlibc/debug.hpp> +#include <bits/ensure.h> + +#include <frg/string.hpp> +#include <mlibc/allocator.hpp> +#include <string.h> +#include <errno.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> + +namespace mlibc { + +namespace { + constexpr unsigned int RECORD_A = 1; + constexpr unsigned int RECORD_CNAME = 5; + constexpr unsigned int RECORD_PTR = 12; +} + +static frg::string<MemoryAllocator> read_dns_name(char *buf, char *&it) { + frg::string<MemoryAllocator> res{getAllocator()}; + while (true) { + char code = *it++; + if ((code & 0xC0) == 0xC0) { + // pointer + uint8_t offset = ((code & 0x3F) << 8) | *it++; + auto offset_it = buf + offset; + return res + read_dns_name(buf, offset_it); + } else if (!(code & 0xC0)) { + if (!code) + break; + + for (int i = 0; i < code; i++) + res += (*it++); + + if (*it) + res += '.'; + } else { + break; + } + } + + return res; +} + +int lookup_name_dns(struct lookup_result &buf, const char *name, + frg::string<MemoryAllocator> &canon_name) { + frg::string<MemoryAllocator> request{getAllocator()}; + + int num_q = 1; + struct dns_header header; + header.identification = htons(123); + header.flags = htons(0x100); + header.no_q = htons(num_q); + header.no_ans = htons(0); + header.no_auths = htons(0); + header.no_additional = htons(0); + + request.resize(sizeof(header)); + memcpy(request.data(), &header, sizeof(header)); + + const char *end = name; + while (*end != '\0') { + end = strchrnul(name, '.'); + size_t length = end - name; + frg::string_view substring{name, length}; + name += length + 1; + request += char(length); + request += substring; + } + + request += char(0); + // set question type to fetch A records + request += 0; + request += 1; + // set CLASS to IN + request += 0; + request += 1; + + struct sockaddr_in sin = {}; + sin.sin_family = AF_INET; + // TODO(geert): we could probably make this use the service lookup + // for dns + sin.sin_port = htons(53); + + auto nameserver = get_nameserver(); + if (!inet_aton(nameserver ? nameserver->name.data() : "127.0.0.1", &sin.sin_addr)) { + mlibc::infoLogger() << "lookup_name_dns(): inet_aton() failed!" << frg::endlog; + return -EAI_SYSTEM; + } + + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + mlibc::infoLogger() << "lookup_name_dns(): socket() failed" << frg::endlog; + return -EAI_SYSTEM; + } + + size_t sent = sendto(fd, request.data(), request.size(), 0, + (struct sockaddr*)&sin, sizeof(sin)); + if (sent != request.size()) { + mlibc::infoLogger() << "lookup_name_dns(): sendto() failed to send everything" << frg::endlog; + return -EAI_SYSTEM; + } + + char response[256]; + ssize_t rlen; + int num_ans = 0; + while ((rlen = recvfrom(fd, response, 256, 0, NULL, NULL)) >= 0) { + if ((size_t)rlen < sizeof(struct dns_header)) + continue; + auto response_header = reinterpret_cast<struct dns_header*>(response); + if (response_header->identification != header.identification) + return -EAI_FAIL; + + auto it = response + sizeof(struct dns_header); + for (int i = 0; i < ntohs(response_header->no_q); i++) { + auto dns_name = read_dns_name(response, it); + (void) dns_name; + it += 4; + } + + for (int i = 0; i < ntohs(response_header->no_ans); i++) { + struct dns_addr_buf buffer; + auto dns_name = read_dns_name(response, it); + + uint16_t rr_type = (it[0] << 8) | it[1]; + uint16_t rr_class = (it[2] << 8) | it[3]; + uint16_t rr_length = (it[8] << 8) | it[9]; + it += 10; + (void)rr_class; + + switch (rr_type) { + case RECORD_A: + memcpy(buffer.addr, it, rr_length); + it += rr_length; + buffer.family = AF_INET; + buffer.name = std::move(dns_name); + buf.buf.push(std::move(buffer)); + break; + case RECORD_CNAME: + canon_name = read_dns_name(response, it); + buf.aliases.push(std::move(dns_name)); + break; + default: + mlibc::infoLogger() << "lookup_name_dns: unknown rr type " + << rr_type << frg::endlog; + break; + } + } + num_ans += ntohs(response_header->no_ans); + + if (num_ans >= num_q) + break; + } + + close(fd); + return buf.buf.size(); +} + +int lookup_addr_dns(frg::span<char> name, frg::array<uint8_t, 16> &addr, int family) { + frg::string<MemoryAllocator> request{getAllocator()}; + + int num_q = 1; + struct dns_header header; + header.identification = htons(123); + header.flags = htons(0x100); + header.no_q = htons(num_q); + header.no_ans = htons(0); + header.no_auths = htons(0); + header.no_additional = htons(0); + + request.resize(sizeof(header)); + memcpy(request.data(), &header, sizeof(header)); + + char addr_str[64]; + if(!inet_ntop(family, addr.data(), addr_str, sizeof(addr_str))) { + switch(errno) { + case EAFNOSUPPORT: + return -EAI_FAMILY; + case ENOSPC: + return -EAI_OVERFLOW; + default: + return -EAI_FAIL; + } + } + frg::string<MemoryAllocator> req_str{getAllocator(), addr_str}; + req_str += ".in-addr.arpa"; + + frg::string_view req_view{req_str.data(), req_str.size()}; + size_t ptr = 0; + do { + size_t next = req_view.find_first('.', ptr); + size_t length = next != (size_t)-1 ? next - ptr : req_view.size() - ptr; + frg::string_view substring = req_view.sub_string(ptr, length); + request += char(length); + request += substring; + ptr = next + 1; + } while(ptr != 0); + + request += char(0); + // set question type to fetch PTR records + request += 0; + request += 12; + // set CLASS to IN + request += 0; + request += 1; + + + struct sockaddr_in sin = {}; + sin.sin_family = AF_INET; + // TODO(geert): we could probably make this use the service lookup + // for dns + sin.sin_port = htons(53); + + auto nameserver = get_nameserver(); + if (!inet_aton(nameserver ? nameserver->name.data() : "127.0.0.1", &sin.sin_addr)) { + mlibc::infoLogger() << "lookup_name_dns(): inet_aton() failed!" << frg::endlog; + return -EAI_SYSTEM; + } + + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + mlibc::infoLogger() << "lookup_name_dns(): socket() failed" << frg::endlog; + return -EAI_SYSTEM; + } + + size_t sent = sendto(fd, request.data(), request.size(), 0, + (struct sockaddr*)&sin, sizeof(sin)); + if (sent != request.size()) { + mlibc::infoLogger() << "lookup_name_dns(): sendto() failed to send everything" << frg::endlog; + return -EAI_SYSTEM; + } + + char response[256]; + ssize_t rlen; + int num_ans = 0; + while ((rlen = recvfrom(fd, response, 256, 0, NULL, NULL)) >= 0) { + if ((size_t)rlen < sizeof(struct dns_header)) + continue; + auto response_header = reinterpret_cast<struct dns_header*>(response); + if (response_header->identification != header.identification) + return -EAI_FAIL; + + auto it = response + sizeof(struct dns_header); + for (int i = 0; i < ntohs(response_header->no_q); i++) { + auto dns_name = read_dns_name(response, it); + (void) dns_name; + it += 4; + } + + for (int i = 0; i < ntohs(response_header->no_ans); i++) { + struct dns_addr_buf buffer; + auto dns_name = read_dns_name(response, it); + + uint16_t rr_type = (it[0] << 8) | it[1]; + uint16_t rr_class = (it[2] << 8) | it[3]; + uint16_t rr_length = (it[8] << 8) | it[9]; + it += 10; + (void)rr_class; + (void)rr_length; + + (void)dns_name; + + switch (rr_type) { + case RECORD_PTR: { + auto ptr_name = read_dns_name(response, it); + if (ptr_name.size() >= name.size()) + return -EAI_OVERFLOW; + std::copy(ptr_name.begin(), ptr_name.end(), name.data()); + name.data()[ptr_name.size()] = '\0'; + return 1; + } + default: + mlibc::infoLogger() << "lookup_addr_dns: unknown rr type " + << rr_type << frg::endlog; + break; + } + num_ans += ntohs(response_header->no_ans); + + if (num_ans >= num_q) + break; + } + } + + close(fd); + return 0; +} + +int lookup_name_hosts(struct lookup_result &buf, const char *name, + frg::string<MemoryAllocator> &canon_name) { + auto file = fopen("/etc/hosts", "r"); + if (!file) { + switch (errno) { + case ENOENT: + case ENOTDIR: + case EACCES: + return -EAI_SERVICE; + default: + return -EAI_SYSTEM; + } + } + + char line[128]; + int name_length = strlen(name); + while (fgets(line, 128, file)) { + char *pos; + // same way to deal with comments as in services.cpp + if ((pos = strchr(line, '#'))) { + *pos++ = '\n'; + *pos = '\0'; + } + + for(pos = line + 1; (pos = strstr(pos, name)) && + (!isspace(pos[-1]) || !isspace(pos[name_length])); pos++); + if (!pos) + continue; + + for (pos = line; !isspace(*pos); pos++); + *pos = '\0'; + + // TODO(geert): we assume ipv4 for now + struct in_addr addr; + if (!inet_aton(line, &addr)) + continue; + + pos++; + for(; *pos && isspace(*pos); pos++); + char *end; + for(end = pos; *end && !isspace(*end); end++); + + struct dns_addr_buf buffer; + memcpy(buffer.addr, &addr, 4); + buffer.family = AF_INET; + buffer.name = frg::string<MemoryAllocator>{pos, + static_cast<size_t>(end - pos), getAllocator()}; + canon_name = buffer.name; + + buf.buf.push(std::move(buffer)); + + pos = end; + while (pos[1]) { + for (; *pos && isspace(*pos); pos++); + for (end = pos; *end && !isspace(*end); end++); + auto name = frg::string<MemoryAllocator>{pos, + static_cast<size_t>(end - pos), getAllocator()}; + buf.aliases.push(std::move(name)); + pos = end; + } + } + + fclose(file); + return buf.buf.size(); +} + +int lookup_addr_hosts(frg::span<char> name, frg::array<uint8_t, 16> &addr, int family) { + auto file = fopen("/etc/hosts", "r"); + if (!file) { + switch (errno) { + case ENOENT: + case ENOTDIR: + case EACCES: + return -EAI_SERVICE; + default: + return -EAI_SYSTEM; + } + } + + // Buffer to hold ASCII version of address + char addr_str[64]; + if(!inet_ntop(family, addr.data(), addr_str, sizeof(addr_str))) { + switch(errno) { + case EAFNOSUPPORT: + return -EAI_FAMILY; + case ENOSPC: + return -EAI_OVERFLOW; + default: + return -EAI_FAIL; + } + } + int addr_str_len = strlen(addr_str); + + char line[128]; + while (fgets(line, 128, file)) { + char *pos; + // same way to deal with comments as in services.cpp + if ((pos = strchr(line, '#'))) { + *pos++ = '\n'; + *pos = '\0'; + } + if (strncmp(line, addr_str, addr_str_len)) + continue; + + for (pos = line + addr_str_len + 1; isspace(*pos); pos++); + char *begin = pos; + for (; !isspace(*pos); pos++); + char *end = pos; + + size_t size = end - begin; + if (size >= name.size()) + return -EAI_OVERFLOW; + std::copy(begin, end, name.data()); + name.data()[size] = '\0'; + return 1; + } + return 0; +} + +int lookup_name_null(struct lookup_result &buf, int flags, int family) { + if (flags & AI_PASSIVE) { + if (family != AF_INET6) { + struct dns_addr_buf addr_buf; + addr_buf.family = AF_INET; + + in_addr_t addr = INADDR_ANY; + memcpy(&addr_buf.addr, &addr, 4); + + buf.buf.push_back(addr_buf); + } + if (family != AF_INET) { + struct dns_addr_buf addr_buf; + addr_buf.family = AF_INET6; + + struct in6_addr addr = IN6ADDR_ANY_INIT; + memcpy(&addr_buf.addr, &addr, 16); + + buf.buf.push_back(addr_buf); + } + } else { + if (family != AF_INET6) { + struct dns_addr_buf addr_buf; + addr_buf.family = AF_INET; + + in_addr_t addr = INADDR_LOOPBACK; + memcpy(&addr_buf.addr, &addr, 4); + + buf.buf.push_back(addr_buf); + } + if (family != AF_INET) { + struct dns_addr_buf addr_buf; + addr_buf.family = AF_INET6; + + struct in6_addr addr = IN6ADDR_LOOPBACK_INIT; + memcpy(&addr_buf.addr, &addr, 16); + + buf.buf.push_back(addr_buf); + } + } + return buf.buf.size(); +} + +int lookup_name_ip(struct lookup_result &buf, const char *name, int family) { + if (family == AF_INET) { + in_addr_t addr = 0; + int res = inet_pton(AF_INET, name, &addr); + + if (res <= 0) + return -EAI_NONAME; + + struct dns_addr_buf addr_buf; + addr_buf.family = AF_INET; + memcpy(&addr_buf.addr, &addr, 4); + + buf.buf.push_back(addr_buf); + return 1; + } + + if (family == AF_INET6) { + struct in6_addr addr{0}; + int res = inet_pton(AF_INET6, name, &addr); + + if (res <= 0) + return -EAI_NONAME; + + struct dns_addr_buf addr_buf; + addr_buf.family = AF_INET6; + memcpy(&addr_buf.addr, &addr, 16); + + buf.buf.push_back(addr_buf); + return 1; + } + + // If no family was specified we try ipv4 and then ipv6. + in_addr_t addr4 = 0; + int res = inet_pton(AF_INET, name, &addr4); + + if (res > 0) { + struct dns_addr_buf addr_buf; + addr_buf.family = AF_INET; + memcpy(&addr_buf.addr, &addr4, 4); + + buf.buf.push_back(addr_buf); + return 1; + } + + struct in6_addr addr6{0}; + res = inet_pton(AF_INET6, name, &addr6); + + if (res <= 0) + return -EAI_NONAME; + + struct dns_addr_buf addr_buf; + addr_buf.family = AF_INET6; + memcpy(&addr_buf.addr, &addr6, 16); + + buf.buf.push_back(addr_buf); + return 1; +} + +} // namespace mlibc diff --git a/lib/mlibc/options/posix/generic/mqueue.cpp b/lib/mlibc/options/posix/generic/mqueue.cpp new file mode 100644 index 0000000..d635419 --- /dev/null +++ b/lib/mlibc/options/posix/generic/mqueue.cpp @@ -0,0 +1,22 @@ +#include <mqueue.h> +#include <bits/ensure.h> + +int mq_getattr(mqd_t, struct mq_attr *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int mq_setattr(mqd_t, const struct mq_attr *__restrict__, struct mq_attr *__restrict__) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int mq_unlink(const char *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +mqd_t mq_open(const char *, int, ...) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/net-if-stubs.cpp b/lib/mlibc/options/posix/generic/net-if-stubs.cpp new file mode 100644 index 0000000..6a65a5c --- /dev/null +++ b/lib/mlibc/options/posix/generic/net-if-stubs.cpp @@ -0,0 +1,40 @@ +#include <errno.h> +#include <net/if.h> +#include <stdlib.h> + +#include <bits/ensure.h> +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> + +void if_freenameindex(struct if_nameindex *) { + mlibc::infoLogger() << "mlibc: if_freenameindex is a no-op" << frg::endlog; +} + +char *if_indextoname(unsigned int index, char *name) { + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_if_indextoname, NULL); + + if(int e = sysdep(index, name); e) { + errno = e; + return NULL; + } + + return name; +} + +struct if_nameindex *if_nameindex(void) { + mlibc::infoLogger() << "mlibc: if_nameindex() is a no-op" << frg::endlog; + errno = ENOSYS; + return NULL; +} + +unsigned int if_nametoindex(const char *name) { + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_if_nametoindex, 0); + unsigned int ret = 0; + + if(int e = sysdep(name, &ret); e) { + errno = e; + return 0; + } + + return ret; +} diff --git a/lib/mlibc/options/posix/generic/netdb-stubs.cpp b/lib/mlibc/options/posix/generic/netdb-stubs.cpp new file mode 100644 index 0000000..455444b --- /dev/null +++ b/lib/mlibc/options/posix/generic/netdb-stubs.cpp @@ -0,0 +1,486 @@ +#include <netdb.h> +#include <bits/ensure.h> + +#include <mlibc/debug.hpp> +#include <mlibc/lookup.hpp> +#include <mlibc/allocator.hpp> +#include <mlibc/services.hpp> +#include <frg/vector.hpp> +#include <frg/array.hpp> +#include <frg/span.hpp> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +#include <errno.h> + +__thread int __mlibc_h_errno; + +// This function is from musl +int *__h_errno_location(void) { + return &__mlibc_h_errno; +} + +void endhostent(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void endnetent(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void endprotoent(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void endservent(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void freeaddrinfo(struct addrinfo *ptr) { + if (ptr) { + auto buf = (struct mlibc::ai_buf*) ptr - offsetof(struct mlibc::ai_buf, ai); + // this string was allocated by a frg::string + getAllocator().free(ptr->ai_canonname); + free(buf); + } +} + +const char *gai_strerror(int code) { + static thread_local char buffer[128]; + snprintf(buffer, sizeof(buffer), "Unknown error (%d)", code); + return buffer; +} + +int getaddrinfo(const char *__restrict node, const char *__restrict service, + const struct addrinfo *__restrict hints, struct addrinfo **__restrict res) { + if (!node && !service) + return EAI_NONAME; + + int socktype = 0, protocol = 0, family = AF_UNSPEC, flags = AI_V4MAPPED | AI_ADDRCONFIG; + if (hints) { + socktype = hints->ai_socktype; + protocol = hints->ai_protocol; + family = hints->ai_family; + flags = hints->ai_flags; + + int mask = AI_V4MAPPED | AI_ADDRCONFIG | AI_NUMERICHOST | AI_PASSIVE | + AI_CANONNAME | AI_ALL | AI_NUMERICSERV; + if ((flags & mask) != flags) + return EAI_BADFLAGS; + + if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC) + return EAI_FAMILY; + } + + mlibc::service_result serv_buf{getAllocator()}; + int serv_count = mlibc::lookup_serv_by_name(serv_buf, service, protocol, socktype, flags); + if (serv_count < 0) + return -serv_count; + + struct mlibc::lookup_result addr_buf; + int addr_count = 1; + frg::string<MemoryAllocator> canon{getAllocator()}; + if (node) { + if ((addr_count = mlibc::lookup_name_ip(addr_buf, node, family)) <= 0) { + if (flags & AI_NUMERICHOST) + addr_count = -EAI_NONAME; + else if ((addr_count = mlibc::lookup_name_hosts(addr_buf, node, canon)) <= 0) + addr_count = mlibc::lookup_name_dns(addr_buf, node, canon); + else + addr_count = 1; + } + + if (addr_count < 0) + return -addr_count; + if (!addr_count) + return EAI_NONAME; + } else { + /* There is no node specified */ + if (flags & AI_NUMERICHOST) + return EAI_NONAME; + addr_count = lookup_name_null(addr_buf, flags, family); + } + + auto out = (struct mlibc::ai_buf *) calloc(serv_count * addr_count, + sizeof(struct mlibc::ai_buf)); + + if (node && !canon.size()) + canon = frg::string<MemoryAllocator>{node, getAllocator()}; + + for (int i = 0, k = 0; i < addr_count; i++) { + for (int j = 0; j < serv_count; j++, k++) { + out[i].ai.ai_family = addr_buf.buf[i].family; + out[i].ai.ai_socktype = serv_buf[j].socktype; + out[i].ai.ai_protocol = serv_buf[j].protocol; + out[i].ai.ai_flags = flags; + out[i].ai.ai_addr = (struct sockaddr *) &out[i].sa; + if (canon.size()) + out[i].ai.ai_canonname = canon.data(); + else + out[i].ai.ai_canonname = NULL; + out[i].ai.ai_next = NULL; + switch (addr_buf.buf[i].family) { + case AF_INET: + out[i].ai.ai_addrlen = sizeof(struct sockaddr_in); + out[i].sa.sin.sin_port = htons(serv_buf[j].port); + out[i].sa.sin.sin_family = AF_INET; + memcpy(&out[i].sa.sin.sin_addr, addr_buf.buf[i].addr, 4); + break; + case AF_INET6: + out[i].ai.ai_addrlen = sizeof(struct sockaddr_in6); + out[i].sa.sin6.sin6_family = htons(serv_buf[j].port); + out[i].sa.sin6.sin6_family = AF_INET6; + memcpy(&out[i].sa.sin6.sin6_addr, addr_buf.buf[i].addr, 16); + break; + } + } + } + if (canon.size()) + canon.detach(); + + *res = &out[0].ai; + return 0; +} + +struct hostent *gethostent(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int getnameinfo(const struct sockaddr *__restrict addr, socklen_t addr_len, + char *__restrict host, socklen_t host_len, char *__restrict serv, + socklen_t serv_len, int flags) { + frg::array<uint8_t, 16> addr_array; + int family = addr->sa_family; + + switch(family) { + case AF_INET: { + if (addr_len < sizeof(struct sockaddr_in)) + return EAI_FAMILY; + auto sockaddr = reinterpret_cast<const struct sockaddr_in*>(addr); + memcpy(addr_array.data(), reinterpret_cast<const char*>(&sockaddr->sin_addr), 4); + break; + } + case AF_INET6: { + mlibc::infoLogger() << "getnameinfo(): ipv6 is not fully supported in this function" << frg::endlog; + if (addr_len < sizeof(struct sockaddr_in6)) + return EAI_FAMILY; + auto sockaddr = reinterpret_cast<const struct sockaddr_in6*>(addr); + memcpy(addr_array.data(), reinterpret_cast<const char*>(&sockaddr->sin6_addr), 16); + break; + } + default: + return EAI_FAMILY; + } + + if (host && host_len) { + frg::span<char> host_span{host, host_len}; + int res = 0; + if (!(flags & NI_NUMERICHOST)) + res = mlibc::lookup_addr_hosts(host_span, addr_array, family); + if (!(flags & NI_NUMERICHOST) && !res) + res = mlibc::lookup_addr_dns(host_span, addr_array, family); + + if (!res) { + if (flags & NI_NAMEREQD) + return EAI_NONAME; + if(!inet_ntop(family, addr_array.data(), host, host_len)) { + switch(errno) { + case EAFNOSUPPORT: + return EAI_FAMILY; + case ENOSPC: + return EAI_OVERFLOW; + default: + return EAI_FAIL; + } + } + } + + if (res < 0) + return -res; + } + + if (serv && serv_len) { + __ensure("getnameinfo(): not implemented service resolution yet!"); + __builtin_unreachable(); + } + + return 0; +} + +struct netent *getnetbyaddr(uint32_t, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +struct netent *getnetbyname(const char *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +struct netent *getnetent(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +struct hostent *gethostbyname(const char *name) { + if (!name) { + h_errno = HOST_NOT_FOUND; + return NULL; + } + + struct mlibc::lookup_result buf; + frg::string<MemoryAllocator> canon{getAllocator()}; + int ret = 0; + if ((ret = mlibc::lookup_name_hosts(buf, name, canon)) <= 0) + ret = mlibc::lookup_name_dns(buf, name, canon); + if (ret <= 0) { + h_errno = HOST_NOT_FOUND; + return NULL; + } + + static struct hostent h; + if (h.h_name) { + getAllocator().free(h.h_name); + for (int i = 0; h.h_aliases[i] != NULL; i++) + getAllocator().free(h.h_aliases[i]); + free(h.h_aliases); + + if (h.h_addr_list) { + for (int i = 0; h.h_addr_list[i] != NULL; i++) + free(h.h_addr_list[i]); + free(h.h_addr_list); + } + } + h = {}; + + if (!canon.size()) + canon = frg::string<MemoryAllocator>{name, getAllocator()}; + + h.h_name = canon.data(); + + h.h_aliases = reinterpret_cast<char**>(malloc((buf.aliases.size() + 1) + * sizeof(char*))); + int alias_pos = 0; + for (auto &buf_name : buf.aliases) { + h.h_aliases[alias_pos] = buf_name.data(); + buf_name.detach(); + alias_pos++; + } + h.h_aliases[alias_pos] = NULL; + canon.detach(); + + // just pick the first family as the one for all addresses...?? + h.h_addrtype = buf.buf[0].family; + if (h.h_addrtype != AF_INET && h.h_addrtype != AF_INET6) { + // this is not allowed per spec + h_errno = NO_DATA; + return NULL; + } + + // can only be AF_INET or AF_INET6 + h.h_length = h.h_addrtype == AF_INET ? 4 : 16; + h.h_addr_list = reinterpret_cast<char**>(malloc((ret + 1) * sizeof(char*))); + int addr_pos = 0; + for (int i = 0; i < ret; i++) { + if (buf.buf[i].family != h.h_addrtype) + continue; + h.h_addr_list[addr_pos] = reinterpret_cast<char*>(malloc(h.h_length)); + memcpy(h.h_addr_list[addr_pos], buf.buf[i].addr, h.h_length); + addr_pos++; + } + h.h_addr_list[addr_pos] = NULL; + + return &h; +} + +struct hostent *gethostbyname2(const char *, int) { + __ensure(!"gethostbyname2() not implemented"); + __builtin_unreachable(); +} + +struct hostent *gethostbyaddr(const void *, socklen_t, int) { + __ensure(!"gethostbyaddr() not implemented"); + __builtin_unreachable(); +} + +int gethostbyaddr_r(const void *__restrict, socklen_t, int, struct hostent *__restrict, + char *__restrict, size_t, struct hostent **__restrict, int *__restrict) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int gethostbyname_r(const char *__restrict, struct hostent *__restrict, char *__restrict, size_t, + struct hostent **__restrict, int *__restrict) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +struct protoent *getprotobyname(const char *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +struct protoent *getprotobynumber(int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +struct protoent *getprotoent(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +struct servent *getservbyname(const char *name, const char *proto) { + int iproto = -1; + if (proto &&(!strncmp(proto, "tcp", 3) || !strncmp(proto, "TCP", 3))) + iproto = IPPROTO_TCP; + else if (proto && (!strncmp(proto, "udp", 3) || !strncmp(proto, "UDP", 3))) + iproto = IPPROTO_UDP; + + static struct servent ret; + if (ret.s_name) { + free(ret.s_name); + ret.s_name = nullptr; + + for (char **alias = ret.s_aliases; *alias != NULL; alias++) { + free(*alias); + *alias = nullptr; + } + + free(ret.s_proto); + ret.s_proto = nullptr; + } + + mlibc::service_result serv_buf{getAllocator()}; + int count = mlibc::lookup_serv_by_name(serv_buf, name, iproto, + 0, 0); + if (count <= 0) + return NULL; + + ret.s_name = serv_buf[0].name.data(); + serv_buf[0].name.detach(); + // Sanity check. + if (strncmp(name, serv_buf[0].name.data(), serv_buf[0].name.size())) + return NULL; + + ret.s_aliases = reinterpret_cast<char**>(malloc((serv_buf[0].aliases.size() + 1) * sizeof(char*))); + int alias_pos = 0; + for (auto &buf_name : serv_buf[0].aliases) { + ret.s_aliases[alias_pos] = buf_name.data(); + buf_name.detach(); + alias_pos++; + } + ret.s_aliases[alias_pos] = NULL; + + ret.s_port = htons(serv_buf[0].port); + + auto proto_string = frg::string<MemoryAllocator>(getAllocator()); + if (!proto) { + if (serv_buf[0].protocol == IPPROTO_TCP) + proto_string = frg::string<MemoryAllocator>("tcp", getAllocator()); + else if (serv_buf[0].protocol == IPPROTO_UDP) + proto_string = frg::string<MemoryAllocator>("udp", getAllocator()); + else + return NULL; + } else { + proto_string = frg::string<MemoryAllocator>(proto, getAllocator()); + } + ret.s_proto = proto_string.data(); + proto_string.detach(); + + return &ret; +} + +struct servent *getservbyport(int port, const char *proto) { + int iproto = -1; + if (proto && (!strncmp(proto, "tcp", 3) || !strncmp(proto, "TCP", 3))) + iproto = IPPROTO_TCP; + else if (proto && (!strncmp(proto, "udp", 3) || !strncmp(proto, "UDP", 3))) + iproto = IPPROTO_UDP; + + static struct servent ret; + if (ret.s_name) { + free(ret.s_name); + ret.s_name = nullptr; + + for (char **alias = ret.s_aliases; *alias != NULL; alias++) { + free(*alias); + *alias = nullptr; + } + + free(ret.s_proto); + ret.s_proto = nullptr; + } + + mlibc::service_result serv_buf{getAllocator()}; + int count = mlibc::lookup_serv_by_port(serv_buf, iproto, ntohs(port)); + if (count <= 0) + return NULL; + + ret.s_name = serv_buf[0].name.data(); + serv_buf[0].name.detach(); + + ret.s_aliases = reinterpret_cast<char**>(malloc((serv_buf[0].aliases.size() + 1) * sizeof(char*))); + int alias_pos = 0; + for (auto &buf_name : serv_buf[0].aliases) { + ret.s_aliases[alias_pos] = buf_name.data(); + buf_name.detach(); + alias_pos++; + } + ret.s_aliases[alias_pos] = NULL; + + ret.s_port = port; + + auto proto_string = frg::string<MemoryAllocator>(getAllocator()); + if (!proto) { + if (serv_buf[0].protocol == IPPROTO_TCP) + proto_string = frg::string<MemoryAllocator>("tcp", getAllocator()); + else if (serv_buf[0].protocol == IPPROTO_UDP) + proto_string = frg::string<MemoryAllocator>("udp", getAllocator()); + else + return NULL; + } else { + proto_string = frg::string<MemoryAllocator>(proto, getAllocator()); + } + ret.s_proto = proto_string.data(); + proto_string.detach(); + + return &ret; +} + +struct servent *getservent(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void sethostent(int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void setnetent(int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void setprotoent(int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void setservent(int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +const char *hstrerror(int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/poll.cpp b/lib/mlibc/options/posix/generic/poll.cpp new file mode 100644 index 0000000..c34e25e --- /dev/null +++ b/lib/mlibc/options/posix/generic/poll.cpp @@ -0,0 +1,31 @@ + +#include <errno.h> +#include <poll.h> + +#include <bits/ensure.h> +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> + +int poll(struct pollfd *fds, nfds_t count, int timeout) { + int num_events; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_poll, -1); + if(int e = mlibc::sys_poll(fds, count, timeout, &num_events); e) { + errno = e; + return -1; + } + return num_events; +} + +#if __MLIBC_LINUX_OPTION +int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts, const sigset_t *sigmask) { + sigset_t origmask; + int timeout = (timeout_ts == NULL) ? -1 : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000); + + sigprocmask(SIG_SETMASK, sigmask, &origmask); + int ready = poll(fds, nfds, timeout); + sigprocmask(SIG_SETMASK, &origmask, NULL); + + return ready; +} +#endif // __MLIBC_LINUX_OPTION + diff --git a/lib/mlibc/options/posix/generic/posix-file-io.cpp b/lib/mlibc/options/posix/generic/posix-file-io.cpp new file mode 100644 index 0000000..1a4f38b --- /dev/null +++ b/lib/mlibc/options/posix/generic/posix-file-io.cpp @@ -0,0 +1,275 @@ +#include <mlibc/posix-file-io.hpp> +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> + +#include <errno.h> + +namespace mlibc { + +int mem_file::reopen(const char *, const char *) { + mlibc::panicLogger() << "mlibc: freopen() on a mem_file stream is unimplemented!" << frg::endlog; + return -1; +} + +int mem_file::determine_type(stream_type *type) { + *type = stream_type::file_like; + return 0; +} + +int mem_file::determine_bufmode(buffer_mode *mode) { + *mode = buffer_mode::no_buffer; + return 0; +} + +memstream_mem_file::memstream_mem_file(char **ptr, size_t *sizeloc, int flags, void (*do_dispose)(abstract_file *)) +: mem_file{flags, do_dispose}, _bufloc{ptr}, _sizeloc{sizeloc} { } + + +int memstream_mem_file::close() { + _update_ptrs(); + _buf.detach(); + + return 0; +} + +int memstream_mem_file::io_read(char *buffer, size_t max_size, size_t *actual_size) { + if ((_pos >= 0 && _pos >= _max_size) || !max_size) { + *actual_size = 0; + return 0; + } + + size_t bytes_read = std::min(size_t(_max_size - _pos), max_size); + memcpy(buffer, _buffer().data() + _pos, bytes_read); + _pos += bytes_read; + *actual_size = bytes_read; + return 0; +} + +int memstream_mem_file::io_write(const char *buffer, size_t max_size, size_t *actual_size) { + if (_pos + max_size >= _buffer_size()) { + _buf.resize(_pos + max_size + 1, '\0'); + _update_ptrs(); + } + + size_t bytes_write = std::min(static_cast<size_t>(_buffer_size() - _pos), max_size); + memcpy(_buffer().data() + _pos, buffer, bytes_write); + _pos += max_size; + *actual_size = max_size; + + return 0; +} + +int memstream_mem_file::io_seek(off_t offset, int whence, off_t *new_offset) { + switch (whence) { + case SEEK_SET: + _pos = offset; + if (_pos >= 0 && size_t(_pos) >= _buffer_size()) { + _buf.resize(_pos + 1, '\0'); + _update_ptrs(); + } + *new_offset = _pos; + break; + case SEEK_CUR: + _pos += offset; + if (_pos >= 0 && size_t(_pos) >= _buffer_size()) { + _buf.resize(_pos + 1, '\0'); + _update_ptrs(); + } + *new_offset = _pos; + break; + case SEEK_END: + _pos = _buffer_size() ? _buffer_size() - 1 + offset : _buffer_size() + offset; + _buf.resize(_pos + 1, '\0'); + _update_ptrs(); + *new_offset = _pos; + break; + default: + return EINVAL; + } + return 0; +} + +void memstream_mem_file::_update_ptrs() { + *_bufloc = _buf.data(); + *_sizeloc = _buf.size() - 1; +} + +fmemopen_mem_file::fmemopen_mem_file(void *in_buf, size_t size, int flags, void (*do_dispose)(abstract_file *)) +: mem_file{flags, do_dispose}, _inBuffer{in_buf}, _inBufferSize{size} { + if(!_inBuffer) { + _inBuffer = getAllocator().allocate(size); + _needsDeallocation = true; + } + + if(_flags & O_APPEND) { + // the initial seek-size for append is zero if buf was NULL, or the first '\0' found, or the size + _max_size = (_needsDeallocation) ? 0 : strnlen(reinterpret_cast<char *>(_inBuffer), _inBufferSize); + _pos = _max_size; + } else if((_flags & O_WRONLY || _flags & O_RDWR) && _flags & O_CREAT && _flags & O_TRUNC) { + // modes: "w", "w+" + _max_size = 0; + } else { + _max_size = size; + } +} + +int fmemopen_mem_file::close() { + if(_needsDeallocation) { + getAllocator().free(_inBuffer); + } + + return 0; +} + +int fmemopen_mem_file::io_read(char *buffer, size_t max_size, size_t *actual_size) { + if ((_pos >= 0 && _pos >= _max_size) || !max_size) { + *actual_size = 0; + return 0; + } + + size_t bytes_read = std::min(size_t(_max_size - _pos), max_size); + memcpy(buffer, _buffer().data() + _pos, bytes_read); + _pos += bytes_read; + *actual_size = bytes_read; + return 0; +} + +int fmemopen_mem_file::io_write(const char *buffer, size_t max_size, size_t *actual_size) { + off_t bytes_write = std::min(static_cast<size_t>(_buffer_size() - _pos), max_size); + memcpy(_buffer().data() + _pos, buffer, bytes_write); + _pos += bytes_write; + *actual_size = bytes_write; + + if(_pos > _max_size) { + _max_size = _pos; + } + + // upon flushing, we need to put a null byte at the current position or at the end of the buffer + size_t null = _pos; + // a special case is if the mode is set to updating ('+'), then it always goes at the end + if(null >= _buffer_size() || _flags & O_RDWR) { + null = _buffer_size() - 1; + } + + if(_buffer_size()) { + _buffer()[null] = '\0'; + } + + return 0; +} + +int fmemopen_mem_file::io_seek(off_t offset, int whence, off_t *new_offset) { + switch (whence) { + case SEEK_SET: + if(offset < 0 || size_t(offset) > _buffer_size()) { + return EINVAL; + } + _pos = offset; + *new_offset = _pos; + break; + case SEEK_CUR: + // seeking to negative positions or positions larger than the buffer is disallowed in fmemopen(3) + if((_pos + offset) < 0 || size_t(_pos + offset) > _buffer_size()) { + return EINVAL; + } + _pos += offset; + *new_offset = _pos; + break; + case SEEK_END: + if((_max_size + offset) < 0 || size_t(_max_size + offset) > _buffer_size()) { + return EINVAL; + } + _pos = _max_size + offset; + *new_offset = _pos; + break; + default: + return EINVAL; + } + return 0; +} + +int cookie_file::close() { + if(!_funcs.close) { + return 0; + } + + return _funcs.close(_cookie); +} + +int cookie_file::reopen(const char *, const char *) { + mlibc::panicLogger() << "mlibc: freopen() on a cookie_file stream is unimplemented!" << frg::endlog; + return -1; +} + +int cookie_file::determine_type(stream_type *type) { + *type = stream_type::file_like; + return 0; +} + +int cookie_file::determine_bufmode(buffer_mode *mode) { + *mode = buffer_mode::no_buffer; + return 0; +} + +int cookie_file::io_read(char *buffer, size_t max_size, size_t *actual_size) { + if(!_funcs.read) { + return EOF; + } + + *actual_size = _funcs.read(_cookie, buffer, max_size); + + return 0; +} + +int cookie_file::io_write(const char *buffer, size_t max_size, size_t *actual_size) { + if(!_funcs.write) { + return 0; + } + + *actual_size = _funcs.write(_cookie, buffer, max_size); + + return 0; +} + +int cookie_file::io_seek(off_t offset, int whence, off_t *new_offset) { + if(!_funcs.seek) { + return ENOTSUP; + } + + *new_offset = offset; + + return _funcs.seek(_cookie, new_offset, whence); +} + +} // namespace mlibc + +FILE *fdopen(int fd, const char *mode) { + int flags = mlibc::fd_file::parse_modestring(mode); + + flags &= ~O_TRUNC; // 'w' should not truncate the file + + if (flags & O_APPEND) { + int cur_flags = fcntl(fd, F_GETFL, 0); + if (cur_flags < 0) { + errno = EINVAL; + return nullptr; + } else if (!(cur_flags & O_APPEND)) { + if (fcntl(fd, F_SETFL, cur_flags | O_APPEND)) { + errno = EINVAL; + return nullptr; + } + } + } + + if (flags & O_CLOEXEC) { + if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { + errno = EINVAL; + return nullptr; + } + } + + // TODO: We may need to activate line buffered mode for terminals. + + return frg::construct<mlibc::fd_file>(getAllocator(), fd, + mlibc::file_dispose_cb<mlibc::fd_file>); +} diff --git a/lib/mlibc/options/posix/generic/posix_ctype.cpp b/lib/mlibc/options/posix/generic/posix_ctype.cpp new file mode 100644 index 0000000..19f129f --- /dev/null +++ b/lib/mlibc/options/posix/generic/posix_ctype.cpp @@ -0,0 +1,136 @@ +#include <ctype.h> +#include <wctype.h> + +#include <bits/ensure.h> + +int isalnum_l(int c, locale_t) { + return isalnum(c); +} + +int isalpha_l(int c, locale_t) { + return isalpha(c); +} + +int isblank_l(int c, locale_t) { + return isblank(c); +} + +int iscntrl_l(int c, locale_t) { + return iscntrl(c); +} + +int isdigit_l(int c, locale_t) { + return isdigit(c); +} + +int isgraph_l(int c, locale_t) { + return isgraph(c); +} + +int islower_l(int c, locale_t) { + return islower(c); +} + +int isprint_l(int c, locale_t) { + return isprint(c); +} + +int ispunct_l(int c, locale_t) { + return ispunct(c); +} + +int isspace_l(int c, locale_t) { + return isspace(c); +} + +int isupper_l(int c, locale_t) { + return isupper(c); +} + +int isxdigit_l(int c, locale_t) { + return isxdigit(c); +} + +int isascii_l(int c, locale_t) { + return isascii(c); +} + +int tolower_l(int c, locale_t) { + return tolower(c); +} + +int toupper_l(int c, locale_t) { + return toupper(c); +} + +int iswalnum_l(wint_t c, locale_t) { + return iswalnum(c); +} + +int iswblank_l(wint_t c, locale_t) { + return iswblank(c); +} + +int iswcntrl_l(wint_t c, locale_t) { + return iswcntrl(c); +} + +int iswdigit_l(wint_t c, locale_t) { + return iswdigit(c); +} + +int iswgraph_l(wint_t c, locale_t) { + return iswgraph(c); +} + +int iswlower_l(wint_t c, locale_t) { + return iswlower(c); +} + +int iswprint_l(wint_t c, locale_t) { + return iswprint(c); +} + +int iswpunct_l(wint_t c, locale_t) { + return iswpunct(c); +} + +int iswspace_l(wint_t c, locale_t) { + return iswspace(c); +} + +int iswupper_l(wint_t c, locale_t) { + return iswupper(c); +} + +int iswxdigit_l(wint_t c, locale_t) { + return iswxdigit(c); +} + +int iswalpha_l(wint_t c, locale_t) { + return iswalpha(c); +} + +wctype_t wctype_l(const char* p, locale_t) { + return wctype(p); +} + +int iswctype_l(wint_t w, wctype_t t, locale_t) { + return iswctype(w, t); +} + +wint_t towlower_l(wint_t c, locale_t) { + return towlower(c); +} + +wint_t towupper_l(wint_t c, locale_t) { + return towupper(c); +} + +wctrans_t wctrans_l(const char* c, locale_t) { + return wctrans(c); +} + +wint_t towctrans_l(wint_t c, wctrans_t desc, locale_t) { + return towctrans(c, desc); +} diff --git a/lib/mlibc/options/posix/generic/posix_locale.cpp b/lib/mlibc/options/posix/generic/posix_locale.cpp new file mode 100644 index 0000000..bd8710a --- /dev/null +++ b/lib/mlibc/options/posix/generic/posix_locale.cpp @@ -0,0 +1,37 @@ +#include <bits/posix/posix_locale.h> +#include <bits/ensure.h> +#include <mlibc/debug.hpp> + +namespace { + +bool newlocale_seen = false; +bool uselocale_seen = false; + +} + +locale_t newlocale(int, const char *, locale_t) { + // Due to all of the locale functions being stubs, the locale will not be used + if(!newlocale_seen) { + mlibc::infoLogger() << "mlibc: newlocale() is a no-op" << frg::endlog; + newlocale_seen = true; + } + return nullptr; +} + +void freelocale(locale_t) { + mlibc::infoLogger() << "mlibc: freelocale() is a no-op" << frg::endlog; + return; +} + +locale_t uselocale(locale_t) { + if(!uselocale_seen) { + mlibc::infoLogger() << "mlibc: uselocale() is a no-op" << frg::endlog; + uselocale_seen = true; + } + return nullptr; +} + +locale_t duplocale(locale_t) { + mlibc::infoLogger() << "mlibc: duplocale() is a no-op" << frg::endlog; + return nullptr; +} diff --git a/lib/mlibc/options/posix/generic/posix_signal.cpp b/lib/mlibc/options/posix/generic/posix_signal.cpp new file mode 100644 index 0000000..eef3ef3 --- /dev/null +++ b/lib/mlibc/options/posix/generic/posix_signal.cpp @@ -0,0 +1,151 @@ + +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <bits/ensure.h> + +#include <mlibc/posix-sysdeps.hpp> +#include <mlibc/tcb.hpp> + +int sigsuspend(const sigset_t *sigmask) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sigsuspend, -1); + + // This is guaranteed to return an error (EINTR most probably) + errno = mlibc::sys_sigsuspend(sigmask); + return -1; +} + +int pthread_sigmask(int how, const sigset_t *__restrict set, sigset_t *__restrict retrieve) { + if(!mlibc::sys_sigprocmask) { + MLIBC_MISSING_SYSDEP(); + return ENOSYS; + } + if(int e = mlibc::sys_sigprocmask(how, set, retrieve); e) { + return e; + } + return 0; +} + +int pthread_kill(pthread_t thread, int sig) { + auto tcb = reinterpret_cast<Tcb *>(thread); + auto pid = getpid(); + + if(!mlibc::sys_tgkill) { + MLIBC_MISSING_SYSDEP(); + return ENOSYS; + } + + if(int e = mlibc::sys_tgkill(pid, tcb->tid, sig); e) { + return e; + } + + return 0; +} + +int sigaction(int signum, const struct sigaction *__restrict act, struct sigaction *__restrict oldact) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sigaction, -1); + if(int e = mlibc::sys_sigaction(signum, act, oldact); e) { + errno = e; + return -1; + } + return 0; +} + +int siginterrupt(int sig, int flag) { + int ret; + struct sigaction act; + + sigaction(sig, NULL, &act); + if (flag) + act.sa_flags &= ~SA_RESTART; + else + act.sa_flags |= SA_RESTART; + + ret = sigaction(sig, &act, NULL); + return ret; +} + +int kill(pid_t pid, int number) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_kill, -1); + if(int e = mlibc::sys_kill(pid, number); e) { + errno = e; + return -1; + } + return 0; +} + +int killpg(pid_t pgrp, int sig) { + if(pgrp > 1) { + return kill(-pgrp, sig); + } + + errno = EINVAL; + return -1; +} + +int sigtimedwait(const sigset_t *__restrict set, siginfo_t *__restrict info, const struct timespec *__restrict timeout) { + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sigtimedwait, -1); + + int signo; + + if (int e = sysdep(set, info, timeout, &signo)) { + errno = e; + return -1; + } + + return signo; +} + +int sigwaitinfo(const sigset_t *__restrict set, siginfo_t *__restrict info) { + // NOTE: This assumes the sysdep behavior noted in mlibc/posix-sysdeps.hpp + return sigtimedwait(set, info, nullptr); +} + +int sigwait(const sigset_t *__restrict set, int *__restrict sig) { + if (int e = sigwaitinfo(set, nullptr); e < 0) { + return e; + } else { + if (sig) + *sig = e; + + return 0; + } +} + +int sigpending(sigset_t *set) { + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sigpending, -1); + + if(int e = sysdep(set)) { + errno = e; + return -1; + } + + return 0; +} + +int sigaltstack(const stack_t *__restrict ss, stack_t *__restrict oss) { + if (ss && ss->ss_size < MINSIGSTKSZ && !(ss->ss_flags & SS_DISABLE)) { + errno = ENOMEM; + return -1; + } + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sigaltstack, -1); + if (int e = mlibc::sys_sigaltstack(ss, oss); e) { + errno = e; + return -1; + } + + return 0; +} + +#if __MLIBC_GLIBC_OPTION +int sigisemptyset(const sigset_t *set) { + return !(*set); +} +#endif // __MLIBC_GLIBC_OPTION + +int sigqueue(pid_t, int, const union sigval) { + __ensure(!"sigqueue() not implemented"); + __builtin_unreachable(); +} + diff --git a/lib/mlibc/options/posix/generic/posix_stdio.cpp b/lib/mlibc/options/posix/generic/posix_stdio.cpp new file mode 100644 index 0000000..fc77a54 --- /dev/null +++ b/lib/mlibc/options/posix/generic/posix_stdio.cpp @@ -0,0 +1,209 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> + +#include <bits/ensure.h> +#include <mlibc/ansi-sysdeps.hpp> +#include <mlibc/debug.hpp> +#include <mlibc/file-io.hpp> +#include <mlibc/posix-file-io.hpp> +#include <mlibc/posix-sysdeps.hpp> + +struct popen_file : mlibc::fd_file { + popen_file(int fd, void (*do_dispose)(abstract_file *) = nullptr) + : fd_file(fd, do_dispose) {} + + pid_t get_popen_pid() { + return _popen_pid; + } + + void set_popen_pid(pid_t new_pid) { + _popen_pid = new_pid; + } + +private: + // Underlying PID in case of popen() + pid_t _popen_pid; +}; + +FILE *fmemopen(void *buf, size_t size, const char *__restrict mode) { + int flags = mlibc::fd_file::parse_modestring(mode); + + return frg::construct<mlibc::fmemopen_mem_file>(getAllocator(), buf, size, flags, + mlibc::file_dispose_cb<mlibc::mem_file>); +} + +int pclose(FILE *stream) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_waitpid, -1); + + auto file = static_cast<popen_file *>(stream); + + int status; + pid_t pid = file->get_popen_pid(); + + fclose(file); + + if (mlibc::sys_waitpid(pid, &status, 0, NULL, &pid) != 0) { + errno = ECHILD; + return -1; + } + + return status; +} + +FILE *popen(const char *command, const char *typestr) { + bool is_write; + pid_t child; + FILE *ret = nullptr; + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fork && mlibc::sys_dup2 && mlibc::sys_execve && + mlibc::sys_sigprocmask && mlibc::sys_sigaction && mlibc::sys_pipe, nullptr); + + if (typestr == NULL) { + errno = EINVAL; + return nullptr; + } + + if (strstr(typestr, "w") != NULL) { + is_write = true; + } else if (strstr(typestr, "r") != NULL) { + is_write = false; + } else { + errno = EINVAL; + return nullptr; + } + + bool cloexec = false; + if (strstr(typestr, "e") != NULL) { + // Set FD_CLOEXEC on the new file descriptor + cloexec = true; + } + + int fds[2]; + if (int e = mlibc::sys_pipe(fds, 0)) { + errno = e; + return nullptr; + } + + struct sigaction new_sa, old_int, old_quit; + sigset_t new_mask, old_mask; + + new_sa.sa_handler = SIG_IGN; + new_sa.sa_flags = 0; + sigemptyset(&new_sa.sa_mask); + mlibc::sys_sigaction(SIGINT, &new_sa, &old_int); + mlibc::sys_sigaction(SIGQUIT, &new_sa, &old_quit); + + sigemptyset(&new_mask); + sigaddset(&new_mask, SIGCHLD); + mlibc::sys_sigprocmask(SIG_BLOCK, &new_mask, &old_mask); + + int parent_end = is_write ? 1 : 0; + int child_end = is_write ? 0 : 1; + + if (int e = mlibc::sys_fork(&child)) { + errno = e; + mlibc::sys_close(fds[0]); + mlibc::sys_close(fds[1]); + } else if (!child) { + // For the child + mlibc::sys_sigaction(SIGINT, &old_int, nullptr); + mlibc::sys_sigaction(SIGQUIT, &old_quit, nullptr); + mlibc::sys_sigprocmask(SIG_SETMASK, &old_mask, nullptr); + + mlibc::sys_close(fds[parent_end]); + + if (mlibc::sys_dup2(fds[child_end], 0, is_write ? 0 : 1)) { + __ensure(!"sys_dup2() failed in popen()"); + } + mlibc::sys_close(fds[child_end]); + + const char *args[] = { + "sh", "-c", command, nullptr + }; + + mlibc::sys_execve("/bin/sh", const_cast<char **>(args), environ); + _Exit(127); + } else { + // For the parent + mlibc::sys_close(fds[child_end]); + + ret = frg::construct<popen_file>( + getAllocator(), + fds[parent_end], + mlibc::file_dispose_cb<popen_file> + ); + __ensure(ret); + + auto file = static_cast<popen_file *>(ret); + + file->set_popen_pid(child); + + if (cloexec == true) { + fcntl(file->fd(), F_SETFD, O_CLOEXEC); + } + } + + mlibc::sys_sigaction(SIGINT, &old_int, nullptr); + mlibc::sys_sigaction(SIGQUIT, &old_quit, nullptr); + mlibc::sys_sigprocmask(SIG_SETMASK, &old_mask, nullptr); + + return ret; +} + +FILE *open_memstream(char **buf, size_t *sizeloc) { + return frg::construct<mlibc::memstream_mem_file>(getAllocator(), buf, sizeloc, O_RDWR, + mlibc::file_dispose_cb<mlibc::mem_file>); +} + +int fseeko(FILE *file_base, off_t offset, int whence) { + auto file = static_cast<mlibc::abstract_file *>(file_base); + if(int e = file->seek(offset, whence); e) { + errno = e; + return -1; + } + return 0; +} + +off_t ftello(FILE *file_base) { + auto file = static_cast<mlibc::abstract_file *>(file_base); + off_t current_offset; + if(int e = file->tell(¤t_offset); e) { + errno = e; + return -1; + } + return current_offset; +} + +int dprintf(int fd, const char *format, ...) { + va_list args; + va_start(args, format); + int result = vdprintf(fd, format, args); + va_end(args); + return result; +} + +int vdprintf(int fd, const char *format, __builtin_va_list args) { + mlibc::fd_file file{fd}; + int ret = vfprintf(&file, format, args); + file.flush(); + return ret; +} + +char *fgetln(FILE *, size_t *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +FILE *fopencookie(void *cookie, const char *__restrict mode, cookie_io_functions_t funcs) { + int flags = mlibc::fd_file::parse_modestring(mode); + + return frg::construct<mlibc::cookie_file>(getAllocator(), cookie, flags, funcs, + mlibc::file_dispose_cb<mlibc::cookie_file>); +} diff --git a/lib/mlibc/options/posix/generic/posix_stdlib.cpp b/lib/mlibc/options/posix/generic/posix_stdlib.cpp new file mode 100644 index 0000000..4010998 --- /dev/null +++ b/lib/mlibc/options/posix/generic/posix_stdlib.cpp @@ -0,0 +1,513 @@ + +#include <abi-bits/fcntl.h> +#include <bits/ensure.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> + +#include <frg/small_vector.hpp> +#include <mlibc/allocator.hpp> +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> +#include <mlibc/rtdl-config.hpp> + +namespace { + constexpr bool debugPathResolution = false; +} + +// Borrowed from musl +static uint32_t init[] = { +0x00000000,0x5851f42d,0xc0b18ccf,0xcbb5f646, +0xc7033129,0x30705b04,0x20fd5db4,0x9a8b7f78, +0x502959d8,0xab894868,0x6c0356a7,0x88cdb7ff, +0xb477d43f,0x70a3a52b,0xa8e4baf1,0xfd8341fc, +0x8ae16fd9,0x742d2f7a,0x0d1f0796,0x76035e09, +0x40f7702c,0x6fa72ca5,0xaaa84157,0x58a0df74, +0xc74a0364,0xae533cc4,0x04185faf,0x6de3b115, +0x0cab8628,0xf043bfa4,0x398150e9,0x37521657}; + +static int n = 31; +static int i = 3; +static int j = 0; +static uint32_t *x = init + 1; + + +static uint32_t lcg31(uint32_t x) { + return (1103515245 * x + 12345) & 0x7fffffff; +} + +static uint64_t lcg64(uint64_t x) { + return 6364136223846793005ull * x + 1; +} + +static void *savestate(void) { + x[-1] = (n << 16) | (i << 8) | j; + return x - 1; +} + +static void loadstate(uint32_t *state) { + x = state + 1; + n = x[-1] >> 16; + i = (x[-1] >> 8) & 0xff; + j = x[-1] & 0xff; +} + +long random(void) { + long k; + + if(n == 0) { + k = x[0] = lcg31(x[0]); + return k; + } + x[i] += x[j]; + k = x[i] >> 1; + if(++i == n) + i = 0; + if(++j == n) + j = 0; + + return k; +} + +double drand48(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void srand48(long int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +// Borrowed from musl +void srandom(unsigned int seed) { + int k; + uint64_t s = seed; + + if(n == 0) { + x[0] = s; + return; + } + i = n == 31 || n == 7 ? 3 : 1; + j = 0; + for(k = 0; k < n; k++) { + s = lcg64(s); + x[k] = s >> 32; + } + // Make sure x contains at least one odd number + x[0] |= 1; +} + +char *initstate(unsigned int seed, char *state, size_t size) { + void *old; + + if(size < 8) + return 0; + old = savestate(); + if(size < 32) + n = 0; + else if(size < 64) + n = 7; + else if(size < 128) + n = 15; + else if(size < 256) + n = 31; + else + n = 63; + x = (uint32_t *)state + 1; + srandom(seed); + savestate(); + return (char *)old; +} + +char *setstate(char *state) { + void *old; + + old = savestate(); + loadstate((uint32_t *)state); + return (char *)old; +} + +// ---------------------------------------------------------------------------- +// Path handling. +// ---------------------------------------------------------------------------- + + +int mkostemps(char *pattern, int suffixlen, int flags) { + auto n = strlen(pattern); + if(n < (6 + static_cast<size_t>(suffixlen))) { + errno = EINVAL; + return -1; + } + + flags &= ~O_WRONLY; + + for(size_t i = 0; i < 6; i++) { + if(pattern[n - (6 + suffixlen) + i] == 'X') + continue; + errno = EINVAL; + return -1; + } + + // TODO: Do an exponential search. + for(size_t i = 0; i < 999999; i++) { + char sfx = pattern[n - suffixlen]; + __ensure(sprintf(pattern + (n - (6 + suffixlen)), "%06zu", i) == 6); + pattern[n - suffixlen] = sfx; + + int fd; + if(int e = mlibc::sys_open(pattern, O_RDWR | O_CREAT | O_EXCL | flags, S_IRUSR | S_IWUSR, &fd); !e) { + return fd; + }else if(e != EEXIST) { + errno = e; + return -1; + } + } + + errno = EEXIST; + return -1; +} + +int mkostemp(char *pattern, int flags) { + return mkostemps(pattern, flags, 0); +} + +int mkstemp(char *path) { + return mkostemp(path, 0); +} + +int mkstemps(char *pattern, int suffixlen) { + return mkostemps(pattern, suffixlen, 0); +} + +char *mkdtemp(char *pattern) { + mlibc::infoLogger() << "mlibc mkdtemp(" << pattern << ") called" << frg::endlog; + auto n = strlen(pattern); + __ensure(n >= 6); + if(n < 6) { + errno = EINVAL; + return NULL; + } + for(size_t i = 0; i < 6; i++) { + if(pattern[n - 6 + i] == 'X') + continue; + errno = EINVAL; + return NULL; + } + + // TODO: Do an exponential search. + for(size_t i = 0; i < 999999; i++) { + __ensure(sprintf(pattern + (n - 6), "%06zu", i) == 6); + if(int e = mlibc::sys_mkdir(pattern, S_IRWXU); !e) { + return pattern; + }else if(e != EEXIST) { + errno = e; + return NULL; + } + } + + errno = EEXIST; + return NULL; +} + +char *realpath(const char *path, char *out) { + if(debugPathResolution) + mlibc::infoLogger() << "mlibc realpath(): Called on '" << path << "'" << frg::endlog; + frg::string_view path_view{path}; + + // In case of the root, the string only contains the null-terminator. + frg::small_vector<char, PATH_MAX, MemoryAllocator> resolv{getAllocator()}; + size_t ps; + + // If the path is relative, we have to preprend the working directory. + if(path[0] == '/') { + resolv.push_back(0); + ps = 1; + }else{ + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getcwd, nullptr); + + // Try to getcwd() until the buffer is large enough. + resolv.resize(128); + while(true) { + int e = mlibc::sys_getcwd(resolv.data(), resolv.size()); + if(e == ERANGE) { + resolv.resize(2 * resolv.size()); + }else if(!e) { + break; + }else{ + errno = e; + return nullptr; + } + } + frg::string_view cwd_view{resolv.data()}; + if(cwd_view == "/") { + // Restore our invariant that we only store the null-terminator for the root. + resolv.resize(1); + resolv[0] = 0; + }else{ + resolv.resize(cwd_view.size() + 1); + } + ps = 0; + } + + // Contains unresolved links as a relative path compared to resolv. + frg::small_vector<char, PATH_MAX, MemoryAllocator> lnk{getAllocator()}; + size_t ls = 0; + + auto process_segment = [&] (frg::string_view s_view) -> int { + if(debugPathResolution) + mlibc::infoLogger() << "mlibc realpath(): resolv is '" << resolv.data() << "'" + << ", segment is " << s_view.data() + << ", size: " << s_view.size() << frg::endlog; + + if(!s_view.size() || s_view == ".") { + // Keep resolv invariant. + return 0; + }else if(s_view == "..") { + // Remove a single segment from resolv. + if(resolv.size() > 1) { + auto slash = strrchr(resolv.data(), '/'); + __ensure(slash); // We never remove the leading sla. + resolv.resize((slash - resolv.data()) + 1); + *slash = 0; // Replace the slash by a null-terminator. + } + return 0; + } + + // Append the segment to resolv. + auto rsz = resolv.size(); + resolv[rsz - 1] = '/'; // Replace null-terminator by a slash. + resolv.resize(rsz + s_view.size() + 1); + memcpy(resolv.data() + rsz, s_view.data(), s_view.size()); + resolv[rsz + s_view.size()] = 0; + + // stat() the path to (1) see if it exists and (2) see if it is a link. + if(!mlibc::sys_stat) { + MLIBC_MISSING_SYSDEP(); + return ENOSYS; + } + if(debugPathResolution) + mlibc::infoLogger() << "mlibc realpath(): stat()ing '" + << resolv.data() << "'" << frg::endlog; + struct stat st; + if(int e = mlibc::sys_stat(mlibc::fsfd_target::path, + -1, resolv.data(), AT_SYMLINK_NOFOLLOW, &st); e) + return e; + + if(S_ISLNK(st.st_mode)) { + if(debugPathResolution) { + mlibc::infoLogger() << "mlibc realpath(): Encountered symlink '" + << resolv.data() << "'" << frg::endlog; + } + + if(!mlibc::sys_readlink) { + MLIBC_MISSING_SYSDEP(); + return ENOSYS; + } + + ssize_t sz = 0; + char path[512]; + + if (int e = mlibc::sys_readlink(resolv.data(), path, 512, &sz); e) + return e; + + if(debugPathResolution) { + mlibc::infoLogger() << "mlibc realpath(): Symlink resolves to '" + << frg::string_view{path, static_cast<size_t>(sz)} << "'" << frg::endlog; + } + + if (path[0] == '/') { + // Absolute path, replace resolv + resolv.resize(sz); + strncpy(resolv.data(), path, sz - 1); + resolv.data()[sz - 1] = 0; + + if(debugPathResolution) { + mlibc::infoLogger() << "mlibc realpath(): Symlink is absolute, resolv: '" + << resolv.data() << "'" << frg::endlog; + } + } else { + // Relative path, revert changes to resolv, prepend to lnk + resolv.resize(rsz); + resolv[rsz - 1] = 0; + + auto lsz = lnk.size(); + lnk.resize((lsz - ls) + sz + 1); + memmove(lnk.data() + sz, lnk.data() + ls, lsz - ls); + memcpy(lnk.data(), path, sz); + lnk[(lsz - ls) + sz] = 0; + + ls = 0; + + if(debugPathResolution) { + mlibc::infoLogger() << "mlibc realpath(): Symlink is relative, resolv: '" + << resolv.data() << "' lnk: '" + << frg::string_view{lnk.data(), lnk.size()} << "'" << frg::endlog; + } + } + } + + return 0; + }; + + // Each iteration of this outer loop consumes segment of the input path. + // This design avoids copying the input path into lnk; + // the latter could often involve additional allocations. + while(ps < path_view.size()) { + frg::string_view ps_view; + if(auto slash = strchr(path + ps, '/'); slash) { + ps_view = frg::string_view{path + ps, static_cast<size_t>(slash - (path + ps))}; + }else{ + ps_view = frg::string_view{path + ps, strlen(path) - ps}; + } + ps += ps_view.size() + 1; + + // Handle one segment from the input path. + if(int e = process_segment(ps_view); e) { + errno = e; + return nullptr; + } + + // This inner loop consumes segments of lnk. + while(ls < lnk.size()) { + frg::string_view ls_view; + if(auto slash = strchr(lnk.data() + ls, '/'); slash) { + ls_view = frg::string_view{lnk.data() + ls, static_cast<size_t>(slash - (lnk.data() + ls))}; + }else{ + ls_view = frg::string_view{lnk.data() + ls, strlen(lnk.data()) - ls}; + } + ls += ls_view.size() + 1; + + // Handle one segment from the link + if(int e = process_segment(ls_view); e) { + errno = e; + return nullptr; + } + } + + // All of lnk was consumed, reset it + lnk.resize(0); + ls = 0; + } + + if(resolv.size() == 1) { + resolv.resize(0); + resolv.push_back('/'); + resolv.push_back(0); + } + + if(debugPathResolution) + mlibc::infoLogger() << "mlibc realpath(): Returns '" << resolv.data() << "'" << frg::endlog; + + if(resolv.size() > PATH_MAX) { + errno = ENAMETOOLONG; + return nullptr; + } + + if(!out) + out = reinterpret_cast<char *>(getAllocator().allocate(resolv.size())); + strcpy(out, resolv.data()); + return out; +} + +// ---------------------------------------------------------------------------- +// Pseudoterminals +// ---------------------------------------------------------------------------- + +int ptsname_r(int fd, char *buffer, size_t length) { + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ptsname, ENOSYS); + + if(int e = sysdep(fd, buffer, length); e) + return e; + + return 0; +} + +char *ptsname(int fd) { + static char buffer[128]; + + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ptsname, NULL); + + if(int e = sysdep(fd, buffer, 128); e) { + errno = e; + return NULL; + } + + return buffer; +} + +int posix_openpt(int flags) { + int fd; + if(int e = mlibc::sys_open("/dev/ptmx", flags, 0, &fd); e) { + errno = e; + return -1; + } + + return fd; +} + +int unlockpt(int fd) { + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_unlockpt, -1); + + if(int e = sysdep(fd); e) { + errno = e; + return -1; + } + + return 0; +} + +int grantpt(int) { + return 0; +} + +double strtod_l(const char *__restrict__ nptr, char ** __restrict__ endptr, locale_t) { + mlibc::infoLogger() << "mlibc: strtod_l ignores locale!" << frg::endlog; + return strtod(nptr, endptr); +} + +long double strtold_l(const char *__restrict__, char ** __restrict__, locale_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +float strtof_l(const char *__restrict__ nptr, char **__restrict__ endptr, locale_t) { + mlibc::infoLogger() << "mlibc: strtof_l ignores locales" << frg::endlog; + return strtof(nptr, endptr); +} + +int strcoll_l(const char *, const char *, locale_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int getloadavg(double *, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +char *secure_getenv(const char *name) { + if (mlibc::rtdlConfig().secureRequired) + return NULL; + else + return getenv(name); +} + +void *reallocarray(void *ptr, size_t m, size_t n) { + if(n && m > -1 / n) { + errno = ENOMEM; + return 0; + } + + return realloc(ptr, m * n); +} + +char *canonicalize_file_name(const char *name) { + return realpath(name, NULL); +} diff --git a/lib/mlibc/options/posix/generic/posix_string.cpp b/lib/mlibc/options/posix/generic/posix_string.cpp new file mode 100644 index 0000000..d0bc7b5 --- /dev/null +++ b/lib/mlibc/options/posix/generic/posix_string.cpp @@ -0,0 +1,174 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <bits/ensure.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <signal.h> + +#include <mlibc/debug.hpp> + +char *strdup(const char *string) { + auto num_bytes = strlen(string); + + char *new_string = (char *)malloc(num_bytes + 1); + if(!new_string) // TODO: set errno + return nullptr; + + memcpy(new_string, string, num_bytes); + new_string[num_bytes] = 0; + return new_string; +} + +char *strndup(const char *string, size_t max_size) { + auto num_bytes = strnlen(string, max_size); + char *new_string = (char *)malloc(num_bytes + 1); + if(!new_string) // TODO: set errno + return nullptr; + + memcpy(new_string, string, num_bytes); + new_string[num_bytes] = 0; + return new_string; +} + +char *stpcpy(char *__restrict dest, const char *__restrict src) { + auto n = strlen(src); + memcpy(dest, src, n + 1); + return dest + n; +} + +char *stpncpy(char *__restrict dest, const char *__restrict src, size_t n) { + size_t nulls, copied, srcLen = strlen(src); + if (n >= srcLen) { + nulls = n - srcLen; + copied = srcLen; + } else { + nulls = 0; + copied = n; + } + + memcpy(dest, src, copied); + memset(dest + srcLen, 0, nulls); + return dest + n - nulls; +} + +size_t strnlen(const char *s, size_t n) { + size_t len = 0; + while(len < n && s[len]) + ++len; + return len; +} + +char *strsep(char **m, const char *del) { + __ensure(m); + + auto tok = *m; + if(!tok) + return nullptr; + + // Replace the following delimiter by a null-terminator. + // After this loop: *p is null iff we reached the end of the string. + auto p = tok; + while(*p && !strchr(del, *p)) + p++; + + if(*p) { + *p = 0; + *m = p + 1; + }else{ + *m = nullptr; + } + return tok; +} + +char *strsignal(int sig) { + #define CASE_FOR(sigconst) case sigconst: s = #sigconst; break; + const char *s; + switch(sig) { + CASE_FOR(SIGABRT) + CASE_FOR(SIGFPE) + CASE_FOR(SIGILL) + CASE_FOR(SIGINT) + CASE_FOR(SIGSEGV) + CASE_FOR(SIGTERM) + CASE_FOR(SIGPROF) + CASE_FOR(SIGIO) + CASE_FOR(SIGPWR) + CASE_FOR(SIGALRM) + CASE_FOR(SIGBUS) + CASE_FOR(SIGCHLD) + CASE_FOR(SIGCONT) + CASE_FOR(SIGHUP) + CASE_FOR(SIGKILL) + CASE_FOR(SIGPIPE) + CASE_FOR(SIGQUIT) + CASE_FOR(SIGSTOP) + CASE_FOR(SIGTSTP) + CASE_FOR(SIGTTIN) + CASE_FOR(SIGTTOU) + CASE_FOR(SIGUSR1) + CASE_FOR(SIGUSR2) + CASE_FOR(SIGSYS) + CASE_FOR(SIGTRAP) + CASE_FOR(SIGURG) + CASE_FOR(SIGVTALRM) + CASE_FOR(SIGXCPU) + CASE_FOR(SIGXFSZ) + CASE_FOR(SIGWINCH) + default: + mlibc::infoLogger() << "mlibc: Unknown signal number " << sig << frg::endlog; + s = "Unknown signal number"; + } + return const_cast<char *>(s); +} + +char *strcasestr(const char *s, const char *pattern) { + size_t plen = strlen(pattern); + const char *p = s; + while(*p) { + // Need strncasecmp() to avoid checking past the end of a successful match. + if(!strncasecmp(p, pattern, plen)) + return const_cast<char *>(p); + ++p; + } + return nullptr; +} + +void *memccpy(void *__restrict, const void *__restrict, int, size_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +// This implementation was taken from musl +void *memrchr(const void *m, int c, size_t n) { + const unsigned char *s = (const unsigned char *)m; + c = (unsigned char)c; + while(n--) { + if(s[n] == c) + return (void *)(s + n); + } + return 0; +} + +// BSD extensions. +// Taken from musl +size_t strlcpy(char *d, const char *s, size_t n) { + char *d0 = d; + + if(!n--) + goto finish; + for(; n && (*d=*s); n--, s++, d++); + *d = 0; +finish: + return d-d0 + strlen(s); +} + +size_t strlcat(char *d, const char *s, size_t n) { + size_t l = strnlen(d, n); + if(l == n) { + return l + strlen(s); + } + return l + strlcpy(d + l, s, n - l); +} diff --git a/lib/mlibc/options/posix/generic/posix_time.cpp b/lib/mlibc/options/posix/generic/posix_time.cpp new file mode 100644 index 0000000..d93ebbc --- /dev/null +++ b/lib/mlibc/options/posix/generic/posix_time.cpp @@ -0,0 +1,32 @@ +#include <bits/posix/posix_time.h> +#include <bits/ensure.h> +#include <mlibc/posix-sysdeps.hpp> +#include <errno.h> + +int utimes(const char *filename, const struct timeval times[2]) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_utimensat, -1); + struct timespec time[2]; + if(times == nullptr) { + time[0].tv_sec = UTIME_NOW; + time[0].tv_nsec = UTIME_NOW; + time[1].tv_sec = UTIME_NOW; + time[1].tv_nsec = UTIME_NOW; + } else { + time[0].tv_sec = times[0].tv_sec; + time[0].tv_nsec = times[0].tv_usec * 1000; + time[1].tv_sec = times[1].tv_sec; + time[1].tv_nsec = times[1].tv_usec * 1000; + } + + if (int e = mlibc::sys_utimensat(AT_FDCWD, filename, time, 0); e) { + errno = e; + return -1; + } + + return 0; +} + +int futimes(int, const struct timeval[2]) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/pthread-stubs.cpp b/lib/mlibc/options/posix/generic/pthread-stubs.cpp new file mode 100644 index 0000000..9720bc2 --- /dev/null +++ b/lib/mlibc/options/posix/generic/pthread-stubs.cpp @@ -0,0 +1,1426 @@ + +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <unistd.h> +#include <errno.h> +#include <inttypes.h> + +#include <bits/ensure.h> +#include <frg/allocation.hpp> +#include <frg/array.hpp> +#include <mlibc/allocator.hpp> +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> +#include <mlibc/thread.hpp> +#include <mlibc/tcb.hpp> +#include <mlibc/tid.hpp> +#include <mlibc/threads.hpp> + +static bool enableTrace = false; + +struct ScopeTrace { + ScopeTrace(const char *file, int line, const char *function) + : _file(file), _line(line), _function(function) { + if(!enableTrace) + return; + mlibc::infoLogger() << "trace: Enter scope " + << _file << ":" << _line << " (in function " + << _function << ")" << frg::endlog; + } + + ~ScopeTrace() { + if(!enableTrace) + return; + mlibc::infoLogger() << "trace: Exit scope" << frg::endlog; + } + +private: + const char *_file; + int _line; + const char *_function; +}; + +#define SCOPE_TRACE() ScopeTrace(__FILE__, __LINE__, __FUNCTION__) + +static constexpr unsigned int mutexRecursive = 1; +static constexpr unsigned int mutexErrorCheck = 2; + +// TODO: either use uint32_t or determine the bit based on sizeof(int). +static constexpr unsigned int mutex_owner_mask = (static_cast<uint32_t>(1) << 30) - 1; +static constexpr unsigned int mutex_waiters_bit = static_cast<uint32_t>(1) << 31; + +// Only valid for the internal __mlibc_m mutex of wrlocks. +static constexpr unsigned int mutex_excl_bit = static_cast<uint32_t>(1) << 30; + +static constexpr unsigned int rc_count_mask = (static_cast<uint32_t>(1) << 31) - 1; +static constexpr unsigned int rc_waiters_bit = static_cast<uint32_t>(1) << 31; + +static constexpr size_t default_stacksize = 0x200000; +static constexpr size_t default_guardsize = 4096; + +// ---------------------------------------------------------------------------- +// pthread_attr and pthread functions. +// ---------------------------------------------------------------------------- + +// pthread_attr functions. +int pthread_attr_init(pthread_attr_t *attr) { + *attr = pthread_attr_t{}; + attr->__mlibc_stacksize = default_stacksize; + attr->__mlibc_guardsize = default_guardsize; + attr->__mlibc_detachstate = PTHREAD_CREATE_JOINABLE; + return 0; +} + +int pthread_attr_destroy(pthread_attr_t *) { + return 0; +} + +int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) { + *detachstate = attr->__mlibc_detachstate; + return 0; +} +int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) { + if (detachstate != PTHREAD_CREATE_DETACHED && + detachstate != PTHREAD_CREATE_JOINABLE) + return EINVAL; + + attr->__mlibc_detachstate = detachstate; + return 0; +} + +int pthread_attr_getstacksize(const pthread_attr_t *__restrict attr, size_t *__restrict stacksize) { + *stacksize = attr->__mlibc_stacksize; + return 0; +} + +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) { + if (stacksize < PTHREAD_STACK_MIN) + return EINVAL; + attr->__mlibc_stacksize = stacksize; + return 0; +} + +int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) { + *stackaddr = attr->__mlibc_stackaddr; + return 0; +} +int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) { + attr->__mlibc_stackaddr = stackaddr; + return 0; +} + +int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize) { + *stackaddr = attr->__mlibc_stackaddr; + *stacksize = attr->__mlibc_stacksize; + return 0; +} +int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize) { + if (stacksize < PTHREAD_STACK_MIN) + return EINVAL; + attr->__mlibc_stacksize = stacksize; + attr->__mlibc_stackaddr = stackaddr; + return 0; +} + +int pthread_attr_getguardsize(const pthread_attr_t *__restrict attr, size_t *__restrict guardsize) { + *guardsize = attr->__mlibc_guardsize; + return 0; +} +int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) { + attr->__mlibc_guardsize = guardsize; + return 0; +} + +int pthread_attr_getscope(const pthread_attr_t *attr, int *scope) { + *scope = attr->__mlibc_scope; + return 0; +} +int pthread_attr_setscope(pthread_attr_t *attr, int scope) { + if (scope != PTHREAD_SCOPE_SYSTEM && + scope != PTHREAD_SCOPE_PROCESS) + return EINVAL; + if (scope == PTHREAD_SCOPE_PROCESS) + return ENOTSUP; + attr->__mlibc_scope = scope; + return 0; +} + +int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched) { + *inheritsched = attr->__mlibc_inheritsched; + return 0; +} +int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched) { + if (inheritsched != PTHREAD_INHERIT_SCHED && + inheritsched != PTHREAD_EXPLICIT_SCHED) + return EINVAL; + attr->__mlibc_inheritsched = inheritsched; + return 0; +} + +int pthread_attr_getschedparam(const pthread_attr_t *__restrict attr, struct sched_param *__restrict schedparam) { + *schedparam = attr->__mlibc_schedparam; + return 0; +} +int pthread_attr_setschedparam(pthread_attr_t *__restrict attr, const struct sched_param *__restrict schedparam) { + // TODO: this is supposed to return EINVAL for when the schedparam doesn't make sense + // for the given schedpolicy. + attr->__mlibc_schedparam = *schedparam; + return 0; +} + +int pthread_attr_getschedpolicy(const pthread_attr_t *__restrict attr, int *__restrict policy) { + *policy = attr->__mlibc_schedpolicy; + return 0; +} +int pthread_attr_setschedpolicy(pthread_attr_t *__restrict attr, int policy) { + if (policy != SCHED_FIFO && policy != SCHED_RR && + policy != SCHED_OTHER) + return EINVAL; + attr->__mlibc_schedpolicy = policy; + return 0; +} + +#if __MLIBC_LINUX_OPTION +int pthread_attr_getaffinity_np(const pthread_attr_t *__restrict attr, + size_t cpusetsize, cpu_set_t *__restrict cpusetp) { + if (!attr) + return EINVAL; + + if (!attr->__mlibc_cpuset) { + memset(cpusetp, -1, cpusetsize); + return 0; + } + + for (size_t cnt = cpusetsize; cnt < attr->__mlibc_cpusetsize; cnt++) + if (reinterpret_cast<char*>(attr->__mlibc_cpuset)[cnt] != '\0') + return ERANGE; + + auto p = memcpy(cpusetp, attr->__mlibc_cpuset, + std::min(cpusetsize, attr->__mlibc_cpusetsize)); + if (cpusetsize > attr->__mlibc_cpusetsize) + memset(p, '\0', cpusetsize - attr->__mlibc_cpusetsize); + + return 0; +} + +int pthread_attr_setaffinity_np(pthread_attr_t *__restrict attr, + size_t cpusetsize, const cpu_set_t *__restrict cpusetp) { + if (!attr) + return EINVAL; + + if (!cpusetp || !cpusetsize) { + attr->__mlibc_cpuset = NULL; + attr->__mlibc_cpusetsize = 0; + return 0; + } + + if (attr->__mlibc_cpusetsize != cpusetsize) { + auto newp = realloc(attr->__mlibc_cpuset, cpusetsize); + if (!newp) + return ENOMEM; + + attr->__mlibc_cpuset = static_cast<cpu_set_t*>(newp); + attr->__mlibc_cpusetsize = cpusetsize; + } + + memcpy(attr->__mlibc_cpuset, cpusetp, cpusetsize); + return 0; +} + +int pthread_attr_getsigmask_np(const pthread_attr_t *__restrict attr, + sigset_t *__restrict sigmask) { + if (!attr) + return EINVAL; + + if (!attr->__mlibc_sigmaskset) { + sigemptyset(sigmask); + return PTHREAD_ATTR_NO_SIGMASK_NP; + } + + *sigmask = attr->__mlibc_sigmask; + + return 0; +} +int pthread_attr_setsigmask_np(pthread_attr_t *__restrict attr, + const sigset_t *__restrict sigmask) { + if (!attr) + return EINVAL; + + if (!sigmask) { + attr->__mlibc_sigmaskset = 0; + return 0; + } + + attr->__mlibc_sigmask = *sigmask; + attr->__mlibc_sigmaskset = 1; + + // Filter out internally used signals. + sigdelset(&attr->__mlibc_sigmask, SIGCANCEL); + + return 0; +} + +namespace { + void get_own_stackinfo(void **stack_addr, size_t *stack_size) { + auto fp = fopen("/proc/self/maps", "r"); + if (!fp) { + mlibc::infoLogger() << "mlibc pthreads: /proc/self/maps does not exist! Producing incorrect" + " stack results!" << frg::endlog; + return; + } + + char line[256]; + auto sp = mlibc::get_sp(); + while (fgets(line, 256, fp)) { + uintptr_t from, to; + if(sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &from, &to) != 2) + continue; + if (sp < to && sp > from) { + // We need to return the lowest byte of the stack. + *stack_addr = reinterpret_cast<void*>(from); + *stack_size = to - from; + fclose(fp); + return; + } + } + + fclose(fp); + } +} + +int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) { + auto tcb = reinterpret_cast<Tcb*>(thread); + *attr = pthread_attr_t{}; + + if (!tcb->stackAddr || !tcb->stackSize) { + get_own_stackinfo(&attr->__mlibc_stackaddr, &attr->__mlibc_stacksize); + } else { + attr->__mlibc_stacksize = tcb->stackSize; + attr->__mlibc_stackaddr = tcb->stackAddr; + } + + attr->__mlibc_guardsize = tcb->guardSize; + attr->__mlibc_detachstate = tcb->isJoinable ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED; + mlibc::infoLogger() << "pthread_getattr_np(): Implementation is incomplete!" << frg::endlog; + return 0; +} + +int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *mask) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getthreadaffinity, ENOSYS); + return mlibc::sys_getthreadaffinity(reinterpret_cast<Tcb*>(thread)->tid, cpusetsize, mask); +} + +int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *mask) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setthreadaffinity, ENOSYS); + return mlibc::sys_setthreadaffinity(reinterpret_cast<Tcb*>(thread)->tid, cpusetsize, mask); +} +#endif // __MLIBC_LINUX_OPTION + +extern "C" Tcb *__rtdl_allocateTcb(); + +// pthread functions. +int pthread_create(pthread_t *__restrict thread, const pthread_attr_t *__restrict attrp, + void *(*entry) (void *), void *__restrict user_arg) { + return mlibc::thread_create(thread, attrp, reinterpret_cast<void *>(entry), user_arg, false); +} + +pthread_t pthread_self(void) { + return reinterpret_cast<pthread_t>(mlibc::get_current_tcb()); +} + +int pthread_equal(pthread_t t1, pthread_t t2) { + if(t1 == t2) + return 1; + return 0; +} + +namespace { + struct key_global_info { + bool in_use; + + void (*dtor)(void *); + uint64_t generation; + }; + + constinit frg::array< + key_global_info, + PTHREAD_KEYS_MAX + > key_globals_{}; + + FutexLock key_mutex_; +} + +namespace mlibc { + __attribute__ ((__noreturn__)) void do_exit() { + sys_thread_exit(); + __builtin_unreachable(); + } +} + +__attribute__ ((__noreturn__)) void pthread_exit(void *ret_val) { + auto self = mlibc::get_current_tcb(); + + if (__atomic_load_n(&self->cancelBits, __ATOMIC_RELAXED) & tcbExitingBit) + mlibc::do_exit(); + + __atomic_fetch_or(&self->cancelBits, tcbExitingBit, __ATOMIC_RELAXED); + + auto hand = self->cleanupEnd; + while (hand) { + auto old = hand; + hand->func(hand->arg); + hand = hand->prev; + frg::destruct(getAllocator(), old); + } + + for (size_t j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { + for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++) { + if (auto v = pthread_getspecific(i)) { + key_mutex_.lock(); + auto dtor = key_globals_[i].dtor; + key_mutex_.unlock(); + + if (dtor) { + dtor(v); + (*self->localKeys)[i].value = nullptr; + } + } + } + } + + self->returnValue.voidPtr = ret_val; + __atomic_store_n(&self->didExit, 1, __ATOMIC_RELEASE); + mlibc::sys_futex_wake(&self->didExit); + + // TODO: clean up thread resources when we are detached. + + // TODO: do exit(0) when we're the only thread instead + mlibc::do_exit(); +} + +int pthread_join(pthread_t thread, void **ret) { + return mlibc::thread_join(thread, ret); +} + +int pthread_detach(pthread_t thread) { + auto tcb = reinterpret_cast<Tcb*>(thread); + if (!__atomic_load_n(&tcb->isJoinable, __ATOMIC_RELAXED)) + return EINVAL; + + int expected = 1; + if(!__atomic_compare_exchange_n(&tcb->isJoinable, &expected, 0, false, __ATOMIC_RELEASE, + __ATOMIC_RELAXED)) + return EINVAL; + + return 0; +} + +void pthread_cleanup_push(void (*func) (void *), void *arg) { + auto self = mlibc::get_current_tcb(); + + auto hand = frg::construct<Tcb::CleanupHandler>(getAllocator()); + hand->func = func; + hand->arg = arg; + hand->next = nullptr; + hand->prev = self->cleanupEnd; + + if (self->cleanupEnd) + self->cleanupEnd->next = hand; + + self->cleanupEnd = hand; + + if (!self->cleanupBegin) + self->cleanupBegin = self->cleanupEnd; +} + +void pthread_cleanup_pop(int execute) { + auto self = mlibc::get_current_tcb(); + + auto hand = self->cleanupEnd; + + if (self->cleanupEnd) + self->cleanupEnd = self->cleanupEnd->prev; + if (self->cleanupEnd) + self->cleanupEnd->next = nullptr; + + if (execute) + hand->func(hand->arg); + + frg::destruct(getAllocator(), hand); +} + +int pthread_setname_np(pthread_t thread, const char *name) { + auto tcb = reinterpret_cast<Tcb*>(thread); + + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_thread_setname, ENOSYS); + if(int e = sysdep(tcb, name); e) { + return e; + } + + return 0; +} + +int pthread_getname_np(pthread_t thread, char *name, size_t size) { + auto tcb = reinterpret_cast<Tcb*>(thread); + + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_thread_getname, ENOSYS); + if(int e = sysdep(tcb, name, size); e) { + return e; + } + + return 0; +} + +int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param) { + auto tcb = reinterpret_cast<Tcb*>(thread); + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setschedparam, ENOSYS); + if(int e = mlibc::sys_setschedparam(tcb, policy, param); e) { + return e; + } + + return 0; +} + +int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param) { + auto tcb = reinterpret_cast<Tcb*>(thread); + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getschedparam, ENOSYS); + if(int e = mlibc::sys_getschedparam(tcb, policy, param); e) { + return e; + } + + return 0; +} + +//pthread cancel functions + +extern "C" void __mlibc_do_cancel() { + //TODO(geert): for now the same as pthread_exit() + pthread_exit(PTHREAD_CANCELED); +} + +namespace { + + void sigcancel_handler(int signal, siginfo_t *info, void *ucontext) { + ucontext_t *uctx = static_cast<ucontext_t*>(ucontext); + // The function could be called from other signals, or from another + // process, in which case we should do nothing. + if (signal != SIGCANCEL || info->si_pid != getpid() || + info->si_code != SI_TKILL) + return; + + auto tcb = reinterpret_cast<Tcb*>(mlibc::get_current_tcb()); + int old_value = tcb->cancelBits; + + /* + * When a thread is marked with deferred cancellation and performs a blocking syscall, + * the spec mandates that the syscall can get interrupted before it has caused any side + * effects (e.g. before a read() has read any bytes from disk). If the syscall has + * already caused side effects it should return its partial work, and set the program + * counter just after the syscall. If the syscall hasn't caused any side effects, it + * should fail with EINTR and set the program counter to the syscall instruction. + * + * cancellable_syscall: + * test whether_a_cancel_is_queued + * je cancel + * syscall + * end_cancellable_syscall + * + * The mlibc::sys_before_cancellable_syscall sysdep should return 1 when the + * program counter is between the 'canellable_syscall' and 'end_cancellable_syscall' label. + */ + if (!(old_value & tcbCancelAsyncBit) && + mlibc::sys_before_cancellable_syscall && !mlibc::sys_before_cancellable_syscall(uctx)) + return; + + int bitmask = tcbCancelTriggerBit | tcbCancelingBit; + while (1) { + int new_value = old_value | bitmask; + + // Check if we are already cancelled or exiting + if (old_value == new_value || old_value & tcbExitingBit) + return; + + int current_value = old_value; + if (__atomic_compare_exchange_n(&tcb->cancelBits, ¤t_value, + new_value, true,__ATOMIC_RELAXED, __ATOMIC_RELAXED)) { + tcb->returnValue.voidPtr = PTHREAD_CANCELED; + + // Perform cancellation + __mlibc_do_cancel(); + + break; + } + + old_value = current_value; + } + } +} + +namespace mlibc { +namespace { + +struct PthreadSignalInstaller { + PthreadSignalInstaller() { + struct sigaction sa; + sa.sa_sigaction = sigcancel_handler; + sa.sa_flags = SA_SIGINFO; + auto e = ENOSYS; + if(sys_sigaction) + e = sys_sigaction(SIGCANCEL, &sa, NULL); + // Opt-out of cancellation support. + if(e == ENOSYS) + return; + __ensure(!e); + } +}; + +PthreadSignalInstaller pthread_signal_installer; + +} // anonymous namespace +} // namespace mlibc + +int pthread_setcanceltype(int type, int *oldtype) { + if (type != PTHREAD_CANCEL_DEFERRED && type != PTHREAD_CANCEL_ASYNCHRONOUS) + return EINVAL; + + auto self = reinterpret_cast<Tcb *>(mlibc::get_current_tcb()); + int old_value = self->cancelBits; + while (1) { + int new_value = old_value & ~tcbCancelAsyncBit; + if (type == PTHREAD_CANCEL_ASYNCHRONOUS) + new_value |= tcbCancelAsyncBit; + + if (oldtype) + *oldtype = ((old_value & tcbCancelAsyncBit) + ? PTHREAD_CANCEL_ASYNCHRONOUS + : PTHREAD_CANCEL_DEFERRED); + + // Avoid unecessary atomic op. + if (old_value == new_value) + break; + + int current_value = old_value; + if (__atomic_compare_exchange_n(&self->cancelBits, ¤t_value, + new_value, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) { + + if (mlibc::tcb_async_cancelled(new_value)) + __mlibc_do_cancel(); + + break; + } + + old_value = current_value; + } + + return 0; +} +int pthread_setcancelstate(int state, int *oldstate) { + if (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE) + return EINVAL; + + auto self = reinterpret_cast<Tcb *>(mlibc::get_current_tcb()); + int old_value = self->cancelBits; + while (1) { + int new_value = old_value & ~tcbCancelEnableBit; + if (state == PTHREAD_CANCEL_ENABLE) + new_value |= tcbCancelEnableBit; + + if (oldstate) + *oldstate = ((old_value & tcbCancelEnableBit) + ? PTHREAD_CANCEL_ENABLE + : PTHREAD_CANCEL_DISABLE); + + // Avoid unecessary atomic op. + if (old_value == new_value) + break; + + int current_value = old_value; + if (__atomic_compare_exchange_n(&self->cancelBits, ¤t_value, + new_value, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) { + + if (mlibc::tcb_async_cancelled(new_value)) + __mlibc_do_cancel(); + + sigset_t set = {}; + sigaddset(&set, SIGCANCEL); + if (new_value & PTHREAD_CANCEL_ENABLE) + sigprocmask(SIG_UNBLOCK, &set, NULL); + else + sigprocmask(SIG_BLOCK, &set, NULL); + break; + } + + old_value = current_value; + } + + return 0; +} +void pthread_testcancel(void) { + auto self = reinterpret_cast<Tcb *>(mlibc::get_current_tcb()); + int value = self->cancelBits; + if ((value & tcbCancelEnableBit) && (value & tcbCancelTriggerBit)) { + __mlibc_do_cancel(); + __builtin_unreachable(); + } +} +int pthread_cancel(pthread_t thread) { + if (!mlibc::sys_tgkill) { + MLIBC_MISSING_SYSDEP(); + return ENOSYS; + } + + auto tcb = reinterpret_cast<Tcb *>(thread); + // Check if the TCB is valid, somewhat.. + if (tcb->selfPointer != tcb) + return ESRCH; + + int old_value = __atomic_load_n(&tcb->cancelBits, __ATOMIC_RELAXED); + while (1) { + int bitmask = tcbCancelTriggerBit; + + int new_value = old_value | bitmask; + if (old_value == new_value) + break; + + int current_value = old_value; + if (__atomic_compare_exchange_n(&tcb->cancelBits, ¤t_value, + new_value, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) { + if (mlibc::tcb_cancel_enabled(new_value)) { + pid_t pid = getpid(); + + int res = mlibc::sys_tgkill(pid, tcb->tid, SIGCANCEL); + + current_value = __atomic_load_n(&tcb->cancelBits, __ATOMIC_RELAXED); + + // If we can't find the thread anymore, it's possible that it exited between + // us setting the cancel trigger bit, and us sending the signal. Check the + // cancelBits for tcbExitingBit to confirm that. + // XXX(qookie): This will be an use-after-free once we start freeing TCBs on + // exit. Perhaps the TCB should be refcounted. + if (!(res == ESRCH && (current_value & tcbExitingBit))) + return res; + } + + break; + } + + old_value = current_value; + } + + return 0; +} + +int pthread_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void)) { + auto self = mlibc::get_current_tcb(); + + auto hand = frg::construct<Tcb::AtforkHandler>(getAllocator()); + if (!hand) + return -1; + + hand->prepare = prepare; + hand->parent = parent; + hand->child = child; + hand->next = nullptr; + hand->prev = self->atforkEnd; + + if (self->atforkEnd) + self->atforkEnd->next = hand; + + self->atforkEnd = hand; + + if (!self->atforkBegin) + self->atforkBegin = self->atforkEnd; + + return 0; +} + +// ---------------------------------------------------------------------------- +// pthread_key functions. +// ---------------------------------------------------------------------------- + +int pthread_key_create(pthread_key_t *out, void (*destructor)(void *)) { + SCOPE_TRACE(); + + auto g = frg::guard(&key_mutex_); + + pthread_key_t key = PTHREAD_KEYS_MAX; + for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++) { + if (!key_globals_[i].in_use) { + key = i; + break; + } + } + + if (key == PTHREAD_KEYS_MAX) + return EAGAIN; + + key_globals_[key].in_use = true; + key_globals_[key].dtor = destructor; + + *out = key; + + return 0; +} + +int pthread_key_delete(pthread_key_t key) { + SCOPE_TRACE(); + + auto g = frg::guard(&key_mutex_); + + if (key >= PTHREAD_KEYS_MAX || !key_globals_[key].in_use) + return EINVAL; + + key_globals_[key].in_use = false; + key_globals_[key].dtor = nullptr; + key_globals_[key].generation++; + + return 0; +} + +void *pthread_getspecific(pthread_key_t key) { + SCOPE_TRACE(); + + auto self = mlibc::get_current_tcb(); + auto g = frg::guard(&key_mutex_); + + if (key >= PTHREAD_KEYS_MAX || !key_globals_[key].in_use) + return nullptr; + + if (key_globals_[key].generation > (*self->localKeys)[key].generation) { + (*self->localKeys)[key].value = nullptr; + (*self->localKeys)[key].generation = key_globals_[key].generation; + } + + return (*self->localKeys)[key].value; +} + +int pthread_setspecific(pthread_key_t key, const void *value) { + SCOPE_TRACE(); + + auto self = mlibc::get_current_tcb(); + auto g = frg::guard(&key_mutex_); + + if (key >= PTHREAD_KEYS_MAX || !key_globals_[key].in_use) + return EINVAL; + + (*self->localKeys)[key].value = const_cast<void *>(value); + (*self->localKeys)[key].generation = key_globals_[key].generation; + + return 0; +} + +// ---------------------------------------------------------------------------- +// pthread_once functions. +// ---------------------------------------------------------------------------- + +static constexpr unsigned int onceComplete = 1; +static constexpr unsigned int onceLocked = 2; + +int pthread_once(pthread_once_t *once, void (*function) (void)) { + SCOPE_TRACE(); + + auto expected = __atomic_load_n(&once->__mlibc_done, __ATOMIC_ACQUIRE); + + // fast path: the function was already run. + while(!(expected & onceComplete)) { + if(!expected) { + // try to acquire the mutex. + if(!__atomic_compare_exchange_n(&once->__mlibc_done, + &expected, onceLocked, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) + continue; + + function(); + + // unlock the mutex. + __atomic_exchange_n(&once->__mlibc_done, onceComplete, __ATOMIC_RELEASE); + if(int e = mlibc::sys_futex_wake((int *)&once->__mlibc_done); e) + __ensure(!"sys_futex_wake() failed"); + return 0; + }else{ + // a different thread is currently running the initializer. + __ensure(expected == onceLocked); + if(int e = mlibc::sys_futex_wait((int *)&once->__mlibc_done, onceLocked, nullptr); e) + __ensure(!"sys_futex_wait() failed"); + expected = __atomic_load_n(&once->__mlibc_done, __ATOMIC_ACQUIRE); + } + } + + return 0; +} + +// ---------------------------------------------------------------------------- +// pthread_mutexattr and pthread_mutex functions. +// ---------------------------------------------------------------------------- + +// pthread_mutexattr functions +int pthread_mutexattr_init(pthread_mutexattr_t *attr) { + SCOPE_TRACE(); + return mlibc::thread_mutexattr_init(attr); +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { + SCOPE_TRACE(); + return mlibc::thread_mutexattr_destroy(attr); +} + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *__restrict attr, int *__restrict type) { + return mlibc::thread_mutexattr_gettype(attr, type); +} + +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { + return mlibc::thread_mutexattr_settype(attr, type); +} + +int pthread_mutexattr_getrobust(const pthread_mutexattr_t *__restrict attr, + int *__restrict robust) { + *robust = attr->__mlibc_robust; + return 0; +} +int pthread_mutexattr_setrobust(pthread_mutexattr_t *attr, int robust) { + if (robust != PTHREAD_MUTEX_STALLED && robust != PTHREAD_MUTEX_ROBUST) + return EINVAL; + + attr->__mlibc_robust = robust; + return 0; +} + +int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared) { + *pshared = attr->__mlibc_pshared; + return 0; +} +int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) { + if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) + return EINVAL; + + attr->__mlibc_pshared = pshared; + return 0; +} + +int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *__restrict attr, + int *__restrict protocol) { + *protocol = attr->__mlibc_protocol; + return 0; +} + +int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol) { + if (protocol != PTHREAD_PRIO_NONE && protocol != PTHREAD_PRIO_INHERIT + && protocol != PTHREAD_PRIO_PROTECT) + return EINVAL; + + attr->__mlibc_protocol = protocol; + return 0; +} + +int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *__restrict attr, + int *__restrict prioceiling) { + (void)attr; + (void)prioceiling; + return EINVAL; +} + +int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int prioceiling) { + (void)attr; + (void)prioceiling; + return EINVAL; +} + +// pthread_mutex functions +int pthread_mutex_init(pthread_mutex_t *__restrict mutex, + const pthread_mutexattr_t *__restrict attr) { + SCOPE_TRACE(); + + return mlibc::thread_mutex_init(mutex, attr); +} + +int pthread_mutex_destroy(pthread_mutex_t *mutex) { + return mlibc::thread_mutex_destroy(mutex); +} + +int pthread_mutex_lock(pthread_mutex_t *mutex) { + SCOPE_TRACE(); + + return mlibc::thread_mutex_lock(mutex); +} + +int pthread_mutex_trylock(pthread_mutex_t *mutex) { + SCOPE_TRACE(); + + unsigned int this_tid = mlibc::this_tid(); + unsigned int expected = __atomic_load_n(&mutex->__mlibc_state, __ATOMIC_RELAXED); + if(!expected) { + // Try to take the mutex here. + if(__atomic_compare_exchange_n(&mutex->__mlibc_state, + &expected, this_tid, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) { + __ensure(!mutex->__mlibc_recursion); + mutex->__mlibc_recursion = 1; + return 0; + } + } else { + // If this (recursive) mutex is already owned by us, increment the recursion level. + if((expected & mutex_owner_mask) == this_tid) { + if(!(mutex->__mlibc_flags & mutexRecursive)) { + return EBUSY; + } + ++mutex->__mlibc_recursion; + return 0; + } + } + + return EBUSY; +} + +int pthread_mutex_timedlock(pthread_mutex_t *__restrict, + const struct timespec *__restrict) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int pthread_mutex_unlock(pthread_mutex_t *mutex) { + SCOPE_TRACE(); + + return mlibc::thread_mutex_unlock(mutex); +} + +int pthread_mutex_consistent(pthread_mutex_t *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +// ---------------------------------------------------------------------------- +// pthread_condattr and pthread_cond functions. +// ---------------------------------------------------------------------------- + +int pthread_condattr_init(pthread_condattr_t *attr) { + attr->__mlibc_pshared = PTHREAD_PROCESS_PRIVATE; + attr->__mlibc_clock = CLOCK_REALTIME; + return 0; +} + +int pthread_condattr_destroy(pthread_condattr_t *attr) { + memset(attr, 0, sizeof(*attr)); + return 0; +} + +int pthread_condattr_getclock(const pthread_condattr_t *__restrict attr, + clockid_t *__restrict clock) { + *clock = attr->__mlibc_clock; + return 0; +} + +int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock) { + if (clock != CLOCK_REALTIME && clock != CLOCK_MONOTONIC + && clock != CLOCK_MONOTONIC_RAW && clock != CLOCK_REALTIME_COARSE + && clock != CLOCK_MONOTONIC_COARSE && clock != CLOCK_BOOTTIME) + return EINVAL; + + attr->__mlibc_clock = clock; + return 0; +} + +int pthread_condattr_getpshared(const pthread_condattr_t *__restrict attr, + int *__restrict pshared) { + *pshared = attr->__mlibc_pshared; + return 0; +} + +int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) { + if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) + return EINVAL; + + attr->__mlibc_pshared = pshared; + return 0; +} + +int pthread_cond_init(pthread_cond_t *__restrict cond, const pthread_condattr_t *__restrict attr) { + SCOPE_TRACE(); + + return mlibc::thread_cond_init(cond, attr); +} + +int pthread_cond_destroy(pthread_cond_t *cond) { + SCOPE_TRACE(); + + return mlibc::thread_cond_destroy(cond); +} + +int pthread_cond_wait(pthread_cond_t *__restrict cond, pthread_mutex_t *__restrict mutex) { + return pthread_cond_timedwait(cond, mutex, nullptr); +} + +int pthread_cond_timedwait(pthread_cond_t *__restrict cond, pthread_mutex_t *__restrict mutex, + const struct timespec *__restrict abstime) { + return mlibc::thread_cond_timedwait(cond, mutex, abstime); +} + +int pthread_cond_signal(pthread_cond_t *cond) { + SCOPE_TRACE(); + + return pthread_cond_broadcast(cond); +} + +int pthread_cond_broadcast(pthread_cond_t *cond) { + SCOPE_TRACE(); + + return mlibc::thread_cond_broadcast(cond); +} + +// ---------------------------------------------------------------------------- +// pthread_barrierattr and pthread_barrier functions. +// ---------------------------------------------------------------------------- + +int pthread_barrierattr_init(pthread_barrierattr_t *attr) { + attr->__mlibc_pshared = PTHREAD_PROCESS_PRIVATE; + return 0; +} + +int pthread_barrierattr_getpshared(const pthread_barrierattr_t *__restrict attr, + int *__restrict pshared) { + *pshared = attr->__mlibc_pshared; + return 0; +} + +int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) { + if (pshared != PTHREAD_PROCESS_SHARED && pshared != PTHREAD_PROCESS_PRIVATE) + return EINVAL; + + attr->__mlibc_pshared = pshared; + return 0; +} + +int pthread_barrierattr_destroy(pthread_barrierattr_t *) { + return 0; +} + +int pthread_barrier_init(pthread_barrier_t *__restrict barrier, + const pthread_barrierattr_t *__restrict attr, unsigned count) { + if (count == 0) + return EINVAL; + + barrier->__mlibc_waiting = 0; + barrier->__mlibc_inside = 0; + barrier->__mlibc_seq = 0; + barrier->__mlibc_count = count; + + // Since we don't implement these yet, set a flag to error later. + auto pshared = attr ? attr->__mlibc_pshared : PTHREAD_PROCESS_PRIVATE; + barrier->__mlibc_flags = pshared; + + return 0; +} + +int pthread_barrier_destroy(pthread_barrier_t *barrier) { + // Wait until there are no threads still using the barrier. + unsigned inside = 0; + do { + unsigned expected = __atomic_load_n(&barrier->__mlibc_inside, __ATOMIC_RELAXED); + if (expected == 0) + break; + + int e = mlibc::sys_futex_wait((int *)&barrier->__mlibc_inside, expected, nullptr); + if (e != 0 && e != EAGAIN && e != EINTR) + mlibc::panicLogger() << "mlibc: sys_futex_wait() returned error " << e << frg::endlog; + } while (inside > 0); + + memset(barrier, 0, sizeof *barrier); + return 0; +} + +int pthread_barrier_wait(pthread_barrier_t *barrier) { + if (barrier->__mlibc_flags != 0) { + mlibc::panicLogger() << "mlibc: pthread_barrier_t flags were non-zero" + << frg::endlog; + } + + // inside is incremented on entry and decremented on exit. + // This is used to synchronise with pthread_barrier_destroy, to ensure that a thread doesn't pass + // the barrier and immediately destroy its state while other threads still rely on it. + + __atomic_fetch_add(&barrier->__mlibc_inside, 1, __ATOMIC_ACQUIRE); + + auto leave = [&](){ + unsigned inside = __atomic_sub_fetch(&barrier->__mlibc_inside, 1, __ATOMIC_RELEASE); + if (inside == 0) + mlibc::sys_futex_wake((int *)&barrier->__mlibc_inside); + }; + + unsigned seq = __atomic_load_n(&barrier->__mlibc_seq, __ATOMIC_ACQUIRE); + + while (true) { + unsigned expected = __atomic_load_n(&barrier->__mlibc_waiting, __ATOMIC_RELAXED); + bool swapped = __atomic_compare_exchange_n(&barrier->__mlibc_waiting, &expected, expected + 1, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); + + if (swapped) { + if (expected + 1 == barrier->__mlibc_count) { + // We were the last thread to hit the barrier. Reset waiters and wake the others. + __atomic_fetch_add(&barrier->__mlibc_seq, 1, __ATOMIC_ACQUIRE); + __atomic_store_n(&barrier->__mlibc_waiting, 0, __ATOMIC_RELEASE); + + mlibc::sys_futex_wake((int *)&barrier->__mlibc_seq); + + leave(); + return PTHREAD_BARRIER_SERIAL_THREAD; + } + + while (true) { + int e = mlibc::sys_futex_wait((int *)&barrier->__mlibc_seq, seq, nullptr); + if (e != 0 && e != EAGAIN && e != EINTR) + mlibc::panicLogger() << "mlibc: sys_futex_wait() returned error " << e << frg::endlog; + + unsigned newSeq = __atomic_load_n(&barrier->__mlibc_seq, __ATOMIC_ACQUIRE); + if (newSeq > seq) { + leave(); + return 0; + } + } + } + } +} + +// ---------------------------------------------------------------------------- +// pthread_rwlock functions. +// ---------------------------------------------------------------------------- + +namespace { + void rwlock_m_lock(pthread_rwlock_t *rw, bool excl) { + unsigned int m_expected = __atomic_load_n(&rw->__mlibc_m, __ATOMIC_RELAXED); + while(true) { + if(m_expected) { + __ensure(m_expected & mutex_owner_mask); + + // Try to set the waiters bit. + if(!(m_expected & mutex_waiters_bit)) { + unsigned int desired = m_expected | mutex_waiters_bit; + if(!__atomic_compare_exchange_n(&rw->__mlibc_m, + &m_expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) + continue; + } + + // Wait on the futex. + mlibc::sys_futex_wait((int *)&rw->__mlibc_m, m_expected | mutex_waiters_bit, nullptr); + + // Opportunistically try to take the lock after we wake up. + m_expected = 0; + }else{ + // Try to lock the mutex. + unsigned int desired = 1; + if(excl) + desired |= mutex_excl_bit; + if(__atomic_compare_exchange_n(&rw->__mlibc_m, + &m_expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) + break; + } + } + } + + int rwlock_m_trylock(pthread_rwlock_t *rw, bool excl) { + unsigned int m_expected = __atomic_load_n(&rw->__mlibc_m, __ATOMIC_RELAXED); + if(!m_expected) { + // Try to lock the mutex. + unsigned int desired = 1; + if(excl) + desired |= mutex_excl_bit; + if(__atomic_compare_exchange_n(&rw->__mlibc_m, + &m_expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) + return 0; + } + + __ensure(m_expected & mutex_owner_mask); + + // POSIX says that this function should never block but also that + // readers should not be blocked by readers. We implement this by returning EAGAIN + // (and not EBUSY) if a reader would block a reader. + if(!excl && !(m_expected & mutex_excl_bit)) + return EAGAIN; + + return EBUSY; + } + + void rwlock_m_unlock(pthread_rwlock_t *rw) { + auto m = __atomic_exchange_n(&rw->__mlibc_m, 0, __ATOMIC_RELEASE); + if(m & mutex_waiters_bit) + mlibc::sys_futex_wake((int *)&rw->__mlibc_m); + } +} + +int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) { + attr->__mlibc_pshared = PTHREAD_PROCESS_PRIVATE; + return 0; +} + +int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *__restrict attr, + int *__restrict pshared) { + *pshared = attr->__mlibc_pshared; + return 0; +} + +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) { + if (pshared != PTHREAD_PROCESS_SHARED && pshared != PTHREAD_PROCESS_PRIVATE) + return EINVAL; + + attr->__mlibc_pshared = pshared; + return 0; +} + +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *) { + return 0; +} + +int pthread_rwlock_init(pthread_rwlock_t *__restrict rw, const pthread_rwlockattr_t *__restrict attr) { + SCOPE_TRACE(); + rw->__mlibc_m = 0; + rw->__mlibc_rc = 0; + + // Since we don't implement this yet, set a flag to error later. + auto pshared = attr ? attr->__mlibc_pshared : PTHREAD_PROCESS_PRIVATE; + rw->__mlibc_flags = pshared; + return 0; +} + +int pthread_rwlock_destroy(pthread_rwlock_t *rw) { + __ensure(!rw->__mlibc_m); + __ensure(!rw->__mlibc_rc); + return 0; +} + +int pthread_rwlock_trywrlock(pthread_rwlock_t *rw) { + SCOPE_TRACE(); + + if (rw->__mlibc_flags != 0) { + mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero" + << frg::endlog; + } + + // Take the __mlibc_m mutex. + // Will be released in pthread_rwlock_unlock(). + if(int e = rwlock_m_trylock(rw, true)) + return e; + + // Check that there are no readers. + unsigned int rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_ACQUIRE); + if(rc_expected) { + rwlock_m_unlock(rw); + return EBUSY; + } + + return 0; +} + +int pthread_rwlock_wrlock(pthread_rwlock_t *rw) { + SCOPE_TRACE(); + + if (rw->__mlibc_flags != 0) { + mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero" + << frg::endlog; + } + + // Take the __mlibc_m mutex. + // Will be released in pthread_rwlock_unlock(). + rwlock_m_lock(rw, true); + + // Now wait until there are no more readers. + unsigned int rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_ACQUIRE); + while(true) { + if(!rc_expected) + break; + + __ensure(rc_expected & rc_count_mask); + + // Try to set the waiters bit. + if(!(rc_expected & rc_waiters_bit)) { + unsigned int desired = rc_expected | rc_count_mask; + if(!__atomic_compare_exchange_n(&rw->__mlibc_rc, + &rc_expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) + continue; + } + + // Wait on the futex. + mlibc::sys_futex_wait((int *)&rw->__mlibc_rc, rc_expected | rc_waiters_bit, nullptr); + + // Re-check the reader counter. + rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_ACQUIRE); + } + + return 0; +} + +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rw) { + SCOPE_TRACE(); + + if (rw->__mlibc_flags != 0) { + mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero" + << frg::endlog; + } + + // Increment the reader count while holding the __mlibc_m mutex. + if(int e = rwlock_m_trylock(rw, false); e) + return e; + __atomic_fetch_add(&rw->__mlibc_rc, 1, __ATOMIC_ACQUIRE); + rwlock_m_unlock(rw); + + return 0; +} + +int pthread_rwlock_rdlock(pthread_rwlock_t *rw) { + SCOPE_TRACE(); + + if (rw->__mlibc_flags != 0) { + mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero" + << frg::endlog; + } + + // Increment the reader count while holding the __mlibc_m mutex. + rwlock_m_lock(rw, false); + __atomic_fetch_add(&rw->__mlibc_rc, 1, __ATOMIC_ACQUIRE); + rwlock_m_unlock(rw); + + return 0; +} + +int pthread_rwlock_unlock(pthread_rwlock_t *rw) { + SCOPE_TRACE(); + + unsigned int rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_RELAXED); + if(!rc_expected) { + // We are doing a write-unlock. + rwlock_m_unlock(rw); + return 0; + }else{ + // We are doing a read-unlock. + while(true) { + unsigned int count = rc_expected & rc_count_mask; + __ensure(count); + + // Try to decrement the count. + if(count == 1 && (rc_expected & rc_waiters_bit)) { + unsigned int desired = 0; + if(!__atomic_compare_exchange_n(&rw->__mlibc_rc, + &rc_expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED)) + continue; + + // Wake the futex. + mlibc::sys_futex_wake((int *)&rw->__mlibc_rc); + break; + }else{ + unsigned int desired = (rc_expected & ~rc_count_mask) | (count - 1); + if(!__atomic_compare_exchange_n(&rw->__mlibc_rc, + &rc_expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED)) + continue; + break; + } + } + + return 0; + } +} + +int pthread_getcpuclockid(pthread_t, clockid_t *) { + mlibc::infoLogger() << "mlibc: pthread_getcpuclockid() always returns ENOENT" + << frg::endlog; + return ENOENT; +} diff --git a/lib/mlibc/options/posix/generic/pwd-stubs.cpp b/lib/mlibc/options/posix/generic/pwd-stubs.cpp new file mode 100644 index 0000000..5d8f618 --- /dev/null +++ b/lib/mlibc/options/posix/generic/pwd-stubs.cpp @@ -0,0 +1,284 @@ + +#include <errno.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <bits/ensure.h> + +#include <mlibc/debug.hpp> + +namespace { + FILE *global_file; // Used by setpwent/getpwent/endpwent. + + bool open_global_file() { + if(!global_file) { + global_file = fopen("/etc/passwd", "r"); + if(!global_file) { + errno = EIO; + return false; + } + } + + return true; + } + + void close_global_file() { + if(global_file) { + fclose(global_file); + global_file = nullptr; + } + } + + bool extract_entry(frg::string_view line, passwd *entry) { + frg::string_view segments[8]; + + // Parse the line into 7 or 8 segments. + size_t s = 0; + int n; + for(n = 0; n < 7; n++) { + size_t d = line.find_first(':', s); + if(d == size_t(-1)) + break; + segments[n] = line.sub_string(s, d - s); + s = d + 1; + } + if(line.find_first(':', s) != size_t(-1)) + return false; + segments[n] = line.sub_string(s, line.size() - s); + n++; + + if(n < 7) + return false; + + // TODO: Handle strndup() failure. + auto name = strndup(segments[0].data(), segments[0].size()); + __ensure(name); + + auto passwd = strndup(segments[1].data(), segments[1].size()); + __ensure(passwd); + + auto uid = segments[2].to_number<int>(); + if(!uid) + return false; + auto gid = segments[3].to_number<int>(); + if(!gid) + return false; + + auto real_name = strndup(segments[4].data(), segments[4].size()); + __ensure(real_name); + auto dir = strndup(segments[5].data(), segments[5].size()); + __ensure(dir); + auto shell = strndup(segments[6].data(), segments[6].size()); + __ensure(shell); + + // Chop the newline off the end of shell + __ensure(strlen(shell) > 0); + shell[strlen(shell) - 1] = '\0'; + + entry->pw_name = name; + entry->pw_passwd = passwd; + entry->pw_uid = *uid; + entry->pw_gid = *gid; + entry->pw_dir = dir; + entry->pw_shell = shell; + entry->pw_gecos = real_name; + return true; + } + + void copy_to_buffer(passwd *pwd, char *buffer, size_t size) { + char *pw_dir = stpcpy(buffer, pwd->pw_name) + 1; + free(pwd->pw_name); + pwd->pw_name = buffer; + + char *pw_shell = stpcpy(pw_dir, pwd->pw_dir) + 1; + free(pwd->pw_dir); + pwd->pw_dir = pw_dir; + + char *pw_passwd = stpcpy(pw_shell, pwd->pw_shell) + 1; + free(pwd->pw_shell); + pwd->pw_shell = pw_shell; + + char *end = stpcpy(pw_passwd, pwd->pw_passwd); + __ensure(end <= buffer + size); + free(pwd->pw_passwd); + pwd->pw_passwd = pw_passwd; + } + + void clear_entry(passwd *entry) { + free(entry->pw_name); + free(entry->pw_dir); + free(entry->pw_passwd); + free(entry->pw_shell); + entry->pw_name = nullptr; + entry->pw_dir = nullptr; + entry->pw_passwd = nullptr; + entry->pw_shell = nullptr; + } +} + +struct passwd *getpwent(void) { + static passwd entry; + char line[NSS_BUFLEN_PASSWD]; + + if(!open_global_file()) { + return nullptr; + } + + if (fgets(line, NSS_BUFLEN_PASSWD, global_file)) { + clear_entry(&entry); + if(!extract_entry(line, &entry)) { + errno = EINVAL; // I suppose this can be a valid errno? + return nullptr; + } + return &entry; + } + + if(ferror(global_file)) { + errno = EIO; + } + + return nullptr; +} + +struct passwd *getpwnam(const char *name) { + static passwd entry; + auto file = fopen("/etc/passwd", "r"); + if(!file) + return nullptr; + + char line[NSS_BUFLEN_PASSWD]; + while(fgets(line, NSS_BUFLEN_PASSWD, file)) { + clear_entry(&entry); + if(!extract_entry(line, &entry)) + continue; + if(!strcmp(entry.pw_name, name)) { + fclose(file); + return &entry; + } + } + + int err = errno; + if(ferror(file)) { + err = EIO; + } + + fclose(file); + errno = err; + return nullptr; +} + +int getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t size, struct passwd **result) { + *result = nullptr; + auto file = fopen("/etc/passwd", "r"); + if(!file) { + return EIO; + } + + char line[NSS_BUFLEN_PASSWD]; + while(fgets(line, NSS_BUFLEN_PASSWD, file)) { + if(!extract_entry(line, pwd)) + continue; + if(!strcmp(pwd->pw_name, name)) { + fclose(file); + + size_t required_size = strlen(pwd->pw_name) + strlen(pwd->pw_dir) + + strlen(pwd->pw_shell) + strlen(pwd->pw_passwd) + 4; + if (size < required_size) + return ERANGE; + + copy_to_buffer(pwd, buffer, size); + *result = pwd; + return 0; + } + } + + int ret = 0; + if(ferror(file)) { + ret = EIO; + } + + fclose(file); + return ret; +} + +struct passwd *getpwuid(uid_t uid) { + static passwd entry; + auto file = fopen("/etc/passwd", "r"); + if(!file) + return nullptr; + + char line[NSS_BUFLEN_PASSWD]; + while(fgets(line, NSS_BUFLEN_PASSWD, file)) { + clear_entry(&entry); + if(!extract_entry(line, &entry)) + continue; + if(entry.pw_uid == uid) { + fclose(file); + return &entry; + } + } + + int err = ESRCH; + if(ferror(file)) { + err = EIO; + } + + fclose(file); + errno = err; + return nullptr; +} + +int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t size, struct passwd **result) { + *result = nullptr; + auto file = fopen("/etc/passwd", "r"); + if(!file) { + return EIO; + } + + char line[NSS_BUFLEN_PASSWD]; + while(fgets(line, NSS_BUFLEN_PASSWD, file)) { + if(!extract_entry(line, pwd)) + continue; + if(pwd->pw_uid == uid) { + fclose(file); + + size_t required_size = strlen(pwd->pw_name) + strlen(pwd->pw_dir) + + strlen(pwd->pw_shell) + + strlen(pwd->pw_passwd) + 4; + if (size < required_size) + return ERANGE; + + copy_to_buffer(pwd, buffer, size); + *result = pwd; + return 0; + } + } + + int ret = 0; + if(ferror(file)) { + ret = EIO; + } + + fclose(file); + return ret; +} + +void setpwent(void) { + if(!open_global_file()) { + return; + } + rewind(global_file); +} + +void endpwent(void) { + close_global_file(); +} + +int putpwent(const struct passwd *, FILE *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +struct passwd *fgetpwent(FILE *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/resolv_conf.cpp b/lib/mlibc/options/posix/generic/resolv_conf.cpp new file mode 100644 index 0000000..a5c3aa7 --- /dev/null +++ b/lib/mlibc/options/posix/generic/resolv_conf.cpp @@ -0,0 +1,42 @@ +#include <mlibc/resolv_conf.hpp> +#include <mlibc/allocator.hpp> +#include <stdio.h> +#include <ctype.h> + +namespace mlibc { + +frg::optional<struct nameserver_data> get_nameserver() { + auto file = fopen("/etc/resolv.conf", "r"); + if (!file) + return frg::null_opt; + + char line[128]; + struct nameserver_data ret; + while (fgets(line, 128, file)) { + char *pos; + if (!strchr(line, '\n') && !feof(file)) { + // skip truncated lines + for (int c = getc(file); c != '\n' && c != EOF; c = getc(file)); + continue; + } + + // TODO(geert): resolv.conf can actually have multiple nameservers + // but we just pick the first one for now + if (!strncmp(line, "nameserver", 10) && isspace(line[10])) { + char *end; + for (pos = line + 11; isspace(*pos); pos++); + for (end = pos; *end && !isspace(*end); end++); + *end = '\0'; + ret.name = frg::string<MemoryAllocator>( + pos, end - pos, getAllocator()); + break; + } + } + + fclose(file); + if(ret.name.empty()) + return frg::null_opt; + return ret; +} + +} // namespace mlibc diff --git a/lib/mlibc/options/posix/generic/sched-stubs.cpp b/lib/mlibc/options/posix/generic/sched-stubs.cpp new file mode 100644 index 0000000..9d75d50 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sched-stubs.cpp @@ -0,0 +1,50 @@ + +#include <bits/ensure.h> +#include <errno.h> +#include <limits.h> +#include <sched.h> + +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> + +int sched_yield(void) { + if(mlibc::sys_yield) { + mlibc::sys_yield(); + }else{ + // Missing sched_yield() is not an error. + MLIBC_MISSING_SYSDEP(); + } + return 0; +} + +int sched_get_priority_max(int policy) { + int res = 0; + + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_get_max_priority, -1); + if(int e = sysdep(policy, &res); e) { + errno = e; + return -1; + } + return res; +} + +int sched_get_priority_min(int policy) { + int res = 0; + + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_get_min_priority, -1); + if(int e = sysdep(policy, &res); e) { + errno = e; + return -1; + } + return res; +} + +int sched_setscheduler(pid_t, int, const struct sched_param *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int sched_getparam(pid_t, struct sched_param *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/search.cpp b/lib/mlibc/options/posix/generic/search.cpp new file mode 100644 index 0000000..e6f8c1d --- /dev/null +++ b/lib/mlibc/options/posix/generic/search.cpp @@ -0,0 +1,151 @@ + +#include <bits/ensure.h> +#include <search.h> +#include <stddef.h> +#include <new> +#include <mlibc/allocator.hpp> +#include <frg/stack.hpp> +#include <stdlib.h> + +struct node { + const void *key; + void *a[2]; + int h; +}; + +namespace { + int height(struct node *node) { + return node ? node->h : 0; + } + + int rotate(struct node **nodep, int side) { + struct node *node = *nodep; + struct node *x = static_cast<struct node *>(node->a[side]); + struct node *y = static_cast<struct node *>(x->a[!side]); + struct node *z = static_cast<struct node *>(x->a[side]); + + int height_node = node->h; + int height_y = height(y); + if (height_y > height(z)) { + // Perform double rotation + node->a[side] = y->a[!side]; + x->a[!side] = y->a[side]; + y->a[!side] = node; + y->a[side] = x; + node->h = height_y; + x->h = height_y; + y->h = height_y + 1; + } else { + // Perform single rotation + node->a[side] = y; + x->a[!side] = node; + node->h = height_y + 1; + x->h = height_y + 2; + y = x; + + } + *nodep = y; + return y->h - height_node; + } + + int balance_tree(struct node **nodep) { + struct node *node = *nodep; + int height_a = height(static_cast<struct node *>(node->a[0])); + int height_b = height(static_cast<struct node *>(node->a[1])); + if (height_a - height_b < 2) { + int old = node->h; + node->h = height_a < height_b ? height_b + 1 : height_a + 1; + return node->h - old; + } + + return rotate(nodep, height_a < height_b); + } +} + +void *tsearch(const void *key, void **rootp, int(*compar)(const void *, const void *)) { + if (!rootp) + return NULL; + + struct node *n = static_cast<struct node *>(*rootp); + frg::stack<struct node **, MemoryAllocator> nodes(getAllocator()); + nodes.push(reinterpret_cast<struct node **>(rootp)); + int c = 0; + for (;;) { + if (!n) + break; + c = compar(key, n->key); + if (!c) + return n; + nodes.push(reinterpret_cast<struct node **>(&n->a[c > 0])); + n = static_cast<struct node *>(n->a[c > 0]); + } + + struct node *insert = static_cast<struct node*>(malloc(sizeof(struct node))); + if (!insert) + return NULL; + insert->key = key; + insert->a[0] = insert->a[1] = NULL; + insert->h = 1; + + (*nodes.top()) = insert; + nodes.pop(); + while(nodes.size() && balance_tree(nodes.top())) nodes.pop(); + return insert; +} + +// This implementation is taken from musl +void *tfind(const void *key, void *const *rootp, int (*compar)(const void *, const void *)) { + if(!rootp) + return 0; + + struct node *n = (struct node *)*rootp; + for(;;) { + if(!n) + break; + int c = compar(key, n->key); + if(!c) + break; + n = (struct node *)n->a[c > 0]; + } + return n; +} + +void *tdelete(const void *, void **, int(*compar)(const void *, const void *)) { + (void)compar; + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void twalk(const void *, void (*action)(const void *, VISIT, int)) { + (void)action; + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void tdestroy(void *, void (*free_node)(void *)) { + (void)free_node; + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void *lsearch(const void *key, void *base, size_t *nelp, size_t width, + int (*compar)(const void *, const void *)) { + (void)key; + (void)base; + (void)nelp; + (void)width; + (void)compar; + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void *lfind(const void *key, const void *base, size_t *nelp, + size_t width, int (*compar)(const void *, const void *)) { + (void)key; + (void)base; + (void)nelp; + (void)width; + (void)compar; + __ensure(!"Not implemented"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/semaphore-stubs.cpp b/lib/mlibc/options/posix/generic/semaphore-stubs.cpp new file mode 100644 index 0000000..d41eccb --- /dev/null +++ b/lib/mlibc/options/posix/generic/semaphore-stubs.cpp @@ -0,0 +1,114 @@ +#include <semaphore.h> +#include <errno.h> + +#include <bits/ensure.h> +#include <mlibc/debug.hpp> +#include <mlibc/ansi-sysdeps.hpp> +#include <mlibc/posix-sysdeps.hpp> + +static constexpr unsigned int semaphoreHasWaiters = static_cast<uint32_t>(1 << 31); +static constexpr unsigned int semaphoreCountMask = static_cast<uint32_t>(1 << 31) - 1; + +int sem_init(sem_t *sem, int pshared, unsigned int initial_count) { + if (pshared) { + mlibc::infoLogger() << "mlibc: shared semaphores are unsuppored" << frg::endlog; + errno = ENOSYS; + return -1; + } + + if (initial_count > SEM_VALUE_MAX) { + errno = EINVAL; + return -1; + } + + sem->__mlibc_count = initial_count; + + return 0; +} + +int sem_destroy(sem_t *) { + return 0; +} + +int sem_wait(sem_t *sem) { + unsigned int state = 0; + + while (1) { + if (!(state & semaphoreCountMask)) { + if (__atomic_compare_exchange_n(&sem->__mlibc_count, &state, semaphoreHasWaiters, + false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) { + int e = mlibc::sys_futex_wait((int *)&sem->__mlibc_count, state, nullptr); + if (e == 0 || e == EAGAIN) { + continue; + } else if (e == EINTR) { + errno = EINTR; + return -1; + } else { + mlibc::panicLogger() << "sys_futex_wait() failed with error code " << e << frg::endlog; + } + } + } else { + unsigned int desired = (state - 1); + if (__atomic_compare_exchange_n(&sem->__mlibc_count, &state, desired, false, + __ATOMIC_RELAXED, __ATOMIC_RELAXED)) + return 0; + } + } +} + +int sem_timedwait(sem_t *sem, const struct timespec *) { + mlibc::infoLogger() << "\e[31mmlibc: sem_timedwait is implemented as sem_wait\e[0m" << frg::endlog; + return sem_wait(sem); +} + +int sem_post(sem_t *sem) { + auto old_count = __atomic_load_n(&sem->__mlibc_count, __ATOMIC_RELAXED) & semaphoreCountMask; + + if (old_count + 1 > SEM_VALUE_MAX) { + errno = EOVERFLOW; + return -1; + } + + auto state = __atomic_exchange_n(&sem->__mlibc_count, old_count + 1, __ATOMIC_RELEASE); + + if (state & semaphoreHasWaiters) + if (int e = mlibc::sys_futex_wake((int *)&sem->__mlibc_count); e) + __ensure(!"sys_futex_wake() failed"); + + return 0; +} + +sem_t *sem_open(const char *, int, ...) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int sem_close(sem_t *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int sem_getvalue(sem_t *, int *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int sem_unlink(const char *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int sem_trywait(sem_t *sem) { + while (true) { + auto state = __atomic_load_n(&sem->__mlibc_count, __ATOMIC_ACQUIRE); + + if (state & semaphoreHasWaiters) { + return EAGAIN; + } + + auto desired = state - 1; + if (__atomic_compare_exchange_n(&sem->__mlibc_count, &state, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED)) { + return 0; + } + } +} diff --git a/lib/mlibc/options/posix/generic/services.cpp b/lib/mlibc/options/posix/generic/services.cpp new file mode 100644 index 0000000..8ae0656 --- /dev/null +++ b/lib/mlibc/options/posix/generic/services.cpp @@ -0,0 +1,222 @@ +#include <mlibc/services.hpp> +#include <netdb.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <mlibc/debug.hpp> + +namespace mlibc { + +static int parse_rest(service_buf &buf, char *end, int proto) { + if (!strncmp(end, "/udp", 4)) { + if (proto == IPPROTO_TCP && proto != -1) + return 0; + buf.protocol = IPPROTO_UDP; + buf.socktype = SOCK_DGRAM; + } else if (!strncmp(end, "/tcp", 4)) { + if (proto == IPPROTO_UDP && proto != -1) + return 0; + buf.protocol = IPPROTO_TCP; + buf.socktype = SOCK_STREAM; + } else { + return 0; + } + + //TODO(geert): also parse aliases. + + return 1; +} + +static int lookup_serv_file_port(service_result &buf, int proto, int port) { + auto file = fopen(_PATH_SERVICES, "r"); + if (!file) { + switch (errno) { + case ENOENT: + case ENOTDIR: + case EACCES: + return -EAI_SERVICE; + default: + return -EAI_SYSTEM; + } + } + + char line_buf[129] = {0}; + char *line = line_buf + 1; + while(fgets(line, 128, file)) { + int name_length = 0; + char *pos; + // easy way to handle comments, just move the end of the line + // to the beginning of the comment + if ((pos = strchr(line, '#'))) { + *pos++ = '\n'; + *pos = '\0'; + } + + char *end = NULL; + for (pos = line; *pos; pos++) { + for (; isalpha(*pos); pos++); + int rport = strtoul(pos, &end, 10); + if (rport != port || rport > 65535) { + pos = end; + continue; + } + + // We have found the port, time to rewind to the start + // of the line. + for (; pos[-1]; pos--) + if(!isspace(pos[-1])) + name_length++; + break; + } + + if (!pos) + continue; + + if (!name_length) + continue; + + auto name = frg::string<MemoryAllocator>(pos, name_length, + getAllocator()); + + struct service_buf sbuf = {}; + sbuf.port = port; + sbuf.name = std::move(name); + if (!parse_rest(sbuf, end, proto)) + continue; + buf.push_back(std::move(sbuf)); + } + + fclose(file); + return buf.size(); +} + +static int lookup_serv_file_name(service_result &buf, const char *name, + int proto) { + auto file = fopen(_PATH_SERVICES, "r"); + if (!file) { + switch (errno) { + case ENOENT: + case ENOTDIR: + case EACCES: + return -EAI_SERVICE; + default: + return -EAI_SYSTEM; + } + } + + char line[128]; + int name_length = strlen(name); + while(fgets(line, 128, file)) { + char *pos; + // easy way to handle comments, just move the end of the line + // to the beginning of the comment + if ((pos = strchr(line, '#'))) { + *pos++ = '\n'; + *pos = '\0'; + } + + for (pos = line; (pos = strstr(pos, name)); pos++) { + // the name must start and end with a space + if (pos > line && !isspace(pos[-1])) + continue; + if (pos[name_length] && !isspace(pos[name_length])) + continue; + break; + } + if (!pos) + continue; + + // Skip the name at the beginning of the line. + for(pos = line; *pos && !isspace(*pos); pos++) + ; + + char *end = NULL; + int port = strtoul(pos, &end, 10); + if (port > 65535 || end == pos) + continue; + + struct service_buf sbuf; + sbuf.port = port; + sbuf.name = frg::string<MemoryAllocator>(name, getAllocator()); + if (!parse_rest(sbuf, end, proto)) + continue; + + buf.push_back(sbuf); + + } + + fclose(file); + return buf.size(); +} + + +// This function returns a negative error code, since a positive +// return code means success. +int lookup_serv_by_name(service_result &buf, const char *name, int proto, + int socktype, int flags) { + switch(socktype) { + case SOCK_STREAM: + if (!proto) + proto = IPPROTO_TCP; + else if (proto != IPPROTO_TCP) + return -EAI_SERVICE; + break; + case SOCK_DGRAM: + if (!proto) + proto = IPPROTO_UDP; + else if (proto != IPPROTO_UDP) + return -EAI_SERVICE; + break; + case 0: + break; + default: + if (name) + return -EAI_SERVICE; + buf[0].port = 0; + buf[0].socktype = socktype; + buf[0].protocol = proto; + return 1; + } + + char *end = nullptr; + unsigned int port = 0; + int count = 0; + + if (name) { + if (!*name) + return -EAI_SERVICE; + port = strtoul(name, &end, 10); + } + // The end pointer is a null pointer so the name was a port + // or the name was not specified. + if (!end || !*end) { + if (proto != IPPROTO_UDP) { + buf[count].port = port; + buf[count].protocol = IPPROTO_TCP; + buf[count].socktype = SOCK_STREAM; + count++; + } + if (proto != IPPROTO_TCP) { + buf[count].port = port; + buf[count].protocol = IPPROTO_UDP; + buf[count].socktype = SOCK_DGRAM; + count++; + } + return count; + } + + if (flags & AI_NUMERICSERV) + return -EAI_NONAME; + + return lookup_serv_file_name(buf, name, proto); +} + +int lookup_serv_by_port(service_result &buf, int proto, int port) { + return lookup_serv_file_port(buf, proto, port); +} + +} // namespace mlibc diff --git a/lib/mlibc/options/posix/generic/spawn-stubs.cpp b/lib/mlibc/options/posix/generic/spawn-stubs.cpp new file mode 100644 index 0000000..cf7edfc --- /dev/null +++ b/lib/mlibc/options/posix/generic/spawn-stubs.cpp @@ -0,0 +1,376 @@ + +#include <spawn.h> +#include <errno.h> +#include <pthread.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <sched.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/wait.h> + +#include <bits/ensure.h> +#include <mlibc/debug.hpp> + +/* + * Musl places this in a seperate header called fdop.h + * This header isn't present in glibc, or on my host, so I + * include it's contents here + */ + +#define FDOP_CLOSE 1 +#define FDOP_DUP2 2 +#define FDOP_OPEN 3 +#define FDOP_CHDIR 4 +#define FDOP_FCHDIR 5 + +struct fdop { + struct fdop *next, *prev; + int cmd, fd, srcfd, oflag; + mode_t mode; + char path[]; +}; + +/* + * This posix_spawn implementation is taken from musl + */ + +static unsigned long handler_set[NSIG / (8 * sizeof(long))]; + +static void __get_handler_set(sigset_t *set) { + memcpy(set, handler_set, sizeof handler_set); +} + +struct args { + int p[2]; + sigset_t oldmask; + const char *path; + const posix_spawn_file_actions_t *fa; + const posix_spawnattr_t *__restrict attr; + char *const *argv, *const *envp; +}; + +static int child(void *args_vp) { + int i, ret; + struct sigaction sa = {}; + struct args *args = (struct args *)args_vp; + int p = args->p[1]; + const posix_spawn_file_actions_t *fa = args->fa; + const posix_spawnattr_t *__restrict attr = args->attr; + sigset_t hset; + bool use_execvpe = false; + + if(attr->__fn) + use_execvpe = true; + + close(args->p[0]); + + /* All signal dispositions must be either SIG_DFL or SIG_IGN + * before signals are unblocked. Otherwise a signal handler + * from the parent might get run in the child while sharing + * memory, with unpredictable and dangerous results. To + * reduce overhead, sigaction has tracked for us which signals + * potentially have a signal handler. */ + __get_handler_set(&hset); + for(i = 1; i < NSIG; i++) { + if((attr->__flags & POSIX_SPAWN_SETSIGDEF) && sigismember(&attr->__def, i)) { + sa.sa_handler = SIG_DFL; + } else if(sigismember(&hset, i)) { + if (i - 32 < 3) { + sa.sa_handler = SIG_IGN; + } else {; + sigaction(i, 0, &sa); + if(sa.sa_handler == SIG_IGN) + continue; + sa.sa_handler = SIG_DFL; + } + } else { + continue; + } + sigaction(i, &sa, 0); + } + + if(attr->__flags & POSIX_SPAWN_SETSID) { + if((ret = setsid()) < 0) + goto fail; + } + + if(attr->__flags & POSIX_SPAWN_SETPGROUP) { + mlibc::infoLogger() << "mlibc: posix_spawn: ignoring SETPGROUP" << frg::endlog; + //if((ret = setpgid(0, attr->__pgrp))) + // goto fail; + } + + if(attr->__flags & POSIX_SPAWN_RESETIDS) { + if((ret = setgid(getgid())) || (ret = setuid(getuid())) ) + goto fail; + } + + if(fa && fa->__actions) { + struct fdop *op; + int fd; + for(op = (struct fdop *)fa->__actions; op->next; op = op->next); + for(; op; op = op->prev) { + /* It's possible that a file operation would clobber + * the pipe fd used for synchronizing with the + * parent. To avoid that, we dup the pipe onto + * an unoccupied fd. */ + if(op->fd == p) { + ret = dup(p); + if(ret < 0) + goto fail; + close(p); + p = ret; + } + switch(op->cmd) { + case FDOP_CLOSE: + close(op->fd); + break; + case FDOP_DUP2: + fd = op->srcfd; + if(fd == p) { + ret = -EBADF; + goto fail; + } + if(fd != op->fd) { + if((ret = dup2(fd, op->fd)) < 0) + goto fail; + } else { + ret = fcntl(fd, F_GETFD); + ret = fcntl(fd, F_SETFD, ret & ~FD_CLOEXEC); + if(ret < 0) + goto fail; + } + break; + case FDOP_OPEN: + fd = open(op->path, op->oflag, op->mode); + if((ret = fd) < 0) + goto fail; + if(fd != op->fd) { + if((ret = dup2(fd, op->fd)) < 0) + goto fail; + close(fd); + } + break; + case FDOP_CHDIR: + ret = chdir(op->path); + if(ret < 0) + goto fail; + break; + case FDOP_FCHDIR: + ret = fchdir(op->fd); + if(ret < 0) + goto fail; + break; + } + } + } + + /* Close-on-exec flag may have been lost if we moved the pipe + * to a different fd. */ + fcntl(p, F_SETFD, FD_CLOEXEC); + + pthread_sigmask(SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK) + ? &attr->__mask : &args->oldmask, 0); + + if(use_execvpe) + execvpe(args->path, args->argv, args->envp); + else + execve(args->path, args->argv, args->envp); + ret = -errno; + +fail: + /* Since sizeof errno < PIPE_BUF, the write is atomic. */ + ret = -ret; + if(ret) + while(write(p, &ret, sizeof ret) < 0); + _exit(127); +} + +int posix_spawn(pid_t *__restrict res, const char *__restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *__restrict attrs, + char *const argv[], char *const envp[]) { + pid_t pid; + int ec = 0, cs; + struct args args; + const posix_spawnattr_t empty_attr = {}; + sigset_t full_sigset; + sigfillset(&full_sigset); + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); + + args.path = path; + args.fa = file_actions; + args.attr = attrs ? attrs : &empty_attr; + args.argv = argv; + args.envp = envp; + pthread_sigmask(SIG_BLOCK, &full_sigset, &args.oldmask); + + /* The lock guards both against seeing a SIGABRT disposition change + * by abort and against leaking the pipe fd to fork-without-exec. */ + //LOCK(__abort_lock); + + if(pipe2(args.p, O_CLOEXEC)) { + //UNLOCK(__abort_lock); + ec = errno; + goto fail; + } + + /* Mlibc change: We use fork + execve, as clone is not implemented. + * This yields the same result in the end. */ + //pid = clone(child, stack + sizeof stack, CLONE_VM | CLONE_VFORK | SIGCHLD, &args); + pid = fork(); + if(!pid) { + child(&args); + } + close(args.p[1]); + //UNLOCK(__abort_lock); + + if(pid > 0) { + if(read(args.p[0], &ec, sizeof ec) != sizeof ec) + ec = 0; + else + waitpid(pid, 0, 0); + } else { + ec = -pid; + } + + close(args.p[0]); + + if(!ec && res) + *res = pid; + +fail: + pthread_sigmask(SIG_SETMASK, &args.oldmask, 0); + pthread_setcancelstate(cs, 0); + + return ec; +} + +int posix_spawnattr_init(posix_spawnattr_t *attr) { + *attr = (posix_spawnattr_t){}; + return 0; +} + +int posix_spawnattr_destroy(posix_spawnattr_t *) { + return 0; +} + +int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) { + const unsigned all_flags = + POSIX_SPAWN_RESETIDS | + POSIX_SPAWN_SETPGROUP | + POSIX_SPAWN_SETSIGDEF | + POSIX_SPAWN_SETSIGMASK | + POSIX_SPAWN_SETSCHEDPARAM | + POSIX_SPAWN_SETSCHEDULER | + POSIX_SPAWN_USEVFORK | + POSIX_SPAWN_SETSID; + if(flags & ~all_flags) + return EINVAL; + attr->__flags = flags; + return 0; +} + +int posix_spawnattr_setsigdefault(posix_spawnattr_t *__restrict attr, + const sigset_t *__restrict sigdefault) { + attr->__def = *sigdefault; + return 0; +} + +int posix_spawnattr_setschedparam(posix_spawnattr_t *__restrict, + const struct sched_param *__restrict) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int posix_spawnattr_setschedpolicy(posix_spawnattr_t *, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int posix_spawnattr_setsigmask(posix_spawnattr_t *__restrict attr, + const sigset_t *__restrict sigmask) { + attr->__mask = *sigmask; + return 0; +} + +int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup) { + attr->__pgrp = pgroup; + return 0; +} + +int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions) { + file_actions->__actions = 0; + return 0; +} + +int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions) { + struct fdop *op = (struct fdop *)file_actions->__actions, *next; + while(op) { + next = op->next; + free(op); + op = next; + } + return 0; +} + +int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions, + int fildes, int newfildes) { + struct fdop *op = (struct fdop *)malloc(sizeof *op); + if(!op) + return ENOMEM; + op->cmd = FDOP_DUP2; + op->srcfd = fildes; + op->fd = newfildes; + if((op->next = (struct fdop *)file_actions->__actions)) + op->next->prev = op; + op->prev = 0; + file_actions->__actions = op; + return 0; +} + +int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions, + int fildes) { + struct fdop *op = (struct fdop *)malloc(sizeof *op); + if(!op) + return ENOMEM; + op->cmd = FDOP_CLOSE; + op->fd = fildes; + if((op->next = (struct fdop *)file_actions->__actions)) + op->next->prev = op; + op->prev = 0; + file_actions->__actions = op; + return 0; +} + +int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *__restrict file_actions, + int fildes, const char *__restrict path, int oflag, mode_t mode) { + struct fdop *op = (struct fdop *)malloc(sizeof *op + strlen(path) + 1); + if(!op) + return ENOMEM; + op->cmd = FDOP_OPEN; + op->fd = fildes; + op->oflag = oflag; + op->mode = mode; + strcpy(op->path, path); + if((op->next = (struct fdop *)file_actions->__actions)) + op->next->prev = op; + op->prev = 0; + file_actions->__actions = op; + return 0; +} + +int posix_spawnp(pid_t *__restrict pid, const char *__restrict file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *__restrict attrp, + char *const argv[], char *const envp[]) { + posix_spawnattr_t spawnp_attr = {}; + if(attrp) + spawnp_attr = *attrp; + spawnp_attr.__fn = (void *)execvpe; + return posix_spawn(pid, file, file_actions, &spawnp_attr, argv, envp); +} + diff --git a/lib/mlibc/options/posix/generic/strings-stubs.cpp b/lib/mlibc/options/posix/generic/strings-stubs.cpp new file mode 100644 index 0000000..524c424 --- /dev/null +++ b/lib/mlibc/options/posix/generic/strings-stubs.cpp @@ -0,0 +1,107 @@ + +#include <strings.h> +#include <string.h> + +#include <ctype.h> +#include <bits/ensure.h> +#include <mlibc/strings.hpp> + +char *index (const char *s, int c) { + return strchr(s, c); +} + +char *rindex(const char *s, int c) { + return strrchr(s, c); +} + +namespace { + + template<typename T> + int ffs_generic(T i) { + //Non-portably assume a byte has 8 bits; fine in all plausible cases. + for(size_t b = 0; b < sizeof(T) * 8;) + if(i & (static_cast<T>(0x1) << b++)) + return b; + + return 0; + } + +} + +// On RISC-V, __builtin_ffs just calls into ffs, so we can't use it here. +#if defined(__has_builtin) && !defined(__riscv) +# if __has_builtin(__builtin_ffs) +# define __mlibc_ffs __builtin_ffs +# endif +# if __has_builtin(__builtin_ffsl) +# define __mlibc_ffsl __builtin_ffsl +# endif +# if __has_builtin(__builtin_ffsll) +# define __mlibc_ffsll __builtin_ffsll +# endif +#endif + +int ffs(int i) { +#ifdef __mlibc_ffs + return __mlibc_ffs(i); +#else + return ffs_generic<int>(i); +#endif +} + +/* + Both ffsl() and ffsll() are glibc extensions + defined in string.h. They are however implemented + here because of similarity in logic and + shared code. +*/ + +int ffsl(long i) { +#ifdef __mlibc_ffsl + return __mlibc_ffsl(i); +#else + return ffs_generic<long>(i); +#endif +} + +int ffsll(long long i) { +#ifdef __mlibc_ffsll + return __mlibc_ffsll(i); +#else + return ffs_generic<long long>(i); +#endif +} + +int strcasecmp(const char *a, const char *b) { + size_t i = 0; + while(true) { + unsigned char a_byte = tolower(a[i]); + unsigned char b_byte = tolower(b[i]); + if(!a_byte && !b_byte) + return 0; + // If only one char is null, one of the following cases applies. + if(a_byte < b_byte) + return -1; + if(a_byte > b_byte) + return 1; + i++; + } +} + +int strncasecmp(const char *a, const char *b, size_t size) { + return mlibc::strncasecmp(a, b, size); +} + +// Marked as obsolete in posix 2008 but used by at least tracker +int bcmp(const void *s1, const void *s2, size_t n) { + return memcmp(s1, s2, n); +} + +void bcopy(const void *s1, void *s2, size_t n) { + memmove(s2, s1, n); +} + +void bzero(void *s, size_t n) { + memset(s, 0, n); +} + diff --git a/lib/mlibc/options/posix/generic/sys-file-stubs.cpp b/lib/mlibc/options/posix/generic/sys-file-stubs.cpp new file mode 100644 index 0000000..e1cc9ab --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-file-stubs.cpp @@ -0,0 +1,16 @@ + +#include <sys/file.h> +#include <mlibc/posix-sysdeps.hpp> +#include <errno.h> + +#include <bits/ensure.h> + +int flock(int fd, int opt) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_flock, -1); + if(int e = mlibc::sys_flock(fd, opt); e) { + errno = e; + return -1; + } + return 0; +} + diff --git a/lib/mlibc/options/posix/generic/sys-ipc.cpp b/lib/mlibc/options/posix/generic/sys-ipc.cpp new file mode 100644 index 0000000..b9e0d3d --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-ipc.cpp @@ -0,0 +1,8 @@ +#include <sys/ipc.h> + +#include <bits/ensure.h> + +key_t ftok(const char *, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/sys-mman-stubs.cpp b/lib/mlibc/options/posix/generic/sys-mman-stubs.cpp new file mode 100644 index 0000000..9383976 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-mman-stubs.cpp @@ -0,0 +1,177 @@ + +#include <errno.h> +#include <limits.h> +#include <pthread.h> +#include <unistd.h> +#include <sys/mman.h> +#include <bits/ensure.h> + +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> + +int mprotect(void *pointer, size_t size, int prot) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_vm_protect, -1); + if(int e = mlibc::sys_vm_protect(pointer, size, prot); e) { + errno = e; + return -1; + } + return 0; +} + +int mlock(const void *addr, size_t len) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mlock, -1); + if(int e = mlibc::sys_mlock(addr, len); e) { + errno = e; + return -1; + } + return 0; +} + +int mlockall(int flags) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mlockall, -1); + if(int e = mlibc::sys_mlockall(flags); e) { + errno = e; + return -1; + } + return 0; +} + +int munlock(const void *addr, size_t len) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_munlock, -1); + if(int e = mlibc::sys_munlock(addr, len); e) { + errno = e; + return -1; + } + return 0; +} + +int munlockall(void) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_munlockall, -1); + if(int e = mlibc::sys_munlockall(); e) { + errno = e; + return -1; + } + return 0; +} + + +int posix_madvise(void *, size_t, int) { + mlibc::infoLogger() << "\e[31m" "mlibc: posix_madvise() fails unconditionally" "\e[39m" + << frg::endlog; + return ENOSYS; +} + +int msync(void *addr, size_t length, int flags) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_msync, -1); + if(int e = mlibc::sys_msync(addr, length, flags); e) { + errno = e; + return -1; + } + return 0; +} + +void *mremap(void *pointer, size_t size, size_t new_size, int flags, ...) { + __ensure(flags == MREMAP_MAYMOVE); + + void *window; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_vm_remap, (void *)-1); + if(int e = mlibc::sys_vm_remap(pointer, size, new_size, &window); e) { + errno = e; + return (void *)-1; + } + return window; +} + +int remap_file_pages(void *, size_t, int, size_t, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void *mmap(void *hint, size_t size, int prot, int flags, int fd, off_t offset) { + void *window; + if(int e = mlibc::sys_vm_map(hint, size, prot, flags, fd, offset, &window); e) { + errno = e; + return (void *)-1; + } + return window; +} + +int munmap(void *pointer, size_t size) { + if(int e = mlibc::sys_vm_unmap(pointer, size); e) { + errno = e; + return -1; + } + return 0; +} + +// The implementation of shm_open and shm_unlink is taken from musl. +namespace { + char *shm_mapname(const char *name, char *buf) { + char *p; + while(*name == '/') + name++; + if(*(p = strchrnul(name, '/')) || p == name || + (p - name <= 2 && name[0] == '.' && p[-1] == '.')) { + errno = EINVAL; + return 0; + } + if(p - name > NAME_MAX) { + errno = ENAMETOOLONG; + return 0; + } + memcpy(buf, "/dev/shm/", 9); + memcpy(buf + 9, name, p - name + 1); + return buf; + } +} + +int shm_open(const char *name, int flags, mode_t mode) { + int cs; + char buf[NAME_MAX + 10]; + if(!(name = shm_mapname(name, buf))) + return -1; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); + int fd = open(name, flags | O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK, mode); + pthread_setcancelstate(cs, 0); + return fd; +} + +int shm_unlink(const char *name) { + char buf[NAME_MAX + 10]; + if(!(name = shm_mapname(name, buf))) + return -1; + return unlink(name); +} + +#if __MLIBC_LINUX_OPTION +int memfd_create(const char *name, unsigned int flags) { + int ret = -1; + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_memfd_create, -1); + if(int e = mlibc::sys_memfd_create(name, flags, &ret)) { + errno = e; + return -1; + } + + return ret; +} + +int madvise(void *addr, size_t length, int advice) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_madvise, -1); + if(int e = mlibc::sys_madvise(addr, length, advice)) { + errno = e; + return -1; + } + + return 0; +} + +int mincore(void *addr, size_t length, unsigned char *vec) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_munlockall, -1); + if(int e = mlibc::sys_mincore(addr, length, vec); e) { + errno = e; + return -1; + } + return 0; +} +#endif /* __MLIBC_LINUX_OPTION */ diff --git a/lib/mlibc/options/posix/generic/sys-msg.cpp b/lib/mlibc/options/posix/generic/sys-msg.cpp new file mode 100644 index 0000000..95f067e --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-msg.cpp @@ -0,0 +1,23 @@ + +#include <bits/ensure.h> +#include <sys/msg.h> + +int msgget(key_t, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int msgctl(int, int, struct msqid_ds *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +ssize_t msgrcv(int, void *, size_t, long, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int msgsnd(int, const void *, size_t, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/sys-resource-stubs.cpp b/lib/mlibc/options/posix/generic/sys-resource-stubs.cpp new file mode 100644 index 0000000..597de4d --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-resource-stubs.cpp @@ -0,0 +1,57 @@ + +#include <errno.h> +#include <sys/resource.h> + +#include <bits/ensure.h> +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> + +int getpriority(int which, id_t who) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getpriority, -1); + int value = 0; + if(int e = mlibc::sys_getpriority(which, who, &value); e) { + errno = e; + } + return value; +} + +int setpriority(int which, id_t who, int prio) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setpriority, -1); + if(int e = mlibc::sys_setpriority(which, who, prio); e) { + errno = e; + return -1; + } + return 0; +} + +int getrusage(int scope, struct rusage *usage) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getrusage, -1); + if(int e = mlibc::sys_getrusage(scope, usage); e) { + errno = e; + return -1; + } + return 0; +} + +int getrlimit(int resource, struct rlimit *limit) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getrlimit, -1); + if(int e = mlibc::sys_getrlimit(resource, limit); e) { + errno = e; + return -1; + } + return 0; +} + +int setrlimit(int resource, const struct rlimit *limit) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setrlimit, -1); + if(int e = mlibc::sys_setrlimit(resource, limit); e) { + errno = e; + return -1; + } + return 0; +} + +int prlimit(pid_t, int, const struct rlimit *, struct rlimit *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/sys-select-stubs.cpp b/lib/mlibc/options/posix/generic/sys-select-stubs.cpp new file mode 100644 index 0000000..e8ff927 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-select-stubs.cpp @@ -0,0 +1,58 @@ + +#include <string.h> +#include <sys/select.h> +#include <unistd.h> +#include <errno.h> + +#include <bits/ensure.h> +#include <mlibc-config.h> + +#include <mlibc/posix-sysdeps.hpp> + +void __FD_CLR(int fd, fd_set *set) { + __ensure(fd < FD_SETSIZE); + set->__mlibc_elems[fd / 8] &= ~(1 << (fd % 8)); +} +int __FD_ISSET(int fd, fd_set *set) { + __ensure(fd < FD_SETSIZE); + return set->__mlibc_elems[fd / 8] & (1 << (fd % 8)); +} +void __FD_SET(int fd, fd_set *set) { + __ensure(fd < FD_SETSIZE); + set->__mlibc_elems[fd / 8] |= 1 << (fd % 8); +} +void __FD_ZERO(fd_set *set) { + memset(set->__mlibc_elems, 0, sizeof(fd_set)); +} + +int select(int num_fds, fd_set *__restrict read_set, fd_set *__restrict write_set, + fd_set *__restrict except_set, struct timeval *__restrict timeout) { + int num_events = 0; + struct timespec timeouts = {}; + struct timespec *timeout_ptr = NULL; + if (timeout) { + timeouts.tv_sec = timeout->tv_sec; + timeouts.tv_nsec = timeout->tv_usec * 1000; + timeout_ptr = &timeouts; + } + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pselect, -1); + if(int e = mlibc::sys_pselect(num_fds, read_set, write_set, except_set, + timeout_ptr, NULL, &num_events); e) { + errno = e; + return -1; + } + return num_events; +} + +int pselect(int num_fds, fd_set *read_set, fd_set *write_set, fd_set *except_set, + const struct timespec *timeout, const sigset_t *sigmask) { + int num_events = 0; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pselect, -1); + if(int e = mlibc::sys_pselect(num_fds, read_set, write_set, except_set, + timeout, sigmask, &num_events); e) { + errno = e; + return -1; + } + return num_events; +} diff --git a/lib/mlibc/options/posix/generic/sys-sem.cpp b/lib/mlibc/options/posix/generic/sys-sem.cpp new file mode 100644 index 0000000..ac3df69 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-sem.cpp @@ -0,0 +1,51 @@ + +#include <bits/ensure.h> +#include <errno.h> +#include <limits.h> +#include <sys/sem.h> + +#include <mlibc/posix-sysdeps.hpp> + +int semget(key_t key, int n, int fl) { + if(n > USHRT_MAX) { + errno = EINVAL; + return -1; + } + + int id = 0; + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_semget, -1); + if(int e = sysdep(key, n, fl, &id); e) { + errno = e; + return -1; + } + return id; +} + +int semop(int, struct sembuf *, size_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +union semun { + int val; + struct semid_ds *buf; + unsigned short *array; +}; + +int semctl(int id, int num, int cmd, ...) { + union semun semun; + int ret = 0; + + va_list ap; + va_start(ap, cmd); + semun = va_arg(ap, union semun); + va_end(ap); + + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_semctl, -1); + if(int e = sysdep(id, num, cmd, semun.buf, &ret); e) { + errno = e; + return -1; + } + + return ret; +} diff --git a/lib/mlibc/options/posix/generic/sys-shm.cpp b/lib/mlibc/options/posix/generic/sys-shm.cpp new file mode 100644 index 0000000..3af7e90 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-shm.cpp @@ -0,0 +1,24 @@ +#include <sys/shm.h> + +#include <bits/ensure.h> +#include <mlibc/debug.hpp> + +void *shmat(int, const void *, int) { + __ensure(!"Function is not implemented"); + __builtin_unreachable(); +} + +int shmctl(int, int, struct shmid_ds *) { + __ensure(!"Function is not implemented"); + __builtin_unreachable(); +} + +int shmdt(const void *) { + __ensure(!"Function is not implemented"); + __builtin_unreachable(); +} + +int shmget(key_t, size_t, int) { + mlibc::infoLogger() << "mlibc: shmget() is a no-op!" << frg::endlog; + return -1; +} diff --git a/lib/mlibc/options/posix/generic/sys-socket-stubs.cpp b/lib/mlibc/options/posix/generic/sys-socket-stubs.cpp new file mode 100644 index 0000000..037a994 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-socket-stubs.cpp @@ -0,0 +1,225 @@ + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/socket.h> + +#include <bits/ensure.h> +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> + +int accept(int fd, struct sockaddr *__restrict addr_ptr, socklen_t *__restrict addr_length) { + int newfd; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_accept, -1); + if(int e = mlibc::sys_accept(fd, &newfd, addr_ptr, addr_length, 0); e) { + errno = e; + return -1; + } + return newfd; +} + +int accept4(int fd, struct sockaddr *__restrict addr_ptr, socklen_t *__restrict addr_length, int flags) { + int newfd; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_accept, -1); + if(int e = mlibc::sys_accept(fd, &newfd, addr_ptr, addr_length, flags); e) { + errno = e; + return -1; + } + + return newfd; +} + +int bind(int fd, const struct sockaddr *addr_ptr, socklen_t addr_len) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_bind, -1); + if(int e = mlibc::sys_bind(fd, addr_ptr, addr_len); e) { + errno = e; + return -1; + } + return 0; +} + +int connect(int fd, const struct sockaddr *addr_ptr, socklen_t addr_len) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_connect, -1); + if(int e = mlibc::sys_connect(fd, addr_ptr, addr_len); e) { + errno = e; + return -1; + } + return 0; +} + +int getpeername(int fd, struct sockaddr *addr_ptr, socklen_t *__restrict addr_length) { + socklen_t actual_length; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_peername, -1); + if(int e = mlibc::sys_peername(fd, addr_ptr, *addr_length, &actual_length); e) { + errno = e; + return -1; + } + *addr_length = actual_length; + return 0; +} + +int getsockname(int fd, struct sockaddr *__restrict addr_ptr, socklen_t *__restrict addr_length) { + socklen_t actual_length; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sockname, -1); + if(int e = mlibc::sys_sockname(fd, addr_ptr, *addr_length, &actual_length); e) { + errno = e; + return -1; + } + *addr_length = actual_length; + return 0; +} + +int getsockopt(int fd, int layer, int number, + void *__restrict buffer, socklen_t *__restrict size) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getsockopt, -1); + return mlibc::sys_getsockopt(fd, layer, number, buffer, size); +} + +int listen(int fd, int backlog) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_listen, -1); + if(int e = mlibc::sys_listen(fd, backlog); e) { + errno = e; + return -1; + } + return 0; +} + +ssize_t recv(int sockfd, void *__restrict buf, size_t len, int flags) { + return recvfrom(sockfd, buf, len, flags, NULL, NULL); +} + +ssize_t recvfrom(int sockfd, void *__restrict buf, size_t len, int flags, + struct sockaddr *__restrict src_addr, socklen_t *__restrict addrlen) { + if(mlibc::sys_recvfrom) { + ssize_t length; + if(int e = mlibc::sys_recvfrom(sockfd, buf, len, flags, src_addr, addrlen, &length); e) { + errno = e; + return -1; + } + return length; + } + + struct iovec iov = {}; + iov.iov_base = buf; + iov.iov_len = len; + + struct msghdr hdr = {}; + hdr.msg_name = src_addr; + if (addrlen) { + hdr.msg_namelen = *addrlen; + } + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + + int ret = recvmsg(sockfd, &hdr, flags); + if (ret < 0) + return ret; + + if(addrlen) + *addrlen = hdr.msg_namelen; + return ret; +} + +ssize_t recvmsg(int fd, struct msghdr *hdr, int flags) { + ssize_t length; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_msg_recv, -1); + if(int e = mlibc::sys_msg_recv(fd, hdr, flags, &length); e) { + errno = e; + return -1; + } + return length; +} + +int recvmmsg(int, struct mmsghdr *, unsigned int, int, struct timespec *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +ssize_t send(int fd, const void *buffer, size_t size, int flags) { + return sendto(fd, buffer, size, flags, nullptr, 0); +} + +ssize_t sendto(int fd, const void *buffer, size_t size, int flags, + const struct sockaddr *sock_addr, socklen_t addr_length) { + if(mlibc::sys_sendto) { + ssize_t length; + if(int e = mlibc::sys_sendto(fd, buffer, size, flags, sock_addr, addr_length, &length); e) { + errno = e; + return -1; + } + return length; + } + + struct iovec iov = {}; + iov.iov_base = const_cast<void *>(buffer); + iov.iov_len = size; + + struct msghdr hdr = {}; + hdr.msg_name = const_cast<struct sockaddr *>(sock_addr); + hdr.msg_namelen = addr_length; + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + + return sendmsg(fd, &hdr, flags); +} + +ssize_t sendmsg(int fd, const struct msghdr *hdr, int flags) { + if(hdr->msg_iovlen > IOV_MAX) + return EMSGSIZE; + + ssize_t length; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_msg_send, -1); + if(int e = mlibc::sys_msg_send(fd, hdr, flags, &length); e) { + errno = e; + return -1; + } + return length; +} + +int sendmmsg(int, struct mmsghdr *, unsigned int, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int setsockopt(int fd, int layer, int number, + const void *buffer, socklen_t size) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setsockopt, -1); + return mlibc::sys_setsockopt(fd, layer, number, buffer, size); +} + +int shutdown(int sockfd, int how) { + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_shutdown, -1); + if(int e = sysdep(sockfd, how); e) { + errno = e; + return -1; + } + + return 0; +} + +int sockatmark(int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int socket(int family, int type, int protocol) { + int fd; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_socket, -1); + if(int e = mlibc::sys_socket(family, type, protocol, &fd); e) { + errno = e; + return -1; + } + return fd; +} + +int socketpair(int domain, int type, int protocol, int sv[2]) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_socketpair, -1); + if(int e = mlibc::sys_socketpair(domain, type, protocol, sv); e) { + errno = e; + return -1; + } + return 0; +} + +// connectpair() is provided by the platform + diff --git a/lib/mlibc/options/posix/generic/sys-stat-stubs.cpp b/lib/mlibc/options/posix/generic/sys-stat-stubs.cpp new file mode 100644 index 0000000..2eca445 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-stat-stubs.cpp @@ -0,0 +1,155 @@ + +#include <errno.h> +#include <bits/ensure.h> +#include <sys/stat.h> + +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> + +int chmod(const char *pathname, mode_t mode) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_chmod, -1); + if(int e = mlibc::sys_chmod(pathname, mode); e) { + errno = e; + return -1; + } + return 0; +} + +int fchmod(int fd, mode_t mode) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchmod, -1); + if(int e = mlibc::sys_fchmod(fd, mode); e) { + errno = e; + return -1; + } + return 0; +} + +int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchmodat, -1); + if(int e = mlibc::sys_fchmodat(dirfd, pathname, mode, flags); e) { + errno = e; + return -1; + } + return 0; +} + +int fstatat(int dirfd, const char *path, struct stat *result, int flags) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_stat, -1); + if(int e = mlibc::sys_stat(mlibc::fsfd_target::fd_path, dirfd, path, flags, result); e) { + errno = e; + return -1; + } + return 0; +} + +int futimens(int fd, const struct timespec times[2]) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_utimensat, -1); + + if (int e = mlibc::sys_utimensat(fd, nullptr, times, 0); e) { + errno = e; + return -1; + } + + return 0; +} + +int mkdir(const char *path, mode_t mode) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mkdir, -1); + if(int e = mlibc::sys_mkdir(path, mode); e) { + errno = e; + return -1; + } + return 0; +} + +int mkdirat(int dirfd, const char *path, mode_t mode) { + mlibc::infoLogger() << "\e[31mmlibc: mkdirat() ignores its mode\e[39m" << frg::endlog; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mkdirat, -1); + if(int e = mlibc::sys_mkdirat(dirfd, path, mode); e) { + errno = e; + return -1; + } + return 0; +} + +int mkfifo(const char *path, mode_t mode) { + return mkfifoat(AT_FDCWD, path, mode); +} + +int mkfifoat(int dirfd, const char *path, mode_t mode) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mkfifoat, -1); + if (int e = mlibc::sys_mkfifoat(dirfd, path, mode); e) { + errno = e; + return -1; + } + + return 0; +} + +int mknod(const char *path, mode_t mode, dev_t dev) { + return mknodat(AT_FDCWD, path, mode, dev); +} + +int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mknodat, -1); + if (int e = mlibc::sys_mknodat(dirfd, path, mode, dev); e) { + errno = e; + return -1; + } + + return 0; +} + +mode_t umask(mode_t mode) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_umask, -1); + mode_t old; + if (int e = mlibc::sys_umask(mode, &old); e) { + errno = e; + return -1; + } + return old; +} + +int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags) { + if(pathname == nullptr) { + errno = EINVAL; + return -1; + } + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_utimensat, -1); + if (int e = mlibc::sys_utimensat(dirfd, pathname, times, flags); e) { + errno = e; + return -1; + } + + return 0; +} + +int stat(const char *path, struct stat *result) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_stat, -1); + if(int e = mlibc::sys_stat(mlibc::fsfd_target::path, -1, path, 0, result); e) { + errno = e; + return -1; + } + return 0; +} + +int lstat(const char *path, struct stat *result) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_stat, -1); + if(int e = mlibc::sys_stat(mlibc::fsfd_target::path, + -1, path, AT_SYMLINK_NOFOLLOW, result); e) { + errno = e; + return -1; + } + return 0; +} + +int fstat(int fd, struct stat *result) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_stat, -1); + if(int e = mlibc::sys_stat(mlibc::fsfd_target::fd, fd, "", 0, result); e) { + errno = e; + return -1; + } + return 0; +} + diff --git a/lib/mlibc/options/posix/generic/sys-statvfs-stubs.cpp b/lib/mlibc/options/posix/generic/sys-statvfs-stubs.cpp new file mode 100644 index 0000000..c02cc39 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-statvfs-stubs.cpp @@ -0,0 +1,24 @@ +#include <errno.h> +#include <sys/statvfs.h> + +#include <bits/ensure.h> +#include <mlibc/posix-sysdeps.hpp> + +int statvfs(const char *path, struct statvfs *out) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_statvfs, -1); + if(int e = mlibc::sys_statvfs(path, out); e) { + errno = e; + return -1; + } + return 0; +} + +int fstatvfs(int fd, struct statvfs *out) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fstatvfs, -1); + if(int e = mlibc::sys_fstatvfs(fd, out); e) { + errno = e; + return -1; + } + return 0; +} + diff --git a/lib/mlibc/options/posix/generic/sys-time-stubs.cpp b/lib/mlibc/options/posix/generic/sys-time-stubs.cpp new file mode 100644 index 0000000..5cc0fe5 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-time-stubs.cpp @@ -0,0 +1,107 @@ + +#include <errno.h> +#include <sys/time.h> +#include <time.h> + +#include <bits/ensure.h> +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> + +int gettimeofday(struct timeval *__restrict result, void *__restrict unused) { + (void)unused; // Linux just ignores gettimeofday(). + + if(result) { + long nanos; + if(int e = mlibc::sys_clock_get(CLOCK_REALTIME, &result->tv_sec, &nanos); e) { + errno = e; + return -1; + } + result->tv_usec = nanos / 1000; + } + return 0; +} + +int settimeofday(const struct timeval *, const struct timezone *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void timeradd(const struct timeval *a, const struct timeval *b, struct timeval *res) { + res->tv_sec = a->tv_sec + b->tv_sec; + res->tv_usec = a->tv_usec + b->tv_usec; + while(res->tv_usec > 999999) { + res->tv_usec -= 1000000; + res->tv_sec += 1; + } +} + +void timersub(const struct timeval *a, const struct timeval *b, struct timeval *res) { + res->tv_sec = a->tv_sec - b->tv_sec; + res->tv_usec = a->tv_usec - b->tv_usec; + while(res->tv_usec < 0) { + res->tv_usec += 1000000; + res->tv_sec -= 1; + } +} + +void timerclear(struct timeval *tvp) { + tvp->tv_sec = 0; + tvp->tv_usec = 0; +} + +int timerisset(struct timeval *tvp) { + if(tvp->tv_sec != 0 || tvp->tv_usec != 0) { + return 1; + } + return 0; +} + +int getitimer(int which, struct itimerval *curr_value) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getitimer, -1); + if(int e = mlibc::sys_getitimer(which, curr_value); e) { + errno = e; + return -1; + } + return 0; +} + +int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setitimer, -1); + if(int e = mlibc::sys_setitimer(which, new_value, old_value); e) { + errno = e; + return -1; + } + return 0; +} + +int timer_create(clockid_t clk, struct sigevent *__restrict evp, timer_t *__restrict res) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_timer_create, -1); + if(int e = mlibc::sys_timer_create(clk, evp, res); e) { + errno = e; + return -1; + } + return 0; +} + +int timer_settime(timer_t t, int flags, const struct itimerspec *__restrict val, struct itimerspec *__restrict old) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_timer_settime, -1); + if(int e = mlibc::sys_timer_settime(t, flags, val, old); e) { + errno = e; + return -1; + } + return 0; +} + +int timer_gettime(timer_t, struct itimerspec *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int timer_delete(timer_t t) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_timer_delete, -1); + if(int e = mlibc::sys_timer_delete(t); e) { + errno = e; + return -1; + } + return 0; +} diff --git a/lib/mlibc/options/posix/generic/sys-times.cpp b/lib/mlibc/options/posix/generic/sys-times.cpp new file mode 100644 index 0000000..61b6e25 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-times.cpp @@ -0,0 +1,19 @@ + +#include <errno.h> +#include <sys/times.h> + +#include <bits/ensure.h> +#include <mlibc/debug.hpp> +#include <internal-config.h> +#include <mlibc/posix-sysdeps.hpp> + +clock_t times(struct tms *tms) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_times, -1); + clock_t ret; + if(int e = mlibc::sys_times(tms, &ret); e) { + errno = e; + return -1; + } + return ret; +} + diff --git a/lib/mlibc/options/posix/generic/sys-uio.cpp b/lib/mlibc/options/posix/generic/sys-uio.cpp new file mode 100644 index 0000000..0f14bc0 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-uio.cpp @@ -0,0 +1,67 @@ + +#include <sys/uio.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include <frg/vector.hpp> +#include <mlibc/allocator.hpp> +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> +#include <bits/ensure.h> + +ssize_t readv(int fd, const struct iovec *iovs, int iovc) { + ssize_t read_bytes = 0; + + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_readv, -1); + + if (int e = sysdep(fd, iovs, iovc, &read_bytes); e) { + errno = e; + return -1; + } + + return read_bytes; +} + +ssize_t writev(int fd, const struct iovec *iovs, int iovc) { + __ensure(iovc); + + ssize_t written = 0; + size_t bytes = 0; + for(int i = 0; i < iovc; i++) { + if(SSIZE_MAX - bytes < iovs[i].iov_len) { + errno = EINVAL; + return -1; + } + bytes += iovs[i].iov_len; + } + frg::vector<char, MemoryAllocator> buffer{getAllocator()}; + buffer.resize(bytes); + + size_t to_copy = bytes; + char *bp = buffer.data(); + for(int i = 0; i < iovc; i++) { + size_t copy = frg::min(iovs[i].iov_len, to_copy); + + bp = (char *)mempcpy((void *)bp, (void *)iovs[i].iov_base, copy); + + to_copy -= copy; + if(to_copy == 0) + break; + } + + written = write(fd, buffer.data(), bytes); + return written; +} + +ssize_t preadv(int, const struct iovec *, int, off_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +ssize_t pwritev(int, const struct iovec *, int, off_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/sys-utsname.cpp b/lib/mlibc/options/posix/generic/sys-utsname.cpp new file mode 100644 index 0000000..3176574 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-utsname.cpp @@ -0,0 +1,24 @@ + +#include <string.h> +#include <sys/utsname.h> +#include <errno.h> + +#include <bits/ensure.h> +#include <mlibc/debug.hpp> +#include <internal-config.h> +#include <mlibc/posix-sysdeps.hpp> + +int uname(struct utsname *p) { + if (p == NULL) { + errno = EFAULT; + return -1; + } + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_uname, -1); + if(int e = mlibc::sys_uname(p); e) { + errno = e; + return -1; + } + return 0; +} + diff --git a/lib/mlibc/options/posix/generic/sys-wait-stubs.cpp b/lib/mlibc/options/posix/generic/sys-wait-stubs.cpp new file mode 100644 index 0000000..6e7cc78 --- /dev/null +++ b/lib/mlibc/options/posix/generic/sys-wait-stubs.cpp @@ -0,0 +1,52 @@ + +#include <errno.h> +#include <sys/wait.h> +#include <bits/ensure.h> + +#include <mlibc/ansi-sysdeps.hpp> +#include <mlibc/posix-sysdeps.hpp> +#include <mlibc/debug.hpp> + +int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) { + auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_waitid, -1); + if(int e = sysdep(idtype, id, info, options); e) { + errno = e; + return -1; + } + return 0; +} + +pid_t waitpid(pid_t pid, int *status, int flags) { + pid_t ret; + int tmp_status = 0; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_waitpid, -1); + if(int e = mlibc::sys_waitpid(pid, &tmp_status, flags, NULL, &ret); e) { + errno = e; + return -1; + } + if(status) { + *status = tmp_status; + } + return ret; +} + +pid_t wait(int *status) { + return waitpid(-1, status, 0); +} + +pid_t wait3(int *status, int options, struct rusage *rusage) { + (void) rusage; + mlibc::infoLogger() << "\e[31mmlibc: wait3() is not implemented correctly\e[39m" + << frg::endlog; + return waitpid(-1, status, options); +} + +pid_t wait4(pid_t pid, int *status, int options, struct rusage *ru) { + pid_t ret; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_waitpid, -1); + if(int e = mlibc::sys_waitpid(pid, status, options, ru, &ret); e) { + errno = e; + return -1; + } + return ret; +} diff --git a/lib/mlibc/options/posix/generic/syslog-stubs.cpp b/lib/mlibc/options/posix/generic/syslog-stubs.cpp new file mode 100644 index 0000000..6a80ff9 --- /dev/null +++ b/lib/mlibc/options/posix/generic/syslog-stubs.cpp @@ -0,0 +1,152 @@ + +#include <syslog.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <time.h> +#include <fcntl.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <bits/ensure.h> + +#include <frg/mutex.hpp> +#include <mlibc/lock.hpp> +#include <mlibc/debug.hpp> + +// This syslog implementation is largely taken from musl + +static char log_ident[32]; +static int log_options; +static int log_facility = LOG_USER; +static int log_fd = -1; +static int log_opt; +static int log_mask = 0xff; + +static int use_mlibc_logger = 0; +static FutexLock __syslog_lock; + +static const struct sockaddr_un log_addr {AF_UNIX, "/dev/log"}; + +void closelog(void) { + frg::unique_lock<FutexLock> holder { __syslog_lock }; + close(log_fd); + log_fd = -1; +} + +static void __openlog() { + log_fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if(log_fd >= 0) { + int ret = connect(log_fd, (const sockaddr *)&log_addr, sizeof log_addr); + if(ret) { + mlibc::infoLogger() << "\e[31mmlibc: syslog: connect returned an error, falling back to infoLogger\e[39m" << frg::endlog; + use_mlibc_logger = 1; + } + } +} + +void openlog(const char *ident, int options, int facility) { + frg::unique_lock<FutexLock> holder { __syslog_lock }; + if(ident) { + size_t n = strnlen(ident, sizeof log_ident - 1); + memcpy(log_ident, ident, n); + log_ident[n] = 0; + } else { + log_ident[0] = 0; + } + log_options = options; + log_facility = facility; + + if((options & LOG_NDELAY) && log_fd < 0) + __openlog(); +} + +int setlogmask(int mask) { + int old_mask = log_mask; + + log_mask = mask; + + return old_mask; +} + +static void _vsyslog(int priority, const char *message, va_list ap) { + auto is_lost_conn = [] (int e) { + return e == ECONNREFUSED || e == ECONNRESET || e == ENOTCONN || e == EPIPE; + }; + + if(!(priority & log_mask)) { + return; + } + + char timebuf[16]; + time_t now; + struct tm tm; + char buf[1024]; + int errno_save = errno; + int pid; + int l, l2; + int hlen; + int fd; + + if(log_fd < 0) + __openlog(); + + if(use_mlibc_logger) { + vsnprintf(buf, sizeof buf, message, ap); + mlibc::infoLogger() << "mlibc: syslog: " << buf << frg::endlog; + return; + } + + if(!(priority & LOG_FACMASK)) + priority |= log_facility; + + now = time(NULL); + gmtime_r(&now, &tm); + strftime(timebuf, sizeof timebuf, "%b %e %T", &tm); + + pid = (log_opt & LOG_PID) ? getpid() : 0; + l = snprintf(buf, sizeof buf, "<%d>%s %n%s%s%.0d%s: ", + priority, timebuf, &hlen, log_ident, "[" + !pid, pid, "]" + !pid); + errno = errno_save; + l2 = vsnprintf(buf + l, sizeof buf - l, message, ap); + if(l2 >= 0) { + if(l2 >= (long int)(sizeof buf - l)) + l = sizeof buf - 1; + else + l += l2; + if(buf[l - 1] != '\n') + buf[l++] = '\n'; + if(send(log_fd, buf, l, 0) < 0 && (!is_lost_conn(errno) + || connect(log_fd, (const sockaddr *)&log_addr, sizeof log_addr) < 0 + || send(log_fd, buf, l, 0) < 0) + && (log_opt & LOG_CONS)) { + fd = open("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if(fd >= 0) { + dprintf(fd, "%.*s", l - hlen, buf + hlen); + close(fd); + } + } + if(log_opt & LOG_PERROR) + dprintf(STDERR_FILENO, "%.*s", l - hlen, buf + hlen); + } +} + +void syslog(int priority, const char *format, ...) { + va_list ap; + va_start(ap, format); + vsyslog(priority, format, ap); + va_end(ap); +} + +void vsyslog(int priority, const char *message, va_list ap) { + int cs; + if(!(log_mask & LOG_MASK(priority & 7)) || (priority & ~0x3ff)) { + mlibc::infoLogger() << "\e[31mmlibc: syslog: log_mask or priority out of range, not printing anything\e[39m" << frg::endlog; + return; + } + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); + frg::unique_lock<FutexLock> lock(__syslog_lock); + _vsyslog(priority, message, ap); + pthread_setcancelstate(cs, 0); +} diff --git a/lib/mlibc/options/posix/generic/termios-stubs.cpp b/lib/mlibc/options/posix/generic/termios-stubs.cpp new file mode 100644 index 0000000..631456a --- /dev/null +++ b/lib/mlibc/options/posix/generic/termios-stubs.cpp @@ -0,0 +1,103 @@ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include <errno.h> +#include <termios.h> +#include <sys/ioctl.h> + +#include <bits/ensure.h> +#include <mlibc/posix-sysdeps.hpp> + +speed_t cfgetispeed(const struct termios *tios) { + return tios->c_cflag & CBAUD; +} + +speed_t cfgetospeed(const struct termios *tios) { + return tios->c_cflag & CBAUD; +} + +int cfsetispeed(struct termios *termios, speed_t speed) { + return speed ? cfsetospeed(termios, speed) : 0; +} + +int cfsetospeed(struct termios *termios, speed_t speed) { + if(speed & ~CBAUD) { + errno = EINVAL; + return -1; + } + + termios->c_cflag &= ~CBAUD; + termios->c_cflag |= speed; + + return 0; +} + +void cfmakeraw(struct termios *t) { + t->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + t->c_oflag &= ~OPOST; + t->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + t->c_cflag &= ~(CSIZE | PARENB); + t->c_cflag |= CS8; + t->c_cc[VMIN] = 1; + t->c_cc[VTIME] = 0; +} + +int tcdrain(int fd) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_tcdrain, -1); + if(int e = mlibc::sys_tcdrain(fd); e) { + errno = e; + return -1; + } + return 0; +} + +int tcflow(int fd, int action) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_tcflow, -1); + if(int e = mlibc::sys_tcflow(fd, action); e) { + errno = e; + return -1; + } + return 0; +} + +int tcflush(int fd, int queue_selector) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_tcflush, -1); + if(int e = mlibc::sys_tcflush(fd, queue_selector); e) { + errno = e; + return -1; + } + return 0; +} + +int tcgetattr(int fd, struct termios *attr) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_tcgetattr, -1); + if(int e = mlibc::sys_tcgetattr(fd, attr); e) { + errno = e; + return -1; + } + return 0; +} + +pid_t tcgetsid(int fd) { + int sid; + if(ioctl(fd, TIOCGSID, &sid) < 0) { + return -1; + } + return sid; +} + +int tcsendbreak(int, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int tcsetattr(int fd, int opts, const struct termios *attr) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_tcsetattr, -1); + if(int e = mlibc::sys_tcsetattr(fd, opts, attr); e) { + errno = e; + return -1; + } + return 0; +} + diff --git a/lib/mlibc/options/posix/generic/time.cpp b/lib/mlibc/options/posix/generic/time.cpp new file mode 100644 index 0000000..14193af --- /dev/null +++ b/lib/mlibc/options/posix/generic/time.cpp @@ -0,0 +1,505 @@ +#include <ctype.h> +#include <langinfo.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <bits/ensure.h> +#include <mlibc/strings.hpp> + +namespace { + +int month_to_day(int month) { + switch(month){ + case 0: return 0; + case 1: return 31; + case 2: return 59; + case 3: return 90; + case 4: return 120; + case 5: return 151; + case 6: return 181; + case 7: return 212; + case 8: return 243; + case 9: return 273; + case 10: return 304; + case 11: return 334; + } + return -1; +} + +int is_leapyear(int year) { + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); +} + +int month_and_year_to_day_in_year(int month, int year){ + int day = month_to_day(month); + if(is_leapyear(year) && month < 2) + return day + 1; + + return day; +} + +int target_determination(int month) { + switch(month){ + case 0: return 3; + case 1: return 14; + case 2: return 14; + case 3: return 4; + case 4: return 9; + case 5: return 6; + case 6: return 11; + case 7: return 8; + case 8: return 5; + case 9: return 10; + case 10: return 7; + case 11: return 12; + } + + return -1; +} + +int doom_determination(int full_year) { + int century = full_year / 100; + int anchor = 2 + 5 * (century % 4) % 7; + + int year = full_year % 100; + + if(year % 2) + year += 11; + + year /= 2; + + if(year % 2) + year += 11; + + return 7 - (year % 7) + anchor; +} + +//Determine day of week through the doomsday algorithm. +int day_determination(int day, int month, int year) { + int doom = doom_determination(year); + bool leap = is_leapyear(year); + + int target = target_determination(month); + if(leap && month < 2) + target++; + + int doom_dif = (day - target) % 7; + return (doom + doom_dif) % 7; +} + +struct strptime_internal_state { + bool has_century; + bool has_year; + bool has_month; + bool has_day_of_month; + bool has_day_of_year; + bool has_day_of_week; + + bool full_year_given; + + int century; + + size_t format_index; + size_t input_index; +}; + +char *strptime_internal(const char *__restrict input, const char *__restrict format, + struct tm *__restrict tm, struct strptime_internal_state *__restrict state) { + auto matchLanginfoItem = [&] (int start, size_t num, int &dest, bool &flag) -> bool { + for(size_t i = start; i < (start + num); i++) { + const char *mon = nl_langinfo(i); + size_t len = strlen(mon); + if(mlibc::strncasecmp(&input[state->input_index], mon, len)) + continue; + state->input_index += len; + dest = i - start; + flag = true; + return true; + } + return false; + }; + + auto matchNumericRange = [&] (int start, int end, int &dest, bool *flag) -> bool { + int product = 0, n = 0; + sscanf(&input[state->input_index], "%d%n", &product, &n); + if(n == 0 || 2 < n) + return false; + if(product < start || product > end) + return false; + state->input_index += n; + dest = product; + if(flag) *flag = true; + return true; + }; + + while(isspace(input[state->input_index])) + state->input_index++; + + if(input[state->input_index] == '\0') + return NULL; + + while(format[state->format_index] != '\0'){ + if(format[state->format_index] != '%'){ + if(isspace(format[state->format_index])){ + while(isspace(input[state->input_index++])); + state->input_index--; + } + else { + if(format[state->format_index] != input[state->input_index++]) + return NULL; + } + state->format_index++; + continue; + } + state->format_index++; + switch(format[state->format_index]){ + case '%': + if(input[state->input_index++] != '%') + return NULL; + break; + case 'a': + case 'A': { + if (!matchLanginfoItem(DAY_1, 7, tm->tm_wday, state->has_day_of_week) && \ + !matchLanginfoItem(ABDAY_1, 7, tm->tm_wday, state->has_day_of_week)) + return NULL; + break; + } + case 'b': + case 'B': + case 'h': { + if (!matchLanginfoItem(MON_1, 12, tm->tm_mon, state->has_month) && \ + !matchLanginfoItem(ABMON_1, 12, tm->tm_mon, state->has_month)) + return NULL; + break; + } + case 'c': + __ensure(!"strptime() %c directive unimplemented."); + __builtin_unreachable(); + break; + case 'C': { + int product = 0, n = 0; + sscanf(&input[state->input_index], "%d%n", &product, &n); + if(n == 0 || 2 < n) + return NULL; + state->input_index += n; + state->century = product; + state->has_century = true; + break; + } + case 'd': //`%d` and `%e` are equivalent + case 'e': { + if(!matchNumericRange(1, 31, tm->tm_mday, &state->has_day_of_month)) + return NULL; + break; + } + case 'D': { //equivalent to `%m/%d/%y` + size_t pre_fi = state->format_index; + state->format_index = 0; + + char *result = strptime_internal(input, "%m/%d/%y", tm, state); + if(result == NULL) + return NULL; + + state->format_index = pre_fi; + break; + } + case 'H': { + if(!matchNumericRange(0, 23, tm->tm_hour, nullptr)) + return NULL; + break; + } + case 'I': { + if(!matchNumericRange(1, 12, tm->tm_hour, nullptr)) + return NULL; + break; + } + case 'j': { + if(!matchNumericRange(1, 366, tm->tm_yday, &state->has_day_of_year)) + return NULL; + tm->tm_yday--; + break; + } + case 'm': { + if(!matchNumericRange(1, 12, tm->tm_mon, &state->has_month)) + return NULL; + tm->tm_mon--; + break; + } + case 'M': { + if(!matchNumericRange(0, 59, tm->tm_min, nullptr)) + return NULL; + break; + } + case 'n': + case 't': { + size_t n = 0; + while(isspace(input[state->input_index++])) + n++; + if(n == 0) + return NULL; + state->input_index--; + break; + } + case 'p': { + const char *meridian_str = nl_langinfo(AM_STR); + size_t len = strlen(meridian_str); + if (!mlibc::strncasecmp(&input[state->input_index], meridian_str, len)) { + tm->tm_hour %= 12; + state->input_index += len; + break; + } + meridian_str = nl_langinfo(PM_STR); + len = strlen(meridian_str); + if (!mlibc::strncasecmp(&input[state->input_index], meridian_str, len)) { + tm->tm_hour %= 12; + tm->tm_hour += 12; + state->input_index += len; + break; + } + break; + } + case 'r': { //equivalent to `%I:%M:%S %p` + size_t pre_fi = state->format_index; + state->format_index = 0; + + char *result = strptime_internal(input, "%I:%M:%S %p", tm, state); + if(result == NULL) + return NULL; + + state->format_index = pre_fi; + break; + } + case 'R': { //equivalent to `%H:%M` + size_t pre_fi = state->format_index; + state->format_index = 0; + + char *result = strptime_internal(input, "%H:%M", tm, state); + if(result == NULL) + return NULL; + + state->format_index = pre_fi; + break; + } + case 'S': { + if(!matchNumericRange(0, 60, tm->tm_sec, nullptr)) + return NULL; + break; + } + case 'T': { //equivalent to `%H:%M:%S` + size_t pre_fi = state->format_index; + state->format_index = 0; + + char *result = strptime_internal(input, "%H:%M:%S", tm, state); + if(result == NULL) + return NULL; + + state->format_index = pre_fi; + break; + } + case 'U': + __ensure(!"strptime() %U directive unimplemented."); + __builtin_unreachable(); + break; + case 'w': { + int product = 0, n = 0; + sscanf(&input[state->input_index], "%d%n", &product, &n); + if(n == 0 || 1 < n) + return NULL; + state->input_index += n; + tm->tm_wday = product; + state->has_day_of_week = true; + break; + } + case 'W': + __ensure(!"strptime() %W directive unimplemented."); + __builtin_unreachable(); + break; + case 'x': + __ensure(!"strptime() %x directive unimplemented."); + __builtin_unreachable(); + break; + case 'X': + __ensure(!"strptime() %X directive unimplemented."); + __builtin_unreachable(); + break; + case 'y': { + int product = 0, n = 0; + sscanf(&input[state->input_index], "%d%n", &product, &n); + if(n == 0 || 2 < n) + return NULL; + if(product < 69) + product += 100; + state->input_index += n; + tm->tm_year = product; + state->has_year = true; + break; + } + case 'Y': { + int product = 0, n = 0; + sscanf(&input[state->input_index], "%d%n", &product, &n); + if(n == 0 || 4 < n) + return NULL; + state->input_index += n; + tm->tm_year = product - 1900; + state->has_year = true; + state->has_century = true; + state->full_year_given = true; + state->century = product / 100; + break; + } + case 'F': { //GNU extensions + //equivalent to `%Y-%m-%d` + size_t pre_fi = state->format_index; + state->format_index = 0; + + char *result = strptime_internal(input, "%Y-%m-%d", tm, state); + if(result == NULL) + return NULL; + + state->format_index = pre_fi; + break; + } + case 'g': + __ensure(!"strptime() %g directive unimplemented."); + __builtin_unreachable(); + break; + case 'G': + __ensure(!"strptime() %G directive unimplemented."); + __builtin_unreachable(); + break; + case 'u': { + if(!matchNumericRange(1, 7, tm->tm_wday, nullptr)) + return NULL; + tm->tm_wday--; + break; + } + case 'V': + __ensure(!"strptime() %V directive unimplemented."); + __builtin_unreachable(); + break; + case 'z': + __ensure(!"strptime() %z directive unimplemented."); + __builtin_unreachable(); + break; + case 'Z': + __ensure(!"strptime() %Z directive unimplemented."); + __builtin_unreachable(); + break; + case 's': //end of GNU extensions + __ensure(!"strptime() %s directive unimplemented."); + __builtin_unreachable(); + break; + case 'E': { //locale-dependent date & time representation + __ensure(!"strptime() %E* directives unimplemented."); + __builtin_unreachable(); + /* + state->format_index++; + switch(format[state->format_index]){ + case 'c': + break; + case 'C': + break; + case 'x': + break; + case 'X': + break; + case 'y': + break; + case 'Y': + break; + default: + return NULL; + } + */ + } + case 'O': { //locale-dependent numeric symbols + __ensure(!"strptime() %O* directives unimplemented."); + __builtin_unreachable(); + /* + state->format_index++; + switch(format[state->format_index]){ + case 'd': + case 'e': + break; + case 'H': + break; + case 'I': + break; + case 'm': + break; + case 'M': + break; + case 'S': + break; + case 'U': + break; + case 'w': + break; + case 'W': + break; + case 'y': + break; + default: + return NULL; + } + */ + } + default: + return NULL; + } + state->format_index++; + } + + return (char*)input + state->input_index; +} + +} //anonymous namespace + +char *strptime(const char *__restrict s, const char *__restrict format, struct tm *__restrict tm){ + struct strptime_internal_state state = {}; + + char *result = strptime_internal(s, format, tm, &state); + + if(result == NULL) + return NULL; + + if(state.has_century && !state.full_year_given){ + int full_year = state.century * 100; + + if(state.has_year){ + //Compensate for default century-adjustment of `%j` operand + if(tm->tm_year >= 100) + full_year += tm->tm_year - 100; + else + full_year += tm->tm_year; + } + + tm->tm_year = full_year - 1900; + + state.has_year = true; + } + + if(state.has_month && !state.has_day_of_year){ + int day = 0; + if(state.has_year) + day = month_and_year_to_day_in_year(tm->tm_mon, tm->tm_year); + else + day = month_to_day(tm->tm_mon); + + tm->tm_yday = day + tm->tm_mday - 1; + state.has_day_of_year = true; + } + + if(state.has_year && !state.has_day_of_week){ + if(!state.has_month && !state.has_day_of_month){ + tm->tm_wday = day_determination(0, 0, tm->tm_year + 1900); + } + else if(state.has_month && state.has_day_of_month){ + tm->tm_wday = day_determination(tm->tm_mday, tm->tm_mon, tm->tm_year + 1900); + } + state.has_day_of_week = true; + } + + return result; +} diff --git a/lib/mlibc/options/posix/generic/ucontext-stubs.cpp b/lib/mlibc/options/posix/generic/ucontext-stubs.cpp new file mode 100644 index 0000000..9413a78 --- /dev/null +++ b/lib/mlibc/options/posix/generic/ucontext-stubs.cpp @@ -0,0 +1,19 @@ +#include <ucontext.h> +#include <bits/ensure.h> + +int getcontext(ucontext_t *) { + __ensure(!"Not implemented!"); + __builtin_unreachable(); +} +int setcontext(const ucontext_t *) { + __ensure(!"Not implemented!"); + __builtin_unreachable(); +} +void makecontext(ucontext_t *, void (*)(), int, ...) { + __ensure(!"Not implemented!"); + __builtin_unreachable(); +} +int swapcontext(ucontext_t *, const ucontext_t *) { + __ensure(!"Not implemented!"); + __builtin_unreachable(); +} diff --git a/lib/mlibc/options/posix/generic/unistd-stubs.cpp b/lib/mlibc/options/posix/generic/unistd-stubs.cpp new file mode 100644 index 0000000..39cf76a --- /dev/null +++ b/lib/mlibc/options/posix/generic/unistd-stubs.cpp @@ -0,0 +1,1227 @@ +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <sys/resource.h> +#include <unistd.h> +#include <limits.h> +#include <termios.h> +#include <pwd.h> +#include <sys/ioctl.h> + +#include <bits/ensure.h> +#include <mlibc/allocator.hpp> +#include <mlibc/arch-defs.hpp> +#include <mlibc/debug.hpp> +#include <mlibc/posix-sysdeps.hpp> +#include <mlibc/bsd-sysdeps.hpp> +#include <mlibc/thread.hpp> + +namespace { + +constexpr bool logExecvpeTries = false; + +} + +unsigned int alarm(unsigned int seconds) { + struct itimerval it = {}, old = {}; + it.it_value.tv_sec = seconds; + setitimer(ITIMER_REAL, &it, &old); + return old.it_value.tv_sec + !! old.it_value.tv_usec; +} + +int chdir(const char *path) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_chdir, -1); + if(int e = mlibc::sys_chdir(path); e) { + errno = e; + return -1; + } + return 0; +} + +int fchdir(int fd) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchdir, -1); + if(int e = mlibc::sys_fchdir(fd); e) { + errno = e; + return -1; + } + return 0; +} + +int chown(const char *path, uid_t uid, gid_t gid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchownat, -1); + if(int e = mlibc::sys_fchownat(AT_FDCWD, path, uid, gid, 0); e) { + errno = e; + return -1; + } + return 0; +} + +ssize_t confstr(int name, char *buf, size_t len) { + const char *str = ""; + if (name == _CS_PATH) { + str = "/bin:/usr/bin"; + } else if(name == _CS_GNU_LIBPTHREAD_VERSION) { + // We are not glibc, so we can return 0 here. + return 0; + } else if(name == _CS_GNU_LIBC_VERSION) { + // We are not glibc, so we can return 0 here. + return 0; + } else { + mlibc::infoLogger() << "\e[31mmlibc: confstr() request " << name << " is unimplemented\e[39m" + << frg::endlog; + __ensure(!"Not implemented"); + } + + return snprintf(buf, len, "%s", str) + 1; +} + +void _exit(int status) { + mlibc::sys_exit(status); +} + +int execl(const char *path, const char *arg0, ...) { + // TODO: It's a stupid idea to limit the number of arguments here. + char *argv[16]; + argv[0] = const_cast<char *>(arg0); + + va_list args; + int n = 1; + va_start(args, arg0); + while(true) { + __ensure(n < 15); + auto argn = va_arg(args, const char *); + argv[n++] = const_cast<char *>(argn); + if(!argn) + break; + } + va_end(args); + argv[n] = nullptr; + + return execve(path, argv, environ); +} + +// This function is taken from musl. +int execle(const char *path, const char *arg0, ...) { + int argc; + va_list ap; + va_start(ap, arg0); + for(argc = 1; va_arg(ap, const char *); argc++); + va_end(ap); + + int i; + char *argv[argc + 1]; + char **envp; + va_start(ap, arg0); + argv[0] = (char *)argv; + for(i = 1; i <= argc; i++) + argv[i] = va_arg(ap, char *); + envp = va_arg(ap, char **); + va_end(ap); + return execve(path, argv, envp); +} + +// This function is taken from musl +int execlp(const char *file, const char *argv0, ...) { + int argc; + va_list ap; + va_start(ap, argv0); + for(argc = 1; va_arg(ap, const char *); argc++); + va_end(ap); + { + int i; + char *argv[argc + 1]; + va_start(ap, argv0); + argv[0] = (char *)argv0; + for(i = 1; i < argc; i++) + argv[i] = va_arg(ap, char *); + argv[i] = NULL; + va_end(ap); + return execvp(file, argv); + } +} + +int execv(const char *path, char *const argv[]) { + return execve(path, argv, environ); +} + +int execvp(const char *file, char *const argv[]) { + return execvpe(file, argv, environ); +} + +int execvpe(const char *file, char *const argv[], char *const envp[]) { + char *null_list[] = { + nullptr + }; + + if(!argv) + argv = null_list; + if(!envp) + envp = null_list; + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_execve, -1); + + if(strchr(file, '/')) { + int e = mlibc::sys_execve(file, argv, envp); + __ensure(e && "sys_execve() is supposed to never return with success"); + errno = e; + return -1; + } + + frg::string_view dirs; + if(const char *pv = getenv("PATH"); pv) { + dirs = pv; + }else{ + dirs = "/bin:/usr/bin"; + } + + size_t p = 0; + int res = ENOENT; + while(p < dirs.size()) { + size_t s; // Offset of next colon or end of string. + if(size_t cs = dirs.find_first(':', p); cs != size_t(-1)) { + s = cs; + }else{ + s = dirs.size(); + } + + frg::string<MemoryAllocator> path{getAllocator()}; + path += dirs.sub_string(p, s - p); + path += "/"; + path += file; + + if(logExecvpeTries) + mlibc::infoLogger() << "mlibc: execvpe() tries '" << path.data() << "'" << frg::endlog; + + int e = mlibc::sys_execve(path.data(), argv, envp); + __ensure(e && "sys_execve() is supposed to never return with success"); + switch(e) { + case ENOENT: + case ENOTDIR: + break; + case EACCES: + res = EACCES; + break; + default: + errno = e; + return -1; + } + + p = s + 1; + } + + errno = res; + return -1; +} + +int faccessat(int dirfd, const char *pathname, int mode, int flags) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_faccessat, -1); + if(int e = mlibc::sys_faccessat(dirfd, pathname, mode, flags); e) { + errno = e; + return -1; + } + return 0; +} + +int fchown(int fd, uid_t uid, gid_t gid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchownat, -1); + if(int e = mlibc::sys_fchownat(fd, "", uid, gid, AT_EMPTY_PATH); e) { + errno = e; + return -1; + } + return 0; +} + +int fchownat(int fd, const char *path, uid_t uid, gid_t gid, int flags) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchownat, -1); + if(int e = mlibc::sys_fchownat(fd, path, uid, gid, flags); e) { + errno = e; + return -1; + } + return 0; +} + +int fdatasync(int fd) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fdatasync, -1); + if(int e = mlibc::sys_fdatasync(fd); e) { + errno = e; + return -1; + } + return 0; +} + +int fexecve(int, char *const [], char *const []) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +long fpathconf(int, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int fsync(int fd) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fsync, -1); + if(auto e = mlibc::sys_fsync(fd); e) { + errno = e; + return -1; + } + return 0; +} + +int ftruncate(int fd, off_t size) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ftruncate, -1); + if(int e = mlibc::sys_ftruncate(fd, size); e) { + errno = e; + return -1; + } + return 0; +} + +char *getcwd(char *buffer, size_t size) { + if (buffer) { + if (size == 0) { + errno = EINVAL; + return NULL; + } + } else if (!buffer) { + if (size == 0) + size = PATH_MAX; + + buffer = (char *)malloc(size); + } + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getcwd, nullptr); + if(int e = mlibc::sys_getcwd(buffer, size); e) { + errno = e; + return NULL; + } + + return buffer; +} + +int getgroups(int size, gid_t list[]) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getgroups, -1); + int ret; + if(int e = mlibc::sys_getgroups(size, list, &ret); e) { + errno = e; + return -1; + } + return ret; +} + +long gethostid(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int gethostname(char *buffer, size_t bufsize) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_gethostname, -1); + if(auto e = mlibc::sys_gethostname(buffer, bufsize); e) { + errno = e; + return -1; + } + return 0; +} + +int sethostname(const char *buffer, size_t bufsize) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sethostname, -1); + if(auto e = mlibc::sys_sethostname(buffer, bufsize); e) { + errno = e; + return -1; + } + return 0; +} + +// Code taken from musl +char *getlogin(void) { + return getenv("LOGNAME"); +} + +int getlogin_r(char *, size_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +// optarg and optind are provided to us by the GLIBC part of the mlibc. + +static char *scan = NULL; /* Private scan pointer. */ + +int getopt(int argc, char *const argv[], const char *optstring) { + char c; + char *place; + + optarg = NULL; + + if (!scan || *scan == '\0') { + if (optind == 0) + optind++; + + if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') + return EOF; + if (argv[optind][1] == '-' && argv[optind][2] == '\0') { + optind++; + return EOF; + } + + scan = argv[optind]+1; + optind++; + } + + c = *scan++; + place = strchr(optstring, c); + + if (!place || c == ':') { + fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); + return '?'; + } + + place++; + if (*place == ':') { + if (*scan != '\0') { + optarg = scan; + scan = NULL; + } else if( optind < argc ) { + optarg = argv[optind]; + optind++; + } else { + fprintf(stderr, "%s: option requires argument -%c\n", argv[0], c); + return ':'; + } + } + + return c; +} + +pid_t getpgid(pid_t pid) { + pid_t pgid; + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getpgid, -1); + if(int e = mlibc::sys_getpgid(pid, &pgid); e) { + errno = e; + return -1; + } + return pgid; +} + +pid_t getpgrp(void) { + return getpgid(0); +} + +pid_t getsid(pid_t pid) { + if(!mlibc::sys_getsid) { + MLIBC_MISSING_SYSDEP(); + return -1; + } + pid_t sid; + if(int e = mlibc::sys_getsid(pid, &sid); e) { + errno = e; + return -1; + } + return sid; +} + +int lchown(const char *path, uid_t uid, gid_t gid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchownat, -1); + if(int e = mlibc::sys_fchownat(AT_FDCWD, path, uid, gid, AT_SYMLINK_NOFOLLOW); e) { + errno = e; + return -1; + } + return 0; +} + +int link(const char *old_path, const char *new_path) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_link, -1); + if(int e = mlibc::sys_link(old_path, new_path); e) { + errno = e; + return -1; + } + return 0; +} + +int linkat(int olddirfd, const char *old_path, int newdirfd, const char *new_path, int flags) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_linkat, -1); + if(int e = mlibc::sys_linkat(olddirfd, old_path, newdirfd, new_path, flags); e) { + errno = e; + return -1; + } + return 0; +} + +// Code take from musl +int lockf(int fd, int op, off_t size) { + struct flock l = { + .l_type = F_WRLCK, + .l_whence = SEEK_CUR, + .l_start = 0, + .l_len = size, + .l_pid = 0, + }; + + switch(op) { + case F_TEST: + l.l_type = F_RDLCK; + if(fcntl(fd, F_GETLK, &l) < 0) + return -1; + if(l.l_type == F_UNLCK || l.l_pid == getpid()) + return 0; + errno = EACCES; + return -1; + case F_ULOCK: + l.l_type = F_UNLCK; + [[fallthrough]]; + case F_TLOCK: + return fcntl(fd, F_SETLK, &l); + case F_LOCK: + return fcntl(fd, F_SETLKW, &l); + } + + errno = EINVAL; + return -1; +} + +int nice(int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +long pathconf(const char *, int name) { + switch (name) { + case _PC_NAME_MAX: + return NAME_MAX; + default: + mlibc::infoLogger() << "missing pathconf() entry " << name << frg::endlog; + errno = EINVAL; + return -1; + } +} + +int pause(void) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pause, -1); + if(int e = mlibc::sys_pause(); e) { + errno = e; + return -1; + } + __ensure(!"There is no successful completion return value for pause"); + __builtin_unreachable(); +} + +int pipe(int *fds) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pipe, -1); + if(int e = mlibc::sys_pipe(fds, 0); e) { + errno = e; + return -1; + } + return 0; +} + +int pipe2(int *fds, int flags) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pipe, -1); + if(int e = mlibc::sys_pipe(fds, flags); e) { + errno = e; + return -1; + } + return 0; +} + +ssize_t pread(int fd, void *buf, size_t n, off_t off) { + ssize_t num_read; + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pread, -1); + if(int e = mlibc::sys_pread(fd, buf, n, off, &num_read); e) { + errno = e; + return -1; + } + return num_read; +} + +ssize_t pwrite(int fd, const void *buf, size_t n, off_t off) { + ssize_t num_written; + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pwrite, -1); + if(int e = mlibc::sys_pwrite(fd, buf, n, off, &num_written); e) { + errno = e; + return -1; + } + return num_written; +} + +ssize_t readlink(const char *__restrict path, char *__restrict buffer, size_t max_size) { + ssize_t length; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_readlink, -1); + if(int e = mlibc::sys_readlink(path, buffer, max_size, &length); e) { + errno = e; + return -1; + } + return length; +} + +ssize_t readlinkat(int, const char *__restrict, char *__restrict, size_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int rmdir(const char *path) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_rmdir, -1); + if(int e = mlibc::sys_rmdir(path); e) { + errno = e; + return -1; + } + return 0; +} + +int setegid(gid_t egid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setegid, 0); + if(int e = mlibc::sys_setegid(egid); e) { + errno = e; + return -1; + } + return 0; +} + +int seteuid(uid_t euid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_seteuid, 0); + if(int e = mlibc::sys_seteuid(euid); e) { + errno = e; + return -1; + } + return 0; +} + +int setgid(gid_t gid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setgid, 0); + if(int e = mlibc::sys_setgid(gid); e) { + errno = e; + return -1; + } + return 0; +} + +int setpgid(pid_t pid, pid_t pgid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setpgid, -1); + if(int e = mlibc::sys_setpgid(pid, pgid); e) { + errno = e; + return -1; + } + return 0; +} + +pid_t setpgrp(void) { + return setpgid(0, 0); +} + +int setregid(gid_t rgid, gid_t egid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setregid, -1); + if(int e = mlibc::sys_setregid(rgid, egid); e) { + errno = e; + return -1; + } + return 0; +} + +int setreuid(uid_t ruid, uid_t euid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setreuid, -1); + if(int e = mlibc::sys_setreuid(ruid, euid); e) { + errno = e; + return -1; + } + return 0; +} + +pid_t setsid(void) { + if(!mlibc::sys_setsid) { + MLIBC_MISSING_SYSDEP(); + return -1; + } + pid_t sid; + if(int e = mlibc::sys_setsid(&sid); e) { + errno = e; + return -1; + } + return sid; +} + +int setuid(uid_t uid) { + if(!mlibc::sys_setuid) { + MLIBC_MISSING_SYSDEP(); + mlibc::infoLogger() << "mlibc: missing sysdep sys_setuid(). Returning 0" << frg::endlog; + return 0; + } + if(int e = mlibc::sys_setuid(uid); e) { + errno = e; + return -1; + } + return 0; +} + +void swab(const void *__restrict, void *__restrict, ssize_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int symlink(const char *target_path, const char *link_path) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_symlink, -1); + if(int e = mlibc::sys_symlink(target_path, link_path); e) { + errno = e; + return -1; + } + return 0; +} + +int symlinkat(const char *target_path, int dirfd, const char *link_path) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_symlinkat, -1); + if(int e = mlibc::sys_symlinkat(target_path, dirfd, link_path); e) { + errno = e; + return -1; + } + return 0; +} + +void sync(void) { + if(!mlibc::sys_sync) { + MLIBC_MISSING_SYSDEP(); + } else { + mlibc::sys_sync(); + } +} + +long sysconf(int number) { + if(mlibc::sys_sysconf) { + long ret = 0; + + int e = mlibc::sys_sysconf(number, &ret); + + if(e && e != EINVAL) { + errno = e; + return -1; + } + + if(e != EINVAL) { + return ret; + } + } + + /* default return values, if not overriden by sysdep */ + switch(number) { + case _SC_ARG_MAX: + // On linux, it is defined to 2097152 in most cases, so define it to be 2097152 + return 2097152; + case _SC_PAGE_SIZE: + return mlibc::page_size; + case _SC_OPEN_MAX: + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_OPEN_MAX) returns fallback value 256\e[39m" << frg::endlog; + return 256; + case _SC_PHYS_PAGES: + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_PHYS_PAGES) returns fallback value 1024\e[39m" << frg::endlog; + return 1024; + case _SC_NPROCESSORS_ONLN: + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_NPROCESSORS_ONLN) returns fallback value 1\e[39m" << frg::endlog; + return 1; + case _SC_GETPW_R_SIZE_MAX: + return NSS_BUFLEN_PASSWD; + case _SC_GETGR_R_SIZE_MAX: + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_GETGR_R_SIZE_MAX) returns fallback value 1024\e[39m" << frg::endlog; + return 1024; + case _SC_CHILD_MAX: + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_CHILD_MAX) returns fallback value 25\e[39m" << frg::endlog; + // On linux, it is defined to 25 in most cases, so define it to be 25 + return 25; + case _SC_JOB_CONTROL: + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_JOB_CONTROL) returns fallback value 1\e[39m" << frg::endlog; + // If 1, job control is supported + return 1; + case _SC_CLK_TCK: + // TODO: This should be obsolete? + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_CLK_TCK) is obsolete and returns arbitrary value 1000000\e[39m" << frg::endlog; + return 1000000; + case _SC_NGROUPS_MAX: + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_NGROUPS_MAX) returns fallback value 65536\e[39m" << frg::endlog; + // On linux, it is defined to 65536 in most cases, so define it to be 65536 + return 65536; + case _SC_RE_DUP_MAX: + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_RE_DUP_MAX) returns fallback value RE_DUP_MAX\e[39m" << frg::endlog; + return RE_DUP_MAX; + case _SC_LINE_MAX: + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_LINE_MAX) returns fallback value 2048\e[39m" << frg::endlog; + // Linux defines it as 2048. + return 2048; + case _SC_XOPEN_CRYPT: +#if __MLIBC_CRYPT_OPTION + return _XOPEN_CRYPT; +#else + return -1; +#endif /* __MLIBC_CRYPT_OPTION */ + case _SC_NPROCESSORS_CONF: + // TODO: actually return a proper value for _SC_NPROCESSORS_CONF + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_NPROCESSORS_CONF) unconditionally returns fallback value 1\e[39m" << frg::endlog; + return 1; + case _SC_HOST_NAME_MAX: + mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_HOST_NAME_MAX) unconditionally returns fallback value 256\e[39m" << frg::endlog; + return 256; + default: + mlibc::infoLogger() << "\e[31mmlibc: sysconf() call is not implemented, number: " << number << "\e[39m" << frg::endlog; + errno = EINVAL; + return -1; + } +} + +pid_t tcgetpgrp(int fd) { + int pgrp; + if(ioctl(fd, TIOCGPGRP, &pgrp) < 0) { + return -1; + } + return pgrp; +} + +int tcsetpgrp(int fd, pid_t pgrp) { + return ioctl(fd, TIOCSPGRP, &pgrp); +} + +int truncate(const char *, off_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +char *ttyname(int fd) { + const size_t size = 128; + static thread_local char buf[size]; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ttyname, nullptr); + if(int e = mlibc::sys_ttyname(fd, buf, size); e) { + errno = e; + return nullptr; + } + return buf; +} + +int ttyname_r(int fd, char *buf, size_t size) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ttyname, -1); + if(int e = mlibc::sys_ttyname(fd, buf, size); e) { + return e; + } + return 0; +} + +int unlink(const char *path) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_unlinkat, -1); + if(int e = mlibc::sys_unlinkat(AT_FDCWD, path, 0); e) { + errno = e; + return -1; + } + return 0; +} + +int unlinkat(int fd, const char *path, int flags) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_unlinkat, -1); + if(int e = mlibc::sys_unlinkat(fd, path, flags); e) { + errno = e; + return -1; + } + return 0; +} + +int getpagesize() { + return mlibc::page_size; +} + +// Code taken from musl +// GLIBC extension for stdin/stdout +char *getpass(const char *prompt) { + int fdin, fdout; + struct termios s, t; + ssize_t l; + static char password[128]; + + if((fdin = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0) { + fdin = STDIN_FILENO; + fdout = STDOUT_FILENO; + } else { + fdout = fdin; + } + + tcgetattr(fdin, &t); + s = t; + t.c_lflag &= ~(ECHO | ISIG); + t.c_lflag |= ICANON; + t.c_iflag &= ~(INLCR | IGNCR); + t.c_iflag |= ICRNL; + tcsetattr(fdin, TCSAFLUSH, &t); + tcdrain(fdin); + + dprintf(fdout, "%s", prompt); + + l = read(fdin, password, sizeof password); + if(l >= 0) { + if((l > 0 && password[l - 1] == '\n') || l == sizeof password) + l--; + password[l] = 0; + } + + tcsetattr(fdin, TCSAFLUSH, &s); + + dprintf(fdout, "\n"); + if(fdin != STDIN_FILENO) { + close(fdin); + } + + return l < 0 ? 0 : password; +} + +char *get_current_dir_name(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +// This is a Linux extension +pid_t gettid(void) { + if(!mlibc::sys_gettid) { + MLIBC_MISSING_SYSDEP(); + __ensure(!"Cannot continue without sys_gettid()"); + } + return mlibc::sys_gettid(); +} + +int getentropy(void *buffer, size_t length) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getentropy, -1); + if(length > 256) { + errno = EIO; + return -1; + } + if(int e = mlibc::sys_getentropy(buffer, length); e) { + errno = e; + return -1; + } + return 0; +} + +ssize_t write(int fd, const void *buf, size_t count) { + ssize_t bytes_written; + if(int e = mlibc::sys_write(fd, buf, count, &bytes_written); e) { + errno = e; + return (ssize_t)-1; + } + return bytes_written; +} + +ssize_t read(int fd, void *buf, size_t count) { + ssize_t bytes_read; + if(int e = mlibc::sys_read(fd, buf, count, &bytes_read); e) { + errno = e; + return (ssize_t)-1; + } + return bytes_read; +} + +off_t lseek(int fd, off_t offset, int whence) { + off_t new_offset; + if(int e = mlibc::sys_seek(fd, offset, whence, &new_offset); e) { + errno = e; + return (off_t)-1; + } + return new_offset; +} + +off64_t lseek64(int fd, off64_t offset, int whence) { + off64_t new_offset; + if(int e = mlibc::sys_seek(fd, offset, whence, &new_offset); e) { + errno = e; + return (off64_t)-1; + } + return new_offset; +} + +int close(int fd) { + return mlibc::sys_close(fd); +} + +unsigned int sleep(unsigned int secs) { + time_t seconds = secs; + long nanos = 0; + if(!mlibc::sys_sleep) { + MLIBC_MISSING_SYSDEP(); + __ensure(!"Cannot continue without sys_sleep()"); + } + mlibc::sys_sleep(&seconds, &nanos); + return seconds; +} + +// In contrast to sleep() this functions returns 0/-1 on success/failure. +int usleep(useconds_t usecs) { + time_t seconds = 0; + long nanos = usecs * 1000; + if(!mlibc::sys_sleep) { + MLIBC_MISSING_SYSDEP(); + __ensure(!"Cannot continue without sys_sleep()"); + } + return mlibc::sys_sleep(&seconds, &nanos); +} + +int dup(int fd) { + int newfd; + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_dup, -1); + if(int e = mlibc::sys_dup(fd, 0, &newfd); e) { + errno = e; + return -1; + } + return newfd; +} + +int dup2(int fd, int newfd) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_dup2, -1); + if(int e = mlibc::sys_dup2(fd, 0, newfd); e) { + errno = e; + return -1; + } + return newfd; +} + +pid_t fork(void) { + auto self = mlibc::get_current_tcb(); + pid_t child; + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fork, -1); + + auto hand = self->atforkEnd; + while (hand) { + if (hand->prepare) + hand->prepare(); + + hand = hand->prev; + } + + if(int e = mlibc::sys_fork(&child); e) { + errno = e; + return -1; + } + + hand = self->atforkBegin; + while (hand) { + if (!child) { + if (hand->child) + hand->child(); + } else { + if (hand->parent) + hand->parent(); + } + hand = hand->next; + } + + return child; +} + +pid_t vfork(void) { + pid_t child; + /* + * Fork handlers established using pthread_atfork(3) are not + * called when a multithreaded program employing the NPTL + * threading library calls vfork(). Fork handlers are called + * in this case in a program using the LinuxThreads threading + * library. (See pthreads(7) for a description of Linux + * threading libraries.) + * - vfork(2), release 5.13 of the Linux man-pages project + * + * as a result, we call sys_fork instead of running atforks + */ + + /* deferring to fork as implementing vfork correctly requires assembly + * to handle not mucking up the stack + */ + if(!mlibc::sys_fork) { + MLIBC_MISSING_SYSDEP(); + errno = ENOSYS; + return -1; + } + + if(int e = mlibc::sys_fork(&child); e) { + errno = e; + return -1; + } + + return child; +} + +int execve(const char *path, char *const argv[], char *const envp[]) { + char *null_list[] = { + nullptr + }; + + if(!argv) + argv = null_list; + if(!envp) + envp = null_list; + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_execve, -1); + int e = mlibc::sys_execve(path, argv, envp); + __ensure(e && "sys_execve() is expected to fail if it returns"); + errno = e; + return -1; +} + +gid_t getgid(void) { + if(!mlibc::sys_getgid) { + MLIBC_MISSING_SYSDEP(); + __ensure(!"Cannot continue without sys_getgid()"); + } + return mlibc::sys_getgid(); +} + +gid_t getegid(void) { + if(!mlibc::sys_getegid) { + MLIBC_MISSING_SYSDEP(); + __ensure(!"Cannot continue without sys_getegid()"); + } + return mlibc::sys_getegid(); +} + +uid_t getuid(void) { + if(!mlibc::sys_getuid) { + MLIBC_MISSING_SYSDEP(); + __ensure(!"Cannot continue without sys_getuid()"); + } + return mlibc::sys_getuid(); +} + +uid_t geteuid(void) { + if(!mlibc::sys_geteuid) { + MLIBC_MISSING_SYSDEP(); + __ensure(!"Cannot continue without sys_geteuid()"); + } + return mlibc::sys_geteuid(); +} + +pid_t getpid(void) { + if(!mlibc::sys_getpid) { + MLIBC_MISSING_SYSDEP(); + __ensure(!"Cannot continue without sys_getpid()"); + } + return mlibc::sys_getpid(); +} + +pid_t getppid(void) { + if(!mlibc::sys_getppid) { + MLIBC_MISSING_SYSDEP(); + __ensure(!"Cannot continue without sys_getppid()"); + } + return mlibc::sys_getppid(); +} + +int access(const char *path, int mode) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_access, -1); + if(int e = mlibc::sys_access(path, mode); e) { + errno = e; + return -1; + } + return 0; +} + +char *getusershell(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void setusershell(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +void endusershell(void) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int isatty(int fd) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_isatty, 0); + if(int e = mlibc::sys_isatty(fd); e) { + errno = e; + return 0; + } + return 1; +} + +int chroot(const char *ptr) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_chroot, -1); + if(int e = mlibc::sys_chroot(ptr); e) { + errno = e; + return -1; + } + return 0; +} + +int daemon(int, int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +char *ctermid(char *s) { + return s ? strcpy(s, "/dev/tty") : const_cast<char *>("/dev/tty"); +} + +int setresuid(uid_t ruid, uid_t euid, uid_t suid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setresuid, -1); + if(int e = mlibc::sys_setresuid(ruid, euid, suid); e) { + errno = e; + return -1; + } + return 0; +} + +int setresgid(gid_t rgid, gid_t egid, gid_t sgid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setresgid, -1); + if(int e = mlibc::sys_setresgid(rgid, egid, sgid); e) { + errno = e; + return -1; + } + return 0; +} + +int getdomainname(char *, size_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int setdomainname(const char *, size_t) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getresuid, -1); + if(int e = mlibc::sys_getresuid(ruid, euid, suid); e) { + errno = e; + return -1; + } + return 0; +} + +int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getresgid, -1); + if(int e = mlibc::sys_getresgid(rgid, egid, sgid); e) { + errno = e; + return -1; + } + return 0; +} + +#if __MLIBC_CRYPT_OPTION +void encrypt(char[64], int) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} +#endif + +#if __MLIBC_BSD_OPTION +void *sbrk(intptr_t increment) { + if(increment) { + errno = ENOMEM; + return (void *)-1; + } + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_brk, (void *)-1); + void *out; + if(int e = mlibc::sys_brk(&out); e) { + errno = e; + return (void *)-1; + } + return out; +} +#endif diff --git a/lib/mlibc/options/posix/generic/utime-stubs.cpp b/lib/mlibc/options/posix/generic/utime-stubs.cpp new file mode 100644 index 0000000..f78729f --- /dev/null +++ b/lib/mlibc/options/posix/generic/utime-stubs.cpp @@ -0,0 +1,31 @@ + +#include <utime.h> +#include <fcntl.h> +#include <errno.h> + +#include <bits/ensure.h> +#include <mlibc/posix-sysdeps.hpp> + +int utime(const char *filename, const struct utimbuf *times) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_utimensat, -1); + struct timespec time[2]; + if(times) { + time[0].tv_sec = times->actime; + time[0].tv_nsec = 0; + time[1].tv_sec = times->modtime; + time[1].tv_nsec = 0; + } else { + time[0].tv_sec = UTIME_NOW; + time[0].tv_nsec = UTIME_NOW; + time[1].tv_sec = UTIME_NOW; + time[1].tv_nsec = UTIME_NOW; + } + + if (int e = mlibc::sys_utimensat(AT_FDCWD, filename, time, 0); e) { + errno = e; + return -1; + } + + return 0; +} + diff --git a/lib/mlibc/options/posix/generic/wordexp-stubs.cpp b/lib/mlibc/options/posix/generic/wordexp-stubs.cpp new file mode 100644 index 0000000..8443527 --- /dev/null +++ b/lib/mlibc/options/posix/generic/wordexp-stubs.cpp @@ -0,0 +1,342 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * code taken from OPNSense, with modifications + * + * Copyright (c) 2002 Tim J. Robbins. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <wordexp.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include <stdint.h> + +#define SHELL_PATH "/bin/sh" +#define SHELL_NAME "sh" + +static size_t we_read_fully(int fd, char *buffer, size_t len) { + size_t done = 0; + + do { + ssize_t nread = read(fd, buffer + done, len - done); + if(nread == -1 && errno == EINTR) + continue; + if(nread <= 0) + break; + done += nread; + } while (done != len); + + return done; +} + +static int we_askshell(const char *words, wordexp_t *we, int flags) { + int pdes[2]; /* pipe to child */ + char bbuf[9]; /* buffer for byte count */ + char wbuf[9]; /* buffer for word count */ + size_t nwords = 0; /* number of words from child */ + size_t nbytes = 0; /* number of bytes from child */ + size_t sofs = 0; /* offset into we->we_strings */ + size_t vofs = 0; /* offset into we->we_wordv */ + pid_t pid; /* PID of child */ + pid_t wpid; /* waitpid return value */ + int status; /* child exit status */ + int error; /* our return value */ + int serrno; /* errno to return */ + char *np, *p; /* handy pointers */ + char *nstrings; /* temporary for realloc() */ + char **new_wordv; /* temporary for realloc() */ + sigset_t newsigblock; + sigset_t oldsigblock; + const char *ifs = getenv("IFS"); + + serrno = errno; + + if(pipe2(pdes, O_CLOEXEC) < 0) + return WRDE_NOSPACE; + + (void)sigemptyset(&newsigblock); + (void)sigaddset(&newsigblock, SIGCHLD); + (void)sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); + + if((pid = fork()) < 0) { + serrno = errno; + close(pdes[0]); + close(pdes[1]); + (void)sigprocmask(SIG_SETMASK, &oldsigblock, NULL); + errno = serrno; + return WRDE_NOSPACE; + } else if(pid == 0) { + /* + * We are the child; make /bin/sh expand `words'. + */ + (void)sigprocmask(SIG_SETMASK, &oldsigblock, NULL); + if((pdes[1] != STDOUT_FILENO ? dup2(pdes[1], STDOUT_FILENO) : fcntl(pdes[1], F_SETFD, 0)) < 0) + _exit(1); + + execl(SHELL_PATH, SHELL_NAME, flags & WRDE_UNDEF ? "-u" : "+u", + "-c", "IFS=$1;eval \"$2\";eval \"set -- $3\";IFS=;a=\"$*\";" + "printf '%08x' \"$#\" \"${#a}\";printf '%s\\0' \"$@\"", "", + ifs != NULL ? ifs : " \t\n", + flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", + words, + (char *)NULL); + _exit(1); + } + + /* + * We are the parent; read the output of the shell wordexp function, + * which is a 32-bit hexadecimal word count, a 32-bit hexadecimal + * byte count (not including terminating null bytes), followed by + * the expanded words separated by nulls. + */ + close(pdes[1]); + if(we_read_fully(pdes[0], wbuf, 8) != 8 || we_read_fully(pdes[0], bbuf, 8) != 8) { + error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; + serrno = errno; + goto cleanup; + } + wbuf[8] = bbuf[8] = '\0'; + nwords = strtol(wbuf, NULL, 16); + nbytes = strtol(bbuf, NULL, 16) + nwords; + + /* + * Allocate or reallocate (when flags & WRDE_APPEND) the word vector + * and string storage buffers for the expanded words we're about to + * read from the child. + */ + sofs = we->we_nbytes; + vofs = we->we_wordc; + if((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS | WRDE_APPEND)) + vofs += we->we_offs; + we->we_wordc += nwords; + we->we_nbytes += nbytes; + + if((new_wordv = (char **) realloc(we->we_wordv, (we->we_wordc + 1 + (flags & WRDE_DOOFFS ? we->we_offs : 0)) * sizeof(char *))) == NULL) { + error = WRDE_NOSPACE; + goto cleanup; + } + + we->we_wordv = new_wordv; + + if((nstrings = (char *) realloc(we->we_strings, we->we_nbytes)) == NULL) { + error = WRDE_NOSPACE; + goto cleanup; + } + + for(size_t i = 0; i < vofs; i++) { + if(we->we_wordv[i] != NULL) + we->we_wordv[i] += nstrings - we->we_strings; + } + we->we_strings = nstrings; + + if(we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { + error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; + serrno = errno; + goto cleanup; + } + + error = 0; +cleanup: + close(pdes[0]); + + do { + wpid = waitpid(pid, &status, 0); + } while(wpid < 0 && errno == EINTR); + + (void)sigprocmask(SIG_SETMASK, &oldsigblock, NULL); + + if(error != 0) { + errno = serrno; + return error; + } + + if(wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) + return flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; + + /* + * Break the null-terminated expanded word strings out into + * the vector. + */ + if(vofs == 0 && flags & WRDE_DOOFFS) { + while (vofs < we->we_offs) + we->we_wordv[vofs++] = NULL; + } + + p = we->we_strings + sofs; + while (nwords-- != 0) { + we->we_wordv[vofs++] = p; + if((np = (char *) memchr(p, '\0', nbytes)) == NULL) + return WRDE_NOSPACE; + + nbytes -= np - p + 1; + p = np + 1; + } + + we->we_wordv[vofs] = NULL; + return 0; +} + +/* + * we_check -- + * Check that the string contains none of the following unquoted + * special characters: <newline> |&;<>(){} + * or command substitutions when WRDE_NOCMD is set in flags. + */ +static int we_check(const char *words, int flags) +{ + char c; + int dquote, level, quote, squote; + + quote = squote = dquote = 0; + while ((c = *words++) != '\0') { + switch (c) { + case '\\': { + if(squote == 0) + quote ^= 1; + continue; + } + case '\'': { + if(quote + dquote == 0) + squote ^= 1; + break; + } + case '"': { + if(quote + squote == 0) + dquote ^= 1; + break; + } + case '`': { + if(quote + squote == 0 && flags & WRDE_NOCMD) + return WRDE_CMDSUB; + while ((c = *words++) != '\0' && c != '`') + if(c == '\\' && (c = *words++) == '\0') + break; + if(c == '\0') + return WRDE_SYNTAX; + break; + } + case '|': + case '&': + case ';': + case '<': + case '>': + case '{': + case '}': + case '(': + case ')': + case '\n': { + if(quote + squote + dquote == 0) + return WRDE_BADCHAR; + break; + } + case '$': { + if((c = *words++) == '\0') + break; + else if(quote + squote == 0 && c == '(') { + if(flags & WRDE_NOCMD && *words != '(') + return WRDE_CMDSUB; + level = 1; + while ((c = *words++) != '\0') { + if(c == '\\') { + if((c = *words++) == '\0') + break; + } else if(c == '(') + level++; + else if(c == ')' && --level == 0) + break; + } + if(c == '\0' || level != 0) + return WRDE_SYNTAX; + } else if(quote + squote == 0 && c == '{') { + level = 1; + while ((c = *words++) != '\0') { + if(c == '\\') { + if((c = *words++) == '\0') + break; + } else if(c == '{') + level++; + else if(c == '}' && --level == 0) + break; + } + if(c == '\0' || level != 0) + return WRDE_SYNTAX; + } else + --words; + break; + } + default: { + break; + } + } + quote = 0; + } + + if(quote + squote + dquote != 0) + return WRDE_SYNTAX; + + return 0; +} + +int wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags) { + int error; + + if(flags & WRDE_REUSE) + wordfree(we); + + if((flags & WRDE_APPEND) == 0) { + we->we_wordc = 0; + we->we_wordv = NULL; + we->we_strings = NULL; + we->we_nbytes = 0; + } + + if((error = we_check(words, flags)) != 0) { + wordfree(we); + return error; + } + + if((error = we_askshell(words, we, flags)) != 0) { + wordfree(we); + return error; + } + + return 0; +} + +void wordfree(wordexp_t *we) { + if (we == NULL) + return; + free(we->we_wordv); + free(we->we_strings); + we->we_wordv = NULL; + we->we_strings = NULL; + we->we_nbytes = 0; + we->we_wordc = 0; +} |