[BACK]Return to t_sysv.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / tests / kernel

File: [cvs.NetBSD.org] / src / tests / kernel / t_sysv.c (download)

Revision 1.4, Sun Mar 2 20:13:12 2014 UTC (6 years, 11 months ago) by jmmv
Branch: MAIN
CVS Tags: yamt-pagecache-base9, tls-maxphys-base, tls-earlyentropy-base, tls-earlyentropy, riastradh-xf86-video-intel-2-7-1-pre-2-21-15, riastradh-drm2-base3, prg-localcount2-base3, prg-localcount2-base2, prg-localcount2-base1, prg-localcount2-base, prg-localcount2, pgoyette-localcount-base, pgoyette-localcount-20170426, pgoyette-localcount-20170320, pgoyette-localcount-20170107, pgoyette-localcount-20161104, pgoyette-localcount-20160806, pgoyette-localcount-20160726, pgoyette-localcount, perseant-stdc-iso10646-base, perseant-stdc-iso10646, netbsd-8-base, netbsd-8-2-RELEASE, netbsd-8-1-RELEASE, netbsd-8-1-RC1, netbsd-8-0-RELEASE, netbsd-8-0-RC2, netbsd-8-0-RC1, netbsd-8, netbsd-7-nhusb-base-20170116, netbsd-7-nhusb-base, netbsd-7-nhusb, netbsd-7-base, netbsd-7-2-RELEASE, netbsd-7-1-RELEASE, netbsd-7-1-RC2, netbsd-7-1-RC1, netbsd-7-1-2-RELEASE, netbsd-7-1-1-RELEASE, netbsd-7-1, netbsd-7-0-RELEASE, netbsd-7-0-RC3, netbsd-7-0-RC2, netbsd-7-0-RC1, netbsd-7-0-2-RELEASE, netbsd-7-0-1-RELEASE, netbsd-7-0, netbsd-7, matt-nb8-mediatek-base, matt-nb8-mediatek, localcount-20160914, bouyer-socketcan-base1, bouyer-socketcan-base, bouyer-socketcan
Changes since 1.3: +51 -13 lines

Make cleanup routines actually work.

The only way to pass global state from the body to the cleanup is via the
file system.

Fixes leaks of global system resources (in all cases, given that the body
does not by itself clean things up).

/*	$NetBSD: t_sysv.c,v 1.4 2014/03/02 20:13:12 jmmv Exp $	*/

/*-
 * Copyright (c) 1999, 2007 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center, and by Andrew Doran.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Test the SVID-compatible Message Queue facility.
 */

#include <atf-c.h>

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/param.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/wait.h>

volatile int did_sigsys, did_sigchild;
volatile int child_status, child_count;

void	sigsys_handler(int);
void	sigchld_handler(int);

key_t	get_ftok(int);

void	print_msqid_ds(struct msqid_ds *, mode_t);
void	receiver(void);

void	print_semid_ds(struct semid_ds *, mode_t);
void	waiter(void);

void	print_shmid_ds(struct shmid_ds *, mode_t);
void	sharer(void);

#define	MESSAGE_TEXT_LEN	256

struct mymsg {
	long	mtype;
	char	mtext[MESSAGE_TEXT_LEN];
};

const char *m1_str = "California is overrated.";
const char *m2_str = "The quick brown fox jumped over the lazy dog.";

size_t	pgsize;

#define	MTYPE_1		1
#define	MTYPE_1_ACK	2

#define	MTYPE_2		3
#define	MTYPE_2_ACK	4

pid_t	child_pid;

key_t	msgkey, semkey, shmkey;

int	maxloop = 1;

union semun {
	int	val;		/* value for SETVAL */
	struct	semid_ds *buf;	/* buffer for IPC_{STAT,SET} */
	u_short	*array;		/* array for GETALL & SETALL */
};


/* Writes an integer to a file.  To be used from the body of the test
 * cases below to pass any global identifiers to the cleanup routine. */
