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,27 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <sched.h>
|
||||
|
||||
#define SET_SIZE 15
|
||||
|
||||
int main() {
|
||||
cpu_set_t *set = CPU_ALLOC(SET_SIZE);
|
||||
size_t setsize = CPU_ALLOC_SIZE(SET_SIZE);
|
||||
|
||||
CPU_ZERO_S(setsize, set);
|
||||
|
||||
assert(!CPU_ISSET_S(11, setsize, set));
|
||||
|
||||
CPU_SET_S(11, setsize, set);
|
||||
assert(CPU_ISSET_S(11, setsize, set));
|
||||
assert(CPU_COUNT_S(setsize, set) == 1);
|
||||
|
||||
assert(!CPU_ISSET_S(CPU_SETSIZE - 1, setsize, set));
|
||||
|
||||
CPU_CLR_S(11, setsize, set);
|
||||
assert(!CPU_ISSET_S(11, setsize, set));
|
||||
|
||||
CPU_FREE(set);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <linux/if_link.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
struct ifaddrs *ifaddr = 0;
|
||||
char host[NI_MAXHOST];
|
||||
bool loopback_found = false;
|
||||
|
||||
if(getifaddrs(&ifaddr)) {
|
||||
perror("getifaddrs");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for(struct ifaddrs *ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
|
||||
if(ifa->ifa_addr == NULL)
|
||||
continue;
|
||||
|
||||
int family = ifa->ifa_addr->sa_family;
|
||||
|
||||
if(family == AF_INET && !strcmp("lo", ifa->ifa_name)) {
|
||||
loopback_found = true;
|
||||
|
||||
printf("%-10s %s (%d)\n", ifa->ifa_name,
|
||||
(family == AF_INET) ? "AF_INET" : (family == AF_INET6) ? "AF_INET6" : "???",
|
||||
family
|
||||
);
|
||||
|
||||
int s = getnameinfo(ifa->ifa_addr,
|
||||
(family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
|
||||
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
|
||||
assert(!s);
|
||||
|
||||
printf("\t\taddress: <%s>\n", host);
|
||||
if(ifa->ifa_netmask)
|
||||
printf("\t\tnetmask: <%s>\n", inet_ntoa(((struct sockaddr_in *) ifa->ifa_netmask)->sin_addr));
|
||||
|
||||
if(ifa->ifa_flags & IFF_BROADCAST) {
|
||||
assert(ifa->ifa_broadaddr);
|
||||
printf("\t\tbroadcast: <%s>\n", inet_ntoa(((struct sockaddr_in *) ifa->ifa_broadaddr)->sin_addr));
|
||||
}
|
||||
|
||||
if(ifa->ifa_flags & IFF_POINTOPOINT) {
|
||||
assert(ifa->ifa_dstaddr);
|
||||
printf("\t\tdest addr: <%s>\n", inet_ntoa(((struct sockaddr_in *) ifa->ifa_dstaddr)->sin_addr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddr);
|
||||
|
||||
exit(loopback_found ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <assert.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
void *p1 = malloc(1023);
|
||||
fprintf(stderr, "size: %zu\n", malloc_usable_size(p1));
|
||||
assert(malloc_usable_size(p1) >= 1023);
|
||||
free(p1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/pidfd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
int child = fork();
|
||||
|
||||
if(!child) {
|
||||
sleep(1);
|
||||
exit(42);
|
||||
}
|
||||
|
||||
int pidfd = pidfd_open(child, 0);
|
||||
assert(pidfd > 0);
|
||||
|
||||
int ret = lseek(pidfd, 0, SEEK_SET);
|
||||
assert(ret == -1);
|
||||
assert(errno == ESPIPE);
|
||||
|
||||
pid_t outpid = pidfd_getpid(pidfd);
|
||||
assert(outpid == child);
|
||||
|
||||
struct pollfd pollfd;
|
||||
pollfd.fd = pidfd;
|
||||
pollfd.events = POLLIN;
|
||||
|
||||
int ready = poll(&pollfd, 1, 0);
|
||||
assert(ready == 0);
|
||||
|
||||
ready = poll(&pollfd, 1, 2000);
|
||||
if(ready == -1) {
|
||||
perror("poll");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
assert(ready == 1);
|
||||
assert(pollfd.revents == POLLIN);
|
||||
|
||||
siginfo_t info = {};
|
||||
ret = waitid(P_PIDFD, pidfd, &info, WEXITED | WNOHANG);
|
||||
assert(ret == 0);
|
||||
assert(info.si_code == CLD_EXITED);
|
||||
assert(info.si_pid == child);
|
||||
assert(info.si_status == 42);
|
||||
|
||||
close(pidfd);
|
||||
|
||||
child = fork();
|
||||
|
||||
if(!child) {
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
pidfd = pidfd_open(child, 0);
|
||||
assert(pidfd > 0);
|
||||
|
||||
ret = pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
|
||||
assert(ret == 0);
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
ret = waitid(P_PIDFD, pidfd, &info, WEXITED);
|
||||
assert(ret == 0);
|
||||
assert(info.si_code == CLD_KILLED);
|
||||
assert(info.si_pid == child);
|
||||
assert(info.si_status == SIGKILL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void sig_handler(int sig) {
|
||||
(void)sig;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int fds[2];
|
||||
int ret = pipe(fds);
|
||||
assert(ret == 0);
|
||||
|
||||
struct pollfd poll_fds[1];
|
||||
poll_fds[0].fd = fds[0];
|
||||
poll_fds[0].events = POLLIN;
|
||||
|
||||
struct timespec timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_nsec = 1000000; // 1 ms
|
||||
|
||||
// Test that ppoll times out correctly.
|
||||
ret = ppoll(poll_fds, 1, &timeout, NULL);
|
||||
assert(ret == 0);
|
||||
|
||||
// Test that ppoll returns 1 if there is an event.
|
||||
char buf[1];
|
||||
ret = write(fds[1], "a", 1);
|
||||
assert(ret == 1);
|
||||
|
||||
ret = ppoll(poll_fds, 1, &timeout, NULL);
|
||||
assert(ret == 1);
|
||||
assert(poll_fds[0].revents & POLLIN);
|
||||
|
||||
// Test that ppoll returns 0 if there are no events and timeout is 0.
|
||||
ret = read(fds[0], buf, 1);
|
||||
assert(ret == 1);
|
||||
|
||||
timeout.tv_nsec = 0;
|
||||
ret = ppoll(poll_fds, 1, &timeout, NULL);
|
||||
assert(ret == 0);
|
||||
|
||||
// Test that ppoll returns immediately if there is an event and timeout is 0.
|
||||
ret = write(fds[1], "a", 1);
|
||||
assert(ret == 1);
|
||||
|
||||
ret = ppoll(poll_fds, 1, &timeout, NULL);
|
||||
assert(ret == 1);
|
||||
assert(poll_fds[0].revents & POLLIN);
|
||||
|
||||
char recvbuf[2];
|
||||
ret = read(fds[0], &recvbuf, 2);
|
||||
assert(ret == 1);
|
||||
|
||||
// Test that ppoll is interrupted by a signal.
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = sig_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
ret = sigaction(SIGUSR1, &sa, NULL);
|
||||
assert(ret == 0);
|
||||
|
||||
pid_t pid = fork();
|
||||
assert(pid >= 0);
|
||||
|
||||
if (pid == 0) {
|
||||
// Child process.
|
||||
usleep(100000); // 100 ms
|
||||
ret = kill(getppid(), SIGUSR1);
|
||||
assert(ret == 0);
|
||||
exit(0);
|
||||
} else {
|
||||
// Parent process.
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_nsec = 0;
|
||||
ret = ppoll(poll_fds, 1, &timeout, NULL);
|
||||
assert(ret == -1);
|
||||
assert(errno == EINTR);
|
||||
|
||||
int status;
|
||||
ret = waitpid(pid, &status, 0);
|
||||
assert(ret == pid);
|
||||
assert(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
}
|
||||
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Test variable to read and write
|
||||
static int test = 42;
|
||||
|
||||
int main() {
|
||||
// Read the variable using process_vm_readv
|
||||
int temp;
|
||||
struct iovec local_iov = {
|
||||
.iov_base = &temp,
|
||||
.iov_len = sizeof(temp),
|
||||
};
|
||||
struct iovec remote_iov = {
|
||||
.iov_base = &test,
|
||||
.iov_len = sizeof(test),
|
||||
};
|
||||
ssize_t bytes_read = process_vm_readv(getpid(), &local_iov, 1, &remote_iov, 1, 0);
|
||||
assert(bytes_read == sizeof(test));
|
||||
assert(temp == 42);
|
||||
|
||||
// Write a new value to the variable using process_vm_writev
|
||||
int new_value = 1337;
|
||||
struct iovec new_local_iov = {
|
||||
.iov_base = &new_value,
|
||||
.iov_len = sizeof(new_value),
|
||||
};
|
||||
ssize_t bytes_written = process_vm_writev(getpid(), &new_local_iov, 1, &remote_iov, 1, 0);
|
||||
assert(bytes_written == sizeof(new_value));
|
||||
assert(test == 1337);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
static void test_affinity() {
|
||||
pthread_attr_t attr;
|
||||
cpu_set_t set = {0};
|
||||
assert(!pthread_attr_init(&attr));
|
||||
assert(!pthread_attr_setaffinity_np(&attr, 1, &set));
|
||||
|
||||
cpu_set_t other_set = {0};
|
||||
assert(!pthread_attr_getaffinity_np(&attr, 1, &set));
|
||||
|
||||
assert(!memcmp(&set, &other_set, sizeof(cpu_set_t)));
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
||||
#if !defined(USE_HOST_LIBC) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)
|
||||
static void test_sigmask() {
|
||||
pthread_attr_t attr;
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGUSR1);
|
||||
#ifndef USE_HOST_LIBC
|
||||
sigaddset(&set, SIGCANCEL);
|
||||
#endif
|
||||
|
||||
assert(!pthread_attr_init(&attr));
|
||||
assert(!pthread_attr_setsigmask_np(&attr, &set));
|
||||
|
||||
sigset_t other_set;
|
||||
sigemptyset(&other_set);
|
||||
|
||||
assert(!pthread_attr_getsigmask_np(&attr, &other_set));
|
||||
|
||||
assert(sigismember(&other_set, SIGUSR1));
|
||||
#ifndef USE_HOST_LIBC
|
||||
// Test whether internal signals get filtered properly.
|
||||
assert(!sigismember(&other_set, SIGCANCEL));
|
||||
#endif
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
||||
static void *getattr_worker(void *arg) {
|
||||
(void)arg;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test_getattrnp() {
|
||||
pthread_attr_t attr;
|
||||
size_t stacksize = PTHREAD_STACK_MIN;
|
||||
assert(!pthread_attr_init(&attr));
|
||||
assert(!pthread_attr_setstacksize(&attr, stacksize));
|
||||
|
||||
pthread_t thread;
|
||||
assert(!pthread_create(&thread, &attr, getattr_worker, NULL));
|
||||
assert(!pthread_getattr_np(thread, &attr));
|
||||
size_t other_stacksize;
|
||||
assert(!pthread_attr_getstacksize(&attr, &other_stacksize));
|
||||
assert(other_stacksize == stacksize);
|
||||
assert(!pthread_join(thread, NULL));
|
||||
|
||||
pthread_t own_thread = pthread_self();
|
||||
void *stack;
|
||||
assert(!pthread_getattr_np(own_thread, &attr));
|
||||
assert(!pthread_attr_getstack(&attr, &stack, &other_stacksize));
|
||||
assert(stack);
|
||||
assert(other_stacksize);
|
||||
// Check that we can read from the highest byte returned.
|
||||
// pthread_getattr_np() should return the lowest byte
|
||||
// of the stack.
|
||||
printf("highest byte: %hhu\n", *(char *)(stack + other_stacksize - 1));
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
#endif // !defined(USE_HOST_LIBC) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)
|
||||
|
||||
int main() {
|
||||
test_affinity();
|
||||
#if !defined(USE_HOST_LIBC) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)
|
||||
test_sigmask();
|
||||
test_getattrnp();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
int main() {
|
||||
int ret = pthread_setname_np(pthread_self(), "mlibc-test-123");
|
||||
assert(!ret);
|
||||
|
||||
char buf[16];
|
||||
ret = pthread_getname_np(pthread_self(), buf, 16);
|
||||
assert(!ret);
|
||||
assert(!strcmp("mlibc-test-123", buf));
|
||||
|
||||
ret = pthread_setname_np(pthread_self(), "mlibc-test-123-too-long");
|
||||
assert(ret == ERANGE);
|
||||
|
||||
ret = pthread_getname_np(pthread_self(), buf, 1);
|
||||
assert(ret == ERANGE);
|
||||
|
||||
ret = pthread_getname_np(pthread_self(), buf, 15);
|
||||
assert(ret == ERANGE);
|
||||
|
||||
ret = pthread_getname_np(pthread_self(), buf, 16);
|
||||
assert(!ret);
|
||||
assert(!strcmp("mlibc-test-123", buf));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
int t = timerfd_create(CLOCK_MONOTONIC, 0);
|
||||
assert(t > 0);
|
||||
|
||||
struct itimerspec its;
|
||||
int ret = timerfd_gettime(t, &its);
|
||||
assert(ret == 0);
|
||||
assert(!its.it_value.tv_sec);
|
||||
assert(!its.it_value.tv_nsec);
|
||||
assert(!its.it_interval.tv_sec);
|
||||
assert(!its.it_interval.tv_nsec);
|
||||
|
||||
struct itimerspec new_its = {
|
||||
.it_interval = {0, 0},
|
||||
.it_value = {0, 100000000},
|
||||
};
|
||||
ret = timerfd_settime(t, 0, &new_its, &its);
|
||||
assert(ret == 0);
|
||||
assert(!its.it_value.tv_sec);
|
||||
assert(!its.it_value.tv_nsec);
|
||||
assert(!its.it_interval.tv_sec);
|
||||
assert(!its.it_interval.tv_nsec);
|
||||
|
||||
ret = timerfd_gettime(t, &its);
|
||||
assert(ret == 0);
|
||||
assert(!its.it_value.tv_sec);
|
||||
assert(its.it_value.tv_nsec);
|
||||
assert(!its.it_interval.tv_sec);
|
||||
assert(!its.it_interval.tv_nsec);
|
||||
|
||||
struct timeval before;
|
||||
gettimeofday(&before, NULL);
|
||||
assert(ret == 0);
|
||||
uint64_t ev = 0;
|
||||
ret = read(t, &ev, sizeof(ev));
|
||||
struct timeval after;
|
||||
gettimeofday(&after, NULL);
|
||||
|
||||
assert(ret == sizeof(ev));
|
||||
assert(ev == 1);
|
||||
|
||||
struct timeval diff = {};
|
||||
timersub(&after, &before, &diff);
|
||||
assert(diff.tv_sec || diff.tv_usec);
|
||||
|
||||
fcntl(t, F_SETFL, fcntl(t, F_GETFL, 0) | O_NONBLOCK);
|
||||
|
||||
ret = timerfd_settime(t, 0, &new_its, &its);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = read(t, &ev, sizeof(ev));
|
||||
assert(ret == -1);
|
||||
assert(errno == EAGAIN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
|
||||
int main() {
|
||||
char path[] = "/tmp/mlibc-utmp-test-XXXXXX";
|
||||
int fd = mkstemp(path);
|
||||
assert(fd != -1);
|
||||
close(fd);
|
||||
|
||||
int ret = utmpname(path);
|
||||
assert(!ret);
|
||||
|
||||
// Test writing to the utmp file.
|
||||
struct utmp entry1;
|
||||
memset(&entry1, 0, sizeof(struct utmp));
|
||||
entry1.ut_type = USER_PROCESS;
|
||||
entry1.ut_pid = 1234;
|
||||
strcpy(entry1.ut_line, "pts/1");
|
||||
strcpy(entry1.ut_id, "id1");
|
||||
strcpy(entry1.ut_user, "user1");
|
||||
strcpy(entry1.ut_host, "host1");
|
||||
entry1.ut_tv.tv_sec = 1;
|
||||
entry1.ut_tv.tv_usec = 1;
|
||||
|
||||
setutent();
|
||||
struct utmp *res = pututline(&entry1);
|
||||
assert(res);
|
||||
endutent();
|
||||
|
||||
// Test reading from the utmp file.
|
||||
setutent();
|
||||
struct utmp *read_entry = getutent();
|
||||
assert(read_entry);
|
||||
assert(read_entry->ut_type == USER_PROCESS);
|
||||
assert(read_entry->ut_pid == 1234);
|
||||
assert(strcmp(read_entry->ut_line, "pts/1") == 0);
|
||||
assert(strcmp(read_entry->ut_id, "id1") == 0);
|
||||
assert(strcmp(read_entry->ut_user, "user1") == 0);
|
||||
assert(strcmp(read_entry->ut_host, "host1") == 0);
|
||||
assert(read_entry->ut_tv.tv_sec == 1);
|
||||
assert(read_entry->ut_tv.tv_usec == 1);
|
||||
|
||||
read_entry = getutent();
|
||||
assert(!read_entry);
|
||||
assert(errno == ESRCH);
|
||||
endutent();
|
||||
|
||||
// Test getutid
|
||||
struct utmp id_entry;
|
||||
memset(&id_entry, 0, sizeof(struct utmp));
|
||||
id_entry.ut_type = USER_PROCESS;
|
||||
strcpy(id_entry.ut_id, "id1");
|
||||
|
||||
setutent();
|
||||
struct utmp *id_read_entry = getutid(&id_entry);
|
||||
assert(id_read_entry);
|
||||
assert(id_read_entry->ut_pid == 1234);
|
||||
|
||||
read_entry = getutent();
|
||||
assert(!read_entry);
|
||||
assert(errno == ESRCH);
|
||||
endutent();
|
||||
|
||||
// Test getutline
|
||||
struct utmp line_entry;
|
||||
memset(&line_entry, 0, sizeof(struct utmp));
|
||||
line_entry.ut_type = USER_PROCESS;
|
||||
strcpy(line_entry.ut_line, "pts/1");
|
||||
|
||||
setutent();
|
||||
struct utmp *line_read_entry = getutline(&line_entry);
|
||||
assert(line_read_entry);
|
||||
assert(line_read_entry->ut_pid == 1234);
|
||||
endutent();
|
||||
|
||||
// Test getutent_r
|
||||
struct utmp buf;
|
||||
struct utmp *res_r;
|
||||
setutent();
|
||||
int ret_r = getutent_r(&buf, &res_r);
|
||||
assert(!ret_r);
|
||||
assert(res_r == &buf);
|
||||
assert(buf.ut_pid == 1234);
|
||||
endutent();
|
||||
|
||||
// Test updwtmp.
|
||||
char wtmp_path[] = "/tmp/mlibc-wtmp-test-XXXXXX";
|
||||
int wtmp_fd = mkstemp(wtmp_path);
|
||||
assert(wtmp_fd != -1);
|
||||
close(wtmp_fd);
|
||||
|
||||
struct utmp entry2;
|
||||
memset(&entry2, 0, sizeof(struct utmp));
|
||||
entry2.ut_type = USER_PROCESS;
|
||||
entry2.ut_pid = 5678;
|
||||
strcpy(entry2.ut_line, "pts/2");
|
||||
strcpy(entry2.ut_id, "id2");
|
||||
strcpy(entry2.ut_user, "user2");
|
||||
strcpy(entry2.ut_host, "host2");
|
||||
entry2.ut_tv.tv_sec = 2;
|
||||
entry2.ut_tv.tv_usec = 2;
|
||||
|
||||
updwtmp(wtmp_path, &entry2);
|
||||
|
||||
// Verify the wtmp file.
|
||||
FILE *f = fopen(wtmp_path, "r");
|
||||
assert(f);
|
||||
struct utmp wtmp_entry;
|
||||
ret = fread(&wtmp_entry, sizeof(struct utmp), 1, f);
|
||||
assert(ret == 1);
|
||||
fclose(f);
|
||||
|
||||
assert(wtmp_entry.ut_type == USER_PROCESS);
|
||||
assert(wtmp_entry.ut_pid == 5678);
|
||||
assert(strcmp(wtmp_entry.ut_line, "pts/2") == 0);
|
||||
assert(strcmp(wtmp_entry.ut_id, "id2") == 0);
|
||||
assert(strcmp(wtmp_entry.ut_user, "user2") == 0);
|
||||
assert(strcmp(wtmp_entry.ut_host, "host2") == 0);
|
||||
assert(wtmp_entry.ut_tv.tv_sec == 2);
|
||||
assert(wtmp_entry.ut_tv.tv_usec == 2);
|
||||
|
||||
unlink(wtmp_path);
|
||||
unlink(path);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <utmpx.h>
|
||||
|
||||
int main() {
|
||||
char path[] = "/tmp/mlibc-utmpx-test-XXXXXX";
|
||||
int fd = mkstemp(path);
|
||||
assert(fd != -1);
|
||||
close(fd);
|
||||
|
||||
int ret = utmpxname(path);
|
||||
assert(!ret);
|
||||
|
||||
struct utmpx entry;
|
||||
memset(&entry, 0, sizeof(struct utmpx));
|
||||
|
||||
// Test an empty file.
|
||||
setutxent();
|
||||
struct utmpx *read_entry = getutxent();
|
||||
assert(!read_entry);
|
||||
endutxent();
|
||||
|
||||
// Test writing a new entry.
|
||||
entry.ut_type = USER_PROCESS;
|
||||
entry.ut_pid = getpid();
|
||||
strncpy(entry.ut_line, "tty1", sizeof(entry.ut_line));
|
||||
strncpy(entry.ut_id, "t1", sizeof(entry.ut_id));
|
||||
strncpy(entry.ut_user, "root", sizeof(entry.ut_user));
|
||||
strncpy(entry.ut_host, "localhost", sizeof(entry.ut_host));
|
||||
entry.ut_tv.tv_sec = 1234567890;
|
||||
entry.ut_tv.tv_usec = 0;
|
||||
setutxent();
|
||||
assert(pututxline(&entry));
|
||||
endutxent();
|
||||
|
||||
// Test reading the entry back.
|
||||
setutxent();
|
||||
read_entry = getutxent();
|
||||
assert(read_entry);
|
||||
assert(read_entry->ut_type == USER_PROCESS);
|
||||
assert(read_entry->ut_pid == getpid());
|
||||
assert(!strcmp(read_entry->ut_line, "tty1"));
|
||||
assert(!strcmp(read_entry->ut_id, "t1"));
|
||||
assert(!strcmp(read_entry->ut_user, "root"));
|
||||
assert(!strcmp(read_entry->ut_host, "localhost"));
|
||||
assert(read_entry->ut_tv.tv_sec == 1234567890);
|
||||
read_entry = getutxent();
|
||||
assert(!read_entry);
|
||||
endutxent();
|
||||
|
||||
// Test getutxid().
|
||||
struct utmpx id_entry;
|
||||
memset(&id_entry, 0, sizeof(struct utmpx));
|
||||
id_entry.ut_type = USER_PROCESS;
|
||||
strncpy(id_entry.ut_id, "t1", sizeof(id_entry.ut_id));
|
||||
setutxent();
|
||||
struct utmpx *id_read_entry = getutxid(&id_entry);
|
||||
assert(id_read_entry);
|
||||
assert(id_read_entry->ut_pid == getpid());
|
||||
endutxent();
|
||||
|
||||
// Test getutxline().
|
||||
struct utmpx line_entry;
|
||||
memset(&line_entry, 0, sizeof(struct utmpx));
|
||||
line_entry.ut_type = USER_PROCESS;
|
||||
strncpy(line_entry.ut_line, "tty1", sizeof(line_entry.ut_line));
|
||||
setutxent();
|
||||
struct utmpx *line_read_entry = getutxline(&line_entry);
|
||||
assert(line_read_entry);
|
||||
assert(line_read_entry->ut_pid == getpid());
|
||||
endutxent();
|
||||
|
||||
// Test updwtmpx.
|
||||
char wtmpx_path[] = "/tmp/mlibc-wtmpx-test-XXXXXX";
|
||||
int wtmpx_fd = mkstemp(wtmpx_path);
|
||||
assert(wtmpx_fd != -1);
|
||||
close(wtmpx_fd);
|
||||
|
||||
struct utmpx entry2;
|
||||
memset(&entry2, 0, sizeof(struct utmpx));
|
||||
entry2.ut_type = USER_PROCESS;
|
||||
entry2.ut_pid = 5678;
|
||||
strcpy(entry2.ut_line, "pts/2");
|
||||
strcpy(entry2.ut_id, "id2");
|
||||
strcpy(entry2.ut_user, "user2");
|
||||
strcpy(entry2.ut_host, "host2");
|
||||
entry2.ut_tv.tv_sec = 2;
|
||||
entry2.ut_tv.tv_usec = 2;
|
||||
|
||||
updwtmpx(wtmpx_path, &entry2);
|
||||
|
||||
// Verify the wtmpx file.
|
||||
FILE *f = fopen(wtmpx_path, "r");
|
||||
assert(f);
|
||||
struct utmpx wtmpx_entry;
|
||||
ret = fread(&wtmpx_entry, sizeof(struct utmpx), 1, f);
|
||||
assert(ret == 1);
|
||||
fclose(f);
|
||||
|
||||
assert(wtmpx_entry.ut_type == USER_PROCESS);
|
||||
assert(wtmpx_entry.ut_pid == 5678);
|
||||
assert(strcmp(wtmpx_entry.ut_line, "pts/2") == 0);
|
||||
assert(strcmp(wtmpx_entry.ut_id, "id2") == 0);
|
||||
assert(strcmp(wtmpx_entry.ut_user, "user2") == 0);
|
||||
assert(strcmp(wtmpx_entry.ut_host, "host2") == 0);
|
||||
assert(wtmpx_entry.ut_tv.tv_sec == 2);
|
||||
assert(wtmpx_entry.ut_tv.tv_usec == 2);
|
||||
|
||||
unlink(wtmpx_path);
|
||||
unlink(path);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#define assert_perror(x) \
|
||||
({ \
|
||||
int res = (x); \
|
||||
if (res < 0) { \
|
||||
perror(#x); \
|
||||
return 1; \
|
||||
} \
|
||||
res; \
|
||||
})
|
||||
|
||||
#define soft_assert(x, m) \
|
||||
({ \
|
||||
if (!(x)) { \
|
||||
fprintf(stderr, "cond \"%s\" failed: %s\n", #x, m); \
|
||||
return 1; \
|
||||
} \
|
||||
})
|
||||
|
||||
void remove_tmp(const char (*fname)[]) {
|
||||
unlink(&(*fname)[0]);
|
||||
}
|
||||
#define _cleanup_file_ __attribute__((cleanup(remove_tmp)))
|
||||
|
||||
int main(void) {
|
||||
int ret;
|
||||
char buf[32] = { 0 };
|
||||
_cleanup_file_ char filename[] = "xattr_test.XXXXXX";
|
||||
_cleanup_file_ char filename2[] = "xattr_test.XXXXXX.2";
|
||||
|
||||
int tmpfile = assert_perror(mkstemp(filename));
|
||||
memcpy(filename2, filename, sizeof(filename) - 1);
|
||||
assert_perror(symlink(filename, filename2));
|
||||
|
||||
assert_perror(setxattr(filename, "user.T1", "ABC", 3, XATTR_CREATE));
|
||||
assert_perror(fsetxattr(tmpfile, "user.T2", "DEF", 3, XATTR_CREATE));
|
||||
|
||||
// for testing remove
|
||||
assert_perror(fsetxattr(tmpfile, "user.T3", "DEF", 3, XATTR_CREATE));
|
||||
|
||||
if ((ret = getxattr(filename, "user.T1", buf, 3)) != 3) {
|
||||
if (ret < 0) {
|
||||
perror("getxattr");
|
||||
return 1;
|
||||
}
|
||||
|
||||
soft_assert(memcmp(buf, "ABC", 3) == 0, "xattr read wrong");
|
||||
}
|
||||
|
||||
ret = lgetxattr(filename2, "user.T1", buf, 3);
|
||||
soft_assert(ret < 0 && errno == ENODATA, "lgetxattr deref'd");
|
||||
|
||||
if ((ret = fgetxattr(tmpfile, "user.T3", buf, 3)) != 3) {
|
||||
if (ret < 0) {
|
||||
perror("fgetxattr");
|
||||
return 1;
|
||||
}
|
||||
|
||||
soft_assert(memcmp(buf, "DEF", 3) == 0, "xattr read wrong");
|
||||
}
|
||||
|
||||
assert_perror(removexattr(filename, "user.T2"));
|
||||
assert_perror(fremovexattr(tmpfile, "user.T3"));
|
||||
|
||||
ret = assert_perror(listxattr(filename, buf, sizeof(buf) - 1));
|
||||
soft_assert(memmem(buf, ret, "user.T1", 7), "user.T1 not found");
|
||||
soft_assert(!memmem(buf, ret, "user.T2", 7), "user.T2 found");
|
||||
soft_assert(!memmem(buf, ret, "user.T3", 7), "user.T3 found");
|
||||
|
||||
ret = assert_perror(flistxattr(tmpfile, buf, sizeof(buf) - 1));
|
||||
soft_assert(memmem(buf, ret, "user.T1", 7), "user.T1 not found");
|
||||
soft_assert(!memmem(buf, ret, "user.T2", 7), "user.T2 found");
|
||||
soft_assert(!memmem(buf, ret, "user.T3", 7), "user.T3 found");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user