user: implement mlibc as the libc, finally.
It's finally done.. Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void handler(int sig, siginfo_t *info, void *ctx) {
|
||||
(void)sig;
|
||||
(void)info;
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
int main() {
|
||||
struct sigaction sa;
|
||||
sa.sa_sigaction = handler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
int ret = sigaction(SIGABRT, &sa, NULL);
|
||||
assert(!ret);
|
||||
|
||||
abort();
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef USE_HOST_LIBC
|
||||
#define TEST_PORT 31337
|
||||
#else
|
||||
#define TEST_PORT 42069
|
||||
#endif
|
||||
|
||||
static struct sockaddr_in connect_addr, accept_addr;
|
||||
static int listen_fd;
|
||||
|
||||
int permutations[] = {
|
||||
0,
|
||||
SOCK_CLOEXEC,
|
||||
SOCK_NONBLOCK,
|
||||
SOCK_CLOEXEC | SOCK_NONBLOCK,
|
||||
};
|
||||
|
||||
static bool run_test(int flags)
|
||||
{
|
||||
int connect_fd;
|
||||
int fd_flags, access;
|
||||
socklen_t addrlen;
|
||||
|
||||
connect_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
connect(connect_fd, (struct sockaddr *)&connect_addr, sizeof(connect_addr));
|
||||
addrlen = sizeof(accept_addr);
|
||||
|
||||
int accept_fd = accept4(listen_fd, (struct sockaddr *)&accept_addr, &addrlen, flags);
|
||||
|
||||
if(accept_fd == -1) {
|
||||
fprintf(stderr, "accept4 failed: %s\n", strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fd_flags = fcntl(accept_fd, F_GETFD);
|
||||
if(!(!!(fd_flags & FD_CLOEXEC) == !!(flags & SOCK_CLOEXEC))) {
|
||||
fprintf(stderr, "CLOEXEC mismatch, got %d instead of %d\n", !!(fd_flags & FD_CLOEXEC), !!(flags & SOCK_CLOEXEC));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
access = fcntl(accept_fd, F_GETFL);
|
||||
if(!(!!(access & O_NONBLOCK) == !!(flags & SOCK_NONBLOCK))) {
|
||||
fprintf(stderr, "NONBLOCK flag mismatch, %d vs %d\n", !!(access & O_NONBLOCK), !!(flags & SOCK_NONBLOCK));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
close(accept_fd);
|
||||
close(connect_fd);
|
||||
|
||||
fprintf(stderr, "tested CLOEXEC %d, NONBLOCK %d\n", !!(flags & SOCK_CLOEXEC), !!(flags & SOCK_NONBLOCK));
|
||||
return true;
|
||||
|
||||
cleanup:
|
||||
close(accept_fd);
|
||||
close(connect_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
static int socket_setup(void)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int reuseaddr;
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons(TEST_PORT);
|
||||
|
||||
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if(socket_fd == -1) {
|
||||
fprintf(stderr, "socket failed: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
reuseaddr = 1;
|
||||
int ret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
|
||||
|
||||
if(ret == -1) {
|
||||
fprintf(stderr, "setsockopt failed: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = bind(socket_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
|
||||
|
||||
if(ret == -1) {
|
||||
fprintf(stderr, "bind failed: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = listen(socket_fd, 5);
|
||||
|
||||
if(ret == -1) {
|
||||
fprintf(stderr, "listen failed: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return socket_fd;
|
||||
}
|
||||
|
||||
int main() {
|
||||
memset(&connect_addr, 0, sizeof(connect_addr));
|
||||
connect_addr.sin_family = AF_INET;
|
||||
connect_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
connect_addr.sin_port = htons(TEST_PORT);
|
||||
|
||||
for(size_t i = 0; i < 4; i++) {
|
||||
listen_fd = socket_setup();
|
||||
if(!run_test(permutations[i])) {
|
||||
exit(1);
|
||||
}
|
||||
close(listen_fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef USE_HOST_LIBC
|
||||
#define TEST_FILE "access-host-libc.tmp"
|
||||
#else
|
||||
#define TEST_FILE "access.tmp"
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
// Make sure that it wasn't created by a previous test run
|
||||
unlink(TEST_FILE);
|
||||
|
||||
assert(access(TEST_FILE, F_OK) == -1);
|
||||
assert(access(TEST_FILE, W_OK) == -1);
|
||||
assert(access(TEST_FILE, R_OK) == -1);
|
||||
assert(access(TEST_FILE, X_OK) == -1);
|
||||
|
||||
close(open(TEST_FILE, O_CREAT | O_RDWR, 0666));
|
||||
|
||||
assert(access(TEST_FILE, F_OK) == 0);
|
||||
assert(access(TEST_FILE, W_OK) == 0);
|
||||
assert(access(TEST_FILE, R_OK) == 0);
|
||||
assert(access(TEST_FILE, X_OK) == -1);
|
||||
|
||||
unlink(TEST_FILE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static volatile int alarms_fired = 0;
|
||||
|
||||
static void sigalrm_handler(int signal) {
|
||||
if(signal == SIGALRM)
|
||||
alarms_fired++;
|
||||
}
|
||||
|
||||
int main() {
|
||||
signal(SIGALRM, sigalrm_handler);
|
||||
|
||||
alarms_fired = 0;
|
||||
|
||||
unsigned int ret = alarm(10);
|
||||
assert(!ret);
|
||||
|
||||
sleep(1);
|
||||
|
||||
ret = alarm(1);
|
||||
assert(ret == 9);
|
||||
|
||||
sleep(2);
|
||||
|
||||
if(alarms_fired != 1) {
|
||||
fprintf(stderr, "alarm handler fired %u times instead of 1\n", alarms_fired);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#include <assert.h>
|
||||
#include <libgen.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
/* intentionally a macro: basename modifies its argument */
|
||||
#define test_string(x, expectval) do { \
|
||||
char str[] = x; \
|
||||
assert(strcmp(basename(str), expectval) == 0); \
|
||||
} while (0)
|
||||
|
||||
test_string("/usr/lib", "lib");
|
||||
test_string("/usr/", "usr");
|
||||
test_string("/", "/");
|
||||
test_string(".", ".");
|
||||
test_string("..", "..");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef USE_HOST_LIBC
|
||||
#define TEST_FILE "dprintf-host-libc.tmp"
|
||||
#else
|
||||
#define TEST_FILE "dprintf.tmp"
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
int fd = open(TEST_FILE, O_RDWR | O_CREAT, 0666);
|
||||
assert(fd > -1);
|
||||
|
||||
int ret = dprintf(fd, "aaa");
|
||||
assert(ret == 3);
|
||||
|
||||
// Check if we can read back the same things.
|
||||
// Also checks to see if the fd is still open,
|
||||
// which is issue #199.
|
||||
|
||||
off_t seek = lseek(fd, 0, SEEK_SET);
|
||||
assert(seek == 0);
|
||||
|
||||
char buf[3];
|
||||
ssize_t num_read = read(fd, buf, 3);
|
||||
|
||||
assert(num_read == 3);
|
||||
assert(buf[0] == 'a'
|
||||
&& buf[1] == 'a'
|
||||
&& buf[2] == 'a');
|
||||
|
||||
// All the tests pass, now we can unlink the file.
|
||||
ret = unlink(TEST_FILE);
|
||||
assert(ret == 0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define TEST_FILE "fdopen.tmp"
|
||||
|
||||
int main() {
|
||||
int fd = open(TEST_FILE, O_CREAT | O_RDWR, 0666);
|
||||
assert(fd >= 0);
|
||||
|
||||
char *str = "mlibc fdopen test";
|
||||
assert(write(fd, str, strlen(str)));
|
||||
|
||||
// Seek to the beginning, then reopen with fdopen in append mode.
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
FILE *file = fdopen(fd, "a");
|
||||
assert(file);
|
||||
|
||||
// Append and close.
|
||||
str = " appended";
|
||||
fwrite(str, strlen(str), 1, file);
|
||||
fflush(file);
|
||||
fclose(file);
|
||||
|
||||
// Open it again and check that the append succeeded.
|
||||
fd = open(TEST_FILE, O_RDONLY);
|
||||
assert(fd >= 0);
|
||||
file = fdopen(fd, "r");
|
||||
assert(file);
|
||||
str = "mlibc fdopen test appended";
|
||||
char buf[100] = {0};
|
||||
assert(fread(buf, 1, strlen(str), file));
|
||||
assert(!strcmp(buf, str));
|
||||
fclose(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <assert.h>
|
||||
#include <strings.h>
|
||||
#include <limits.h>
|
||||
|
||||
int main(void){
|
||||
assert(ffs(0x8000) == 16);
|
||||
assert(ffs(0) == 0);
|
||||
assert(ffs(INT_MAX - 1) == 2);
|
||||
assert(ffs(INT_MAX) == 1);
|
||||
assert(ffs(INT_MIN) == (int)(sizeof(int) * CHAR_BIT));
|
||||
assert(ffs(INT_MIN + 1) == 1);
|
||||
|
||||
for (int i = 1; i < 0x1000; i++) {
|
||||
assert(ffs(i) - 1 == __builtin_ctz(i));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void *worker(void *arg) {
|
||||
(void)arg;
|
||||
flockfile(stdout);
|
||||
fputs_unlocked("hello from worker", stdout);
|
||||
funlockfile(stdout);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Check that recursive locking works.
|
||||
assert(!ftrylockfile(stdout));
|
||||
flockfile(stdout);
|
||||
flockfile(stdout);
|
||||
funlockfile(stdout);
|
||||
funlockfile(stdout);
|
||||
funlockfile(stdout);
|
||||
|
||||
assert(!ftrylockfile(stdout));
|
||||
|
||||
pthread_t thread;
|
||||
int ret = pthread_create(&thread, NULL, &worker, NULL);
|
||||
assert(!ret);
|
||||
|
||||
sleep(1);
|
||||
funlockfile(stdout);
|
||||
|
||||
assert(!pthread_join(thread, NULL));
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BUFFER0_SIZE 0x1000
|
||||
#define BUFFER1 "Hello world"
|
||||
#define BUFFER1_SIZE (sizeof(BUFFER1))
|
||||
|
||||
int main() {
|
||||
// test seek with mode "r"
|
||||
FILE *f = fmemopen(NULL, BUFFER0_SIZE, "r");
|
||||
assert(f);
|
||||
|
||||
int ret = fseek(f, 0, SEEK_END);
|
||||
assert(!ret);
|
||||
long pos = ftell(f);
|
||||
fprintf(stderr, "pos = %ld\n", pos);
|
||||
// Despite the fact that this is correct behavior (see below),
|
||||
// this sometimes fails on glibc; newlib seems to get this wrong, too.
|
||||
// to quote what posix says about it:
|
||||
// > The stream shall also maintain the size of the current buffer contents;
|
||||
// > use of fseek() or fseeko() on the stream with SEEK_END shall seek relative to this size.
|
||||
// > For modes r and r+ the size shall be set to the value given by the size argument.
|
||||
#if !defined(__GLIBC__)
|
||||
assert(pos == BUFFER0_SIZE);
|
||||
#endif
|
||||
|
||||
fclose(f);
|
||||
|
||||
// test seek with mode "w"
|
||||
f = fmemopen(NULL, BUFFER0_SIZE, "w");
|
||||
assert(f);
|
||||
|
||||
ret = fseek(f, 0, SEEK_END);
|
||||
assert(!ret);
|
||||
pos = ftell(f);
|
||||
assert(pos == 0);
|
||||
|
||||
fclose(f);
|
||||
|
||||
// test seek with mode "a" and a NULL buffer
|
||||
f = fmemopen(NULL, BUFFER0_SIZE, "a");
|
||||
assert(f);
|
||||
|
||||
ret = fseek(f, 0, SEEK_END);
|
||||
assert(!ret);
|
||||
pos = ftell(f);
|
||||
assert(pos == 0);
|
||||
|
||||
fclose(f);
|
||||
|
||||
// test seek with mode "a" and a buffer containing a '\0'
|
||||
f = fmemopen(BUFFER1, BUFFER1_SIZE + 2, "a");
|
||||
assert(f);
|
||||
|
||||
pos = ftell(f);
|
||||
assert(pos == (long) (BUFFER1_SIZE - 1));
|
||||
|
||||
ret = fseek(f, 0, SEEK_SET);
|
||||
assert(!ret);
|
||||
pos = ftell(f);
|
||||
assert(!pos);
|
||||
|
||||
ret = fseek(f, 0, SEEK_END);
|
||||
assert(!ret);
|
||||
pos = ftell(f);
|
||||
assert(pos == (long) (BUFFER1_SIZE - 1));
|
||||
|
||||
fclose(f);
|
||||
|
||||
// test seek with mode "a" and a buffer not containing a '\0'
|
||||
f = fmemopen(BUFFER1, BUFFER1_SIZE - 2, "a");
|
||||
assert(f);
|
||||
|
||||
ret = fseek(f, 0, SEEK_END);
|
||||
assert(!ret);
|
||||
pos = ftell(f);
|
||||
assert(pos == (long) (BUFFER1_SIZE - 2));
|
||||
|
||||
fclose(f);
|
||||
|
||||
f = fmemopen(BUFFER1, BUFFER1_SIZE, "r");
|
||||
assert(f);
|
||||
|
||||
ret = fseek(f, 0, SEEK_SET);
|
||||
assert(!ret);
|
||||
|
||||
char buf[BUFFER1_SIZE];
|
||||
int read = fread(buf, 1, BUFFER1_SIZE - 2, f);
|
||||
assert(read == BUFFER1_SIZE - 2);
|
||||
assert(!strncmp(BUFFER1, buf, BUFFER1_SIZE - 2));
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
|
||||
read = fread(buf, 1, 2, f);
|
||||
assert(read == 0);
|
||||
assert(feof(f));
|
||||
|
||||
fclose(f);
|
||||
|
||||
// Open a buffer for read+write
|
||||
char *buf1 = strdup(BUFFER1);
|
||||
f = fmemopen(buf1, BUFFER1_SIZE, "w+");
|
||||
assert(f);
|
||||
assert(strlen(BUFFER1) == BUFFER1_SIZE - 1);
|
||||
|
||||
// seek to somewhere in the middle of the buffer
|
||||
fseek(f, BUFFER1_SIZE - 5, SEEK_SET);
|
||||
// write as much data to it as possible
|
||||
read = fwrite(BUFFER1, 1, 9, f);
|
||||
rewind(f);
|
||||
|
||||
// seek the the same position in the middle of the buffer
|
||||
ret = fseek(f, BUFFER1_SIZE - 5, SEEK_SET);
|
||||
assert(!ret);
|
||||
memset(buf, 0, BUFFER1_SIZE);
|
||||
// read what we just wrote
|
||||
read = fread(buf, 1, 5, f);
|
||||
// check that the write got correctly truncated
|
||||
fprintf(stderr, "buf '%s' (%zu)\n", buf, strlen(buf));
|
||||
assert(!strncmp(BUFFER1, buf, 4) && strlen(buf) == 4);
|
||||
|
||||
fclose(f);
|
||||
free(buf1);
|
||||
|
||||
char *buf2 = strdup(BUFFER1);
|
||||
f = fmemopen(buf2, 0, "r");
|
||||
assert(f || errno == EINVAL);
|
||||
|
||||
if(f) {
|
||||
memset(buf, 0, BUFFER1_SIZE);
|
||||
read = fread(buf, 10, 1, f);
|
||||
assert(!read);
|
||||
rewind(f);
|
||||
|
||||
read = fwrite(BUFFER1, 1, 12, f);
|
||||
assert(read == 0);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
free(buf2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct testcookie {
|
||||
bool read;
|
||||
bool write;
|
||||
bool seek;
|
||||
bool close;
|
||||
};
|
||||
|
||||
ssize_t cookie_read(void *c, char *buf, size_t size) {
|
||||
(void) buf;
|
||||
struct testcookie *cookie = c;
|
||||
cookie->read = true;
|
||||
return size;
|
||||
}
|
||||
|
||||
ssize_t cookie_write(void *c, const char *buf, size_t size) {
|
||||
(void) buf;
|
||||
struct testcookie *cookie = c;
|
||||
cookie->write = true;
|
||||
return size;
|
||||
}
|
||||
|
||||
int cookie_seek(void *c, off64_t *offset, int whence) {
|
||||
(void) offset;
|
||||
(void) whence;
|
||||
struct testcookie *cookie = c;
|
||||
cookie->seek = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cookie_close(void *c) {
|
||||
struct testcookie *cookie = c;
|
||||
cookie->close = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
struct testcookie cookie = { false, false, false, false };
|
||||
|
||||
cookie_io_functions_t funcs = {
|
||||
.read = cookie_read,
|
||||
.write = cookie_write,
|
||||
.seek = cookie_seek,
|
||||
.close = cookie_close,
|
||||
};
|
||||
|
||||
FILE *stream = fopencookie(&cookie, "w+", funcs);
|
||||
assert(stream);
|
||||
|
||||
unsigned char buf[1];
|
||||
int ret = fread(buf, 1, 1, stream);
|
||||
assert(ret == 1);
|
||||
ret = fwrite(buf, 1, 1, stream);
|
||||
assert(ret == 1);
|
||||
ret = fseek(stream, 0, SEEK_SET);
|
||||
assert(!ret);
|
||||
|
||||
ret = fclose(stream);
|
||||
assert(!ret);
|
||||
|
||||
assert(cookie.read && cookie.write && cookie.seek && cookie.close);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
#if !defined(USE_HOST_LIBC)
|
||||
#include <mlibc-config.h>
|
||||
#endif
|
||||
|
||||
#include <netdb.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if __MLIBC_LINUX_OPTION || defined(USE_HOST_LIBC)
|
||||
#include <ifaddrs.h>
|
||||
|
||||
static bool has_ipv4_addr(void) {
|
||||
struct ifaddrs *addrs;
|
||||
if (getifaddrs(&addrs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int found = 0;
|
||||
for (struct ifaddrs *cur = addrs; cur; cur = cur->ifa_next) {
|
||||
if (cur->ifa_addr && (cur->ifa_flags & IFF_UP) && cur->ifa_addr->sa_family == AF_INET && strncmp(cur->ifa_name, "lo", IF_NAMESIZE)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(addrs);
|
||||
return found;
|
||||
}
|
||||
|
||||
static bool has_ipv6_addr(void) {
|
||||
struct ifaddrs *addrs;
|
||||
if (getifaddrs(&addrs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int found = 0;
|
||||
for (struct ifaddrs *cur = addrs; cur; cur = cur->ifa_next) {
|
||||
if (cur->ifa_addr && (cur->ifa_flags & IFF_UP) && cur->ifa_addr->sa_family == AF_INET6 && strncmp(cur->ifa_name, "lo", IF_NAMESIZE)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(addrs);
|
||||
return found;
|
||||
}
|
||||
#endif // __MLIBC_LINUX_OPTION || defined(USE_HOST_LIBC)
|
||||
|
||||
int main() {
|
||||
struct addrinfo *res = NULL;
|
||||
struct addrinfo hints = {0};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
int ret = getaddrinfo(NULL, "443", &hints, &res);
|
||||
assert(ret == 0);
|
||||
|
||||
struct sockaddr_in *addr = (struct sockaddr_in*)(res[0].ai_addr);
|
||||
assert(addr->sin_port == htons(443));
|
||||
assert(res[0].ai_socktype == SOCK_STREAM);
|
||||
assert(res[0].ai_protocol == IPPROTO_TCP);
|
||||
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
/* check we can resolve any domain */
|
||||
ret = getaddrinfo("example.net", NULL, &hints, &res);
|
||||
assert(ret == 0);
|
||||
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
ret = getaddrinfo("10.10.10.10", NULL, &hints, &res);
|
||||
assert(ret == 0);
|
||||
|
||||
addr = (struct sockaddr_in*)res[0].ai_addr;
|
||||
assert((addr->sin_addr.s_addr & 0xFF) == 10);
|
||||
assert(((addr->sin_addr.s_addr >> 8) & 0xFF) == 10);
|
||||
assert(((addr->sin_addr.s_addr >> 16) & 0xFF) == 10);
|
||||
assert(((addr->sin_addr.s_addr >> 24) & 0xFF) == 10);
|
||||
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
ret = getaddrinfo("example.net", NULL, &hints, &res);
|
||||
assert(ret == EAI_NONAME);
|
||||
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
hints.ai_family = AF_INET6;
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
ret = getaddrinfo("::1", "1234", &hints, &res);
|
||||
assert(ret == 0);
|
||||
|
||||
assert(res[0].ai_family == AF_INET6);
|
||||
struct sockaddr_in6 *addr6 = (struct sockaddr_in6*) res[0].ai_addr;
|
||||
assert(addr6->sin6_port == htons(1234));
|
||||
assert(addr6->sin6_addr.s6_addr[15] == 1);
|
||||
for (int i = 0; i < 15; i++) {
|
||||
assert(addr6->sin6_addr.s6_addr[i] == 0);
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
hints.ai_family = AF_INET6;
|
||||
hints.ai_flags = 0;
|
||||
ret = getaddrinfo("example.net", "443", &hints, &res);
|
||||
assert(ret == 0);
|
||||
|
||||
assert(res[0].ai_family == AF_INET6);
|
||||
addr6 = (struct sockaddr_in6*)res[0].ai_addr;
|
||||
assert(addr6->sin6_port == htons(443));
|
||||
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
#if __MLIBC_LINUX_OPTION || defined(USE_HOST_LIBC)
|
||||
// Test with AF_INET
|
||||
hints.ai_family = AF_INET;
|
||||
ret = getaddrinfo("localhost", NULL, &hints, &res);
|
||||
if (has_ipv4_addr()) {
|
||||
assert(ret == 0);
|
||||
for(struct addrinfo *p = res; p != NULL; p = p->ai_next) {
|
||||
assert(p->ai_family == AF_INET);
|
||||
}
|
||||
} else {
|
||||
assert(ret != 0);
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
// Test with AF_INET6
|
||||
hints.ai_family = AF_INET6;
|
||||
ret = getaddrinfo("localhost", NULL, &hints, &res);
|
||||
assert(ret == 0);
|
||||
for(struct addrinfo *p = res; p != NULL; p = p->ai_next) {
|
||||
assert(p->ai_family == AF_INET6);
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
// Test AI_ADDRCONFIG
|
||||
hints = (struct addrinfo){0};
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
|
||||
ret = getaddrinfo("localhost", NULL, &hints, &res);
|
||||
|
||||
if (ret == 0) {
|
||||
int found_ipv4 = 0;
|
||||
int found_ipv6 = 0;
|
||||
for(struct addrinfo *p = res; p != NULL; p = p->ai_next) {
|
||||
if (p->ai_family == AF_INET)
|
||||
found_ipv4 = 1;
|
||||
else if (p->ai_family == AF_INET6)
|
||||
found_ipv6 = 1;
|
||||
}
|
||||
|
||||
if (has_ipv4_addr())
|
||||
assert(found_ipv4);
|
||||
else
|
||||
assert(!found_ipv4);
|
||||
|
||||
if (has_ipv6_addr())
|
||||
assert(found_ipv6);
|
||||
else
|
||||
assert(!found_ipv6);
|
||||
} else {
|
||||
// If getaddrinfo fails, it should be because no addresses are configured.
|
||||
assert(!has_ipv4_addr() && !has_ipv6_addr());
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
#endif // __MLIBC_LINUX_OPTION || defined(USE_HOST_LIBC)
|
||||
|
||||
// Test AI_NUMERICSERV.
|
||||
hints = (struct addrinfo){0};
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
hints.ai_family = AF_INET;
|
||||
|
||||
ret = getaddrinfo(NULL, "8080", &hints, &res);
|
||||
assert(ret == 0);
|
||||
assert(res->ai_family == AF_INET);
|
||||
struct sockaddr_in *addr_in = (struct sockaddr_in *)res->ai_addr;
|
||||
assert(addr_in->sin_port == htons(8080));
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
ret = getaddrinfo(NULL, "http", &hints, &res);
|
||||
assert(ret == EAI_NONAME);
|
||||
assert(res == NULL);
|
||||
|
||||
hints.ai_family = AF_INET6;
|
||||
|
||||
ret = getaddrinfo(NULL, "9090", &hints, &res);
|
||||
assert(ret == 0);
|
||||
assert(res->ai_family == AF_INET6);
|
||||
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)res->ai_addr;
|
||||
assert(addr_in6->sin6_port == htons(9090));
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
ret = getaddrinfo(NULL, "https", &hints, &res);
|
||||
assert(ret == EAI_NONAME);
|
||||
assert(res == NULL);
|
||||
|
||||
// Test for NXDOMAIN
|
||||
hints = (struct addrinfo){0};
|
||||
ret = getaddrinfo("this-domain-should-not-exist.nxdomain", NULL, &hints, &res);
|
||||
assert(ret == EAI_NONAME);
|
||||
assert(res == NULL);
|
||||
|
||||
// Test AI_CANONNAME.
|
||||
hints = (struct addrinfo){0};
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
ret = getaddrinfo("localhost.localdomain", NULL, &hints, &res);
|
||||
assert(ret == 0);
|
||||
assert(res->ai_canonname);
|
||||
freeaddrinfo(res);
|
||||
res = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
char buf[PATH_MAX];
|
||||
char *ret = getcwd(buf, PATH_MAX);
|
||||
|
||||
assert(ret);
|
||||
assert((strlen(ret) == strlen(buf)) && strlen(ret));
|
||||
assert(!strcmp(ret, buf));
|
||||
|
||||
char *ret2 = getcwd(NULL, 0);
|
||||
assert(ret2);
|
||||
assert(strlen(ret2));
|
||||
assert(!strcmp(ret, ret2));
|
||||
free(ret2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef USE_HOST_LIBC
|
||||
#define TEST_FILE "getdelim-host-libc.tmp"
|
||||
#else
|
||||
#define TEST_FILE "getdelim.tmp"
|
||||
#endif
|
||||
|
||||
int main(void) {
|
||||
FILE *fp;
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t read;
|
||||
|
||||
// We have to open the file for writing and then reading separately,
|
||||
// as mlibc doesn't allow us to do both at the same time (yet).
|
||||
fp = fopen(TEST_FILE, "w");
|
||||
assert(fp);
|
||||
fputs("foo\nbar\nbaz\nquux\n", fp);
|
||||
fclose(fp);
|
||||
|
||||
fp = fopen(TEST_FILE, "r");
|
||||
assert(fp);
|
||||
while ((read = getline(&line, &len, fp)) != -1) {
|
||||
printf("read line of length %zd, capacity %zu\n", read, len);
|
||||
}
|
||||
assert(!ferror(fp));
|
||||
free(line);
|
||||
fclose(fp);
|
||||
|
||||
size_t nchars = 10000;
|
||||
fp = fopen(TEST_FILE, "w");
|
||||
assert(fp);
|
||||
for (size_t i = 0; i < nchars; i++)
|
||||
fputc('a', fp);
|
||||
fputc('b', fp);
|
||||
fclose(fp);
|
||||
|
||||
line = NULL;
|
||||
len = 0;
|
||||
fp = fopen(TEST_FILE, "r");
|
||||
assert(fp);
|
||||
while ((read = getdelim(&line, &len, 'b', fp)) != -1) {
|
||||
printf("read line of length %zd, capacity %zu\n", read, len);
|
||||
assert((size_t)read == nchars + 1);
|
||||
}
|
||||
|
||||
assert(len > nchars + 1);
|
||||
assert(!ferror(fp));
|
||||
assert(feof(fp));
|
||||
|
||||
assert(getdelim(&line, &len, 'b', fp) == -1);
|
||||
assert(feof(fp));
|
||||
|
||||
free(line);
|
||||
fclose(fp);
|
||||
|
||||
fp = fopen(TEST_FILE, "w");
|
||||
assert(fp);
|
||||
assert(fwrite("1234ef\0f", 1, 8, fp) == 8);
|
||||
fclose(fp);
|
||||
|
||||
fp = fopen(TEST_FILE, "r");
|
||||
assert(fp);
|
||||
|
||||
/* test with line = NULL and large len */
|
||||
line = NULL;
|
||||
len = (size_t) ~0;
|
||||
assert(getdelim(&line, &len, 'e', fp) == 5);
|
||||
assert(!memcmp(line, "1234e", 6));
|
||||
assert(5 < len);
|
||||
|
||||
/* test handling of internal nulls */
|
||||
assert(getdelim(&line, &len, 'e', fp) == 3);
|
||||
assert(!memcmp(line, "f\0f", 4));
|
||||
assert(3 < len);
|
||||
|
||||
/* test handling of EOF */
|
||||
assert(getdelim(&line, &len, 'e', fp) == -1);
|
||||
|
||||
free(line);
|
||||
fclose(fp);
|
||||
|
||||
// Delete the file
|
||||
unlink(TEST_FILE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <netdb.h>
|
||||
#include <assert.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
assert(inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr));
|
||||
|
||||
char host[64];
|
||||
assert(!getnameinfo((struct sockaddr*)&addr, sizeof(addr), host,
|
||||
sizeof(host), NULL, 0, 0));
|
||||
|
||||
assert(inet_pton(AF_INET, "8.8.8.8", &addr.sin_addr));
|
||||
|
||||
assert(!getnameinfo((struct sockaddr*)&addr, sizeof(addr), host,
|
||||
sizeof(host), NULL, 0, 0));
|
||||
assert(!strcmp(host, "dns.google"));
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
struct sched_param param = {
|
||||
.sched_priority = 100,
|
||||
};
|
||||
|
||||
int ret = sched_getparam(getpid(), ¶m);
|
||||
assert(!ret);
|
||||
|
||||
ret = sched_setparam(getpid(), ¶m);
|
||||
assert(ret == 0);
|
||||
|
||||
param.sched_priority = 0xD00DFEED;
|
||||
|
||||
ret = sched_setparam(getpid(), ¶m);
|
||||
assert(ret == -1 && errno == EINVAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <assert.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
int main() {
|
||||
struct servent *ret = getservbyname("http", "tcp");
|
||||
assert(ret);
|
||||
assert(!strcmp("http", ret->s_name));
|
||||
assert(ret->s_port == htons(80));
|
||||
assert(!strcmp("tcp", ret->s_proto));
|
||||
|
||||
ret = getservbyname("http", NULL);
|
||||
assert(ret);
|
||||
assert(!strcmp("http", ret->s_name));
|
||||
assert(ret->s_port == htons(80));
|
||||
assert(!strcmp("tcp", ret->s_proto));
|
||||
|
||||
ret = getservbyname("babel", "udp");
|
||||
assert(ret);
|
||||
ret = getservbyname("babel", "tcp");
|
||||
assert(!ret);
|
||||
|
||||
ret = getservbyname("", NULL);
|
||||
assert(!ret);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
struct servent *ret = getservbyport(htons(80), "tcp");
|
||||
assert(ret);
|
||||
assert(!strcmp("http", ret->s_name));
|
||||
assert(ret->s_port == htons(80));
|
||||
assert(!strcmp("tcp", ret->s_proto));
|
||||
|
||||
ret = getservbyport(htons(80), NULL);
|
||||
assert(ret);
|
||||
assert(!strcmp("http", ret->s_name));
|
||||
assert(ret->s_port == htons(80));
|
||||
assert(!strcmp("tcp", ret->s_proto));
|
||||
|
||||
ret = getservbyport(htons(6696), "udp");
|
||||
assert(ret);
|
||||
ret = getservbyport(htons(6696), "tcp");
|
||||
assert(!ret);
|
||||
|
||||
ret = getservbyport(htons(0), NULL);
|
||||
assert(!ret);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <grp.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void check_root_group(struct group *grp) {
|
||||
assert(grp);
|
||||
|
||||
printf("group name: %s\n", grp->gr_name);
|
||||
fflush(stdout);
|
||||
|
||||
assert(grp->gr_gid == 0);
|
||||
assert(!strcmp(grp->gr_name, "root"));
|
||||
|
||||
// Depending on your system, the root group may or may not contain the root user.
|
||||
if (grp->gr_mem[0] != NULL) {
|
||||
bool found = false;
|
||||
for (size_t i = 0; grp->gr_mem[i] != NULL; i++) {
|
||||
printf("group member: %s\n", grp->gr_mem[i]);
|
||||
fflush(stdout);
|
||||
|
||||
if (!strcmp(grp->gr_mem[i], "root")) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(found);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
struct group grp, *result = NULL;
|
||||
char *buf;
|
||||
size_t bufsize;
|
||||
int s;
|
||||
bool password = false;
|
||||
char *members[3];
|
||||
char filename[] = "grpXXXXXX";
|
||||
int tmpfd;
|
||||
FILE *tmp;
|
||||
char *expected = "managarm:passwordhash:12345:managarm,mlibc\n";
|
||||
size_t readsize;
|
||||
|
||||
bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
|
||||
assert(bufsize > 0 && bufsize < 0x100000);
|
||||
|
||||
buf = malloc(bufsize);
|
||||
assert(buf);
|
||||
|
||||
s = getgrnam_r("root", &grp, buf, bufsize, &result);
|
||||
assert(!s);
|
||||
check_root_group(result);
|
||||
|
||||
s = getgrgid_r(0, &grp, buf, bufsize, &result);
|
||||
assert(!s);
|
||||
check_root_group(result);
|
||||
|
||||
result = getgrnam("root");
|
||||
check_root_group(result);
|
||||
|
||||
result = getgrgid(0);
|
||||
check_root_group(result);
|
||||
|
||||
result = getgrnam("this_group_doesnt_exist");
|
||||
assert(!result);
|
||||
#ifndef USE_HOST_LIBC
|
||||
// This is not guaranteed.
|
||||
assert(errno == ESRCH);
|
||||
#endif
|
||||
|
||||
s = getgrnam_r("this_group_doesnt_exist", &grp, buf, bufsize, &result);
|
||||
assert(!result);
|
||||
#ifndef USE_HOST_LIBC
|
||||
// This is not guaranteed.
|
||||
assert(s == ESRCH);
|
||||
#endif
|
||||
|
||||
errno = 0;
|
||||
setgrent();
|
||||
assert(errno == 0);
|
||||
|
||||
result = getgrent();
|
||||
assert(result);
|
||||
grp.gr_name = strdup(result->gr_name);
|
||||
if(result->gr_passwd) {
|
||||
password = true;
|
||||
}
|
||||
grp.gr_passwd = strdup(result->gr_passwd);
|
||||
grp.gr_gid = result->gr_gid;
|
||||
assert(grp.gr_name);
|
||||
if(password)
|
||||
assert(grp.gr_passwd);
|
||||
|
||||
assert(grp.gr_name);
|
||||
if(password)
|
||||
assert(grp.gr_passwd);
|
||||
|
||||
endgrent();
|
||||
|
||||
result = getgrent();
|
||||
assert(result);
|
||||
assert(strcmp(result->gr_name, grp.gr_name) == 0);
|
||||
if(password)
|
||||
assert(strcmp(result->gr_passwd, grp.gr_passwd) == 0);
|
||||
assert(result->gr_gid == grp.gr_gid);
|
||||
|
||||
free(grp.gr_name);
|
||||
if(password)
|
||||
free(grp.gr_passwd);
|
||||
|
||||
grp.gr_name = "managarm";
|
||||
grp.gr_passwd = "passwordhash";
|
||||
grp.gr_gid = 12345;
|
||||
members[0] = "managarm";
|
||||
members[1] = "mli:bc";
|
||||
members[2] = NULL;
|
||||
grp.gr_mem = members;
|
||||
|
||||
// tmpfile() cannot be used because its unimplemented in mlibc.
|
||||
tmpfd = mkstemp(filename);
|
||||
assert(tmpfd);
|
||||
tmp = fdopen(tmpfd, "w+");
|
||||
assert(tmp);
|
||||
|
||||
assert(putgrent(NULL, tmp) < 0);
|
||||
assert(putgrent(&grp, tmp) < 0);
|
||||
|
||||
members[1] = "mlibc";
|
||||
|
||||
grp.gr_name = "mana:garm";
|
||||
assert(putgrent(&grp, tmp) < 0);
|
||||
grp.gr_name = "managarm";
|
||||
|
||||
grp.gr_passwd = "passwordhash\n";
|
||||
assert(putgrent(&grp, tmp) < 0);
|
||||
grp.gr_passwd = "passwordhash";
|
||||
|
||||
assert(putgrent(&grp, tmp) == 0);
|
||||
rewind(tmp);
|
||||
|
||||
readsize = fread(buf, 1, bufsize, tmp);
|
||||
assert(readsize == strlen(expected));
|
||||
assert(strncmp(expected, buf, strlen(expected)) == 0);
|
||||
|
||||
fclose(tmp);
|
||||
unlink(filename);
|
||||
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <assert.h>
|
||||
#include <net/if.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
char name[IF_NAMESIZE];
|
||||
|
||||
assert(name == if_indextoname(1, name));
|
||||
printf("test: name '%s'\n", name);
|
||||
assert(1 == if_nametoindex(name));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include <assert.h>
|
||||
#include <strings.h>
|
||||
#include <stddef.h>
|
||||
|
||||
int main() {
|
||||
char str[] = "This is a sample string";
|
||||
char *pch;
|
||||
// The character 's' is at position 4, 7, 11 and 18
|
||||
pch = index(str, 's');
|
||||
assert(pch - str + 1 == 4);
|
||||
pch = index(pch + 1, 's');
|
||||
assert(pch - str + 1 == 7);
|
||||
pch = index(pch + 1, 's');
|
||||
assert(pch - str + 1 == 11);
|
||||
pch = index(pch + 1, 's');
|
||||
assert(pch - str + 1 == 18);
|
||||
pch = index(pch + 1, 's');
|
||||
assert(pch == NULL);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
struct in_addr addr;
|
||||
addr.s_addr = (1 << 24) |
|
||||
(1 << 16) | (1 << 8) | 1;
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
assert(inet_ntop(AF_INET, &addr, buf, INET_ADDRSTRLEN) != NULL);
|
||||
assert(strncmp("1.1.1.1", buf, INET_ADDRSTRLEN) == 0);
|
||||
|
||||
struct in6_addr addr2 = { .s6_addr = {0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1} };
|
||||
char buf2[INET6_ADDRSTRLEN];
|
||||
assert(inet_ntop(AF_INET6, &addr2, buf2, INET6_ADDRSTRLEN) != NULL);
|
||||
assert(strncmp("2001:db8::1:0:0:1", buf2, INET6_ADDRSTRLEN) == 0);
|
||||
|
||||
struct in6_addr addr3 = { .s6_addr = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} };
|
||||
char buf3[INET6_ADDRSTRLEN];
|
||||
assert(inet_ntop(AF_INET6, &addr3, buf3, INET6_ADDRSTRLEN) != NULL);
|
||||
assert(strncmp("::1", buf3, INET6_ADDRSTRLEN) == 0);
|
||||
|
||||
struct in6_addr addr4 = { .s6_addr = {0x20, 0x1, 0xd, 0xb8, 00, 00, 00, 0x1, 00, 0x1, 00, 0x1, 00, 0x1, 00, 0x1} };
|
||||
char buf4[INET6_ADDRSTRLEN];
|
||||
assert(inet_ntop(AF_INET6, &addr4, buf4, INET6_ADDRSTRLEN) != NULL);
|
||||
assert(strncmp("2001:db8:0:1:1:1:1:1", buf4, INET6_ADDRSTRLEN) == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
struct in_addr addr;
|
||||
assert(inet_pton(AF_INET, "1.1.1.1", &addr));
|
||||
assert((addr.s_addr & 0xFF) == 1);
|
||||
assert(((addr.s_addr >> 8) & 0xFF) == 1);
|
||||
assert(((addr.s_addr >> 16) & 0xFF) == 1);
|
||||
assert(((addr.s_addr >> 24) & 0xFF) == 1);
|
||||
|
||||
assert(!inet_pton(AF_INET, "256.999.1234.555", &addr));
|
||||
assert(!inet_pton(AF_INET, "a.b.c.d", &addr));
|
||||
|
||||
struct in6_addr test6;
|
||||
{
|
||||
struct in6_addr addr6 = {
|
||||
.s6_addr = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||
},
|
||||
};
|
||||
int ret = inet_pton(AF_INET6, "::1", &test6);
|
||||
assert(ret == 1);
|
||||
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
|
||||
}
|
||||
|
||||
memset(&test6, 0, sizeof(test6));
|
||||
|
||||
{
|
||||
struct in6_addr addr6 = {
|
||||
.s6_addr16 = {
|
||||
htons(0x2606), htons(0x4700), htons(0x4700), htons(0x00), htons(0x00), htons(0x00), htons(0x00), htons(0x1111)
|
||||
},
|
||||
};
|
||||
int ret = inet_pton(AF_INET6, "2606:4700:4700::1111", &test6);
|
||||
assert(ret == 1);
|
||||
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
|
||||
}
|
||||
|
||||
memset(&test6, 0, sizeof(test6));
|
||||
|
||||
{
|
||||
struct in6_addr addr6 = {
|
||||
.s6_addr16 = {
|
||||
htons(0x2606), htons(0x4700), htons(0x4700), htons(0x00), htons(0x00), htons(0x00), htons(0x00), htons(0x1111)
|
||||
},
|
||||
};
|
||||
int ret = inet_pton(AF_INET6, "2606:4700:4700:0000:0000:0000:0000:1111", &test6);
|
||||
assert(ret == 1);
|
||||
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
|
||||
}
|
||||
|
||||
memset(&test6, 0, sizeof(test6));
|
||||
|
||||
{
|
||||
struct in6_addr addr6 = {
|
||||
.s6_addr16 = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
},
|
||||
};
|
||||
int ret = inet_pton(AF_INET6, "::", &test6);
|
||||
assert(ret == 1);
|
||||
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
|
||||
}
|
||||
|
||||
memset(&test6, 0, sizeof(test6));
|
||||
|
||||
{
|
||||
int ret = inet_pton(AF_INET6, "mlibc::", &test6);
|
||||
assert(!ret);
|
||||
}
|
||||
|
||||
memset(&test6, 0, sizeof(test6));
|
||||
|
||||
{
|
||||
struct in6_addr addr6 = {
|
||||
.s6_addr = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0xFF, 0xFF, 204, 152, 189, 116,
|
||||
},
|
||||
};
|
||||
int ret = inet_pton(AF_INET6, "::FFFF:204.152.189.116", &test6);
|
||||
assert(ret == 1);
|
||||
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
|
||||
}
|
||||
|
||||
memset(&test6, 0, sizeof(test6));
|
||||
|
||||
{
|
||||
int ret = inet_pton(AF_INET6, "::FFFF:204.152.189.", &test6);
|
||||
assert(!ret);
|
||||
}
|
||||
|
||||
memset(&test6, 0, sizeof(test6));
|
||||
|
||||
{
|
||||
int ret = inet_pton(AF_INET6, "::FFFF:204.152.189.420", &test6);
|
||||
assert(!ret);
|
||||
}
|
||||
|
||||
memset(&test6, 0, sizeof(test6));
|
||||
|
||||
{
|
||||
int ret = inet_pton(AF_INET6, "::FFFF:204.152.420.69", &test6);
|
||||
assert(!ret);
|
||||
}
|
||||
|
||||
memset(&test6, 0, sizeof(test6));
|
||||
|
||||
{
|
||||
int ret = inet_pton(AF_INET6, "1100::FFFF:204.152.189.116", &test6);
|
||||
(void) ret;
|
||||
#if !defined(USE_HOST_LIBC)
|
||||
assert(!ret);
|
||||
#endif
|
||||
}
|
||||
|
||||
memset(&test6, 0, sizeof(test6));
|
||||
|
||||
{
|
||||
struct in6_addr addr6 = {
|
||||
.s6_addr = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0xFF, 0xFF, 204, 152, 189, 116,
|
||||
},
|
||||
};
|
||||
int ret = inet_pton(AF_INET6, "0000:0000:0000:0000:0000:FFFF:204.152.189.116", &test6);
|
||||
assert(ret == 1);
|
||||
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main () {
|
||||
char str[] = "The Last Supper by Leonardo da Vinci";
|
||||
char *str_temp;
|
||||
|
||||
// Test strstr
|
||||
str_temp = strstr(str, "Supper"); /* Find a substring in the string */
|
||||
assert(!strcmp(str_temp, "Supper by Leonardo da Vinci"));
|
||||
|
||||
/* Following calls use memory APIs for the above tasks */
|
||||
// Test memchr
|
||||
str_temp = (char *)memchr((void *)str, 'L', strlen(str));
|
||||
assert(!strcmp(str_temp, "Last Supper by Leonardo da Vinci"));
|
||||
|
||||
// Test memrchr
|
||||
str_temp = (char *)memrchr((void *)str, 'L', strlen(str));
|
||||
assert(!strcmp(str_temp, "Leonardo da Vinci"));
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
void validate_pattern(char *p) {
|
||||
assert(memcmp(p, "XXXXXX", 6));
|
||||
assert(memchr(p, 0, 6) == NULL);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assert(isalnum(p[i]));
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int ret;
|
||||
|
||||
// Make sure the patterns themselves cannot be chosen. This
|
||||
// *could* happen on glibc, or if we widen the character set
|
||||
// used for generating random names. Odds are 1 in 60 billion,
|
||||
// but I'd rather not worry about this.
|
||||
ret = open("prefixXXXXXX", O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
assert(ret >= 0 || (ret == -1 && errno == EEXIST));
|
||||
ret = open("longprefixXXXXXXlongsuffix", O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
assert(ret >= 0 || (ret == -1 && errno == EEXIST));
|
||||
|
||||
ret = mkstemp("short");
|
||||
assert(ret == -1);
|
||||
assert(errno == EINVAL);
|
||||
|
||||
ret = mkstemp("lessthan6XXX");
|
||||
assert(ret == -1);
|
||||
assert(errno == EINVAL);
|
||||
|
||||
ret = mkstemps("lessthan6XXXswithsuffix", 11);
|
||||
assert(ret == -1);
|
||||
assert(errno == EINVAL);
|
||||
|
||||
char *p = strdup("prefixXXXXXX");
|
||||
ret = mkstemp(p);
|
||||
// We can't really protect against EEXIST...
|
||||
assert(ret >= 0 || (ret == -1 && errno == EEXIST));
|
||||
assert(!memcmp(p, "prefix", 6));
|
||||
assert(p[12] == 0);
|
||||
validate_pattern(p + 6);
|
||||
|
||||
if (ret >= 0) {
|
||||
ret = close(ret);
|
||||
assert(!ret);
|
||||
}
|
||||
free(p);
|
||||
|
||||
p = strdup("longprefixXXXXXXlongsuffix");
|
||||
ret = mkstemps(p, 10);
|
||||
// We can't really protect against EEXIST...
|
||||
assert(ret >= 0 || (ret == -1 && errno == EEXIST));
|
||||
assert(!memcmp(p, "longprefix", 10));
|
||||
assert(!memcmp(p + 16, "longsuffix", 10));
|
||||
assert(p[26] == 0);
|
||||
validate_pattern(p + 10);
|
||||
|
||||
if (ret >= 0) {
|
||||
ret = close(ret);
|
||||
assert(!ret);
|
||||
}
|
||||
free(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
int main() {
|
||||
errno = 0;
|
||||
int original_priority = nice(0);
|
||||
|
||||
assert(original_priority != -1 || !errno);
|
||||
|
||||
errno = 0;
|
||||
int ret = nice(1);
|
||||
|
||||
if(ret == -1 && errno) {
|
||||
fprintf(stderr, "%s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
int new_priority = nice(0);
|
||||
|
||||
assert(new_priority != -1 || !errno);
|
||||
assert(new_priority == original_priority + 1);
|
||||
|
||||
errno = 0;
|
||||
new_priority = nice(INT_MAX - new_priority);
|
||||
|
||||
assert(new_priority != -1 || !errno);
|
||||
assert(new_priority != INT_MAX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define WRITE_NO 1024
|
||||
|
||||
int main() {
|
||||
size_t size;
|
||||
char *buf;
|
||||
|
||||
FILE *fp = open_memstream(&buf, &size);
|
||||
assert(fp);
|
||||
|
||||
char c = 'A';
|
||||
for (size_t i = 0; i < WRITE_NO; i++)
|
||||
assert(fwrite(&c, sizeof(char), 1, fp) == 1);
|
||||
|
||||
// Flush the file to update the pointers.
|
||||
assert(!fflush(fp));
|
||||
|
||||
assert(size == WRITE_NO);
|
||||
for (size_t i = 0; i < size; i++)
|
||||
assert(buf[i] == c);
|
||||
|
||||
// Check if the stream maintains a null-bye at the end.
|
||||
assert(buf[size] == '\0');
|
||||
|
||||
// Stream should be expanded, size should be == 2*WRITE_NO.
|
||||
assert(!fseek(fp, WRITE_NO, SEEK_END));
|
||||
assert(!fflush(fp));
|
||||
assert(size == 2*WRITE_NO);
|
||||
assert(buf[size] == '\0');
|
||||
|
||||
// Check if it's filled with zero's.
|
||||
for (size_t i = WRITE_NO; i < size; i++)
|
||||
assert(buf[i] == '\0');
|
||||
|
||||
// Go back and overwrite the 0's with 'B'.
|
||||
assert(!fseek(fp, -WRITE_NO, SEEK_CUR));
|
||||
c = 'B';
|
||||
for (size_t i = 0; i < WRITE_NO; i++)
|
||||
assert(fwrite(&c, sizeof(char), 1, fp) == 1);
|
||||
|
||||
// Check if that happened.
|
||||
assert(size == 2*WRITE_NO);
|
||||
for (size_t i = WRITE_NO; i < size; i++)
|
||||
assert(buf[i] == c);
|
||||
assert(buf[size] == '\0');
|
||||
|
||||
// Go to the front and write 'B'.
|
||||
assert(!fseek(fp, 0, SEEK_SET));
|
||||
for (size_t i = 0; i < WRITE_NO; i++)
|
||||
assert(fwrite(&c, sizeof(char), 1, fp) == 1);
|
||||
|
||||
// Check if that happened.
|
||||
assert(size == 2*WRITE_NO);
|
||||
for (size_t i = 0; i < size; i++)
|
||||
assert(buf[i] == c);
|
||||
assert(buf[size] == '\0');
|
||||
|
||||
// Close the file, we have tested everything.
|
||||
assert(!fclose(fp));
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static void noop(int x) {
|
||||
(void)x;
|
||||
}
|
||||
|
||||
int main() {
|
||||
signal(SIGUSR1, &noop);
|
||||
|
||||
int pid;
|
||||
switch(pid = fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
abort();
|
||||
case 0:
|
||||
pause();
|
||||
assert(errno == EINTR);
|
||||
return 0;
|
||||
default:
|
||||
while (1) {
|
||||
usleep(100);
|
||||
kill(pid, SIGUSR1);
|
||||
usleep(100);
|
||||
int status;
|
||||
|
||||
errno = 0;
|
||||
if (waitpid(-1, &status, WNOHANG) <= 0) {
|
||||
if (errno && errno != EAGAIN) {
|
||||
perror("wait");
|
||||
kill(pid, SIGKILL);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!WIFEXITED(status)) {
|
||||
printf("wait returned %x\n", status);
|
||||
abort();
|
||||
}
|
||||
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef USE_HOST_LIBC
|
||||
#define TEST_FILE "popen-host-libc.tmp"
|
||||
#else
|
||||
#define TEST_FILE "popen.tmp"
|
||||
#endif
|
||||
|
||||
#define TEST_STRING1 "the quick brown fox jumps over the lazy dog"
|
||||
#define TEST_STRING1_LEN 43
|
||||
#define TEST_STRING2 "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||
#define TEST_STRING2_LEN 55
|
||||
|
||||
int main() {
|
||||
// Test reading
|
||||
FILE *f1 = popen("echo -n '" TEST_STRING1 "'", "r");
|
||||
assert(f1);
|
||||
char buf1[TEST_STRING1_LEN];
|
||||
assert(fread(buf1, 1, TEST_STRING1_LEN, f1) == TEST_STRING1_LEN);
|
||||
assert(memcmp(buf1, TEST_STRING1, TEST_STRING1_LEN) == 0);
|
||||
pclose(f1);
|
||||
|
||||
// Test writing
|
||||
FILE *f2 = popen("cat - > " TEST_FILE, "w");
|
||||
assert(f2);
|
||||
assert(fwrite(TEST_STRING2, 1, TEST_STRING2_LEN, f2) == TEST_STRING2_LEN);
|
||||
pclose(f2);
|
||||
|
||||
// Read test file back
|
||||
FILE *test_file = fopen(TEST_FILE, "r");
|
||||
assert(test_file);
|
||||
char buf2[TEST_STRING2_LEN];
|
||||
assert(fread(buf2, 1, TEST_STRING2_LEN, test_file) == TEST_STRING2_LEN);
|
||||
assert(memcmp(buf2, TEST_STRING2, TEST_STRING2_LEN) == 0);
|
||||
fclose(test_file);
|
||||
assert(unlink(TEST_FILE) == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
size_t COUNTER_EXPIRATIONS = 3;
|
||||
|
||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||
size_t count = 0;
|
||||
|
||||
void timer_handler(union sigval val) {
|
||||
(void) val;
|
||||
pthread_mutex_lock(&mutex);
|
||||
count++;
|
||||
fprintf(stderr, "new count %zu\n", count);
|
||||
if(count >= COUNTER_EXPIRATIONS) {
|
||||
pthread_cond_signal(&cond);
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
static void test_thread_notify() {
|
||||
timer_t timer;
|
||||
struct sigevent sev = {
|
||||
.sigev_value = {
|
||||
.sival_ptr = &timer,
|
||||
},
|
||||
.sigev_notify = SIGEV_THREAD,
|
||||
};
|
||||
sev.sigev_notify_function = timer_handler;
|
||||
sev.sigev_notify_attributes = NULL;
|
||||
|
||||
int ret = timer_create(CLOCK_MONOTONIC, &sev, &timer);
|
||||
assert(ret == 0);
|
||||
|
||||
struct itimerspec its = {
|
||||
.it_interval = {
|
||||
.tv_sec = 1,
|
||||
.tv_nsec = 0,
|
||||
},
|
||||
.it_value = {
|
||||
.tv_sec = 1,
|
||||
.tv_nsec = 0,
|
||||
},
|
||||
};
|
||||
ret = timer_settime(timer, 0, &its, NULL);
|
||||
assert(ret == 0);
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
while(count < COUNTER_EXPIRATIONS)
|
||||
pthread_cond_wait(&cond, &mutex);
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
ret = timer_delete(timer);
|
||||
assert(ret == 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
struct sigevent evp;
|
||||
memset(&evp, 0, sizeof(evp));
|
||||
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGUSR1);
|
||||
sigprocmask(SIG_BLOCK, &set, 0);
|
||||
evp.sigev_notify = SIGEV_SIGNAL;
|
||||
evp.sigev_signo = SIGUSR1;
|
||||
|
||||
struct timeval start;
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
timer_t timer;
|
||||
if (timer_create(CLOCK_MONOTONIC, &evp, &timer)) {
|
||||
perror("timer_create");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct itimerspec spec;
|
||||
memset(&spec, 0, sizeof(spec));
|
||||
spec.it_value.tv_sec = 1;
|
||||
spec.it_value.tv_nsec = 0;
|
||||
|
||||
int sig;
|
||||
timer_settime(timer, 0, &spec, NULL);
|
||||
|
||||
struct itimerspec its = {};
|
||||
int ret = timer_gettime(timer, &its);
|
||||
assert(ret == 0);
|
||||
assert(its.it_value.tv_sec || its.it_value.tv_nsec);
|
||||
|
||||
sigwait(&set, &sig);
|
||||
|
||||
struct timeval end;
|
||||
gettimeofday(&end, NULL);
|
||||
|
||||
double diff = end.tv_sec - start.tv_sec;
|
||||
diff += (end.tv_usec - start.tv_usec) / 1000000.0;
|
||||
assert(diff >= 1.0);
|
||||
|
||||
#if defined(__linux__)
|
||||
test_thread_notify();
|
||||
#endif
|
||||
|
||||
timer_delete(timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
int main() {
|
||||
void *p = NULL;
|
||||
|
||||
// align must be a power of two
|
||||
assert(posix_memalign(&p, 3, 1) == EINVAL && p == NULL);
|
||||
|
||||
// align must be a multiple of sizeof(void *)
|
||||
assert(posix_memalign(&p, sizeof(void *) / 2, 8) == EINVAL && p == NULL);
|
||||
|
||||
assert(posix_memalign(&p, sizeof(void *), sizeof(void *)) == 0 && p != NULL && (uintptr_t)p % sizeof(void *) == 0);
|
||||
free(p);
|
||||
assert(posix_memalign(&p, 256, 1) == 0 && p != NULL && (uintptr_t)p % 256 == 0);
|
||||
free(p);
|
||||
assert(posix_memalign(&p, 256, 256) == 0 && p != NULL && (uintptr_t)p % 256 == 0);
|
||||
free(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
extern char **environ;
|
||||
|
||||
void run_cmd(char *cmd)
|
||||
{
|
||||
pid_t pid;
|
||||
char *argv[] = {"sh", "-c", cmd, NULL};
|
||||
int status;
|
||||
printf("Run command: %s\n", cmd);
|
||||
status = posix_spawn(&pid, "/bin/sh", NULL, NULL, argv, environ);
|
||||
if(status == 0) {
|
||||
printf("Child pid: %i\n", pid);
|
||||
if(waitpid(pid, &status, 0) != -1) {
|
||||
printf("Child exited with status %i\n", status);
|
||||
printf("Child exit status: %i\n", WEXITSTATUS(status));
|
||||
assert(WEXITSTATUS(status) == 0);
|
||||
} else {
|
||||
perror("waitpid");
|
||||
assert(0 == 1);
|
||||
}
|
||||
} else {
|
||||
printf("posix_spawn: %s\n", strerror(status));
|
||||
assert(0 == 1);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
run_cmd(":");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
_Atomic int prepare_order = 0;
|
||||
_Atomic int parent_order = 0;
|
||||
_Atomic int child_order = 0;
|
||||
|
||||
static void prepare1() { prepare_order = 1; }
|
||||
static void prepare2() { prepare_order = 2; }
|
||||
|
||||
static void parent1() { parent_order = 1; }
|
||||
static void parent2() { parent_order = 2; }
|
||||
|
||||
static void child1() { child_order = 1; }
|
||||
static void child2() { child_order = 2; }
|
||||
|
||||
int main() {
|
||||
assert(!pthread_atfork(prepare1, parent1, child1));
|
||||
assert(!pthread_atfork(prepare2, parent2, child2));
|
||||
|
||||
pid_t pid = fork();
|
||||
assert(pid >= 0);
|
||||
|
||||
if (!pid) {
|
||||
assert(child_order == 2);
|
||||
exit(0);
|
||||
} else {
|
||||
assert(prepare_order == 1);
|
||||
assert(parent_order == 2);
|
||||
|
||||
while (1) {
|
||||
int status = 0;
|
||||
|
||||
int ret = waitpid(pid, &status, 0);
|
||||
|
||||
if (ret == -1 && errno == EINTR)
|
||||
continue;
|
||||
|
||||
assert(ret > 0);
|
||||
|
||||
if (WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT)
|
||||
return 1;
|
||||
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <alloca.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <signal.h>
|
||||
|
||||
static void test_detachstate() {
|
||||
pthread_attr_t attr;
|
||||
assert(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
|
||||
int detachstate;
|
||||
assert(!pthread_attr_getdetachstate(&attr, &detachstate));
|
||||
assert(detachstate == PTHREAD_CREATE_DETACHED);
|
||||
assert(pthread_attr_setdetachstate(&attr, 2* (PTHREAD_CREATE_DETACHED +
|
||||
PTHREAD_CREATE_JOINABLE)) == EINVAL);
|
||||
}
|
||||
|
||||
static void *stacksize_worker(void *arg) {
|
||||
size_t default_stacksize = (*(size_t*)arg);
|
||||
size_t alloc_size = default_stacksize + default_stacksize/2;
|
||||
void *area = alloca(alloc_size);
|
||||
// If the allocated stack was not enough this will crash.
|
||||
// Trample both the start and end of the area so it works on both upwards-
|
||||
// and downwards-growing stacks.
|
||||
*(volatile char*)area = 1;
|
||||
*(volatile char*)(area + alloc_size - 1) = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test_stacksize() {
|
||||
pthread_attr_t attr;
|
||||
assert(!pthread_attr_init(&attr));
|
||||
size_t stacksize;
|
||||
assert(!pthread_attr_getstacksize(&attr, &stacksize));
|
||||
assert(!pthread_attr_setstacksize(&attr, stacksize * 2));
|
||||
pthread_t thread;
|
||||
assert(!pthread_create(&thread, &attr, stacksize_worker, &stacksize));
|
||||
assert(!pthread_join(thread, NULL));
|
||||
}
|
||||
|
||||
static void test_guardsize() {
|
||||
pthread_attr_t attr;
|
||||
assert(!pthread_attr_init(&attr));
|
||||
assert(!pthread_attr_setguardsize(&attr, 0));
|
||||
size_t guardsize;
|
||||
assert(!pthread_attr_getguardsize(&attr, &guardsize));
|
||||
assert(!guardsize);
|
||||
}
|
||||
|
||||
static void test_scope() {
|
||||
pthread_attr_t attr;
|
||||
assert(!pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM));
|
||||
int scope;
|
||||
assert(!pthread_attr_getscope(&attr, &scope));
|
||||
assert(scope == PTHREAD_SCOPE_SYSTEM);
|
||||
assert(pthread_attr_setscope(&attr, 2* (PTHREAD_SCOPE_SYSTEM +
|
||||
PTHREAD_SCOPE_PROCESS)) == EINVAL);
|
||||
}
|
||||
|
||||
static void test_inheritsched() {
|
||||
pthread_attr_t attr;
|
||||
assert(!pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED));
|
||||
int inheritsched;
|
||||
assert(!pthread_attr_getinheritsched(&attr, &inheritsched));
|
||||
assert(inheritsched == PTHREAD_INHERIT_SCHED);
|
||||
assert(pthread_attr_setinheritsched(&attr, 2* (PTHREAD_INHERIT_SCHED +
|
||||
PTHREAD_EXPLICIT_SCHED)) == EINVAL);
|
||||
}
|
||||
|
||||
static void test_schedparam() {
|
||||
pthread_attr_t attr;
|
||||
struct sched_param init_param = {0};
|
||||
assert(!pthread_attr_setschedparam(&attr, &init_param));
|
||||
struct sched_param param = {1};
|
||||
assert(!pthread_attr_getschedparam(&attr, ¶m));
|
||||
assert(param.sched_priority == init_param.sched_priority);
|
||||
}
|
||||
|
||||
static void test_schedpolicy() {
|
||||
pthread_attr_t attr;
|
||||
assert(!pthread_attr_setschedpolicy(&attr, SCHED_FIFO));
|
||||
int policy;
|
||||
assert(!pthread_attr_getschedpolicy(&attr, &policy));
|
||||
assert(policy == SCHED_FIFO);
|
||||
assert(pthread_attr_setinheritsched(&attr, 2* (SCHED_FIFO + SCHED_RR +
|
||||
SCHED_OTHER)) == EINVAL);
|
||||
}
|
||||
|
||||
static void *stackaddr_worker(void *arg) {
|
||||
void *addr = *(void**)arg;
|
||||
|
||||
void *sp;
|
||||
#if defined(__x86_64__)
|
||||
asm volatile ("mov %%rsp, %0" : "=r"(sp));
|
||||
#elif defined(__i386__)
|
||||
asm volatile ("mov %%esp, %0" : "=r"(sp));
|
||||
#elif defined(__aarch64__)
|
||||
asm volatile ("mov %0, sp" : "=r"(sp));
|
||||
#elif defined (__riscv)
|
||||
asm volatile ("mv %0, sp" : "=r"(sp));
|
||||
#elif defined(__m68k__)
|
||||
asm volatile ("move.l %%sp, %0" : "=r"(sp));
|
||||
#elif defined (__loongarch64)
|
||||
asm volatile ("move %0, $sp" : "=r"(sp));
|
||||
#else
|
||||
# error Unknown architecture
|
||||
#endif
|
||||
|
||||
// Check if our stack pointer is in a sane range.
|
||||
assert(sp > addr);
|
||||
return NULL;
|
||||
}
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
static void test_stackaddr() {
|
||||
pthread_attr_t attr;
|
||||
assert(!pthread_attr_init(&attr));
|
||||
size_t size;
|
||||
assert(!pthread_attr_getstacksize(&attr, &size));
|
||||
void *addr = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
assert(!pthread_attr_setstack(&attr, addr, size));
|
||||
assert(!pthread_attr_setguardsize(&attr, 0));
|
||||
void *new_addr;
|
||||
size_t new_size;
|
||||
assert(!pthread_attr_getstack(&attr, &new_addr, &new_size));
|
||||
assert(new_addr == addr);
|
||||
assert(new_size == size);
|
||||
|
||||
pthread_t thread;
|
||||
assert(!pthread_create(&thread, &attr, stackaddr_worker, &addr));
|
||||
assert(!pthread_join(thread, NULL));
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#if !defined(USE_HOST_LIBC) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)
|
||||
static void test_stack() {
|
||||
pthread_attr_t attr;
|
||||
void *stackaddr = (void*)1;
|
||||
size_t stacksize = PTHREAD_STACK_MIN;
|
||||
|
||||
assert(!pthread_attr_setstack(&attr, stackaddr, stacksize));
|
||||
void *new_addr;
|
||||
size_t new_size;
|
||||
assert(!pthread_attr_getstack(&attr, &new_addr, &new_size));
|
||||
assert(new_addr == stackaddr);
|
||||
assert(new_size == stacksize);
|
||||
}
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
test_detachstate();
|
||||
test_stacksize();
|
||||
test_guardsize();
|
||||
test_scope();
|
||||
test_inheritsched();
|
||||
test_schedparam();
|
||||
test_schedpolicy();
|
||||
test_stackaddr();
|
||||
#if !defined(USE_HOST_LIBC) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)
|
||||
test_stack();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
pthread_barrier_t barrier;
|
||||
_Atomic int hitBarrierCount, pastBarrierCount;
|
||||
|
||||
static void *worker(void *arg) {
|
||||
(void)arg;
|
||||
hitBarrierCount++;
|
||||
pthread_barrier_wait(&barrier);
|
||||
pastBarrierCount++;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// pthread_barrierattr_t
|
||||
pthread_barrierattr_t attr;
|
||||
pthread_barrierattr_init(&attr);
|
||||
|
||||
int pshared;
|
||||
pthread_barrierattr_getpshared(&attr, &pshared);
|
||||
assert(pshared == PTHREAD_PROCESS_PRIVATE);
|
||||
|
||||
pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
||||
pthread_barrierattr_getpshared(&attr, &pshared);
|
||||
assert(pshared == PTHREAD_PROCESS_SHARED);
|
||||
|
||||
pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
|
||||
pthread_barrierattr_getpshared(&attr, &pshared);
|
||||
assert(pshared == PTHREAD_PROCESS_PRIVATE);
|
||||
|
||||
// pthread_barrier_t
|
||||
pthread_barrier_init(&barrier, &attr, 3);
|
||||
pthread_barrierattr_destroy(&attr);
|
||||
|
||||
pthread_t thread1;
|
||||
int ret = pthread_create(&thread1, NULL, &worker, NULL);
|
||||
assert(!ret);
|
||||
|
||||
pthread_t thread2;
|
||||
ret = pthread_create(&thread2, NULL, &worker, NULL);
|
||||
assert(!ret);
|
||||
|
||||
sleep(1);
|
||||
|
||||
// Make sure the barrier actually stops threads from proceeding.
|
||||
assert(pastBarrierCount == 0);
|
||||
assert(hitBarrierCount <= 2);
|
||||
|
||||
hitBarrierCount++;
|
||||
pthread_barrier_wait(&barrier);
|
||||
assert(hitBarrierCount == 3);
|
||||
|
||||
pthread_barrier_destroy(&barrier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
_Atomic int worker_ready = 0;
|
||||
_Atomic int main_ready = 0;
|
||||
|
||||
static void *worker1(void *arg) {
|
||||
(void)arg;
|
||||
assert(!pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
|
||||
assert(!pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
|
||||
|
||||
worker_ready = 1;
|
||||
|
||||
while (1) sleep(1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *worker2(void *arg) {
|
||||
(void) arg;
|
||||
assert(!pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
|
||||
|
||||
worker_ready = 1;
|
||||
|
||||
while(!main_ready);
|
||||
|
||||
// Cancellation point - we should cancel right now
|
||||
sleep(1);
|
||||
|
||||
assert(!"Expected to be cancelled!");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static void *worker3(void *arg) {
|
||||
(void) arg;
|
||||
assert(!pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
|
||||
|
||||
worker_ready = 1;
|
||||
|
||||
while(!main_ready);
|
||||
|
||||
// Cancellation point - we should cancel right now
|
||||
pthread_testcancel();
|
||||
|
||||
assert(!"Expected to be cancelled!");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static void *worker4(void *arg) {
|
||||
(void) arg;
|
||||
assert(!pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
|
||||
|
||||
worker_ready = 1;
|
||||
sleep(1);
|
||||
|
||||
// We expect to be canceled during the sleep
|
||||
|
||||
assert(!"Expected to be cancelled!");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static void *worker5(void *arg) {
|
||||
assert(!pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
|
||||
|
||||
worker_ready = 1;
|
||||
|
||||
while(!main_ready);
|
||||
|
||||
// Cancellation point - we should NOT cancel right now
|
||||
pthread_testcancel();
|
||||
|
||||
int *arg_int = (int*)arg;
|
||||
*arg_int = 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void check_result(pthread_t thread) {
|
||||
|
||||
void *ret_val = NULL;
|
||||
int ret = pthread_join(thread, &ret_val);
|
||||
assert(!ret);
|
||||
assert(ret_val == PTHREAD_CANCELED);
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_t thread;
|
||||
int ret = pthread_create(&thread, NULL, &worker1, NULL);
|
||||
assert(!ret);
|
||||
|
||||
while (!worker_ready);
|
||||
ret = pthread_cancel(thread);
|
||||
assert(!ret);
|
||||
check_result(thread);
|
||||
|
||||
main_ready = 0;
|
||||
worker_ready = 0;
|
||||
main_ready = 0;
|
||||
ret = pthread_create(&thread, NULL, &worker2, NULL);
|
||||
assert(!ret);
|
||||
|
||||
while(!worker_ready);
|
||||
ret = pthread_cancel(thread);
|
||||
assert(!ret);
|
||||
main_ready = 1;
|
||||
check_result(thread);
|
||||
|
||||
main_ready = 0;
|
||||
worker_ready = 0;
|
||||
main_ready = 0;
|
||||
ret = pthread_create(&thread, NULL, &worker3, NULL);
|
||||
assert(!ret);
|
||||
|
||||
while(!worker_ready);
|
||||
ret = pthread_cancel(thread);
|
||||
assert(!ret);
|
||||
main_ready = 1;
|
||||
check_result(thread);
|
||||
|
||||
worker_ready = 0;
|
||||
main_ready = 0;
|
||||
ret = pthread_create(&thread, NULL, &worker4, NULL);
|
||||
assert(!ret);
|
||||
|
||||
while(!worker_ready);
|
||||
ret = pthread_cancel(thread);
|
||||
assert(!ret);
|
||||
main_ready = 1;
|
||||
check_result(thread);
|
||||
|
||||
// Test for bug where pthread_testcancel() was not checking if
|
||||
// cancellation was triggered properly.
|
||||
worker_ready = 0;
|
||||
int pthread_arg = 0;
|
||||
main_ready = 0;
|
||||
ret = pthread_create(&thread, NULL, &worker5, &pthread_arg);
|
||||
assert(!ret);
|
||||
|
||||
while(!worker_ready);
|
||||
main_ready = 1;
|
||||
|
||||
void *ret_val = NULL;
|
||||
ret = pthread_join(thread, &ret_val);
|
||||
assert(!ret);
|
||||
assert(!ret_val);
|
||||
assert(pthread_arg == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
||||
_Atomic uintptr_t cleanup_val = 0;
|
||||
|
||||
static void cleanup(void *arg) {
|
||||
cleanup_val = (uintptr_t)arg;
|
||||
}
|
||||
|
||||
static void *worker(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
pthread_cleanup_push(cleanup, (void *)1);
|
||||
pthread_cleanup_push(cleanup, (void *)2);
|
||||
pthread_cleanup_push(cleanup, (void *)3);
|
||||
pthread_cleanup_push(cleanup, (void *)4);
|
||||
|
||||
pthread_cleanup_pop(1);
|
||||
assert(cleanup_val == 4);
|
||||
|
||||
pthread_cleanup_pop(0);
|
||||
assert(cleanup_val == 4);
|
||||
|
||||
pthread_exit(NULL);
|
||||
|
||||
pthread_cleanup_pop(0);
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_t thread;
|
||||
assert(!pthread_create(&thread, NULL, &worker, NULL));
|
||||
assert(!pthread_join(thread, NULL));
|
||||
|
||||
assert(cleanup_val == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
_Atomic int waiting, should_exit;
|
||||
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void *worker(void *arg) {
|
||||
(void)arg;
|
||||
pthread_mutex_lock(&mtx);
|
||||
++waiting;
|
||||
while (!should_exit)
|
||||
pthread_cond_wait(&cond, &mtx);
|
||||
pthread_mutex_unlock(&mtx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test_broadcast_wakes_all() {
|
||||
pthread_t t1, t2;
|
||||
pthread_create(&t1, NULL, &worker, NULL);
|
||||
pthread_create(&t2, NULL, &worker, NULL);
|
||||
|
||||
// Wait until the workers have actually entered the cond_wait
|
||||
// before doing a broadcast.
|
||||
while (waiting != 2 || pthread_mutex_trylock(&mtx) == EBUSY)
|
||||
usleep(150000); // 150ms
|
||||
|
||||
should_exit = 1;
|
||||
assert(!pthread_cond_broadcast(&cond));
|
||||
pthread_mutex_unlock(&mtx);
|
||||
|
||||
pthread_join(t1, NULL);
|
||||
pthread_join(t2, NULL);
|
||||
}
|
||||
|
||||
static void test_timedwait_timedout() {
|
||||
// Use CLOCK_MONOTONIC.
|
||||
pthread_condattr_t attr;
|
||||
pthread_condattr_init(&attr);
|
||||
assert(!pthread_condattr_setclock(&attr, CLOCK_MONOTONIC));
|
||||
|
||||
struct timespec before_now;
|
||||
assert(!clock_gettime(CLOCK_MONOTONIC, &before_now));
|
||||
before_now.tv_nsec -= 10000;
|
||||
|
||||
pthread_mutex_lock(&mtx);
|
||||
int e = pthread_cond_timedwait(&cond, &mtx, &before_now);
|
||||
assert(e == ETIMEDOUT);
|
||||
pthread_mutex_unlock(&mtx);
|
||||
|
||||
long nanos_per_second = 1000000000;
|
||||
struct timespec after_now;
|
||||
assert(!clock_gettime(CLOCK_MONOTONIC, &after_now));
|
||||
after_now.tv_nsec += nanos_per_second / 10; // 100ms
|
||||
if (after_now.tv_nsec >= nanos_per_second) {
|
||||
after_now.tv_nsec -= nanos_per_second;
|
||||
after_now.tv_sec++;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mtx);
|
||||
e = pthread_cond_timedwait(&cond, &mtx, &after_now);
|
||||
assert(e == ETIMEDOUT);
|
||||
pthread_mutex_unlock(&mtx);
|
||||
|
||||
after_now.tv_nsec += nanos_per_second;
|
||||
pthread_mutex_lock(&mtx);
|
||||
e = pthread_cond_timedwait(&cond, &mtx, &after_now);
|
||||
assert(e == EINVAL);
|
||||
pthread_mutex_unlock(&mtx);
|
||||
}
|
||||
|
||||
static void test_attr() {
|
||||
pthread_condattr_t attr;
|
||||
pthread_condattr_init(&attr);
|
||||
|
||||
clockid_t clock;
|
||||
assert(!pthread_condattr_getclock(&attr, &clock));
|
||||
assert(clock == CLOCK_REALTIME);
|
||||
assert(!pthread_condattr_setclock(&attr, CLOCK_MONOTONIC));
|
||||
assert(!pthread_condattr_getclock(&attr, &clock));
|
||||
assert(clock == CLOCK_MONOTONIC);
|
||||
|
||||
int pshared;
|
||||
assert(!pthread_condattr_getpshared(&attr, &pshared));
|
||||
assert(pshared == PTHREAD_PROCESS_PRIVATE);
|
||||
assert(!pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED));
|
||||
assert(!pthread_condattr_getpshared(&attr, &pshared));
|
||||
assert(pshared == PTHREAD_PROCESS_SHARED);
|
||||
|
||||
pthread_condattr_destroy(&attr);
|
||||
pthread_condattr_init(&attr);
|
||||
|
||||
// Make sure that we can create a pthread_cond_t with an attr.
|
||||
pthread_cond_t cond;
|
||||
pthread_cond_init(&cond, &attr);
|
||||
pthread_cond_destroy(&cond);
|
||||
pthread_condattr_destroy(&attr);
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_attr();
|
||||
test_broadcast_wakes_all();
|
||||
test_timedwait_timedout();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
int variable = 0;
|
||||
|
||||
static void *worker(void *arg) {
|
||||
(void) arg;
|
||||
variable = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_t thread;
|
||||
int ret = pthread_create(&thread, NULL, &worker, NULL);
|
||||
assert(!ret);
|
||||
|
||||
ret = pthread_join(thread, NULL);
|
||||
assert(!ret);
|
||||
assert(variable == 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
_Atomic int dtors_entered = 0;
|
||||
pthread_key_t key3;
|
||||
|
||||
static void dtor1(void *arg) {
|
||||
(void)arg;
|
||||
dtors_entered++;
|
||||
|
||||
// Set the key during destruction to trigger dtor2 (as it only runs if
|
||||
// the key value is non-NULL).
|
||||
assert(!pthread_setspecific(key3, &key3));
|
||||
assert(pthread_getspecific(key3) == &key3);
|
||||
}
|
||||
|
||||
static void dtor2(void *arg) {
|
||||
(void)arg;
|
||||
dtors_entered++;
|
||||
}
|
||||
|
||||
static void *worker1(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
pthread_key_t key1, key2;
|
||||
assert(!pthread_key_create(&key1, NULL));
|
||||
assert(!pthread_key_create(&key2, dtor1));
|
||||
assert(!pthread_key_create(&key3, dtor2));
|
||||
|
||||
assert(!pthread_setspecific(key1, &key1));
|
||||
assert(pthread_getspecific(key1) == &key1);
|
||||
|
||||
assert(!pthread_setspecific(key2, &key2));
|
||||
assert(pthread_getspecific(key2) == &key2);
|
||||
|
||||
pthread_exit(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dtor3(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
// Make sure that we can create and destroy keys inside the dtor.
|
||||
pthread_key_t dtorKey;
|
||||
assert(!pthread_key_create(&dtorKey, NULL));
|
||||
|
||||
assert(!pthread_setspecific(dtorKey, &dtorKey));
|
||||
assert(pthread_getspecific(dtorKey) == &dtorKey);
|
||||
|
||||
assert(!pthread_key_delete(dtorKey));
|
||||
}
|
||||
|
||||
static void *worker2(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
pthread_key_t key;
|
||||
assert(!pthread_key_create(&key, dtor3));
|
||||
|
||||
assert(!pthread_setspecific(key, &key));
|
||||
assert(pthread_getspecific(key) == &key);
|
||||
|
||||
pthread_exit(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// NOTE that the EINVAL return from pthread_setspecific is mlibc-specific,
|
||||
// POSIX specifies that accessing an invalid key is undefined behavior.
|
||||
|
||||
assert(pthread_getspecific(PTHREAD_KEYS_MAX) == NULL);
|
||||
assert(pthread_setspecific(PTHREAD_KEYS_MAX, NULL) == EINVAL);
|
||||
|
||||
pthread_key_t key;
|
||||
assert(!pthread_key_create(&key, NULL));
|
||||
assert(!pthread_setspecific(key, &key));
|
||||
assert(pthread_getspecific(key) == &key);
|
||||
assert(!pthread_key_delete(key));
|
||||
|
||||
pthread_t thread;
|
||||
assert(!pthread_create(&thread, NULL, &worker1, NULL));
|
||||
assert(!pthread_join(thread, NULL));
|
||||
|
||||
assert(pthread_getspecific(key) == NULL);
|
||||
assert(!pthread_setspecific(key, &key));
|
||||
assert(pthread_getspecific(key) == &key);
|
||||
|
||||
assert(dtors_entered == 2);
|
||||
|
||||
assert(!pthread_create(&thread, NULL, &worker2, NULL));
|
||||
assert(!pthread_join(thread, NULL));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
_Atomic int handler_ready = 0;
|
||||
_Atomic int thread_signal_ran = 0;
|
||||
|
||||
static void sig_handler(int sig, siginfo_t *info, void *ctx) {
|
||||
(void)sig;
|
||||
(void)info;
|
||||
(void)ctx;
|
||||
|
||||
thread_signal_ran = 1;
|
||||
}
|
||||
|
||||
static void *worker(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
struct sigaction sa;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_sigaction = sig_handler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
assert(!sigaction(SIGUSR1, &sa, NULL));
|
||||
|
||||
handler_ready = 1;
|
||||
|
||||
while (!thread_signal_ran)
|
||||
;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_t thread;
|
||||
assert(!pthread_create(&thread, NULL, &worker, NULL));
|
||||
|
||||
while (!handler_ready)
|
||||
;
|
||||
|
||||
assert(!pthread_kill(thread, SIGUSR1));
|
||||
assert(!pthread_join(thread, NULL));
|
||||
|
||||
assert(thread_signal_ran);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define TEST_ATTR(attr, field, value) ({ \
|
||||
int x; \
|
||||
assert(!pthread_mutexattr_set ## field (&(attr), (value))); \
|
||||
assert(!pthread_mutexattr_get ## field (&(attr), &x)); \
|
||||
assert(x == (value)); \
|
||||
})
|
||||
|
||||
int variable;
|
||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void *worker(void *arg) {
|
||||
(void)arg;
|
||||
pthread_mutex_lock(&mutex);
|
||||
variable = 1;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void testAttr() {
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
|
||||
TEST_ATTR(attr, type, PTHREAD_MUTEX_DEFAULT);
|
||||
TEST_ATTR(attr, type, PTHREAD_MUTEX_NORMAL);
|
||||
TEST_ATTR(attr, type, PTHREAD_MUTEX_ERRORCHECK);
|
||||
TEST_ATTR(attr, type, PTHREAD_MUTEX_RECURSIVE);
|
||||
|
||||
TEST_ATTR(attr, robust, PTHREAD_MUTEX_STALLED);
|
||||
TEST_ATTR(attr, robust, PTHREAD_MUTEX_ROBUST);
|
||||
|
||||
TEST_ATTR(attr, protocol, PTHREAD_PRIO_NONE);
|
||||
TEST_ATTR(attr, protocol, PTHREAD_PRIO_INHERIT);
|
||||
TEST_ATTR(attr, protocol, PTHREAD_PRIO_PROTECT);
|
||||
|
||||
TEST_ATTR(attr, pshared, PTHREAD_PROCESS_PRIVATE);
|
||||
TEST_ATTR(attr, pshared, PTHREAD_PROCESS_SHARED);
|
||||
|
||||
// TODO: sched_get_priority* is unimplemented.
|
||||
// int prio = sched_get_priority_max(SCHED_FIFO);
|
||||
// TEST_ATTR(attr, prioceiling, prio);
|
||||
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
|
||||
static void testNormal() {
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutex_init(&mutex, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
variable = 0;
|
||||
|
||||
pthread_t thread;
|
||||
int ret = pthread_create(&thread, NULL, &worker, NULL);
|
||||
assert(!ret);
|
||||
|
||||
assert(pthread_mutex_trylock(&mutex) == EBUSY);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
ret = pthread_join(thread, NULL);
|
||||
assert(!ret);
|
||||
assert(variable == 1);
|
||||
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
static void testRecursive() {
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&mutex, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
variable = 0;
|
||||
|
||||
pthread_t thread;
|
||||
int ret = pthread_create(&thread, NULL, &worker, NULL);
|
||||
assert(!ret);
|
||||
|
||||
assert(pthread_mutex_trylock(&mutex) == 0);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
ret = pthread_join(thread, NULL);
|
||||
assert(!ret);
|
||||
assert(variable == 1);
|
||||
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
int main() {
|
||||
testAttr();
|
||||
testNormal();
|
||||
testRecursive();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static void test_write_lock_unlock() {
|
||||
int res;
|
||||
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
|
||||
res = pthread_rwlock_wrlock(&rw);
|
||||
assert(!res);
|
||||
res = pthread_rwlock_unlock(&rw);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
static void test_read_lock_unlock() {
|
||||
int res;
|
||||
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
|
||||
res = pthread_rwlock_rdlock(&rw);
|
||||
assert(!res);
|
||||
res = pthread_rwlock_unlock(&rw);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
static void test_write_trylock_unlock() {
|
||||
int res;
|
||||
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
|
||||
res = pthread_rwlock_trywrlock(&rw);
|
||||
assert(!res);
|
||||
res = pthread_rwlock_unlock(&rw);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
static void test_read_trylock_unlock() {
|
||||
int res;
|
||||
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
|
||||
res = pthread_rwlock_tryrdlock(&rw);
|
||||
assert(!res);
|
||||
res = pthread_rwlock_unlock(&rw);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
static void test_write_prevents_read() {
|
||||
int res;
|
||||
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
|
||||
res = pthread_rwlock_wrlock(&rw);
|
||||
assert(!res);
|
||||
res = pthread_rwlock_tryrdlock(&rw);
|
||||
assert(res == EBUSY);
|
||||
res = pthread_rwlock_unlock(&rw);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
static void test_write_prevents_write() {
|
||||
int res;
|
||||
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
|
||||
res = pthread_rwlock_wrlock(&rw);
|
||||
assert(!res);
|
||||
res = pthread_rwlock_trywrlock(&rw);
|
||||
assert(res == EBUSY);
|
||||
res = pthread_rwlock_unlock(&rw);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
static void test_read_prevents_write() {
|
||||
int res;
|
||||
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
|
||||
res = pthread_rwlock_rdlock(&rw);
|
||||
assert(!res);
|
||||
res = pthread_rwlock_trywrlock(&rw);
|
||||
assert(res == EBUSY);
|
||||
res = pthread_rwlock_unlock(&rw);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
static void test_read_allows_read() {
|
||||
int res;
|
||||
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
|
||||
res = pthread_rwlock_rdlock(&rw);
|
||||
assert(!res);
|
||||
res = pthread_rwlock_tryrdlock(&rw);
|
||||
assert(!res);
|
||||
res = pthread_rwlock_unlock(&rw);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
static void test_attr() {
|
||||
pthread_rwlockattr_t attr;
|
||||
pthread_rwlockattr_init(&attr);
|
||||
|
||||
int pshared;
|
||||
pthread_rwlockattr_getpshared(&attr, &pshared);
|
||||
assert(pshared == PTHREAD_PROCESS_PRIVATE);
|
||||
|
||||
pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
||||
pthread_rwlockattr_getpshared(&attr, &pshared);
|
||||
assert(pshared == PTHREAD_PROCESS_SHARED);
|
||||
|
||||
pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
|
||||
pthread_rwlockattr_getpshared(&attr, &pshared);
|
||||
assert(pshared == PTHREAD_PROCESS_PRIVATE);
|
||||
|
||||
pthread_rwlockattr_destroy(&attr);
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_write_lock_unlock();
|
||||
test_read_lock_unlock();
|
||||
test_write_trylock_unlock();
|
||||
test_read_trylock_unlock();
|
||||
test_write_prevents_read();
|
||||
test_write_prevents_write();
|
||||
test_read_prevents_write();
|
||||
test_read_allows_read();
|
||||
test_attr();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
struct sched_param param = {
|
||||
.sched_priority = 100,
|
||||
};
|
||||
|
||||
int policy = 0xDEADBEEF;
|
||||
|
||||
int ret = pthread_getschedparam(pthread_self(), &policy, ¶m);
|
||||
assert(policy == SCHED_OTHER);
|
||||
assert(!ret);
|
||||
|
||||
param.sched_priority = 10;
|
||||
|
||||
ret = pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);
|
||||
assert(!ret || ret == EPERM);
|
||||
|
||||
if(ret == EPERM) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
param.sched_priority = 0xDEADBEEF;
|
||||
|
||||
ret = pthread_getschedparam(pthread_self(), &policy, ¶m);
|
||||
assert(policy == SCHED_FIFO);
|
||||
assert(param.sched_priority == 10);
|
||||
assert(!ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
|
||||
_Thread_local unsigned int counter = 9999;
|
||||
_Thread_local unsigned int uninitialized;
|
||||
|
||||
void *check_counter(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
fprintf(stderr, "counter for worker thread: %d, at %p\n", counter, &counter);
|
||||
fflush(stderr);
|
||||
assert(counter == 9999);
|
||||
|
||||
fprintf(stderr, "uninitialized data for worker thread: %d, at %p\n", uninitialized, &uninitialized);
|
||||
fflush(stderr);
|
||||
assert(uninitialized == 0);
|
||||
|
||||
++counter;
|
||||
++uninitialized;
|
||||
fprintf(stderr, "counter for worker thread: %d\n", counter);
|
||||
fflush(stderr);
|
||||
assert(counter == 10000);
|
||||
assert(uninitialized == 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
fprintf(stderr, "counter for main thread: %d, at %p\n", counter, &counter);
|
||||
fflush(stderr);
|
||||
assert(counter == 9999);
|
||||
|
||||
fprintf(stderr, "uninitialized data for main thread: %d, at %p\n", uninitialized, &uninitialized);
|
||||
fflush(stderr);
|
||||
assert(uninitialized == 0);
|
||||
|
||||
++counter;
|
||||
++uninitialized;
|
||||
fprintf(stderr, "counter for main: %d\n", counter);
|
||||
fflush(stderr);
|
||||
assert(counter == 10000);
|
||||
assert(uninitialized == 1);
|
||||
|
||||
pthread_t thd;
|
||||
pthread_create(&thd, NULL, check_counter, NULL);
|
||||
pthread_join(thd, NULL);
|
||||
|
||||
fprintf(stderr, "counter for main: %d\n", counter);
|
||||
fflush(stderr);
|
||||
assert(counter == 10000);
|
||||
assert(uninitialized == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
struct passwd pwd, *result = NULL;
|
||||
char *buf;
|
||||
size_t bufsize;
|
||||
int s;
|
||||
|
||||
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
assert(bufsize > 0 && bufsize < 0x100000);
|
||||
|
||||
buf = malloc(bufsize);
|
||||
assert(buf);
|
||||
|
||||
s = getpwnam_r("root", &pwd, buf, bufsize, &result);
|
||||
assert(!s);
|
||||
assert(pwd.pw_uid == 0);
|
||||
assert(!strcmp(pwd.pw_name, "root"));
|
||||
assert(strlen(pwd.pw_passwd) <= 1000);
|
||||
|
||||
s = getpwuid_r(0, &pwd, buf, bufsize, &result);
|
||||
assert(!s);
|
||||
assert(pwd.pw_uid == 0);
|
||||
assert(!strcmp(pwd.pw_name, "root"));
|
||||
assert(strlen(pwd.pw_passwd) <= 1000);
|
||||
|
||||
result = getpwnam("root");
|
||||
assert(result);
|
||||
assert(result->pw_uid == 0);
|
||||
assert(!strcmp(result->pw_name, "root"));
|
||||
assert(strlen(result->pw_passwd) <= 1000);
|
||||
|
||||
result = getpwuid(0);
|
||||
assert(result);
|
||||
assert(result->pw_uid == 0);
|
||||
assert(!strcmp(result->pw_name, "root"));
|
||||
assert(strlen(result->pw_passwd) <= 1000);
|
||||
|
||||
errno = 0;
|
||||
setpwent();
|
||||
assert(errno == 0);
|
||||
|
||||
errno = 0;
|
||||
result = getpwent();
|
||||
assert(result);
|
||||
|
||||
pwd = *result;
|
||||
pwd.pw_name = strdup(result->pw_name);
|
||||
pwd.pw_passwd = strdup(result->pw_passwd);
|
||||
pwd.pw_gecos = strdup(result->pw_gecos);
|
||||
pwd.pw_dir = strdup(result->pw_dir);
|
||||
pwd.pw_shell = strdup(result->pw_shell);
|
||||
assert(pwd.pw_name);
|
||||
assert(pwd.pw_passwd);
|
||||
assert(pwd.pw_gecos);
|
||||
assert(pwd.pw_dir);
|
||||
assert(pwd.pw_shell);
|
||||
|
||||
errno = 0;
|
||||
setpwent();
|
||||
assert(errno == 0);
|
||||
|
||||
errno = 0;
|
||||
result = getpwent();
|
||||
assert(result);
|
||||
|
||||
assert(!strcmp(pwd.pw_name, result->pw_name));
|
||||
assert(!strcmp(pwd.pw_passwd, result->pw_passwd));
|
||||
assert(!strcmp(pwd.pw_gecos, result->pw_gecos));
|
||||
assert(!strcmp(pwd.pw_dir, result->pw_dir));
|
||||
assert(!strcmp(pwd.pw_shell, result->pw_shell));
|
||||
assert(pwd.pw_uid == result->pw_uid);
|
||||
assert(pwd.pw_gid == result->pw_gid);
|
||||
|
||||
free(pwd.pw_name);
|
||||
free(pwd.pw_passwd);
|
||||
free(pwd.pw_gecos);
|
||||
free(pwd.pw_dir);
|
||||
free(pwd.pw_shell);
|
||||
|
||||
pwd.pw_name = "managarm";
|
||||
pwd.pw_passwd = "passwordhash";
|
||||
pwd.pw_uid = 1234;
|
||||
pwd.pw_gid = 12345;
|
||||
pwd.pw_gecos = "/etc/password test";
|
||||
pwd.pw_dir = "/home/managarm\n";
|
||||
pwd.pw_shell = "/usr/bin/bash";
|
||||
|
||||
// tmpfile() cannot be used because its unimplemented in mlibc.
|
||||
char filename[] = "pwdXXXXXX";
|
||||
int tmpfd = mkstemp(filename);
|
||||
assert(tmpfd);
|
||||
FILE *tmp = fdopen(tmpfd, "w+");
|
||||
assert(tmp);
|
||||
|
||||
assert(putpwent(NULL, tmp) < 0);
|
||||
assert(putpwent(&pwd, tmp) < 0);
|
||||
|
||||
pwd.pw_dir = "/home/managarm:";
|
||||
assert(putpwent(&pwd, tmp) < 0);
|
||||
|
||||
pwd.pw_dir = "/home/:managarm\n";
|
||||
assert(putpwent(&pwd, tmp) < 0);
|
||||
|
||||
pwd.pw_dir = "/home/managarm";
|
||||
assert(putpwent(&pwd, tmp) == 0);
|
||||
rewind(tmp);
|
||||
|
||||
char *expected = "managarm:passwordhash:1234:12345:/etc/password test:/home/managarm:/usr/bin/bash\n";
|
||||
size_t expectedlen = strlen(expected);
|
||||
|
||||
size_t readsize = fread(buf, 1, bufsize, tmp);
|
||||
assert(readsize == expectedlen);
|
||||
assert(strncmp(expected, buf, expectedlen) == 0);
|
||||
|
||||
fclose(tmp);
|
||||
unlink(filename);
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#ifdef USE_HOST_LIBC
|
||||
#define TEST_FILE "readv-writev-host-libc.tmp"
|
||||
#else
|
||||
#define TEST_FILE "readv-writev.tmp"
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
// Make sure that it wasn't created by a previous test run
|
||||
unlink(TEST_FILE);
|
||||
|
||||
int fd = open(TEST_FILE, O_RDWR | O_CREAT, 0644);
|
||||
assert(fd != -1);
|
||||
|
||||
char str0[] = "hello ";
|
||||
char str1[] = "world!";
|
||||
|
||||
struct iovec bufs[2] = {
|
||||
{
|
||||
.iov_base = &str0,
|
||||
.iov_len = strlen(str0),
|
||||
},
|
||||
{
|
||||
.iov_base = &str1,
|
||||
.iov_len = strlen(str1),
|
||||
},
|
||||
};
|
||||
|
||||
ssize_t written = writev(fd, bufs, 2);
|
||||
assert(written == 12);
|
||||
|
||||
memset(&str0, 0, strlen(str0));
|
||||
memset(&str1, 0, strlen(str1));
|
||||
|
||||
assert(!lseek(fd, 0, SEEK_SET));
|
||||
|
||||
ssize_t read = readv(fd, bufs, 2);
|
||||
assert(read == 12);
|
||||
|
||||
assert(!strncmp(str0, "hello ", 7));
|
||||
assert(!strncmp(str1, "world!", 7));
|
||||
|
||||
assert(!close(fd));
|
||||
|
||||
unlink(TEST_FILE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef USE_HOST_LIBC
|
||||
#define TEST_BASE "/tmp/mlibc-realpath-host-libc"
|
||||
#else
|
||||
#define TEST_BASE "/tmp/mlibc-realpath"
|
||||
#endif
|
||||
|
||||
void prepare() {
|
||||
assert(!mkdir(TEST_BASE "/", S_IRWXU));
|
||||
assert(!mkdir(TEST_BASE "/dir1", S_IRWXU));
|
||||
assert(!mkdir(TEST_BASE "/dir2", S_IRWXU));
|
||||
assert(!symlink(TEST_BASE "/dir2/", TEST_BASE "/dir1/abs-link"));
|
||||
assert(!symlink(TEST_BASE "/dir2///", TEST_BASE "/dir1/abs-link-trail"));
|
||||
assert(!symlink(TEST_BASE "/dir2", TEST_BASE "/dir1/abs-link-no-trail"));
|
||||
assert(!chdir(TEST_BASE "/dir1"));
|
||||
assert(!symlink("../dir2/", TEST_BASE "/dir1/rel-link"));
|
||||
}
|
||||
|
||||
void cleanup(int do_assert) {
|
||||
if (do_assert) {
|
||||
assert(!unlink(TEST_BASE "/dir1/rel-link"));
|
||||
assert(!unlink(TEST_BASE "/dir1/abs-link"));
|
||||
assert(!unlink(TEST_BASE "/dir1/abs-link-trail"));
|
||||
assert(!unlink(TEST_BASE "/dir1/abs-link-no-trail"));
|
||||
assert(!rmdir(TEST_BASE "/dir2"));
|
||||
assert(!rmdir(TEST_BASE "/dir1"));
|
||||
assert(!rmdir(TEST_BASE "/"));
|
||||
} else {
|
||||
unlink(TEST_BASE "/dir1/rel-link");
|
||||
unlink(TEST_BASE "/dir1/abs-link");
|
||||
unlink(TEST_BASE "/dir1/abs-link-trail");
|
||||
unlink(TEST_BASE "/dir1/abs-link-no-trail");
|
||||
rmdir(TEST_BASE "/dir2");
|
||||
rmdir(TEST_BASE "/dir1");
|
||||
rmdir(TEST_BASE "/");
|
||||
}
|
||||
}
|
||||
|
||||
void signal_handler(int sig, siginfo_t *info, void *ctx) {
|
||||
(void)sig;
|
||||
(void)info;
|
||||
(void)ctx;
|
||||
|
||||
cleanup(0);
|
||||
|
||||
struct sigaction sa;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sa.sa_flags = 0;
|
||||
|
||||
sigaction(SIGABRT, &sa, NULL);
|
||||
abort();
|
||||
}
|
||||
|
||||
#define TEST_PATH(rpath, expected) \
|
||||
path = realpath(rpath, NULL); \
|
||||
assert(path); \
|
||||
assert(!strcmp(path, expected)); \
|
||||
free(path);
|
||||
|
||||
|
||||
int main() {
|
||||
char *path;
|
||||
|
||||
struct sigaction sa;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_sigaction = signal_handler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGABRT, &sa, NULL);
|
||||
|
||||
prepare();
|
||||
|
||||
TEST_PATH(TEST_BASE "/dir1/", TEST_BASE "/dir1");
|
||||
TEST_PATH(TEST_BASE "/dir1/../dir2", TEST_BASE "/dir2");
|
||||
TEST_PATH(TEST_BASE "/dir1/abs-link/", TEST_BASE "/dir2");
|
||||
TEST_PATH(TEST_BASE "/dir1/rel-link/", TEST_BASE "/dir2");
|
||||
TEST_PATH(TEST_BASE "/dir1/abs-link/../", TEST_BASE "");
|
||||
TEST_PATH(TEST_BASE "/dir1/rel-link/../", TEST_BASE "");
|
||||
TEST_PATH(TEST_BASE "/dir1/abs-link/../dir1/abs-link/", TEST_BASE "/dir2");
|
||||
TEST_PATH(TEST_BASE "/dir1/rel-link/../dir1/rel-link/", TEST_BASE "/dir2");
|
||||
TEST_PATH(TEST_BASE "/dir1/abs-link/../dir1/rel-link/", TEST_BASE "/dir2");
|
||||
TEST_PATH(TEST_BASE "/dir1/rel-link/../dir1/abs-link/", TEST_BASE "/dir2");
|
||||
TEST_PATH(TEST_BASE "/dir1/abs-link-no-trail/", TEST_BASE "/dir2");
|
||||
TEST_PATH(TEST_BASE "/dir1/abs-link-trail/", TEST_BASE "/dir2");
|
||||
|
||||
TEST_PATH("/tmp", "/tmp");
|
||||
TEST_PATH("/", "/");
|
||||
TEST_PATH("//", "/");
|
||||
|
||||
cleanup(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <regex.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main(void) {
|
||||
char *testString = "mlibc is the best best best libc";
|
||||
char *pattern = "\\(be[a-z]t\\) \\1";
|
||||
|
||||
regex_t reg;
|
||||
int rc = regcomp(®, pattern, 0);
|
||||
assert(!rc);
|
||||
|
||||
regmatch_t matches[2];
|
||||
rc = regexec(®, testString, 2, matches, 0);
|
||||
assert(!rc);
|
||||
|
||||
printf("Whole pattern: \"%.*s\" at %zd-%zd.\n",
|
||||
(int)(matches[0].rm_eo - matches[0].rm_so), &testString[matches[0].rm_so],
|
||||
(ssize_t)matches[0].rm_so, (ssize_t)(matches[0].rm_eo - 1));
|
||||
assert(matches[0].rm_so == 13 && matches[0].rm_eo == 22);
|
||||
|
||||
printf("Substring: \"%.*s\" at %zd-%zd.\n",
|
||||
(int)(matches[1].rm_eo - matches[1].rm_so), &testString[matches[1].rm_so],
|
||||
(ssize_t)matches[1].rm_so, (ssize_t)matches[1].rm_eo - 1);
|
||||
assert(matches[1].rm_so == 13 && matches[1].rm_eo == 17);
|
||||
|
||||
regfree(®);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <strings.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main() {
|
||||
char str[] = "This is a sample string";
|
||||
char *pch;
|
||||
pch = rindex(str, 's');
|
||||
// The last occurence of 's' is at position 18
|
||||
assert(pch - str + 1 == 18);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include <sys/resource.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
struct rlimit getlim, setlim;
|
||||
|
||||
setlim.rlim_cur = 16;
|
||||
setlim.rlim_max = 4096;
|
||||
|
||||
int ret = setrlimit(RLIMIT_NOFILE, &setlim);
|
||||
|
||||
if(ret == -1) {
|
||||
fprintf(stderr, "%s\n", strerror(errno));
|
||||
assert(!ret);
|
||||
}
|
||||
|
||||
assert(!getrlimit(RLIMIT_NOFILE, &getlim));
|
||||
|
||||
assert(setlim.rlim_cur == getlim.rlim_cur);
|
||||
assert(setlim.rlim_max == getlim.rlim_max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <search.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
static int compare(const void *pa, const void *pb) {
|
||||
if (*(int*)pa < *(int*) pb)
|
||||
return -1;
|
||||
if (*(int*)pa > *(int*) pb)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_key(int key, void *root) {
|
||||
int keyp = key;
|
||||
void *ret = tfind((void*) &keyp, &root, compare);
|
||||
assert(ret);
|
||||
assert(**((int **) ret) == key);
|
||||
}
|
||||
|
||||
static int freed = 0;
|
||||
|
||||
static void free_key(void *key) {
|
||||
freed++;
|
||||
free(key);
|
||||
}
|
||||
|
||||
int main() {
|
||||
void *root = NULL;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
int *ptr = malloc(sizeof(int));
|
||||
assert(ptr);
|
||||
*ptr = i;
|
||||
|
||||
void *ret = tsearch((void*) ptr, &root, compare);
|
||||
assert(ret);
|
||||
assert(**((int **) ret) == i);
|
||||
}
|
||||
|
||||
// Test a couple of keys
|
||||
check_key(1, root);
|
||||
check_key(5, root);
|
||||
check_key(10, root);
|
||||
|
||||
// Verify NULL on non-existent key
|
||||
int key = -1;
|
||||
void *ret = tfind((void*) &key, &root, compare);
|
||||
assert(ret == NULL);
|
||||
|
||||
tdestroy(root, free_key);
|
||||
assert(freed == 12);
|
||||
|
||||
assert(hcreate(3));
|
||||
|
||||
// Search for a non-existent entry
|
||||
ENTRY entry = {
|
||||
.key = (char *)"foo",
|
||||
.data = (void *)0x12345678
|
||||
};
|
||||
ENTRY *result = hsearch(entry, FIND);
|
||||
assert(!result);
|
||||
assert(errno == ESRCH);
|
||||
|
||||
// Add a couple keys
|
||||
assert(hsearch(entry, ENTER));
|
||||
entry.key = (char *)"bar";
|
||||
entry.data = (void *)0x87654321;
|
||||
assert(hsearch(entry, ENTER));
|
||||
entry.key = (char *)"baz";
|
||||
entry.data = (void *)0x12344321;
|
||||
assert(hsearch(entry, ENTER));
|
||||
|
||||
// Make sure that we can't add more keys
|
||||
entry.key = (char *)"not existing";
|
||||
assert(!hsearch(entry, ENTER));
|
||||
assert(errno == ENOMEM);
|
||||
|
||||
// Check that the entries are in the hash table
|
||||
entry.key = (char *)"baz";
|
||||
result = hsearch(entry, FIND);
|
||||
assert(result);
|
||||
assert(result->data == (void *)0x12344321);
|
||||
entry.key = (char *)"foo";
|
||||
result = hsearch(entry, FIND);
|
||||
assert(result);
|
||||
assert(result->data == (void *)0x12345678);
|
||||
entry.key = (char *)"bar";
|
||||
result = hsearch(entry, FIND);
|
||||
assert(result);
|
||||
assert(result->data == (void *)0x87654321);
|
||||
|
||||
hdestroy();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <semaphore.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
int main() {
|
||||
sem_t sem;
|
||||
assert(sem_init(&sem, 0, 1) == 0);
|
||||
assert(sem_destroy(&sem) == 0);
|
||||
assert(sem_init(&sem, 0, -1) == -1);
|
||||
assert(errno == EINVAL);
|
||||
|
||||
assert(sem_init(&sem, 0, 3) == 0);
|
||||
assert(sem_trywait(&sem) == 0);
|
||||
assert(sem_trywait(&sem) == 0);
|
||||
assert(sem_trywait(&sem) == 0);
|
||||
assert(sem_trywait(&sem) == -1);
|
||||
assert(errno == EAGAIN);
|
||||
assert(sem_destroy(&sem) == 0);
|
||||
|
||||
sem_init(&sem, 0, 1);
|
||||
assert(sem_wait(&sem) == 0);
|
||||
assert(sem_trywait(&sem) == -1);
|
||||
assert(sem_post(&sem) == 0);
|
||||
assert(sem_wait(&sem) == 0);
|
||||
assert(sem_destroy(&sem) == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
errno = 0;
|
||||
int original_priority = getpriority(PRIO_PROCESS, getpid());
|
||||
|
||||
assert(original_priority != -1 || !errno);
|
||||
|
||||
int ret = setpriority(PRIO_PROCESS, getpid(), original_priority + 1);
|
||||
|
||||
if(ret) {
|
||||
fprintf(stderr, "%s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
int new_priority = getpriority(PRIO_PROCESS, getpid());
|
||||
|
||||
assert(new_priority != -1 || !errno);
|
||||
assert(new_priority == original_priority + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <sys/shm.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
int main(void) {
|
||||
size_t page_size = getpagesize();
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
int shmid = -1;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
shmid = shmget((key_t)rand(), page_size, 0644 | IPC_CREAT | IPC_EXCL);
|
||||
if (shmid != -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(shmid != -1);
|
||||
|
||||
struct shmid_ds buf;
|
||||
assert(shmctl(shmid, IPC_STAT, &buf) >= 0);
|
||||
assert((size_t)buf.shm_segsz == page_size);
|
||||
assert(buf.shm_cpid == getpid());
|
||||
|
||||
void *attach_addr = shmat(shmid, NULL, SHM_RDONLY);
|
||||
assert(attach_addr != NULL);
|
||||
|
||||
assert(shmctl(shmid, IPC_RMID, &buf) >= 0);
|
||||
assert(shmdt(attach_addr) == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static jmp_buf env;
|
||||
static char *sigStack;
|
||||
|
||||
static void sig_handler(int sig, siginfo_t *info, void *ctx) {
|
||||
(void)sig;
|
||||
(void)info;
|
||||
(void)ctx;
|
||||
|
||||
longjmp(env, 1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
if (setjmp(env)) {
|
||||
free(sigStack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sigStack = malloc(SIGSTKSZ);
|
||||
|
||||
stack_t ss;
|
||||
ss.ss_sp = sigStack;
|
||||
ss.ss_size = SIGSTKSZ;
|
||||
ss.ss_flags = 0;
|
||||
|
||||
assert(!sigaltstack(&ss, NULL));
|
||||
|
||||
struct sigaction sa;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
sa.sa_sigaction = sig_handler;
|
||||
|
||||
assert(!sigaction(SIGSEGV, &sa, NULL));
|
||||
|
||||
// This is used to trash the stack to ensure sigaltstack actually switched stacks.
|
||||
#if defined(__x86_64__)
|
||||
asm volatile ("mov $0, %rsp\n"
|
||||
"\t" "push $0");
|
||||
#elif defined(__i386__)
|
||||
asm volatile ("mov $0, %esp\n"
|
||||
"\t" "push $0");
|
||||
#elif defined(__aarch64__)
|
||||
asm volatile ("mov sp, %0\n"
|
||||
"\t" "stp x0, x1, [sp, #-16]!" :: "r"((uint64_t)0));
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
asm volatile ("li sp, 0\n"
|
||||
"\t" "sd zero, 0(sp)");
|
||||
#elif defined (__m68k__)
|
||||
asm volatile ("move.l #0, %sp\n"
|
||||
"\t" "move.l #0, -(%sp)");
|
||||
#elif defined(__loongarch64)
|
||||
asm volatile ("addi.d $sp, $r0, 0\n"
|
||||
"\t" "st.d $r0, $sp, 0");
|
||||
#else
|
||||
# error Unknown architecture
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
_Atomic int handler_ready = 0;
|
||||
_Atomic int thread_signal_ran = 0;
|
||||
|
||||
static void sig_handler(int sig, siginfo_t *info, void *ctx) {
|
||||
(void)sig;
|
||||
(void)info;
|
||||
(void)ctx;
|
||||
|
||||
thread_signal_ran = 1;
|
||||
}
|
||||
|
||||
static void *worker(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
struct sigaction sa;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_sigaction = sig_handler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
assert(!sigaction(SIGUSR1, &sa, NULL));
|
||||
|
||||
handler_ready = 1;
|
||||
|
||||
sigset_t set;
|
||||
sigfillset(&set);
|
||||
sigdelset(&set, SIGUSR1);
|
||||
|
||||
assert(sigsuspend(&set));
|
||||
assert(thread_signal_ran);
|
||||
assert(errno == EINTR);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_t thread;
|
||||
assert(!pthread_create(&thread, NULL, &worker, NULL));
|
||||
|
||||
while (!handler_ready)
|
||||
;
|
||||
|
||||
sleep(1);
|
||||
|
||||
assert(!pthread_kill(thread, SIGUSR1));
|
||||
assert(!pthread_join(thread, NULL));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <errno.h>
|
||||
|
||||
pid_t parent;
|
||||
pid_t child;
|
||||
|
||||
int fds[2];
|
||||
|
||||
void parent_fn() {
|
||||
int res;
|
||||
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGUSR2);
|
||||
sigprocmask(SIG_BLOCK, &set, NULL);
|
||||
|
||||
res = write(fds[1], "!", 1);
|
||||
assert(res == 1);
|
||||
|
||||
siginfo_t info;
|
||||
res = sigwaitinfo(&set, &info);
|
||||
assert(res == SIGUSR2);
|
||||
assert(info.si_signo == SIGUSR2);
|
||||
|
||||
res = write(fds[1], "!", 1);
|
||||
assert(res == 1);
|
||||
|
||||
// XXX: This may not be long enough to get scheduled
|
||||
struct timespec tout = {1, 0};
|
||||
res = sigtimedwait(&set, &info, &tout);
|
||||
assert(res == SIGUSR2);
|
||||
assert(info.si_signo == SIGUSR2);
|
||||
|
||||
res = write(fds[1], "!", 1);
|
||||
assert(res == 1);
|
||||
|
||||
int sig;
|
||||
res = sigwait(&set, &sig);
|
||||
assert(res == 0);
|
||||
assert(sig == SIGUSR2);
|
||||
|
||||
res = write(fds[1], "!", 1);
|
||||
assert(res == 1);
|
||||
|
||||
res = sigtimedwait(&set, &info, &tout);
|
||||
assert(res < 0);
|
||||
assert(errno == EAGAIN);
|
||||
|
||||
int wsts;
|
||||
res = waitpid(child, &wsts, 0);
|
||||
assert(res >= 0);
|
||||
}
|
||||
|
||||
void child_fn() {
|
||||
int res;
|
||||
char c;
|
||||
|
||||
res = read(fds[0], &c, 1);
|
||||
assert(res == 1);
|
||||
kill(parent, SIGUSR2);
|
||||
res = read(fds[0], &c, 1);
|
||||
assert(res == 1);
|
||||
kill(parent, SIGUSR2);
|
||||
res = read(fds[0], &c, 1);
|
||||
assert(res == 1);
|
||||
kill(parent, SIGUSR2);
|
||||
res = read(fds[0], &c, 1);
|
||||
assert(res == 1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int res;
|
||||
|
||||
parent = getpid();
|
||||
assert(parent > 0);
|
||||
|
||||
res = pipe(fds);
|
||||
assert(res == 0);
|
||||
|
||||
child = fork();
|
||||
assert(child >= 0);
|
||||
if (child)
|
||||
parent_fn();
|
||||
else
|
||||
child_fn();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
char test[19] = "Hello mlibc World!";
|
||||
char *alloca_ed = strdupa(test);
|
||||
assert(!strcmp(test, alloca_ed));
|
||||
|
||||
char *trimmed = strndupa(test, 5);
|
||||
assert(!strcmp("Hello", trimmed));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main() {
|
||||
char buf[4];
|
||||
|
||||
// stpncpy
|
||||
assert(stpncpy(buf, "", 4) == buf);
|
||||
assert(!strcmp(buf, ""));
|
||||
|
||||
assert(stpncpy(buf, "123", 4) == buf + 3);
|
||||
assert(!strcmp(buf, "123"));
|
||||
|
||||
assert(stpncpy(buf, "12", 4) == buf + 2);
|
||||
assert(buf[0] == '1' && buf[1] == '2' && buf[2] == '\0' && buf[3] == '\0');
|
||||
|
||||
assert(stpncpy(buf, "123456", 4) == buf + 4);
|
||||
assert(buf[0] == '1' && buf[1] == '2' && buf[2] == '3' && buf[3] == '4');
|
||||
|
||||
// stpcpy
|
||||
assert(stpcpy(buf, "") == buf);
|
||||
assert(!strcmp(buf, ""));
|
||||
|
||||
assert(stpcpy(buf, "12") == buf + 2);
|
||||
assert(!strcmp(buf, "12"));
|
||||
|
||||
assert(stpcpy(buf, "123") == buf + 3);
|
||||
assert(!strcmp(buf, "123"));
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void test_swab_even_bytes() {
|
||||
uint8_t input[] = {0x01, 0x02, 0x03, 0x04};
|
||||
uint8_t output[4];
|
||||
|
||||
swab(input, output, sizeof(input));
|
||||
|
||||
assert(output[0] == 0x02);
|
||||
assert(output[1] == 0x01);
|
||||
assert(output[2] == 0x04);
|
||||
assert(output[3] == 0x03);
|
||||
}
|
||||
|
||||
void test_swab_odd_bytes() {
|
||||
uint8_t input[] = {0x01, 0x02, 0x03, 0x04, 0x05};
|
||||
uint8_t output[5] = {0, 0, 0, 0, 0};
|
||||
|
||||
swab(input, output, sizeof(input));
|
||||
|
||||
assert(output[0] == 0x02);
|
||||
assert(output[1] == 0x01);
|
||||
assert(output[2] == 0x04);
|
||||
assert(output[3] == 0x03);
|
||||
|
||||
// Last byte is UB, assume unchanged?
|
||||
assert(output[4] == 0);
|
||||
}
|
||||
|
||||
void test_swab_negative_bytes() {
|
||||
uint8_t input[] = {0x01, 0x02, 0x03, 0x04};
|
||||
uint8_t output[4] = {0, 0, 0, 0};
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstringop-overflow"
|
||||
swab(input, output, -1); // Should change nothing
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
assert(output[0] == 0);
|
||||
assert(output[1] == 0);
|
||||
assert(output[2] == 0);
|
||||
assert(output[3] == 0);
|
||||
}
|
||||
|
||||
void test_swab_zero_bytes() {
|
||||
uint8_t input[] = {0x01, 0x02, 0x03, 0x04};
|
||||
uint8_t output[4] = {0, 0, 0, 0};
|
||||
|
||||
swab(input, output, 0); // Should change nothing
|
||||
|
||||
assert(output[0] == 0);
|
||||
assert(output[1] == 0);
|
||||
assert(output[2] == 0);
|
||||
assert(output[3] == 0);
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_swab_even_bytes();
|
||||
test_swab_odd_bytes();
|
||||
test_swab_negative_bytes();
|
||||
test_swab_zero_bytes();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
int main() {
|
||||
int res;
|
||||
|
||||
res = system("true");
|
||||
assert(WIFEXITED(res));
|
||||
assert(WEXITSTATUS(res) == 0);
|
||||
|
||||
res = system("false");
|
||||
assert(WIFEXITED(res));
|
||||
assert(WEXITSTATUS(res) == 1);
|
||||
|
||||
res = system(NULL);
|
||||
assert(res == 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
#include <assert.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#define BUF_SIZE 1024
|
||||
|
||||
int main() {
|
||||
struct tm tm = {0};
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
char *a = strptime("%", "%%", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(strftime(buf, BUF_SIZE, "%%", &tm) == 1);
|
||||
assert(!strcmp(buf, "%"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("1991-11-21", "%F", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_mday == 21);
|
||||
assert(tm.tm_mon == 10);
|
||||
assert(tm.tm_wday == 4);
|
||||
assert(tm.tm_yday == 324);
|
||||
assert(strftime(buf, BUF_SIZE, "%F", &tm) == 10);
|
||||
assert(!strcmp(buf, "1991-11-21"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("10/19/91", "%D", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_mday == 19);
|
||||
assert(tm.tm_mon == 9);
|
||||
assert(tm.tm_year == 91);
|
||||
assert(tm.tm_wday == 6);
|
||||
assert(tm.tm_yday == 291);
|
||||
assert(strftime(buf, BUF_SIZE, "%D", &tm) == 8);
|
||||
assert(!strcmp(buf, "10/19/91"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("15:23", "%R", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_min == 23);
|
||||
assert(tm.tm_hour == 15);
|
||||
assert(strftime(buf, BUF_SIZE, "%R", &tm) == 5);
|
||||
assert(!strcmp(buf, "15:23"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("17:12:56", "%T", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_sec == 56);
|
||||
assert(tm.tm_min == 12);
|
||||
assert(tm.tm_hour == 17);
|
||||
assert(strftime(buf, BUF_SIZE, "%T", &tm) == 8);
|
||||
assert(!strcmp(buf, "17:12:56"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("10", "%m", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_yday == 272);
|
||||
assert(strftime(buf, BUF_SIZE, "%m", &tm) == 2);
|
||||
assert(!strcmp(buf, "10"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("14 83", "%C %y", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_year == -417);
|
||||
assert(strftime(buf, BUF_SIZE, "%C %y", &tm) == 5);
|
||||
assert(!strcmp(buf, "14 83"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("32 16", "%y %C", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_year == -268);
|
||||
assert(tm.tm_wday == 3);
|
||||
assert(strftime(buf, BUF_SIZE, "%y %C", &tm) == 5);
|
||||
assert(!strcmp(buf, "32 16"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("12", "%C", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_year == -700);
|
||||
assert(tm.tm_wday == 5);
|
||||
assert(strftime(buf, BUF_SIZE, "%C", &tm) == 2);
|
||||
assert(!strcmp(buf, "12"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("1683-9-23", "%F", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_mday == 23);
|
||||
assert(tm.tm_mon == 8);
|
||||
assert(tm.tm_year == -217);
|
||||
assert(tm.tm_wday == 4);
|
||||
assert(tm.tm_yday == 265);
|
||||
assert(strftime(buf, BUF_SIZE, "%F", &tm) == 10);
|
||||
assert(!strcmp(buf, "1683-09-23"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("14 53", "%H%t%S", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_sec == 53);
|
||||
assert(tm.tm_hour == 14);
|
||||
assert(strftime(buf, BUF_SIZE, "%H%t%S", &tm) == 5);
|
||||
assert(!strcmp(buf, "14 53"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("24", "%H", &tm);
|
||||
assert(a == NULL);
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("0", "%I", &tm);
|
||||
assert(a == NULL);
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
setlocale(LC_TIME, "en_US.UTF-8");
|
||||
a = strptime("10 21 PM", "%I %M %p", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_hour == 22);
|
||||
assert(tm.tm_min == 21);
|
||||
assert(strftime(buf, BUF_SIZE, "%I %M %p", &tm) == 8);
|
||||
assert(!strcmp(buf, "10 21 PM"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
tm.tm_min = 23;
|
||||
assert(strftime(buf, BUF_SIZE, "%I %M", &tm) == 5);
|
||||
assert(!strcmp(buf, "12 23"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("January", "%h", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_mon == 0);
|
||||
assert(strftime(buf, BUF_SIZE, "%h %b", &tm) == 7);
|
||||
assert(!strcmp(buf, "Jan Jan"));
|
||||
assert(strftime(buf, BUF_SIZE, "%B", &tm) == 7);
|
||||
assert(!strcmp(buf, "January"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("2", "%j", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_yday == 1);
|
||||
assert(strftime(buf, BUF_SIZE, "%j", &tm) == 3);
|
||||
assert(!strcmp(buf, "002"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("Wednesday", "%A", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_wday == 3);
|
||||
assert(strftime(buf, BUF_SIZE, "%A", &tm) == 9);
|
||||
assert(!strcmp(buf, "Wednesday"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("11:51:13 PM", "%r", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_hour == 23);
|
||||
assert(tm.tm_min == 51);
|
||||
assert(tm.tm_sec == 13);
|
||||
assert(strftime(buf, BUF_SIZE, "%r", &tm) == 11);
|
||||
assert(!strcmp(buf, "11:51:13 PM"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
tm.tm_hour = 0;
|
||||
tm.tm_min = 51;
|
||||
tm.tm_sec = 13;
|
||||
assert(strftime(buf, BUF_SIZE, "%r", &tm) == 11);
|
||||
assert(!strcmp(buf, "12:51:13 AM"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat"
|
||||
assert(strftime(buf, BUF_SIZE, "%", &tm) == 1);
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
assert(!strcmp(buf, "%"));
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main() {
|
||||
struct timeval a = {0, 0};
|
||||
struct timeval b = {0, 0};
|
||||
struct timeval res = {0, 0};
|
||||
a.tv_sec = 10;
|
||||
assert(timerisset(&a) == 1);
|
||||
timerclear(&a);
|
||||
assert(timerisset(&a) == 0);
|
||||
a.tv_sec = 40;
|
||||
a.tv_usec = 500;
|
||||
b.tv_sec = 10;
|
||||
b.tv_usec = 20;
|
||||
timeradd(&a, &b, &res);
|
||||
assert(res.tv_sec == 50);
|
||||
assert(res.tv_usec == 520);
|
||||
timerclear(&res);
|
||||
timersub(&a, &b, &res);
|
||||
assert(res.tv_sec == 30);
|
||||
assert(res.tv_usec == 480);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void testfor(const char *expected, char *shell, int *count) {
|
||||
if (strcmp(shell, expected) == 0)
|
||||
*count += 1;
|
||||
}
|
||||
|
||||
#define BUFFER_SIZE (1024 * 512)
|
||||
|
||||
int main(void) {
|
||||
setusershell();
|
||||
|
||||
FILE *f = fopen("/etc/shells", "r");
|
||||
if (f == NULL) {
|
||||
/* /etc/shells file is unreadable, test for the existance of /bin/sh and /bin/csh */
|
||||
char *shell = getusershell();
|
||||
int csh = 0;
|
||||
int sh = 0;
|
||||
|
||||
testfor("/bin/sh", shell, &sh);
|
||||
testfor("/bin/csh", shell, &csh);
|
||||
|
||||
shell = getusershell();
|
||||
assert(shell);
|
||||
|
||||
testfor("/bin/sh", shell, &sh);
|
||||
testfor("/bin/csh", shell, &csh);
|
||||
|
||||
assert(sh == 1 && csh == 1);
|
||||
assert(getusershell() == NULL);
|
||||
} else {
|
||||
/* /etc/shells is there, read the whole file into a buffer and change all '\n's to '\0'. */
|
||||
char buffer[BUFFER_SIZE + 1];
|
||||
int nbytes = fread(buffer, 1, BUFFER_SIZE, f);
|
||||
buffer[nbytes] = '\0';
|
||||
|
||||
for (int i = 0; i < nbytes; ++i) {
|
||||
if (buffer[i] == '\n')
|
||||
buffer[i] = '\0';
|
||||
}
|
||||
|
||||
/* loop through each shell and make sure the result is right */
|
||||
char *shell = buffer;
|
||||
while (shell < &buffer[nbytes]) {
|
||||
char *usershell = getusershell();
|
||||
while (usershell && *usershell == '#')
|
||||
usershell = getusershell();
|
||||
|
||||
while (*shell == '#')
|
||||
shell += strlen(shell) + 1;
|
||||
|
||||
assert(strcmp(shell, usershell) == 0);
|
||||
shell += strlen(shell) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
endusershell();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void prevent_atforks(void){
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
pid_t pid;
|
||||
int status;
|
||||
assert(!pthread_atfork(prevent_atforks, NULL, NULL));
|
||||
|
||||
switch (pid = vfork()) {
|
||||
case -1:
|
||||
perror("vfork");
|
||||
abort();
|
||||
case 0:
|
||||
_exit(12);
|
||||
default: break;
|
||||
}
|
||||
|
||||
assert(wait(&status) == pid);
|
||||
assert(WIFEXITED(status) && WEXITSTATUS(status) == 12);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
siginfo_t si;
|
||||
|
||||
int main() {
|
||||
int pid = fork();
|
||||
|
||||
if(!pid)
|
||||
exit(69);
|
||||
|
||||
int ret = waitid(P_ALL, 0, &si, WEXITED);
|
||||
assert(ret == 0);
|
||||
assert(si.si_pid == pid);
|
||||
assert(si.si_signo == SIGCHLD);
|
||||
assert(si.si_code == CLD_EXITED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#include <assert.h>
|
||||
#include <locale.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#if UINTPTR_MAX == UINT64_MAX
|
||||
#define WCHAR_SPEC ""
|
||||
#else
|
||||
#define WCHAR_SPEC "l"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The code in this test is taken from https://github.com/termux/wcwidth/,
|
||||
* under the following license:
|
||||
*
|
||||
* Copyright (C) Fredrik Fornwall 2016.
|
||||
* Distributed under the MIT License.
|
||||
*
|
||||
* Implementation of wcwidth(3) as a C port of:
|
||||
* https://github.com/jquast/wcwidth
|
||||
*
|
||||
* Report issues at:
|
||||
* https://github.com/termux/wcwidth
|
||||
*/
|
||||
|
||||
static int tests_run;
|
||||
static int test_failures;
|
||||
|
||||
void assertWidthIs(int expected_width, wchar_t c) {
|
||||
tests_run++;
|
||||
int actual_width = wcwidth(c);
|
||||
if (actual_width != expected_width) {
|
||||
fprintf(stderr, "ERROR: wcwidth(U+%04" WCHAR_SPEC "x, '%lc') returned %d, expected %d\n", c, (wint_t) c, actual_width, expected_width);
|
||||
test_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
setlocale(LC_CTYPE, "C.UTF-8");
|
||||
assertWidthIs(1, 'a');
|
||||
assertWidthIs(1, L'ö');
|
||||
|
||||
// Some wide:
|
||||
assertWidthIs(2, L'A');
|
||||
assertWidthIs(2, L'B');
|
||||
assertWidthIs(2, L'C');
|
||||
assertWidthIs(2, L'中');
|
||||
assertWidthIs(2, L'文');
|
||||
assertWidthIs(2, 0x679C);
|
||||
assertWidthIs(2, 0x679D);
|
||||
assertWidthIs(2, 0x2070E);
|
||||
assertWidthIs(2, 0x20731);
|
||||
|
||||
#ifndef USE_HOST_LIBC
|
||||
assertWidthIs(1, 0x11A3);
|
||||
#endif
|
||||
|
||||
assertWidthIs(2, 0x1F428); // Koala emoji.
|
||||
assertWidthIs(2, 0x231a); // Watch emoji.
|
||||
|
||||
if (test_failures > 0) printf("%d tests FAILED, ", test_failures);
|
||||
printf("%d tests OK\n", tests_run - test_failures);
|
||||
return (test_failures == 0) ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <wordexp.h>
|
||||
|
||||
static const char *wordexp_return_stringify(int val, int *should_free);
|
||||
|
||||
struct we_test {
|
||||
int expected_return;
|
||||
const char *words;
|
||||
int flags;
|
||||
const char *wordv[16];
|
||||
size_t wordc;
|
||||
} test_cases[] = {
|
||||
/* (unbalanced) pairs */
|
||||
{WRDE_SYNTAX, "\\", 0, {NULL}, 0},
|
||||
{WRDE_SYNTAX, "\'", 0, {NULL}, 0},
|
||||
{0, "\'\'", 0, {""}, 1},
|
||||
{0, "\'\"\'", 0, {"\""}, 1},
|
||||
{WRDE_SYNTAX, "\"\'", 0, {NULL}, 0},
|
||||
|
||||
/* numbers & calculations */
|
||||
{0, "$((5+5))", 0, {"10"}, 1},
|
||||
{0, "$((-5+5))", 0, {"0"}, 1},
|
||||
{0, "$((010))", 0, {"8"}, 1},
|
||||
{0, "$((0x10))", 0, {"16"}, 1},
|
||||
|
||||
/* errant symbols */
|
||||
{WRDE_BADCHAR, "(string", WRDE_NOCMD, {NULL}, 0},
|
||||
{WRDE_BADCHAR, "string)", WRDE_NOCMD, {NULL}, 0},
|
||||
{WRDE_BADCHAR, "{string", WRDE_NOCMD, {NULL}, 0},
|
||||
{WRDE_BADCHAR, "string}", WRDE_NOCMD, {NULL}, 0},
|
||||
{WRDE_BADCHAR, "<string", WRDE_NOCMD, {NULL}, 0},
|
||||
{WRDE_BADCHAR, "string>", WRDE_NOCMD, {NULL}, 0},
|
||||
{WRDE_BADCHAR, "string|", WRDE_NOCMD, {NULL}, 0},
|
||||
{WRDE_BADCHAR, "string;", WRDE_NOCMD, {NULL}, 0},
|
||||
{WRDE_BADCHAR, "string&", WRDE_NOCMD, {NULL}, 0},
|
||||
|
||||
/* command substitution with WRDE_NOCMD */
|
||||
{WRDE_CMDSUB, "$(pwd)", WRDE_NOCMD, {NULL}, 0},
|
||||
|
||||
/* env vars */
|
||||
{WRDE_BADVAL, "$DOES_NOT_EXIST", WRDE_UNDEF, {NULL}, 0},
|
||||
|
||||
/* normal arguments */
|
||||
{0, "arg1 arg2", 0, {"arg1", "arg2"}, 2},
|
||||
{-1, NULL, 0, {NULL}, 0},
|
||||
};
|
||||
|
||||
int main() {
|
||||
wordexp_t we;
|
||||
|
||||
wordexp("$(pwd)", &we, 0);
|
||||
char *cwd = getcwd(NULL, 0);
|
||||
assert(!strcmp(cwd, we.we_wordv[0]));
|
||||
wordfree(&we);
|
||||
free(cwd);
|
||||
|
||||
char *home;
|
||||
assert(asprintf(&home, "%s/.config", getenv("HOME")) != -1);
|
||||
wordexp("$HOME ~ ~/.config", &we, WRDE_REUSE);
|
||||
assert(!strcmp(getenv("HOME"), we.we_wordv[0]));
|
||||
assert(!strcmp(getenv("HOME"), we.we_wordv[1]));
|
||||
assert(!strcmp(home, we.we_wordv[2]));
|
||||
free(home);
|
||||
wordfree(&we);
|
||||
|
||||
struct passwd *pw = getpwnam("root");
|
||||
assert(asprintf(&home, "%s/.config", pw->pw_dir) != -1);
|
||||
wordexp("~root ~root/.config", &we, WRDE_REUSE);
|
||||
assert(!strcmp(pw->pw_dir, we.we_wordv[0]));
|
||||
assert(!strcmp(home, we.we_wordv[1]));
|
||||
free(home);
|
||||
wordfree(&we);
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
for(struct we_test *test = &test_cases[i]; test->expected_return != -1; test = &test_cases[++i]) {
|
||||
wordexp_t test_we;
|
||||
|
||||
int should_free;
|
||||
const char *expected = wordexp_return_stringify(test->expected_return, &should_free);
|
||||
fprintf(stderr, "TESTCASE %zu: '%s' with flags %d expected to return %s\n", i, test->words, test->flags, expected);
|
||||
int test_ret = wordexp(test->words, &test_we, test->flags);
|
||||
|
||||
if(test_ret != test->expected_return) {
|
||||
fprintf(stderr, "\twordexp() returned %s, but we expect %s\n", wordexp_return_stringify(test_ret, &should_free), wordexp_return_stringify(test->expected_return, &should_free));
|
||||
}
|
||||
|
||||
assert(test_ret == test->expected_return);
|
||||
|
||||
if(test_ret != 0)
|
||||
continue;
|
||||
|
||||
assert(test_we.we_wordc == test->wordc);
|
||||
|
||||
for(size_t j = 0; j < 16 && j < test->wordc; j++) {
|
||||
assert(test->wordv[j] && test_we.we_wordv[j]);
|
||||
assert(!strcmp(test->wordv[j], test_we.we_wordv[j]));
|
||||
}
|
||||
|
||||
wordfree(&test_we);
|
||||
if (should_free)
|
||||
free((void *)expected);
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
struct wordexp_return_val {
|
||||
int val;
|
||||
const char *string;
|
||||
} wordexp_return_vals[] = {
|
||||
{0, "WRDE_SUCCESS"},
|
||||
{WRDE_BADCHAR, "WRDE_BADCHAR"},
|
||||
{WRDE_BADVAL, "WRDE_BADVAL"},
|
||||
{WRDE_CMDSUB, "WRDE_CMDSUB"},
|
||||
{WRDE_SYNTAX, "WRDE_SYNTAX"},
|
||||
{-1, NULL},
|
||||
};
|
||||
|
||||
static const char *wordexp_return_stringify(int val, int *should_free) {
|
||||
for(size_t i = 0; wordexp_return_vals[i].val != -1 || wordexp_return_vals[i].string; i++) {
|
||||
if(wordexp_return_vals[i].val == val) {
|
||||
*should_free = 0;
|
||||
return wordexp_return_vals[i].string;
|
||||
}
|
||||
}
|
||||
|
||||
char *unknown;
|
||||
assert(asprintf(&unknown, "unknown return value %d", val) != -1);
|
||||
*should_free = 1;
|
||||
|
||||
return unknown;
|
||||
}
|
||||
Reference in New Issue
Block a user