static void
write_int(const char *path, const int value)
{
	int output;

	output = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
	ATF_REQUIRE_MSG(output != -1, "Failed to create %s", path);
	write(output, &value, sizeof(value));
	close(output);
}


/* Reads an integer from a file.  To be used from the cleanup routines
 * of the test cases below. */
static int
read_int(const char *path)
{
	int input;

	input = open(path, O_RDONLY);
	if (input == -1)
		return -1;
	else {
		int value;
		read(input, &value, sizeof(value));
		return value;
	}
}


void
sigsys_handler(int signo)
{

	did_sigsys = 1;
}

void
sigchld_handler(int signo)
{
	int c_status;

	did_sigchild = 1;
	/*
	 * Reap the child and return its status
	 */
	if (wait(&c_status) == -1)
		child_status = -errno;
	else
		child_status = c_status;

	child_count--;
}

key_t get_ftok(int id)
{
	int fd;
	char token_key[64], token_dir[64];
	char *tmpdir;
	key_t key;

	strlcpy(token_key, "/tmp/t_sysv.XXXXXX", sizeof(token_key));
	tmpdir = mkdtemp(token_key);
	ATF_REQUIRE_MSG(tmpdir != NULL, "mkdtemp() failed: %d", errno);

	strlcpy(token_dir, tmpdir, sizeof(token_dir));
	strlcpy(token_key, tmpdir, sizeof(token_key));
	strlcat(token_key, "/token_key", sizeof(token_key));

	/* Create the file, since ftok() requires it to exist! */

	fd = open(token_key, O_RDWR | O_CREAT | O_EXCL);
	if (fd == -1) {
		rmdir(tmpdir);
		atf_tc_fail("open() of temp file failed: %d", errno);
		return (key_t)-1;
	} else
		close(fd);

	key = ftok(token_key, id);

	ATF_REQUIRE_MSG(unlink(token_key) != -1, "unlink() failed: %d", errno);
	ATF_REQUIRE_MSG(rmdir(token_dir) != -1, "rmdir() failed: %d", errno);

	return key;
}

ATF_TC_WITH_CLEANUP(msg);
ATF_TC_HEAD(msg, tc)
{  

	atf_tc_set_md_var(tc, "timeout", "3");
	atf_tc_set_md_var(tc, "descr", "Checks sysvmsg passing");
}

