xref: /freebsd/tools/test/stress2/misc/machipc2.sh (revision 8a272653)
1#!/bin/sh
2
3#
4# Copyright (c) 2015 EMC Corp.
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28
29# Non threaded Mach IPC test scenario
30# https://people.freebsd.org/~pho/stress/log/kip018.txt
31
32ps -p1 | grep -q launchd || exit 0
33
34odir=`pwd`
35cd /tmp
36sed '1,/^EOF/d' < $odir/$0 > machipc2.c
37# Test fails without the -lpthread. Need to investigate why.
38cc -o machipc2 -Wall -Wextra -O2 -g machipc2.c -lmach -lpthread || exit 1
39rm machipc2.c
40cd $odir
41
42(cd ../testcases/swap; ./swap -t 5m -i 20 -h -v) &
43sleep 5
44/tmp/machipc2
45pkill swap
46rm -f /tmp/machipc2
47exit 0
48EOF
49/*
50   Inspired by: Michael Weber: http://www.foldr.org/~michaelw/log/2009/03/13/
51 */
52
53#include <sys/types.h>
54#include <sys/wait.h>
55
56#include <mach/mach.h>
57#include <servers/bootstrap.h>
58
59#include <err.h>
60#include <stdlib.h>
61#include <stdio.h>
62#include <string.h>
63#include <unistd.h>
64
65#define MACH_MSG_TYPE_INTEGER_32 2
66#define N 200000000
67
68typedef struct {
69	unsigned int msgt_name : 8,
70		     msgt_size : 8,
71		     msgt_number : 12,
72		     msgt_inline : 1,
73		     msgt_longform : 1,
74		     msgt_deallocate : 1,
75		     msgt_unused : 1;
76} mach_msg_type_t;
77
78struct integer_message {
79	mach_msg_header_t head;
80	mach_msg_type_t type;
81
82	int inline_integer;
83};
84
85struct message_recv
86{
87	mach_msg_header_t head;
88	mach_msg_type_t type;
89	int inline_integer;
90	mach_msg_trailer_t trailer;
91};
92
93static task_t child_task = MACH_PORT_NULL;
94
95mach_port_t bootstrap_port;
96
97#define CHECK_MACH_ERROR(err, s) \
98	do { \
99		if (err != KERN_SUCCESS) { \
100			fprintf(stderr, "%s: %s", s, mach_error_string(err)); \
101			exit(1); \
102		} \
103	} while (0)
104
105static int
106setup_recv_port (mach_port_t *recv_port)
107{
108	kern_return_t kerr;
109	mach_port_t port = MACH_PORT_NULL;
110	kerr = mach_port_allocate (mach_task_self (),
111	    MACH_PORT_RIGHT_RECEIVE, &port);
112	CHECK_MACH_ERROR (kerr, "mach_port_allocate failed:");
113
114	kerr = mach_port_insert_right (mach_task_self (),
115	    port, port, MACH_MSG_TYPE_MAKE_SEND);
116	CHECK_MACH_ERROR (kerr, "mach_port_insert_right failed:");
117
118	*recv_port = port;
119
120	return (0);
121}
122
123static int
124send_port (mach_port_t remote_port, mach_port_t port)
125{
126	kern_return_t kerr;
127
128	struct {
129		mach_msg_header_t header;
130		mach_msg_body_t body;
131		mach_msg_port_descriptor_t task_port;
132	} msg;
133
134	msg.header.msgh_remote_port = remote_port;
135	msg.header.msgh_local_port = MACH_PORT_NULL;
136	msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0) |
137	    MACH_MSGH_BITS_COMPLEX;
138	msg.header.msgh_size = sizeof msg;
139
140	msg.body.msgh_descriptor_count = 1;
141	msg.task_port.name = port;
142	msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
143	msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
144
145	kerr = mach_msg_send (&msg.header);
146	CHECK_MACH_ERROR(kerr, "mach_msg_send failed:");
147
148	return (0);
149}
150
151static int
152recv_port(mach_port_t recv_port, mach_port_t *port)
153{
154	kern_return_t kerr;
155	struct {
156		mach_msg_header_t header;
157		mach_msg_body_t body;
158		mach_msg_port_descriptor_t task_port;
159		mach_msg_trailer_t trailer;
160	} msg;
161
162	kerr = mach_msg(&msg.header, MACH_RCV_MSG,
163	    0, sizeof msg, recv_port,
164	    MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
165	CHECK_MACH_ERROR(kerr, "mach_msg failed:");
166
167	*port = msg.task_port.name;
168	return (0);
169}
170
171void
172writeint(mach_port_t port)
173{
174	struct integer_message message;
175	int kerr, i;
176
177	/* Fill the header fields : */
178	message.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
179	message.head.msgh_size = sizeof( struct integer_message );
180	message.head.msgh_local_port = MACH_PORT_NULL;
181	message.head.msgh_remote_port = port;
182	message.head.msgh_id = 0;			/* Message id */
183	message.head.msgh_size = sizeof(message);	/* Message size */
184
185	/* Set the message type */
186	message.type.msgt_name = MACH_MSG_TYPE_INTEGER_32;
187	message.type.msgt_size = sizeof(message);
188	message.type.msgt_number = 1;
189	message.type.msgt_inline = TRUE;
190	message.type.msgt_longform = FALSE;
191	message.type.msgt_deallocate = FALSE;
192
193	for (i = 0; i < N; i++) {
194		message.inline_integer = i;
195
196		/* Send the message */
197		kerr = mach_msg(&message.head,	/* The header */
198		    MACH_SEND_MSG,		/* Flags */
199		    sizeof(message),		/* Send size */
200		    0,				/* Max receive Size */
201		    port,			/* Send port */
202		    MACH_MSG_TIMEOUT_NONE,	/* No timeout */
203		    MACH_PORT_NULL);		/* No notification */
204		if (kerr)
205			errx(1, "client mach_msg: %s", mach_error_string(kerr));
206	}
207}
208
209void
210readint(mach_port_t port)
211{
212	struct message_recv rmessage = {};
213	int kerr, i;
214
215	rmessage.head.msgh_local_port = port;
216	rmessage.head.msgh_size = sizeof(rmessage);
217
218	for (i = 0; i < N; i++) {
219		/* Receive a message */
220		kerr = mach_msg(&rmessage.head,	/* The header */
221		    MACH_RCV_MSG,		/* Flags */
222		    0,				/* Send size */
223		    sizeof(rmessage),		/* Max receive size */
224		    port,			/* Receive port */
225		    MACH_MSG_TIMEOUT_NONE,	/* No timeout */
226		    MACH_PORT_NULL);		/* No notification */
227		if (kerr)
228			errx(1, "client mach_msg MACH_RCV_MSG: %s", mach_error_string(kerr));
229		if (rmessage.inline_integer != i)
230			errx(1, "FAIL message.inline_integer = %d, i = %d",
231			    rmessage.inline_integer, i);
232	}
233}
234
235void
236sampling_fork(void)
237{
238	pid_t pid;
239	kern_return_t kerr;
240	mach_port_t parent_recv_port = MACH_PORT_NULL;
241	mach_port_t child_recv_port = MACH_PORT_NULL;
242
243	if (setup_recv_port(&parent_recv_port) != 0)
244		return;
245	kerr = task_set_bootstrap_port(mach_task_self(), parent_recv_port);
246	CHECK_MACH_ERROR(kerr, "task_set_bootstrap_port failed:");
247
248	if ((pid = fork()) == -1)
249		err(1, "fork");
250
251	if (pid == 0) {
252		kerr = task_get_bootstrap_port(mach_task_self(), &parent_recv_port);
253		CHECK_MACH_ERROR(kerr, "task_get_bootstrap_port failed:");
254		if (setup_recv_port(&child_recv_port) != 0)
255			return;
256		if (send_port(parent_recv_port, mach_task_self()) != 0)
257			return;
258		if (send_port(parent_recv_port, child_recv_port) != 0)
259			return;
260		if (recv_port(child_recv_port, &bootstrap_port) != 0)
261			return;
262		kerr = task_set_bootstrap_port(mach_task_self(), bootstrap_port);
263		CHECK_MACH_ERROR(kerr, "task_set_bootstrap_port failed:");
264
265		readint(child_recv_port);
266
267		_exit(0);
268	}
269
270	/* parent */
271	kerr = task_set_bootstrap_port(mach_task_self(), bootstrap_port);
272	CHECK_MACH_ERROR(kerr, "task_set_bootstrap_port failed:");
273	if (recv_port(parent_recv_port, &child_task) != 0)
274		return;
275	if (recv_port(parent_recv_port, &child_recv_port) != 0)
276		return;
277	if (send_port(child_recv_port, bootstrap_port) != 0)
278		return;
279	kerr = mach_port_deallocate(mach_task_self(), parent_recv_port);
280	CHECK_MACH_ERROR(kerr, "mach_port_deallocate failed:");
281
282	writeint(child_recv_port);
283}
284
285int
286main(void)
287{
288	sampling_fork();
289	wait(NULL);
290
291	return (0);
292}
293