Improve UDS testing. Fix a few bugs. Add testing of passing file descriptors.

Contributed by Thomas Cort.
This commit is contained in:
Thomas Veerman 2010-08-30 13:49:49 +00:00
parent e8ddc0f46e
commit a4c30acd9d

View file

@ -72,6 +72,10 @@
#define TEST_SYM_A "test.a"
#define TEST_SYM_B "test.b"
/* text file and test phrase for testing file descriptor passing */
#define TEST_TXT_FILE "test.txt"
#define MSG "This raccoon loves to eat bugs.\n"
/* buffer for send/recv */
#define BUFSIZE (128)
@ -79,6 +83,8 @@
/* socket types supported */
int types[3] = {SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM};
char sock_fullpath[PATH_MAX + 1];
/* timestamps for debug and error logs */
char *get_timestamp(void)
@ -127,11 +133,24 @@ fprintf(stderr, "[ERROR][%s] (%s Line %d) %s [pid=%d:errno=%d:%s]\n", \
errct++; \
if (errct++ > MAX_ERROR) { \
printf("Too many errors; test aborted\n"); \
cleanup(); \
quit(); \
exit(1); \
} \
} while (0)
/* Convert name to the full path of the socket. Assumes name is in cwd. */
char *fullpath(char *name)
{
char cwd[PATH_MAX + 1];
if (realpath(".", cwd) == NULL)
test_fail("Couldn't retrieve current working dir");
snprintf(sock_fullpath, PATH_MAX, "%s/%s", cwd, name);
return(sock_fullpath);
}
#if DEBUG == 1
/* macros to display debugging information */
#define debug(msg) \
@ -438,8 +457,11 @@ void test_getsockname(void)
}
if (!(sock_addr.sun_family == AF_UNIX && strncmp(sock_addr.sun_path,
addr.sun_path, sizeof(sock_addr.sun_path) - 1) == 0)) {
fullpath(TEST_SUN_PATH),
sizeof(sock_addr.sun_path) - 1) == 0)) {
test_fail("getsockname() did return the right address");
fprintf(stderr, "exp: '%s' | got: '%s'\n", addr.sun_path,
sock_addr.sun_path);
}
CLOSE(sd);
@ -481,10 +503,13 @@ void test_bind(void)
}
if (!(sock_addr.sun_family == AF_UNIX &&
strncmp(sock_addr.sun_path, addr.sun_path,
strncmp(sock_addr.sun_path,
fullpath(TEST_SUN_PATH),
sizeof(sock_addr.sun_path) - 1) == 0)) {
test_fail("getsockname() didn't return the right addr");
fprintf(stderr, "exp: '%s' | got: '%s'\n", addr.sun_path,
sock_addr.sun_path);
}
debug("Test bind() with a address that has already been bind()'d");
@ -627,7 +652,7 @@ void test_close(void)
{
struct sockaddr_un addr;
int sd, sd2;
int rc;
int rc, i;
debug("entering test_close()");
@ -676,6 +701,16 @@ void test_close(void)
UNLINK(TEST_SUN_PATH);
/* Create and close a socket a bunch of times.
* If the implementation doesn't properly free the
* socket during close(), eventually socket() will
* fail when the internal descriptor table is full.
*/
for (i = 0; i < 1024; i++) {
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
CLOSE(sd);
}
debug("leaving test_close()");
}
@ -1131,7 +1166,7 @@ void test_xfer_server(pid_t pid)
rc = waitpid(pid, &status, 0);
} while (rc == -1 && errno == EINTR);
/* we use the exit status to get it's error count */
/* we use the exit status to get its error count */
errct += WEXITSTATUS(status);
}
@ -1185,6 +1220,7 @@ void test_xfer_client(void)
peer_addr_len = sizeof(struct sockaddr_un);
debug("Creating symlink to TEST_SUN_PATH");
SYMLINK(TEST_SUN_PATH, TEST_SYM_A);
@ -1215,9 +1251,10 @@ void test_xfer_client(void)
/* we need to use the full path "/usr/src/test/DIR_56/test.sock"
* because that is what is returned by getpeername().
*/
if (!(peer_addr.sun_family == AF_UNIX &&
strncmp(peer_addr.sun_path,
"/usr/src/test/DIR_56/test.sock",
fullpath(TEST_SUN_PATH),
sizeof(peer_addr.sun_path) - 1) == 0)) {
test_fail("getpeername() didn't return the right address");
@ -1350,8 +1387,6 @@ void test_xfer_client(void)
debug("[client] closing socket");
CLOSE(sd);
UNLINK(TEST_SYM_A);
debug("[client] leaving test_xfer_client()");
exit(errct);
}
@ -1360,6 +1395,7 @@ void test_xfer(void)
{
pid_t pid;
UNLINK(TEST_SYM_A);
UNLINK(TEST_SUN_PATH);
/* the signal handler is only used by the client, but we have to
@ -1379,18 +1415,18 @@ void test_xfer(void)
if (pid == -1) {
test_fail("fork() failed");
return;
}
if (pid == 0) {
} else if (pid == 0) {
debug("child");
test_xfer_client();
test_fail("we should never get here");
exit(1);
} else {
debug("parent");
test_xfer_server(pid);
debug("parent done");
}
UNLINK(TEST_SYM_A);
UNLINK(TEST_SUN_PATH);
}
@ -1403,7 +1439,7 @@ void test_simple_client(int type)
sd = socket(PF_UNIX, type, 0);
if (sd == -1) {
test_fail("socket");
return;
exit(errct);
}
while (server_ready == 0) {
@ -1417,14 +1453,13 @@ void test_simple_client(int type)
bzero(buf, BUFSIZE);
snprintf(buf, BUFSIZE-1, "Hello, My Name is Client.");
if (type == SOCK_DGRAM) {
rc = sendto(sd, buf, strlen(buf) + 1, 0,
(struct sockaddr *) &addr, sizeof(struct sockaddr_un));
if (rc == -1) {
test_fail("sendto");
return;
exit(errct);
}
} else {
@ -1433,7 +1468,7 @@ void test_simple_client(int type)
sizeof(struct sockaddr_un));
if (rc == -1) {
test_fail("connect");
return;
exit(errct);
}
rc = write(sd, buf, strlen(buf) + 1);
@ -1441,11 +1476,15 @@ void test_simple_client(int type)
test_fail("write");
}
memset(buf, '\0', BUFSIZE);
rc = read(sd, buf, BUFSIZE);
if (rc == -1) {
test_fail("read");
}
if (strcmp("Hello, My Name is Server.", buf) != 0) {
test_fail("didn't read the correct string");
}
}
rc = close(sd);
@ -1463,6 +1502,8 @@ void test_simple_server(int type, pid_t pid)
struct sockaddr_un addr;
socklen_t addr_len;
addr_len = sizeof(struct sockaddr_un);
sd = socket(PF_UNIX, type, 0);
if (sd == -1) {
test_fail("socket");
@ -1504,11 +1545,16 @@ void test_simple_server(int type, pid_t pid)
test_fail("accept");
}
memset(buf, '\0', BUFSIZE);
rc = read(client_sd, buf, BUFSIZE);
if (rc == -1) {
test_fail("read");
}
if (strcmp("Hello, My Name is Client.", buf) != 0) {
test_fail("didn't read the correct string");
}
/* added for extra fun to make the client block on read() */
sleep(1);
@ -1538,7 +1584,7 @@ void test_simple_server(int type, pid_t pid)
rc = waitpid(pid, &status, 0);
} while (rc == -1 && errno == EINTR);
/* we use the exit status to get it's error count */
/* we use the exit status to get its error count */
errct += WEXITSTATUS(status);
}
@ -1546,6 +1592,8 @@ void test_simple_client_server(int type)
{
pid_t pid;
debug("test_simple_client_server()");
UNLINK(TEST_SUN_PATH);
/* the signal handler is only used by the client, but we have to
@ -1565,12 +1613,11 @@ void test_simple_client_server(int type)
if (pid == -1) {
test_fail("fork() failed");
return;
}
if (pid == 0) {
} else if (pid == 0) {
debug("child");
test_simple_client(type);
test_fail("we should never get here");
exit(1);
} else {
debug("parent");
test_simple_server(type, pid);
@ -1667,6 +1714,8 @@ void test_vectorio(int type)
if (rc == -1) {
test_fail("close");
}
debug("done vector io tests");
}
void test_msg(int type)
@ -1791,6 +1840,8 @@ void test_msg_dgram(void)
char buf3[BUFSIZE];
socklen_t addrlen = sizeof(struct sockaddr_un);
debug("test msg_dgram");
UNLINK(TEST_SUN_PATH);
UNLINK(TEST_SUN_PATHB);
@ -1881,7 +1932,7 @@ void test_msg_dgram(void)
* because that is what is returned by recvmsg().
*/
if (addr.sun_family != AF_UNIX || strcmp(addr.sun_path,
"/usr/src/test/DIR_56/testb.sock")) {
fullpath(TEST_SUN_PATHB))) {
test_fail("recvmsg");
}
@ -1899,6 +1950,592 @@ void test_msg_dgram(void)
UNLINK(TEST_SUN_PATHB);
}
void test_scm_credentials(void)
{
int rc;
int src;
int dst;
struct ucred cred;
struct cmsghdr *cmsg = NULL;
struct sockaddr_un addr;
struct iovec iov[3];
struct msghdr msg1;
struct msghdr msg2;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
char ctrl[BUFSIZE];
socklen_t addrlen = sizeof(struct sockaddr_un);
debug("test_scm_credentials");
UNLINK(TEST_SUN_PATH);
UNLINK(TEST_SUN_PATHB);
debug("creating src socket");
src = socket(PF_UNIX, SOCK_DGRAM, 0);
if (src == -1) {
test_fail("socket");
}
debug("creating dst socket");
dst = socket(PF_UNIX, SOCK_DGRAM, 0);
if (dst == -1) {
test_fail("socket");
}
debug("binding src socket");
memset(&addr, '\0', sizeof(struct sockaddr_un));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, TEST_SUN_PATHB, sizeof(addr.sun_path) - 1);
rc = bind(src, (struct sockaddr *) &addr, addrlen);
if (rc == -1) {
test_fail("bind");
}
debug("binding dst socket");
memset(&addr, '\0', sizeof(struct sockaddr_un));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
rc = bind(dst, (struct sockaddr *) &addr, addrlen);
if (rc == -1) {
test_fail("bind");
}
memset(&buf1, '\0', BUFSIZE);
memset(&buf2, '\0', BUFSIZE);
memset(&buf3, '\0', BUFSIZE);
memset(&ctrl, '\0', BUFSIZE);
strncpy(buf1, "Minix ", BUFSIZE-1);
strncpy(buf2, "is ", BUFSIZE-1);
strncpy(buf3, "great!", BUFSIZE-1);
iov[0].iov_base = buf1;
iov[0].iov_len = 6;
iov[1].iov_base = buf2;
iov[1].iov_len = 3;
iov[2].iov_base = buf3;
iov[2].iov_len = 32;
memset(&msg1, '\0', sizeof(struct msghdr));
msg1.msg_name = &addr;
msg1.msg_namelen = addrlen;
msg1.msg_iov = iov;
msg1.msg_iovlen = 3;
msg1.msg_control = NULL;
msg1.msg_controllen = 0;
msg1.msg_flags = 0;
debug("sending msg1");
rc = sendmsg(src, &msg1, 0);
if (rc == -1) {
test_fail("sendmsg");
}
memset(&buf1, '\0', BUFSIZE);
memset(&buf2, '\0', BUFSIZE);
memset(&buf3, '\0', BUFSIZE);
memset(&ctrl, '\0', BUFSIZE);
iov[0].iov_base = buf1;
iov[0].iov_len = 9;
iov[1].iov_base = buf2;
iov[1].iov_len = 32;
memset(&addr, '\0', sizeof(struct sockaddr_un));
memset(&msg2, '\0', sizeof(struct msghdr));
msg2.msg_name = &addr;
msg2.msg_namelen = sizeof(struct sockaddr_un);
msg2.msg_iov = iov;
msg2.msg_iovlen = 2;
msg2.msg_control = ctrl;
msg2.msg_controllen = BUFSIZE;
msg2.msg_flags = 0;
debug("recv msg2");
rc = recvmsg(dst, &msg2, 0);
if (rc == -1) {
test_fail("recvmsg");
}
debug("checking results");
if (strncmp(buf1, "Minix is ", 9) || strncmp(buf2, "great!", 6)) {
test_fail("recvmsg");
}
/* we need to use the full path "/usr/src/test/DIR_56/testb.sock"
* because that is what is returned by recvmsg().
*/
if (addr.sun_family != AF_UNIX || strcmp(addr.sun_path,
fullpath(TEST_SUN_PATHB))) {
test_fail("recvmsg");
}
debug("looking for credentials");
memset(&cred, '\0', sizeof(struct ucred));
for (cmsg = CMSG_FIRSTHDR(&msg2); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg2, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDENTIALS) {
memcpy(&cred, CMSG_DATA(cmsg), sizeof(struct ucred));
break;
}
}
if (cred.pid != getpid() || cred.uid != geteuid() ||
cred.gid != getegid()) {
test_fail("did no receive the proper credentials");
}
rc = close(dst);
if (rc == -1) {
test_fail("close");
}
rc = close(src);
if (rc == -1) {
test_fail("close");
}
UNLINK(TEST_SUN_PATH);
UNLINK(TEST_SUN_PATHB);
}
void test_connect(void)
{
int i, sd, sds[2], rc;
/* connect() is already tested throughout test56, but
* in most cases the client and server end up on /dev/uds
* minor 0 and minor 1. This test opens some sockets first and
* then calls test_simple_client_server(). This forces the
* client and server minor numbers higher in the descriptor table.
*/
debug("starting test_connect()");
sd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sd == -1) {
test_fail("couldn't create a socket");
}
rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sds);
if (rc == -1) {
test_fail("couldn't create a socketpair");
}
for (i = 0; i < 3; i++) {
test_simple_client_server(types[i]);
}
rc = close(sds[1]);
if (rc == -1) {
test_fail("close() failed");
}
rc = close(sds[0]);
if (rc == -1) {
test_fail("close() failed");
}
rc = close(sd);
if (rc == -1) {
test_fail("close() failed");
}
debug("exiting test_connect()");
}
int test_multiproc_read(void)
{
/* test that when we fork() a process with an open socket descriptor,
* the descriptor in each process points to the same thing.
*/
pid_t pid;
int sds[2];
int rc, status;
char buf[3];
debug("entering test_multiproc_read()");
rc = socketpair(PF_UNIX, SOCK_STREAM, 0, sds);
if (rc == -1) {
test_fail("socketpair");
return 1;
}
memset(buf, '\0', 3);
/* the signal handler is only used by the client, but we have to
* install it now. if we don't the server may signal the client
* before the handler is installed.
*/
debug("installing signal handler");
if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) {
test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed");
return 1;
}
debug("signal handler installed");
server_ready = 0;
pid = fork();
if (pid == -1) {
test_fail("fork");
return 1;
} else if (pid == 0) {
while (server_ready == 0) {
debug("waiting for SIGUSR1 from parent");
sleep(1);
}
rc = read(sds[1], buf, 2);
if (rc == -1) {
test_fail("read");
exit(1);
}
if (!(buf[0] == 'X' && buf[1] == '3')) {
test_fail("Didn't read X3");
exit(1);
}
exit(0);
} else {
rc = write(sds[0], "MNX3", 4);
if (rc == -1) {
test_fail("write");
}
rc = read(sds[1], buf, 2);
if (rc == -1) {
test_fail("read");
}
if (!(buf[0] == 'M' && buf[1] == 'N')) {
test_fail("Didn't read MN");
}
/* time to tell the client to start the test */
kill(pid, SIGUSR1);
do {
rc = waitpid(pid, &status, 0);
} while (rc == -1 && errno == EINTR);
/* we use the exit status to get its error count */
errct += WEXITSTATUS(status);
}
return 0;
}
int test_multiproc_write(void)
{
/* test that when we fork() a process with an open socket descriptor,
* the descriptor in each process points to the same thing.
*/
pid_t pid;
int sds[2];
int rc, status;
char buf[7];
debug("entering test_multiproc_write()");
rc = socketpair(PF_UNIX, SOCK_STREAM, 0, sds);
if (rc == -1) {
test_fail("socketpair");
return 1;
}
memset(buf, '\0', 7);
/* the signal handler is only used by the client, but we have to
* install it now. if we don't the server may signal the client
* before the handler is installed.
*/
debug("installing signal handler");
if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) {
test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed");
return 1;
}
debug("signal handler installed");
server_ready = 0;
pid = fork();
if (pid == -1) {
test_fail("fork");
return 1;
} else if (pid == 0) {
while (server_ready == 0) {
debug("waiting for SIGUSR1 from parent");
sleep(1);
}
rc = write(sds[1], "IX3", 3);
if (rc == -1) {
test_fail("write");
exit(1);
}
rc = read(sds[0], buf, 6);
if (rc == -1) {
test_fail("read");
exit(1);
}
if (strcmp(buf, "MINIX3") != 0) {
test_fail("didn't read MINIX3");
exit(1);
}
exit(0);
} else {
rc = write(sds[1], "MIN", 3);
if (rc == -1) {
test_fail("write");
}
/* time to tell the client to start the test */
kill(pid, SIGUSR1);
do {
rc = waitpid(pid, &status, 0);
} while (rc == -1 && errno == EINTR);
/* we use the exit status to get its error count */
errct += WEXITSTATUS(status);
}
return 0;
}
void test_fd_passing_child(int sd)
{
int fd, rc;
char x = 'x';
struct msghdr msghdr;
struct cmsghdr *cmsg;
struct iovec iov;
char buf[BUFSIZE];
memset(buf, '\0', BUFSIZE);
fd = open(TEST_TXT_FILE, O_CREAT|O_TRUNC|O_RDWR);
if (fd == -1) {
test_fail("could not open test.txt");
}
msghdr.msg_name = NULL;
msghdr.msg_namelen = 0;
iov.iov_base = &x;
iov.iov_len = 1;
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
msghdr.msg_control = buf;
msghdr.msg_controllen = CMSG_SPACE(sizeof(int));
msghdr.msg_flags = 0;
cmsg = CMSG_FIRSTHDR(&msghdr);
cmsg->cmsg_len = CMSG_SPACE(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
((int *) CMSG_DATA(cmsg))[0] = fd;
rc = sendmsg(sd, &msghdr, 0);
if (rc == -1) {
test_fail("could not send message");
}
memset(buf, '\0', BUFSIZE);
rc = read(sd, buf, BUFSIZE);
if (rc == -1) {
test_fail("could not read from socket");
}
if (strcmp(buf, "done") != 0) {
test_fail("we didn't read the right message");
}
memset(buf, '\0', BUFSIZE);
rc = lseek(fd, 0, SEEK_SET);
if (rc == -1) {
test_fail("could not seek to start of test.txt");
}
rc = read(fd, buf, BUFSIZE);
if (rc == -1) {
test_fail("could not read from test.txt");
}
if (strcmp(buf, MSG) != 0) {
test_fail("other process didn't write MSG to test.txt");
}
rc = close(fd);
if (rc == -1) {
test_fail("could not close test.txt");
}
rc = close(sd);
if (rc == -1) {
test_fail("could not close socket");
}
rc = unlink(TEST_TXT_FILE);
if (rc == -1) {
test_fail("could not unlink test.txt");
}
exit(errct);
}
void test_fd_passing_parent(int sd)
{
int rc, fd;
char x;
struct msghdr msghdr;
struct cmsghdr *cmsg;
struct iovec iov;
char buf[BUFSIZE];
memset(buf, '\0', BUFSIZE);
msghdr.msg_name = NULL;
msghdr.msg_namelen = 0;
iov.iov_base = &x;
iov.iov_len = 1;
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
msghdr.msg_control = buf;
msghdr.msg_controllen = BUFSIZE;
msghdr.msg_flags = 0;
rc = recvmsg(sd, &msghdr, 0);
if (rc == -1) {
test_fail("could not recv message.");
}
cmsg = CMSG_FIRSTHDR(&msghdr);
fd = ((int *) CMSG_DATA(cmsg))[0];
rc = write(fd, MSG, strlen(MSG));
if (rc != strlen(MSG)) {
test_fail("could not write the full message to test.txt");
}
rc = close(fd);
if (rc == -1) {
test_fail("could not close test.txt");
}
memset(buf, '\0', BUFSIZE);
strcpy(buf, "done");
rc = write(sd, buf, BUFSIZE);
if (rc == -1) {
test_fail("could not write to socket");
}
rc = close(sd);
if (rc == -1) {
test_fail("could not close socket");
}
}
void test_fd_passing(void) {
int status;
int sv[2];
pid_t pid;
int rc;
rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
if (rc == -1) {
test_fail("socketpair failed");
}
pid = fork();
if (pid == -1) {
test_fail("fork() failed");
rc = close(sv[0]);
if (rc == -1) {
test_fail("could not close sv[0]");
}
rc = close(sv[1]);
if (rc == -1) {
test_fail("could not close sv[1]");
}
exit(0);
} else if (pid == 0) {
rc = close(sv[0]);
if (rc == -1) {
test_fail("could not close sv[0]");
}
test_fd_passing_child(sv[1]);
test_fail("should never get here");
exit(1);
} else {
rc = close(sv[1]);
if (rc == -1) {
test_fail("could not close sv[1]");
}
test_fd_passing_parent(sv[0]);
/* wait for client to exit */
do {
errno = 0;
rc = waitpid(pid, &status, 0);
} while (rc == -1 && errno == EINTR);
/* we use the exit status to get its error count */
errct += WEXITSTATUS(status);
}
}
int main(int argc, char *argv[])
{
int i;
@ -1931,9 +2568,14 @@ int main(int argc, char *argv[])
}
test_msg_dgram();
test_connect();
test_multiproc_read();
test_multiproc_write();
test_scm_credentials();
test_fd_passing();
cleanup();
quit();
return -1; /* we should never get here */
}