ATF_TC_BODY(msg, tc)
{
	struct sigaction sa;
	struct msqid_ds m_ds;
	struct mymsg m;
	sigset_t sigmask;
	int sender_msqid;
	int loop;
	int c_status;

	/*
	 * Install a SIGSYS handler so that we can exit gracefully if
	 * System V Message Queue support isn't in the kernel.
	 */
	did_sigsys = 0;
	sa.sa_handler = sigsys_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	ATF_REQUIRE_MSG(sigaction(SIGSYS, &sa, NULL) != -1,
	    "sigaction SIGSYS: %d", errno);

	/*
	 * Install a SIGCHLD handler to deal with all possible exit
	 * conditions of the receiver.
	 */
	did_sigchild = 0;
	child_count = 0;
	sa.sa_handler = sigchld_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	ATF_REQUIRE_MSG(sigaction(SIGCHLD, &sa, NULL) != -1,
	    "sigaction SIGCHLD: %d", errno);

	msgkey = get_ftok(4160);
	ATF_REQUIRE_MSG(msgkey != (key_t)-1, "get_ftok failed");

	sender_msqid = msgget(msgkey, IPC_CREAT | 0640);
	ATF_REQUIRE_MSG(sender_msqid != -1, "msgget: %d", errno);
	write_int("sender_msqid", sender_msqid);

	if (did_sigsys) {
		atf_tc_skip("SYSV Message Queue not supported");
		return;
	}

	ATF_REQUIRE_MSG(msgctl(sender_msqid, IPC_STAT, &m_ds) != -1,
	"msgctl IPC_STAT 1: %d", errno);

	print_msqid_ds(&m_ds, 0640);

	m_ds.msg_perm.mode = (m_ds.msg_perm.mode & ~0777) | 0600;

	ATF_REQUIRE_MSG(msgctl(sender_msqid, IPC_SET, &m_ds) != -1,
	    "msgctl IPC_SET: %d", errno);

	memset(&m_ds, 0, sizeof(m_ds));

	ATF_REQUIRE_MSG(msgctl(sender_msqid, IPC_STAT, &m_ds) != -1,
	    "msgctl IPC_STAT 2: %d", errno);

	ATF_REQUIRE_MSG((m_ds.msg_perm.mode & 0777) == 0600,
	    "IPC_SET of mode didn't hold");

	print_msqid_ds(&m_ds, 0600);

	switch ((child_pid = fork())) {
	case -1:
		atf_tc_fail("fork: %d", errno);
		return;

	case 0:
		child_count++;
		receiver();
		break;

	default:
		break;
	}

	for (loop = 0; loop < maxloop; loop++) {
		/*
		 * Send the first message to the receiver and wait for the ACK.
		 */
		m.mtype = MTYPE_1;
		strcpy(m.mtext, m1_str);
		ATF_REQUIRE_MSG(msgsnd(sender_msqid, &m, MESSAGE_TEXT_LEN,
		    0) != -1, "sender: msgsnd 1: %d", errno);

		ATF_REQUIRE_MSG(msgrcv(sender_msqid, &m, MESSAGE_TEXT_LEN,
				       MTYPE_1_ACK, 0) == MESSAGE_TEXT_LEN,
		    "sender: msgrcv 1 ack: %d", errno);

		print_msqid_ds(&m_ds, 0600);

		/*
		 * Send the second message to the receiver and wait for the ACK.
		 */
		m.mtype = MTYPE_2;
		strcpy(m.mtext, m2_str);
		ATF_REQUIRE_MSG(msgsnd(sender_msqid, &m, MESSAGE_TEXT_LEN, 0) != -1,
		    "sender: msgsnd 2: %d", errno);

		ATF_REQUIRE_MSG(msgrcv(sender_msqid, &m, MESSAGE_TEXT_LEN,
				       MTYPE_2_ACK, 0) == MESSAGE_TEXT_LEN,
		    "sender: msgrcv 2 ack: %d", errno);
	}

	/*
	 * Wait for child to finish
	 */
	sigemptyset(&sigmask);
	(void) sigsuspend(&sigmask);

	/*
	 * ...and any other signal is an unexpected error.
	 */
	if (did_sigchild) {
		c_status = child_status;
		if (c_status < 0)
			atf_tc_fail("waitpid: %d", -c_status);
		else if (WIFEXITED(c_status) == 0)
			atf_tc_fail("child abnormal exit: %d", c_status);
		else if (WEXITSTATUS(c_status) != 0)
			atf_tc_fail("c status: %d", WEXITSTATUS(c_status));
		else {
			ATF_REQUIRE_MSG(msgctl(sender_msqid, IPC_STAT, &m_ds)
			    != -1, "msgctl IPC_STAT: %d", errno);

			print_msqid_ds(&m_ds, 0600);
			atf_tc_pass();
		}
	} else
		atf_tc_fail("sender: received unexpected signal");
}

ATF_TC_CLEANUP(msg, tc)
{
	int sender_msqid;

	/*
	 * Remove the message queue if it exists.
	 */
	sender_msqid = read_int("sender_msqid");
	if (sender_msqid != -1)
		if (msgctl(sender_msqid, IPC_RMID, NULL) == -1)
			err(1, "msgctl IPC_RMID");
}

