1*41a8fc2aSguenther /* $OpenBSD: dlopen.c,v 1.2 2017/09/07 21:35:35 guenther Exp $ */
242d9ae1fSguenther /*
342d9ae1fSguenther * Copyright (c) 2016 Philip Guenther <guenther@openbsd.org>
442d9ae1fSguenther *
542d9ae1fSguenther * Permission to use, copy, modify, and distribute this software for any
642d9ae1fSguenther * purpose with or without fee is hereby granted, provided that the above
742d9ae1fSguenther * copyright notice and this permission notice appear in all copies.
842d9ae1fSguenther *
942d9ae1fSguenther * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1042d9ae1fSguenther * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1142d9ae1fSguenther * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1242d9ae1fSguenther * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1342d9ae1fSguenther * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1442d9ae1fSguenther * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1542d9ae1fSguenther * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1642d9ae1fSguenther */
1742d9ae1fSguenther
1842d9ae1fSguenther /*
1942d9ae1fSguenther * Test that various calls can be interrupted in a non-threaded process,
2042d9ae1fSguenther * then dlopen() libpthread and do that again in a second thread,
2142d9ae1fSguenther * and then verify that they're all correctly acting as cancellation points.
2242d9ae1fSguenther */
2342d9ae1fSguenther
2442d9ae1fSguenther #include <sys/types.h>
2542d9ae1fSguenther #include <sys/ipc.h>
2642d9ae1fSguenther #include <sys/sem.h>
2742d9ae1fSguenther #include <sys/socket.h>
2842d9ae1fSguenther #include <sys/stat.h>
2942d9ae1fSguenther #include <sys/time.h>
3042d9ae1fSguenther #include <sys/wait.h>
3142d9ae1fSguenther
3242d9ae1fSguenther #include <arpa/inet.h>
3342d9ae1fSguenther #include <netinet/in.h>
3442d9ae1fSguenther #include <netinet/tcp.h>
3542d9ae1fSguenther
3642d9ae1fSguenther #include <dlfcn.h>
3742d9ae1fSguenther #include <err.h>
3842d9ae1fSguenther #include <errno.h>
3942d9ae1fSguenther #include <fcntl.h>
4042d9ae1fSguenther #include <poll.h>
4142d9ae1fSguenther #include <pthread.h>
4242d9ae1fSguenther #include <signal.h>
4342d9ae1fSguenther #include <stdio.h>
4442d9ae1fSguenther #include <stdlib.h>
4542d9ae1fSguenther #include <string.h>
4642d9ae1fSguenther #include <time.h>
4742d9ae1fSguenther #include <unistd.h>
4842d9ae1fSguenther
4942d9ae1fSguenther /* path of fifo we remove/create/open/remove */
5042d9ae1fSguenther #define FIFO_PATH "fifo"
5142d9ae1fSguenther
5242d9ae1fSguenther /* path of lock file remove/create/lock/remove */
5342d9ae1fSguenther #define LOCK_PATH "lock"
5442d9ae1fSguenther
5542d9ae1fSguenther #define TEST_ACCEPT 0x001
5642d9ae1fSguenther #define TEST_CONNECT 0x002
5742d9ae1fSguenther #define TEST_FCNTL 0x004
5842d9ae1fSguenther #define TEST_FLOCK 0x008
5942d9ae1fSguenther #define TEST_NANOSLEEP 0x010
6042d9ae1fSguenther #define TEST_OPEN_FIFO 0x020
6142d9ae1fSguenther #define TEST_POLL 0x040
6242d9ae1fSguenther #define TEST_SIGSUSPEND 0x080
6342d9ae1fSguenther #define TEST_SEMOP 0x100
6442d9ae1fSguenther
6542d9ae1fSguenther #define TEST_ALL 0x1ff
6642d9ae1fSguenther
6742d9ae1fSguenther struct test_spec
6842d9ae1fSguenther {
6942d9ae1fSguenther int flag;
7042d9ae1fSguenther const char *name;
7142d9ae1fSguenther void (*init)(void);
7242d9ae1fSguenther void *(*run)(void *);
7342d9ae1fSguenther void (*fini)(void);
7442d9ae1fSguenther };
7542d9ae1fSguenther
7642d9ae1fSguenther
7742d9ae1fSguenther /*
7842d9ae1fSguenther * Functions looked up in libpthread
7942d9ae1fSguenther */
8042d9ae1fSguenther int (*p_attr_init)(pthread_attr_t *);
8142d9ae1fSguenther int (*p_attr_setdetachstate)(pthread_attr_t *, int);
8242d9ae1fSguenther int (*p_cancel)(pthread_t);
8342d9ae1fSguenther int (*p_cond_destroy)(pthread_cond_t *);
8442d9ae1fSguenther int (*p_cond_timedwait)(pthread_cond_t *, pthread_mutex_t *, const struct timespec *);
8542d9ae1fSguenther int (*p_create)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *);
8642d9ae1fSguenther int (*p_join)(pthread_t, void **);
8742d9ae1fSguenther int (*p_mutex_destroy)(pthread_mutex_t *);
8842d9ae1fSguenther int (*p_mutex_lock)(pthread_mutex_t *);
8942d9ae1fSguenther int (*p_mutex_unlock)(pthread_mutex_t *);
9042d9ae1fSguenther pthread_t (*p_self)(void);
9142d9ae1fSguenther
9242d9ae1fSguenther struct funcs
9342d9ae1fSguenther {
9442d9ae1fSguenther const char *name;
9542d9ae1fSguenther void *callback;
9642d9ae1fSguenther } functions[] =
9742d9ae1fSguenther {
9842d9ae1fSguenther #define FUNC(f) { "pthread_"#f, &p_##f }
9942d9ae1fSguenther FUNC(attr_init),
10042d9ae1fSguenther FUNC(attr_setdetachstate),
10142d9ae1fSguenther FUNC(cancel),
10242d9ae1fSguenther FUNC(cond_destroy),
10342d9ae1fSguenther FUNC(cond_timedwait),
10442d9ae1fSguenther FUNC(create),
10542d9ae1fSguenther FUNC(join),
10642d9ae1fSguenther FUNC(mutex_destroy),
10742d9ae1fSguenther FUNC(mutex_lock),
10842d9ae1fSguenther FUNC(mutex_unlock),
10942d9ae1fSguenther FUNC(self),
11042d9ae1fSguenther { NULL, NULL }
11142d9ae1fSguenther #undef FUNC
11242d9ae1fSguenther };
11342d9ae1fSguenther
11442d9ae1fSguenther /*
11542d9ae1fSguenther * Shared cleanup
11642d9ae1fSguenther */
11742d9ae1fSguenther void
finish(const char * msg,const int * retval,const struct timespec * tsp)11842d9ae1fSguenther finish(const char *msg, const int *retval, const struct timespec *tsp)
11942d9ae1fSguenther {
12042d9ae1fSguenther struct timespec after;
12142d9ae1fSguenther const char *fill = "\t\t\t";
12242d9ae1fSguenther
12342d9ae1fSguenther clock_gettime(CLOCK_REALTIME, &after);
12442d9ae1fSguenther after.tv_sec -= tsp->tv_sec;
12542d9ae1fSguenther after.tv_nsec -= tsp->tv_nsec;
12642d9ae1fSguenther if (after.tv_nsec < 0) {
12742d9ae1fSguenther after.tv_sec--;
12842d9ae1fSguenther after.tv_nsec += 1000000000L;
12942d9ae1fSguenther }
13042d9ae1fSguenther
13142d9ae1fSguenther fill += (strlen(msg) - 1) / 8;
13242d9ae1fSguenther if (retval[0] >= 0)
13342d9ae1fSguenther printf("%s: fail%s\ttime = %ld.%09lu\nr = %d\n",
13442d9ae1fSguenther msg, fill, (long)after.tv_sec, after.tv_nsec, retval[0]);
13542d9ae1fSguenther else if (retval[1] != EINTR)
13642d9ae1fSguenther printf("%s: fail%s\ttime = %ld.%09lu\nr = %d\terrno = %d: %s\n",
13742d9ae1fSguenther msg, fill, (long)after.tv_sec, after.tv_nsec,
13842d9ae1fSguenther retval[0], retval[1], strerror(retval[1]));
13942d9ae1fSguenther else
14042d9ae1fSguenther printf("%s: pass%s\ttime = %ld.%09lu\n",
14142d9ae1fSguenther msg, fill, (long)after.tv_sec, after.tv_nsec);
14242d9ae1fSguenther }
14342d9ae1fSguenther
14442d9ae1fSguenther /* noop signal handler */
14542d9ae1fSguenther void
sigusr1(int sig)14642d9ae1fSguenther sigusr1(int sig)
14742d9ae1fSguenther {
14842d9ae1fSguenther }
14942d9ae1fSguenther
15042d9ae1fSguenther /*
15142d9ae1fSguenther * Interrupt via alarm()
15242d9ae1fSguenther */
15342d9ae1fSguenther void
sigalrm(int sig)15442d9ae1fSguenther sigalrm(int sig)
15542d9ae1fSguenther {
15642d9ae1fSguenther write(1, "* ", 2);
15742d9ae1fSguenther }
15842d9ae1fSguenther
15942d9ae1fSguenther void
set_sigalrm(int restart)16042d9ae1fSguenther set_sigalrm(int restart)
16142d9ae1fSguenther {
16242d9ae1fSguenther struct sigaction sa;
16342d9ae1fSguenther
16442d9ae1fSguenther sa.sa_handler = &sigalrm;
16542d9ae1fSguenther sa.sa_flags = restart ? SA_RESTART : 0;
16642d9ae1fSguenther sigemptyset(&sa.sa_mask);
16742d9ae1fSguenther sigaction(SIGALRM, &sa, NULL);
16842d9ae1fSguenther }
16942d9ae1fSguenther
17042d9ae1fSguenther void
run_sig(const struct test_spec * test)17142d9ae1fSguenther run_sig(const struct test_spec *test)
17242d9ae1fSguenther {
17342d9ae1fSguenther struct timespec before;
17442d9ae1fSguenther int retval[2];
17542d9ae1fSguenther
17642d9ae1fSguenther if (test->init != NULL)
17742d9ae1fSguenther test->init();
17842d9ae1fSguenther if (clock_gettime(CLOCK_REALTIME, &before))
17942d9ae1fSguenther err(1, "clock_gettime");
18042d9ae1fSguenther alarm(1);
18142d9ae1fSguenther test->run(retval);
18242d9ae1fSguenther finish(test->name, retval, &before);
18342d9ae1fSguenther if (test->fini != NULL)
18442d9ae1fSguenther test->fini();
18542d9ae1fSguenther }
18642d9ae1fSguenther
18742d9ae1fSguenther
18842d9ae1fSguenther /*
18942d9ae1fSguenther * Interrupt via cancellation
19042d9ae1fSguenther */
19142d9ae1fSguenther
19242d9ae1fSguenther
19342d9ae1fSguenther void
run_cancel(const struct test_spec * test)19442d9ae1fSguenther run_cancel(const struct test_spec *test)
19542d9ae1fSguenther {
19642d9ae1fSguenther struct timespec before, target_time;
19742d9ae1fSguenther pthread_t tester;
19842d9ae1fSguenther pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
19942d9ae1fSguenther pthread_cond_t c = PTHREAD_COND_INITIALIZER;
20042d9ae1fSguenther int retval[2];
20142d9ae1fSguenther int r;
20242d9ae1fSguenther
20342d9ae1fSguenther if (test->init != NULL)
20442d9ae1fSguenther test->init();
20542d9ae1fSguenther
20642d9ae1fSguenther if ((r = p_mutex_lock(&m)))
20742d9ae1fSguenther errc(1, r, "pthread_mutex_lock");
20842d9ae1fSguenther
20942d9ae1fSguenther if (clock_gettime(CLOCK_REALTIME, &before))
21042d9ae1fSguenther err(1, "clock_gettime");
21142d9ae1fSguenther
21242d9ae1fSguenther target_time.tv_sec = before.tv_sec + 1;
21342d9ae1fSguenther target_time.tv_nsec = before.tv_nsec;
21442d9ae1fSguenther
21542d9ae1fSguenther retval[0] = -2;
21642d9ae1fSguenther if ((r = p_create(&tester, NULL, test->run, retval)))
21742d9ae1fSguenther errc(1, r, "pthread_create");
21842d9ae1fSguenther
21942d9ae1fSguenther /* overkill: could have done it with pthread_mutex_timedlock */
22042d9ae1fSguenther do
22142d9ae1fSguenther r = p_cond_timedwait(&c, &m, &target_time);
22242d9ae1fSguenther while (r == 0);
22342d9ae1fSguenther if (r != ETIMEDOUT)
22442d9ae1fSguenther errc(1, r, "pthread_cond_timedwait");
22542d9ae1fSguenther write(1, "* ", 2);
22642d9ae1fSguenther if (retval[0] == -2) {
22742d9ae1fSguenther retval[0] = -1;
22842d9ae1fSguenther retval[1] = EINTR;
22942d9ae1fSguenther }
23042d9ae1fSguenther if ((r = p_cancel(tester)))
23142d9ae1fSguenther errc(1, r, "pthread_cancel");
23242d9ae1fSguenther if ((r = p_mutex_unlock(&m)))
23342d9ae1fSguenther errc(1, r, "pthread_mutex_unlock");
23442d9ae1fSguenther if ((r = p_mutex_destroy(&m)))
23542d9ae1fSguenther errc(1, r, "pthread_mutex_destroy");
23642d9ae1fSguenther if ((r = p_cond_destroy(&c)))
23742d9ae1fSguenther errc(1, r, "pthread_cond_destroy");
23842d9ae1fSguenther
23942d9ae1fSguenther finish(test->name, retval, &before);
24042d9ae1fSguenther if (test->fini != NULL)
24142d9ae1fSguenther test->fini();
24242d9ae1fSguenther }
24342d9ae1fSguenther
24442d9ae1fSguenther void (*run)(const struct test_spec *_test) = run_sig;
24542d9ae1fSguenther
24642d9ae1fSguenther
24742d9ae1fSguenther /*
24842d9ae1fSguenther * The operations that are exercised in the tests
24942d9ae1fSguenther */
25042d9ae1fSguenther
25142d9ae1fSguenther /*
25242d9ae1fSguenther * POLL
25342d9ae1fSguenther */
25442d9ae1fSguenther void *
poll_run(void * arg)25542d9ae1fSguenther poll_run(void *arg)
25642d9ae1fSguenther {
25742d9ae1fSguenther struct pollfd pfd;
25842d9ae1fSguenther int *retval = arg;
25942d9ae1fSguenther
26042d9ae1fSguenther pfd.fd = 0;
26142d9ae1fSguenther pfd.events = POLLIN;
26242d9ae1fSguenther retval[0] = poll(&pfd, 1, 3 * 1000);
26342d9ae1fSguenther retval[1] = errno;
26442d9ae1fSguenther return NULL;
26542d9ae1fSguenther }
26642d9ae1fSguenther
26742d9ae1fSguenther /*
26842d9ae1fSguenther * NANOSLEEP
26942d9ae1fSguenther */
27042d9ae1fSguenther void *
nanosleep_run(void * arg)27142d9ae1fSguenther nanosleep_run(void *arg)
27242d9ae1fSguenther {
27342d9ae1fSguenther struct timespec ts;
27442d9ae1fSguenther int *retval = arg;
27542d9ae1fSguenther
27642d9ae1fSguenther ts.tv_sec = 2;
27742d9ae1fSguenther ts.tv_nsec = 0;
27842d9ae1fSguenther retval[0] = nanosleep(&ts, &ts);
27942d9ae1fSguenther retval[1] = errno;
28042d9ae1fSguenther return NULL;
28142d9ae1fSguenther }
28242d9ae1fSguenther
28342d9ae1fSguenther /*
28442d9ae1fSguenther * FCNTL
28542d9ae1fSguenther */
28642d9ae1fSguenther struct flock fcntl_fl = {
28742d9ae1fSguenther .l_start = 0,
28842d9ae1fSguenther .l_len = 0,
28942d9ae1fSguenther .l_type = F_WRLCK,
29042d9ae1fSguenther .l_whence = SEEK_SET,
29142d9ae1fSguenther };
29242d9ae1fSguenther static int fcntl_fd = -1;
29342d9ae1fSguenther static pid_t fcntl_pid = 0;
29442d9ae1fSguenther void
fcntl_init(void)29542d9ae1fSguenther fcntl_init(void)
29642d9ae1fSguenther {
29742d9ae1fSguenther int fds[2];
29842d9ae1fSguenther char buf[1];
29942d9ae1fSguenther
30042d9ae1fSguenther if (unlink(LOCK_PATH) && errno != ENOENT)
30142d9ae1fSguenther err(1, "unlink %s", LOCK_PATH);
30242d9ae1fSguenther if (pipe(fds))
30342d9ae1fSguenther err(1, "pipe");
30442d9ae1fSguenther fcntl_fd = open(LOCK_PATH, O_RDWR | O_CREAT, 0666);
30542d9ae1fSguenther fcntl_pid = fork();
30642d9ae1fSguenther if (fcntl_pid == 0) {
30742d9ae1fSguenther fcntl(fcntl_fd, F_SETLKW, &fcntl_fl);
30842d9ae1fSguenther close(fds[0]);
30942d9ae1fSguenther close(fds[1]);
31042d9ae1fSguenther sleep(1000);
31142d9ae1fSguenther _exit(0);
31242d9ae1fSguenther }
31342d9ae1fSguenther close(fds[1]);
31442d9ae1fSguenther read(fds[0], buf, 1);
31542d9ae1fSguenther close(fds[0]);
31642d9ae1fSguenther }
31742d9ae1fSguenther void *
fcntl_run(void * arg)31842d9ae1fSguenther fcntl_run(void *arg)
31942d9ae1fSguenther {
32042d9ae1fSguenther int *retval = arg;
32142d9ae1fSguenther
32242d9ae1fSguenther retval[0] = fcntl(fcntl_fd, F_SETLKW, &fcntl_fl);
32342d9ae1fSguenther retval[1] = errno;
32442d9ae1fSguenther return NULL;
32542d9ae1fSguenther }
32642d9ae1fSguenther void
fcntl_fini(void)32742d9ae1fSguenther fcntl_fini(void)
32842d9ae1fSguenther {
32942d9ae1fSguenther if (fcntl_fd >= 0) {
33042d9ae1fSguenther close(fcntl_fd);
33142d9ae1fSguenther fcntl_fd = -1;
33242d9ae1fSguenther }
33342d9ae1fSguenther if (fcntl_pid > 0) {
33442d9ae1fSguenther kill(fcntl_pid, SIGINT);
33542d9ae1fSguenther waitpid(fcntl_pid, NULL, 0);
33642d9ae1fSguenther fcntl_pid = 0;
33742d9ae1fSguenther }
33842d9ae1fSguenther if (unlink(LOCK_PATH))
33942d9ae1fSguenther err(1, "unlink %s", LOCK_PATH);
34042d9ae1fSguenther }
34142d9ae1fSguenther
34242d9ae1fSguenther /*
34342d9ae1fSguenther * FLOCK
34442d9ae1fSguenther */
34542d9ae1fSguenther static int flock_fd = -1;
34642d9ae1fSguenther static pid_t flock_pid = 0;
34742d9ae1fSguenther void
flock_init(void)34842d9ae1fSguenther flock_init(void)
34942d9ae1fSguenther {
35042d9ae1fSguenther int fds[2];
35142d9ae1fSguenther char buf[1];
35242d9ae1fSguenther
35342d9ae1fSguenther if (unlink(LOCK_PATH) && errno != ENOENT)
35442d9ae1fSguenther err(1, "unlink %s", LOCK_PATH);
35542d9ae1fSguenther if (pipe(fds))
35642d9ae1fSguenther err(1, "pipe");
35742d9ae1fSguenther flock_pid = fork();
35842d9ae1fSguenther flock_fd = open(LOCK_PATH, O_RDWR | O_CREAT, 0666);
35942d9ae1fSguenther if (flock_pid == 0) {
36042d9ae1fSguenther flock(flock_fd, LOCK_EX);
36142d9ae1fSguenther close(fds[0]);
36242d9ae1fSguenther close(fds[1]);
36342d9ae1fSguenther sleep(1000);
36442d9ae1fSguenther _exit(0);
36542d9ae1fSguenther }
36642d9ae1fSguenther close(fds[1]);
36742d9ae1fSguenther read(fds[0], buf, 1);
36842d9ae1fSguenther close(fds[0]);
36942d9ae1fSguenther }
37042d9ae1fSguenther void *
flock_run(void * arg)37142d9ae1fSguenther flock_run(void *arg)
37242d9ae1fSguenther {
37342d9ae1fSguenther int *retval = arg;
37442d9ae1fSguenther
37542d9ae1fSguenther retval[0] = flock(flock_fd, LOCK_EX);
37642d9ae1fSguenther retval[1] = errno;
37742d9ae1fSguenther return NULL;
37842d9ae1fSguenther }
37942d9ae1fSguenther void
flock_fini(void)38042d9ae1fSguenther flock_fini(void)
38142d9ae1fSguenther {
38242d9ae1fSguenther if (flock_fd >= 0) {
38342d9ae1fSguenther close(flock_fd);
38442d9ae1fSguenther flock_fd = -1;
38542d9ae1fSguenther }
38642d9ae1fSguenther if (flock_pid > 0) {
38742d9ae1fSguenther kill(flock_pid, SIGINT);
38842d9ae1fSguenther waitpid(flock_pid, NULL, 0);
38942d9ae1fSguenther flock_pid = 0;
39042d9ae1fSguenther }
39142d9ae1fSguenther if (unlink(LOCK_PATH) && errno != ENOENT)
39242d9ae1fSguenther err(1, "unlink %s", LOCK_PATH);
39342d9ae1fSguenther }
39442d9ae1fSguenther
39542d9ae1fSguenther /*
39642d9ae1fSguenther * SIGSUSPEND
39742d9ae1fSguenther */
39842d9ae1fSguenther void *
sigsuspend_run(void * arg)39942d9ae1fSguenther sigsuspend_run(void *arg)
40042d9ae1fSguenther {
40142d9ae1fSguenther sigset_t set;
40242d9ae1fSguenther int *retval = arg;
40342d9ae1fSguenther
40442d9ae1fSguenther sigemptyset(&set);
40542d9ae1fSguenther retval[0] = sigsuspend(&set);
40642d9ae1fSguenther retval[1] = errno;
40742d9ae1fSguenther return NULL;
40842d9ae1fSguenther }
40942d9ae1fSguenther
41042d9ae1fSguenther /*
41142d9ae1fSguenther * CONNECT
41242d9ae1fSguenther */
41342d9ae1fSguenther static int connect_fd = -1;
41442d9ae1fSguenther void
connect_init(void)41542d9ae1fSguenther connect_init(void)
41642d9ae1fSguenther {
41742d9ae1fSguenther int on = 1;
41842d9ae1fSguenther
41942d9ae1fSguenther connect_fd = socket(AF_INET, SOCK_STREAM, 0);
42042d9ae1fSguenther setsockopt(connect_fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
42142d9ae1fSguenther setsockopt(connect_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
42242d9ae1fSguenther }
42342d9ae1fSguenther void *
connect_run(void * arg)42442d9ae1fSguenther connect_run(void *arg)
42542d9ae1fSguenther {
42642d9ae1fSguenther struct sockaddr_in sin;
42742d9ae1fSguenther int *retval = arg;
42842d9ae1fSguenther
42942d9ae1fSguenther sin.sin_family = AF_INET;
43042d9ae1fSguenther inet_pton(AF_INET, "223.255.255.255", &sin.sin_addr);
43142d9ae1fSguenther sin.sin_port = 25;
43242d9ae1fSguenther retval[0] = connect(connect_fd, (struct sockaddr *)&sin, sizeof(sin));
43342d9ae1fSguenther retval[1] = errno;
43442d9ae1fSguenther return NULL;
43542d9ae1fSguenther }
43642d9ae1fSguenther void
connect_fini(void)43742d9ae1fSguenther connect_fini(void)
43842d9ae1fSguenther {
43942d9ae1fSguenther if (connect_fd >= 0) {
44042d9ae1fSguenther close(connect_fd);
44142d9ae1fSguenther connect_fd = -1;
44242d9ae1fSguenther }
44342d9ae1fSguenther }
44442d9ae1fSguenther
44542d9ae1fSguenther /*
44642d9ae1fSguenther * ACCEPT
44742d9ae1fSguenther */
44842d9ae1fSguenther static int accept_fd = -1;
44942d9ae1fSguenther void
accept_init(void)45042d9ae1fSguenther accept_init(void)
45142d9ae1fSguenther {
45242d9ae1fSguenther accept_fd = socket(AF_INET, SOCK_STREAM, 0);
45342d9ae1fSguenther listen(accept_fd, 2);
45442d9ae1fSguenther }
45542d9ae1fSguenther void *
accept_run(void * arg)45642d9ae1fSguenther accept_run(void *arg)
45742d9ae1fSguenther {
45842d9ae1fSguenther struct sockaddr_in sin;
45942d9ae1fSguenther socklen_t sl;
46042d9ae1fSguenther int *retval = arg;
46142d9ae1fSguenther
46242d9ae1fSguenther sl = sizeof(sin);
46342d9ae1fSguenther retval[0] = accept(accept_fd, (struct sockaddr *)&sin, &sl);
46442d9ae1fSguenther retval[1] = errno;
46542d9ae1fSguenther return NULL;
46642d9ae1fSguenther }
46742d9ae1fSguenther void
accept_fini(void)46842d9ae1fSguenther accept_fini(void)
46942d9ae1fSguenther {
47042d9ae1fSguenther if (accept_fd >= 0) {
47142d9ae1fSguenther close(accept_fd);
47242d9ae1fSguenther accept_fd = -1;
47342d9ae1fSguenther }
47442d9ae1fSguenther }
47542d9ae1fSguenther
47642d9ae1fSguenther /*
47742d9ae1fSguenther * OPEN FIFO
47842d9ae1fSguenther */
47942d9ae1fSguenther void
open_fifo_init(void)48042d9ae1fSguenther open_fifo_init(void)
48142d9ae1fSguenther {
48242d9ae1fSguenther /* let's get a fresh fifo */
48342d9ae1fSguenther if (unlink(FIFO_PATH) && errno != ENOENT)
48442d9ae1fSguenther err(1, "unlink %s", FIFO_PATH);
48542d9ae1fSguenther if (mkfifo(FIFO_PATH, 0600))
48642d9ae1fSguenther err(1, "mkfifo %s", FIFO_PATH);
48742d9ae1fSguenther }
48842d9ae1fSguenther void *
open_fifo_run(void * arg)48942d9ae1fSguenther open_fifo_run(void *arg)
49042d9ae1fSguenther {
49142d9ae1fSguenther int *retval = arg;
49242d9ae1fSguenther
49342d9ae1fSguenther retval[0] = open(FIFO_PATH, O_RDONLY);
49442d9ae1fSguenther retval[1] = errno;
49542d9ae1fSguenther return NULL;
49642d9ae1fSguenther }
49742d9ae1fSguenther void
open_fifo_fini(void)49842d9ae1fSguenther open_fifo_fini(void)
49942d9ae1fSguenther {
50042d9ae1fSguenther if (unlink(FIFO_PATH) && errno != ENOENT)
50142d9ae1fSguenther err(1, "unlink %s", FIFO_PATH);
50242d9ae1fSguenther }
50342d9ae1fSguenther
50442d9ae1fSguenther /*
50542d9ae1fSguenther * SEMOP
50642d9ae1fSguenther */
50742d9ae1fSguenther static int semid = -1;
50842d9ae1fSguenther void
semop_init(void)50942d9ae1fSguenther semop_init(void)
51042d9ae1fSguenther {
51142d9ae1fSguenther union {
51242d9ae1fSguenther int val;
51342d9ae1fSguenther struct semid_ds *buf;
51442d9ae1fSguenther unsigned short *array;
51542d9ae1fSguenther } semarg;
51642d9ae1fSguenther unsigned short val;
51742d9ae1fSguenther
51842d9ae1fSguenther semid = semget(IPC_PRIVATE, 1, 0600);
51942d9ae1fSguenther semarg.array = &val;
52042d9ae1fSguenther val = 0;
52142d9ae1fSguenther semctl(semid, 0, SETALL, semarg);
52242d9ae1fSguenther }
52342d9ae1fSguenther void *
semop_run(void * arg)52442d9ae1fSguenther semop_run(void *arg)
52542d9ae1fSguenther {
52642d9ae1fSguenther struct sembuf op;
52742d9ae1fSguenther int *retval = arg;
52842d9ae1fSguenther
52942d9ae1fSguenther op.sem_num = 0;
53042d9ae1fSguenther op.sem_op = -1;
53142d9ae1fSguenther op.sem_flg = 0;
53242d9ae1fSguenther retval[0] = semop(semid, &op, 1);
53342d9ae1fSguenther retval[1] = errno;
53442d9ae1fSguenther return NULL;
53542d9ae1fSguenther }
53642d9ae1fSguenther void
semop_fini(void)53742d9ae1fSguenther semop_fini(void)
53842d9ae1fSguenther {
53942d9ae1fSguenther if (semid >= 0) {
54042d9ae1fSguenther semctl(semid, 0, IPC_RMID, NULL);
54142d9ae1fSguenther semid = -1;
54242d9ae1fSguenther }
54342d9ae1fSguenther }
54442d9ae1fSguenther
54542d9ae1fSguenther #define TESTSPEC_FULL(flag, name, prefix) \
54642d9ae1fSguenther { flag, name, prefix##_init, prefix##_run, prefix##_fini }
54742d9ae1fSguenther #define TESTSPEC(flag, name, prefix) \
54842d9ae1fSguenther { flag, name, NULL, prefix##_run, NULL }
54942d9ae1fSguenther struct test_spec test_specs[] = {
55042d9ae1fSguenther TESTSPEC_FULL(TEST_ACCEPT, "accept", accept),
55142d9ae1fSguenther TESTSPEC_FULL(TEST_CONNECT, "connect", connect),
55242d9ae1fSguenther TESTSPEC_FULL(TEST_FCNTL, "fcntl(F_SETLKW)", fcntl),
55342d9ae1fSguenther TESTSPEC_FULL(TEST_SEMOP, "semop", semop),
55442d9ae1fSguenther TESTSPEC_FULL(TEST_FLOCK, "flock", flock),
55542d9ae1fSguenther TESTSPEC_FULL(TEST_OPEN_FIFO, "open_fifo", open_fifo),
55642d9ae1fSguenther
55742d9ae1fSguenther TESTSPEC(TEST_NANOSLEEP, "nanosleep", nanosleep),
55842d9ae1fSguenther TESTSPEC(TEST_POLL, "poll", poll),
55942d9ae1fSguenther TESTSPEC(TEST_SIGSUSPEND, "sigsuspend", sigsuspend),
56042d9ae1fSguenther { 0 }
56142d9ae1fSguenther };
56242d9ae1fSguenther
56342d9ae1fSguenther
56442d9ae1fSguenther void *
run_tests(void * arg)56542d9ae1fSguenther run_tests(void *arg)
56642d9ae1fSguenther {
56742d9ae1fSguenther int tests = *(int *)arg;
56842d9ae1fSguenther int flag;
56942d9ae1fSguenther struct test_spec *test;
57042d9ae1fSguenther sigset_t mask;
57142d9ae1fSguenther
57242d9ae1fSguenther /* make sure SIGALRM is unblocked for the tests */
57342d9ae1fSguenther sigemptyset(&mask);
57442d9ae1fSguenther sigaddset(&mask, SIGALRM);
57542d9ae1fSguenther sigprocmask(SIG_UNBLOCK, &mask, NULL);
57642d9ae1fSguenther
57742d9ae1fSguenther while (tests > 0) {
57842d9ae1fSguenther flag = tests & ~(tests >> 1);
57942d9ae1fSguenther tests &= ~flag;
58042d9ae1fSguenther for (test = test_specs; test->flag; test++)
58142d9ae1fSguenther if (test->flag == flag) {
58242d9ae1fSguenther run(test);
58342d9ae1fSguenther break;
58442d9ae1fSguenther }
58542d9ae1fSguenther }
58642d9ae1fSguenther
58742d9ae1fSguenther return arg;
58842d9ae1fSguenther }
58942d9ae1fSguenther
59042d9ae1fSguenther int
main(int argc,char ** argv)59142d9ae1fSguenther main(int argc, char **argv)
59242d9ae1fSguenther {
59342d9ae1fSguenther int ch, tests;
59442d9ae1fSguenther sigset_t mask;
59542d9ae1fSguenther int r;
59642d9ae1fSguenther void *handle;
59742d9ae1fSguenther struct funcs *f;
59842d9ae1fSguenther pthread_t t;
59942d9ae1fSguenther void *ret;
60042d9ae1fSguenther
60142d9ae1fSguenther set_sigalrm(0);
60242d9ae1fSguenther
60342d9ae1fSguenther tests = 0;
60442d9ae1fSguenther while ((ch = getopt(argc, argv, "AacFfinoprSs")) != -1)
60542d9ae1fSguenther switch (ch) {
60642d9ae1fSguenther case 'A':
60742d9ae1fSguenther tests |= TEST_ALL;
60842d9ae1fSguenther break;
60942d9ae1fSguenther case 'a':
61042d9ae1fSguenther tests |= TEST_ACCEPT;
61142d9ae1fSguenther break;
61242d9ae1fSguenther case 'c':
61342d9ae1fSguenther tests |= TEST_CONNECT;
61442d9ae1fSguenther break;
61542d9ae1fSguenther case 'F':
61642d9ae1fSguenther tests |= TEST_FCNTL;
61742d9ae1fSguenther break;
61842d9ae1fSguenther case 'f':
61942d9ae1fSguenther tests |= TEST_FLOCK;
62042d9ae1fSguenther break;
62142d9ae1fSguenther case 'i':
62242d9ae1fSguenther set_sigalrm(0);
62342d9ae1fSguenther break;
62442d9ae1fSguenther case 'n':
62542d9ae1fSguenther tests |= TEST_NANOSLEEP;
62642d9ae1fSguenther break;
62742d9ae1fSguenther case 'o':
62842d9ae1fSguenther tests |= TEST_OPEN_FIFO;
62942d9ae1fSguenther break;
63042d9ae1fSguenther case 'p':
63142d9ae1fSguenther tests |= TEST_POLL;
63242d9ae1fSguenther break;
63342d9ae1fSguenther case 'r':
63442d9ae1fSguenther set_sigalrm(1);
63542d9ae1fSguenther break;
63642d9ae1fSguenther case 's':
63742d9ae1fSguenther tests |= TEST_SIGSUSPEND;
63842d9ae1fSguenther break;
63942d9ae1fSguenther case 'S':
64042d9ae1fSguenther tests |= TEST_SEMOP;
64142d9ae1fSguenther break;
64242d9ae1fSguenther }
64342d9ae1fSguenther if (tests == 0)
64442d9ae1fSguenther tests = TEST_ALL;
64542d9ae1fSguenther
64642d9ae1fSguenther /* make sure SIGTERM is unblocked */
64742d9ae1fSguenther sigemptyset(&mask);
64842d9ae1fSguenther sigaddset(&mask, SIGTERM);
64942d9ae1fSguenther sigprocmask(SIG_UNBLOCK, &mask, NULL);
65042d9ae1fSguenther
65142d9ae1fSguenther /*
65242d9ae1fSguenther * Run them in the original thread
65342d9ae1fSguenther */
65442d9ae1fSguenther printf("single threaded\n");
65542d9ae1fSguenther run_tests(&tests);
65642d9ae1fSguenther
65742d9ae1fSguenther
65842d9ae1fSguenther /*
65942d9ae1fSguenther * Open libpthread, create a thread and run them in *that*
66042d9ae1fSguenther */
66142d9ae1fSguenther if ((handle = dlopen("libpthread.so", RTLD_LAZY)) == NULL)
66242d9ae1fSguenther errx(1, "dlopen: %s", dlerror());
66342d9ae1fSguenther
66442d9ae1fSguenther /* look up all the functions. The cast here isn't strictly portable */
66542d9ae1fSguenther for (f = functions; f->name != NULL; f++) {
666*41a8fc2aSguenther if ((*(void **)f->callback = dlsym(handle, f->name)) == NULL &&
667*41a8fc2aSguenther (*(void **)f->callback = dlsym(RTLD_DEFAULT, f->name)) == NULL)
66842d9ae1fSguenther errx(1, "dlsym %s: %s", f->name, dlerror());
66942d9ae1fSguenther }
67042d9ae1fSguenther
67142d9ae1fSguenther /* block SIGALRM in the original thread */
67242d9ae1fSguenther sigemptyset(&mask);
67342d9ae1fSguenther sigaddset(&mask, SIGALRM);
67442d9ae1fSguenther sigprocmask(SIG_BLOCK, &mask, NULL);
67542d9ae1fSguenther
67642d9ae1fSguenther printf("in thread after dlopen(pthread)\n");
67742d9ae1fSguenther if ((r = p_create(&t, NULL, run_tests, &tests)))
67842d9ae1fSguenther errc(1, r, "pthread_create");
67942d9ae1fSguenther if ((r = p_join(t, &ret)))
68042d9ae1fSguenther errc(1, r, "pthread_join");
68142d9ae1fSguenther if (ret != &tests)
68242d9ae1fSguenther errx(1, "bad return by thread: %p != %p", ret, (void *)&tests);
68342d9ae1fSguenther
68442d9ae1fSguenther
68542d9ae1fSguenther /*
68642d9ae1fSguenther * Run the tests again, this time using cancellation
68742d9ae1fSguenther */
68842d9ae1fSguenther printf("using cancellation\n");
68942d9ae1fSguenther run = run_cancel;
69042d9ae1fSguenther run_tests(&tests);
69142d9ae1fSguenther
69242d9ae1fSguenther if (dlclose(handle))
69342d9ae1fSguenther errx(1, "dlclose: %s", dlerror());
69442d9ae1fSguenther
69542d9ae1fSguenther return 0;
69642d9ae1fSguenther }
697