void
print_msqid_ds(mp, mode)
	struct msqid_ds *mp;
	mode_t mode;
{
	uid_t uid = geteuid();
	gid_t gid = getegid();

	printf("PERM: uid %d, gid %d, cuid %d, cgid %d, mode 0%o\n",
	    mp->msg_perm.uid, mp->msg_perm.gid,
	    mp->msg_perm.cuid, mp->msg_perm.cgid,
	    mp->msg_perm.mode & 0777);

	printf("qnum %lu, qbytes %lu, lspid %d, lrpid %d\n",
	    mp->msg_qnum, (u_long)mp->msg_qbytes, mp->msg_lspid,
	    mp->msg_lrpid);

	printf("stime: %s", ctime(&mp->msg_stime));
	printf("rtime: %s", ctime(&mp->msg_rtime));
	printf("ctime: %s", ctime(&mp->msg_ctime));

	/*
	 * Sanity check a few things.
	 */

	ATF_REQUIRE_MSG(mp->msg_perm.uid == uid && mp->msg_perm.cuid == uid,
	    "uid mismatch");

	ATF_REQUIRE_MSG(mp->msg_perm.gid == gid && mp->msg_perm.cgid == gid,
	    "gid mismatch");

	ATF_REQUIRE_MSG((mp->msg_perm.mode & 0777) == mode, "mode mismatch");
}

void
receiver()
{
	struct mymsg m;
	int msqid, loop;

	if ((msqid = msgget(msgkey, 0)) == -1)
		err(1, "receiver: msgget");

	for (loop = 0; loop < maxloop; loop++) {
		/*
		 * Receive the first message, print it, and send an ACK.
		 */
		if (msgrcv(msqid, &m, MESSAGE_TEXT_LEN, MTYPE_1, 0) != MESSAGE_TEXT_LEN)
			err(1, "receiver: msgrcv 1");

		printf("%s\n", m.mtext);
		if (strcmp(m.mtext, m1_str) != 0)
			err(1, "receiver: message 1 data isn't correct");

		m.mtype = MTYPE_1_ACK;

		if (msgsnd(msqid, &m, MESSAGE_TEXT_LEN, 0) == -1)
			err(1, "receiver: msgsnd ack 1");

		/*
		 * Receive the second message, print it, and send an ACK.
		 */

		if (msgrcv(msqid, &m, MESSAGE_TEXT_LEN, MTYPE_2, 0) != MESSAGE_TEXT_LEN)
			err(1, "receiver: msgrcv 2");

		printf("%s\n", m.mtext);
		if (strcmp(m.mtext, m2_str) != 0)
			err(1, "receiver: message 2 data isn't correct");

		m.mtype = MTYPE_2_ACK;

		if (msgsnd(msqid, &m, MESSAGE_TEXT_LEN, 0) == -1)
			err(1, "receiver: msgsnd ack 2");
	}

	exit(0);
}

/*
 * Test the SVID-compatible Semaphore facility.
 */

ATF_TC_WITH_CLEANUP(sem);
ATF_TC_HEAD(sem, tc)
{  

	atf_tc_set_md_var(tc, "timeout", "3");
	atf_tc_set_md_var(tc, "descr", "Checks sysvmsg passing");
}

ATF_TC_BODY(sem, tc)
{
	struct sigaction sa;
	union semun sun;
	struct semid_ds s_ds;
	sigset_t sigmask;
	int sender_semid;
	int i;
	int c_status;

	/*
	 * Install a SIGSYS handler so that we can exit gracefully if
	 * System V Semaphore support isn't in the kernel.
	 */
	did_sigsys = 0;
	sa.sa_handler = sigsys_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	ATF_REQUIRE_MSG(sigaction(SIGSYS, &sa, NULL) != -1,
	    "sigaction SIGSYS: %d", errno);

	/*
	 * Install a SIGCHLD handler to deal with all possible exit
	 * conditions of the receiver.
	 */
	did_sigchild = 0;
	child_count = 0;
	sa.sa_handler = sigchld_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	ATF_REQUIRE_MSG(sigaction(SIGCHLD, &sa, NULL) != -1,
	    "sigaction SIGCHLD: %d", errno);

	semkey = get_ftok(4160);
	ATF_REQUIRE_MSG(semkey != (key_t)-1, "get_ftok failed");

	sender_semid = semget(semkey, 1, IPC_CREAT | 0640);
	ATF_REQUIRE_MSG(sender_semid != -1, "semget: %d", errno);
	write_int("sender_semid", sender_semid);

	if (did_sigsys) {
		atf_tc_skip("SYSV Semaphore not supported");
		return;
	}
	
	sun.buf = &s_ds;
	ATF_REQUIRE_MSG(semctl(sender_semid, 0, IPC_STAT, sun) != -1,
	    "semctl IPC_STAT: %d", errno);

	print_semid_ds(&s_ds, 0640);

	s_ds.sem_perm.mode = (s_ds.sem_perm.mode & ~0777) | 0600;

	sun.buf = &s_ds;
	ATF_REQUIRE_MSG(semctl(sender_semid, 0, IPC_SET, sun) != -1,
	    "semctl IPC_SET: %d", errno);

	memset(&s_ds, 0, sizeof(s_ds));

	sun.buf = &s_ds;
	ATF_REQUIRE_MSG(semctl(sender_semid, 0, IPC_STAT, sun) != -1,
	    "semctl IPC_STAT: %d", errno);

	ATF_REQUIRE_MSG((s_ds.sem_perm.mode & 0777) == 0600,
	    "IPC_SET of mode didn't hold");

	print_semid_ds(&s_ds, 0600);

	for (child_count = 0; child_count < 5; child_count++) {
		switch ((child_pid = fork())) {
		case -1:
			atf_tc_fail("fork: %d", errno);
			return;

		case 0:
			waiter();
			break;

		default:
			break;
		}
	}

	/*
	 * Wait for all of the waiters to be attempting to acquire the
	 * semaphore.
	 */
	for (;;) {
		i = semctl(sender_semid, 0, GETNCNT);
		if (i == -1)
			atf_tc_fail("semctl GETNCNT: %d", i);
		if (i == 5)
			break;
	}

	/*
	 * Now set the thundering herd in motion by initializing the
	 * semaphore to the value 1.
	 */
	sun.val = 1;
	ATF_REQUIRE_MSG(semctl(sender_semid, 0, SETVAL, sun) != -1,
	    "sender: semctl SETVAL to 1: %d", errno);

	/*
	 * Wait for all children to finish
	 */
	sigemptyset(&sigmask);
	for (;;) {
		(void) sigsuspend(&sigmask);
		if (did_sigchild) {
			c_status = child_status;
			if (c_status < 0)
				atf_tc_fail("waitpid: %d", -c_status);
			else if (WIFEXITED(c_status) == 0)
				atf_tc_fail("c abnormal exit: %d", c_status);
			else if (WEXITSTATUS(c_status) != 0)
				atf_tc_fail("c status: %d",
				    WEXITSTATUS(c_status));
			else {
				sun.buf = &s_ds;
				ATF_REQUIRE_MSG(semctl(sender_semid, 0,
						    IPC_STAT, sun) != -1,
				    "semctl IPC_STAT: %d", errno);

				print_semid_ds(&s_ds, 0600);
				atf_tc_pass();
			}
			if (child_count <= 0)
				break;
			did_sigchild = 0;
		} else {
			atf_tc_fail("sender: received unexpected signal");
			break;
		}
	}
}

ATF_TC_CLEANUP(sem, tc)
{
	int sender_semid;

	/*
	 * Remove the semaphore if it exists
	 */
	sender_semid = read_int("sender_semid");
	if (sender_semid != -1)
		if (semctl(sender_semid, 0, IPC_RMID) == -1)
			err(1, "semctl IPC_RMID");
}

void
print_semid_ds(sp, mode)
	struct semid_ds *sp;
	mode_t mode;
{
	uid_t uid = geteuid();
	gid_t gid = getegid();

	printf("PERM: uid %d, gid %d, cuid %d, cgid %d, mode 0%o\n",
	    sp->sem_perm.uid, sp->sem_perm.gid,
	    sp->sem_perm.cuid, sp->sem_perm.cgid,
	    sp->sem_perm.mode & 0777);

	printf("nsems %u\n", sp->sem_nsems);

	printf("otime: %s", ctime(&sp->sem_otime));
	printf("ctime: %s", ctime(&sp->sem_ctime));

	/*
	 * Sanity check a few things.
	 */

	ATF_REQUIRE_MSG(sp->sem_perm.uid == uid && sp->sem_perm.cuid == uid,
	    "uid mismatch");

	ATF_REQUIRE_MSG(sp->sem_perm.gid == gid && sp->sem_perm.cgid == gid,
	    "gid mismatch");

	ATF_REQUIRE_MSG((sp->sem_perm.mode & 0777) == mode,
	    "mode mismatch %o != %o", (sp->sem_perm.mode & 0777), mode);
}

void
waiter()
{
	struct sembuf s;
	int semid;

	if ((semid = semget(semkey, 1, 0)) == -1)
		err(1, "waiter: semget");

	/*
	 * Attempt to acquire the semaphore.
	 */
	s.sem_num = 0;
	s.sem_op = -1;
	s.sem_flg = SEM_UNDO;

	if (semop(semid, &s, 1) == -1)
		err(1, "waiter: semop -1");

	printf("WOO!  GOT THE SEMAPHORE!\n");
	sleep(1);

	/*
	 * Release the semaphore and exit.
	 */
	s.sem_num = 0;
	s.sem_op = 1;
	s.sem_flg = SEM_UNDO;

	if (semop(semid, &s, 1) == -1)
		err(1, "waiter: semop +1");

	exit(0);
}

/*
 * Test the SVID-compatible Shared Memory facility.
 */

ATF_TC_WITH_CLEANUP(shm);
ATF_TC_HEAD(shm, tc)
{  

	atf_tc_set_md_var(tc, "timeout", "3");
	atf_tc_set_md_var(tc, "descr", "Checks sysv shared memory");
}

ATF_TC_BODY(shm, tc)
{
	struct sigaction sa;
	struct shmid_ds s_ds;
	sigset_t sigmask;
	char *shm_buf;
	int sender_shmid;
	int c_status;

	/*
	 * Install a SIGSYS handler so that we can exit gracefully if
	 * System V Shared Memory support isn't in the kernel.
	 */
	did_sigsys = 0;
	sa.sa_handler = sigsys_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	ATF_REQUIRE_MSG(sigaction(SIGSYS, &sa, NULL) != -1,
	    "sigaction SIGSYS: %d", errno);

	/*
	 * Install a SIGCHLD handler to deal with all possible exit
	 * conditions of the sharer.
	 */
	did_sigchild = 0;
	child_count = 0;
	sa.sa_handler = sigchld_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	ATF_REQUIRE_MSG(sigaction(SIGCHLD, &sa, NULL) != -1,
	    "sigaction SIGCHLD: %d", errno);

	pgsize = sysconf(_SC_PAGESIZE);

	shmkey = get_ftok(4160);
	ATF_REQUIRE_MSG(shmkey != (key_t)-1, "get_ftok failed");

	ATF_REQUIRE_MSG((sender_shmid = shmget(shmkey, pgsize,
					       IPC_CREAT | 0640)) != -1,
	    "shmget: %d", errno);
	write_int("sender_shmid", sender_shmid);

	ATF_REQUIRE_MSG(shmctl(sender_shmid, IPC_STAT, &s_ds) != -1,
	    "shmctl IPC_STAT: %d", errno);

	print_shmid_ds(&s_ds, 0640);

	s_ds.shm_perm.mode = (s_ds.shm_perm.mode & ~0777) | 0600;

	ATF_REQUIRE_MSG(shmctl(sender_shmid, IPC_SET, &s_ds) != -1,
	    "shmctl IPC_SET: %d", errno);

	memset(&s_ds, 0, sizeof(s_ds));

	ATF_REQUIRE_MSG(shmctl(sender_shmid, IPC_STAT, &s_ds) != -1,
	    "shmctl IPC_STAT: %d", errno);

	ATF_REQUIRE_MSG((s_ds.shm_perm.mode & 0777) == 0600,
	    "IPC_SET of mode didn't hold");

	print_shmid_ds(&s_ds, 0600);

	shm_buf = shmat(sender_shmid, NULL, 0);
	ATF_REQUIRE_MSG(shm_buf != (void *) -1, "sender: shmat: %d", errno);

	/*
	 * Write the test pattern into the shared memory buffer.
	 */
	strcpy(shm_buf, m2_str);

	switch ((child_pid = fork())) {
	case -1:
		atf_tc_fail("fork: %d", errno);
		return;

	case 0:
		sharer();
		break;

	default:
		break;
	}

	/*
	 * Wait for child to finish
	 */
	sigemptyset(&sigmask);
	(void) sigsuspend(&sigmask);

	if (did_sigchild) {
		c_status = child_status;
		if (c_status < 0)
			atf_tc_fail("waitpid: %d", -c_status);
		else if (WIFEXITED(c_status) == 0)
			atf_tc_fail("c abnormal exit: %d", c_status);
		else if (WEXITSTATUS(c_status) != 0)
			atf_tc_fail("c status: %d", WEXITSTATUS(c_status));
		else {
			ATF_REQUIRE_MSG(shmctl(sender_shmid, IPC_STAT,
					       &s_ds) != -1,
			    "shmctl IPC_STAT: %d", errno);

			print_shmid_ds(&s_ds, 0600);
			atf_tc_pass();
		}
	} else
		atf_tc_fail("sender: received unexpected signal");
}

ATF_TC_CLEANUP(shm, tc)
{
	int sender_shmid;

	/*
	 * Remove the shared memory area if it exists.
	 */
	sender_shmid = read_int("sender_shmid");
	if (sender_shmid != -1)
		if (shmctl(sender_shmid, IPC_RMID, NULL) == -1)
			err(1, "shmctl IPC_RMID");
}

void
print_shmid_ds(sp, mode)
	struct shmid_ds *sp;
	mode_t mode;
{
	uid_t uid = geteuid();
	gid_t gid = getegid();

	printf("PERM: uid %d, gid %d, cuid %d, cgid %d, mode 0%o\n",
	    sp->shm_perm.uid, sp->shm_perm.gid,
	    sp->shm_perm.cuid, sp->shm_perm.cgid,
	    sp->shm_perm.mode & 0777);

	printf("segsz %lu, lpid %d, cpid %d, nattch %u\n",
	    (u_long)sp->shm_segsz, sp->shm_lpid, sp->shm_cpid,
	    sp->shm_nattch);

	printf("atime: %s", ctime(&sp->shm_atime));
	printf("dtime: %s", ctime(&sp->shm_dtime));
	printf("ctime: %s", ctime(&sp->shm_ctime));

	/*
	 * Sanity check a few things.
	 */

	ATF_REQUIRE_MSG(sp->shm_perm.uid == uid && sp->shm_perm.cuid == uid,
	    "uid mismatch");

	ATF_REQUIRE_MSG(sp->shm_perm.gid == gid && sp->shm_perm.cgid == gid,
	    "gid mismatch");

	ATF_REQUIRE_MSG((sp->shm_perm.mode & 0777) == mode, "mode mismatch");
}

void
sharer()
{
	int shmid;
	void *shm_buf;

	shmid = shmget(shmkey, pgsize, 0);
	ATF_REQUIRE_MSG(shmid != -1, "receiver: shmget:%d", errno);

	shm_buf = shmat(shmid, NULL, 0);
	ATF_REQUIRE_MSG(shm_buf != (void *) -1, "receiver: shmat: %d", errno);

	printf("%s\n", (const char *)shm_buf);
	
	ATF_REQUIRE_MSG(strcmp((const char *)shm_buf, m2_str) == 0,
	    "receiver: data isn't correct");

	exit(0);
}

ATF_TP_ADD_TCS(tp)
{

	ATF_TP_ADD_TC(tp, msg); 
	ATF_TP_ADD_TC(tp, sem); 
	ATF_TP_ADD_TC(tp, shm); 

	return atf_no_error();
}