1/*
2 * Copyright (c) International Business Machines  Corp., 2012
3 * Copyright (c) Linux Test Project, 2012
4 *
5 * This program is free software;  you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY;  without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program;  if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#define _GNU_SOURCE
21#include <sys/types.h>
22#include <sys/uio.h>
23#include <sys/wait.h>
24#include <errno.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29
30#include "test.h"
31#include "safe_macros.h"
32#include "process_vm.h"
33
34char *TCID = "process_vm_readv02";
35int TST_TOTAL = 1;
36
37static char *tst_string = "THIS IS A TEST";
38static int len;
39static int pipe_fd[2];
40static pid_t pids[2];
41static int semid;
42
43static void child_alloc(void);
44static void child_invoke(void);
45static void setup(void);
46static void cleanup(void);
47
48int main(int argc, char **argv)
49{
50	int lc, status;
51
52	tst_parse_opts(argc, argv, NULL, NULL);
53
54	setup();
55	for (lc = 0; TEST_LOOPING(lc); lc++) {
56		tst_count = 0;
57		len = strlen(tst_string);
58
59		if (pipe(pipe_fd) < 0)
60			tst_brkm(TBROK | TERRNO, cleanup, "pipe");
61
62		/* the start of child_alloc and child_invoke is already
63		 * synchronized via pipe */
64		pids[0] = fork();
65		switch (pids[0]) {
66		case -1:
67			tst_brkm(TBROK | TERRNO, cleanup, "fork #0");
68		case 0:
69			child_alloc();
70			exit(0);
71		}
72
73		pids[1] = fork();
74		switch (pids[1]) {
75		case -1:
76			tst_brkm(TBROK | TERRNO, cleanup, "fork #1");
77		case 0:
78			child_invoke();
79			exit(0);
80		}
81
82		/* wait until child_invoke reads from child_alloc's VM */
83		if (waitpid(pids[1], &status, 0) == -1)
84			tst_brkm(TBROK | TERRNO, cleanup, "waitpid");
85		if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
86			tst_resm(TFAIL, "child 1 returns %d", status);
87
88		/* child_alloc is free to exit now */
89		safe_semop(semid, 0, 1);
90
91		if (waitpid(pids[0], &status, 0) == -1)
92			tst_brkm(TBROK | TERRNO, cleanup, "waitpid");
93		if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
94			tst_resm(TFAIL, "child 0 returns %d", status);
95	}
96
97	cleanup();
98	tst_exit();
99}
100
101static void child_alloc(void)
102{
103	char *foo;
104	char buf[BUFSIZ];
105
106	foo = SAFE_MALLOC(tst_exit, len + 1);
107	strncpy(foo, tst_string, len);
108	foo[len] = '\0';
109	tst_resm(TINFO, "child 0: memory allocated and initialized.");
110
111	/* passing addr of string "foo" via pipe */
112	SAFE_CLOSE(tst_exit, pipe_fd[0]);
113	snprintf(buf, BUFSIZ, "%p", foo);
114	SAFE_WRITE(tst_exit, 1, pipe_fd[1], buf, strlen(buf));
115	SAFE_CLOSE(tst_exit, pipe_fd[1]);
116
117	/* wait until child_invoke is done reading from our VM */
118	safe_semop(semid, 0, -1);
119}
120
121static void child_invoke(void)
122{
123	char *lp, *rp;
124	char buf[BUFSIZ];
125	struct iovec local, remote;
126
127	/* get addr from pipe */
128	SAFE_CLOSE(tst_exit, pipe_fd[1]);
129	SAFE_READ(tst_exit, 0, pipe_fd[0], buf, BUFSIZ);
130	SAFE_CLOSE(tst_exit, pipe_fd[0]);
131	if (sscanf(buf, "%p", &rp) != 1)
132		tst_brkm(TBROK | TERRNO, tst_exit, "sscanf");
133
134	lp = SAFE_MALLOC(tst_exit, len + 1);
135	local.iov_base = lp;
136	local.iov_len = len;
137	remote.iov_base = rp;
138	remote.iov_len = len;
139
140	tst_resm(TINFO, "child 1: reading string from same memory location.");
141	TEST(test_process_vm_readv(pids[0], &local, 1, &remote, 1, 0));
142	if (TEST_RETURN != len)
143		tst_brkm(TFAIL | TERRNO, tst_exit, "process_vm_readv");
144	if (strncmp(lp, tst_string, len) != 0)
145		tst_brkm(TFAIL, tst_exit, "child 1: expected string: %s, "
146			 "received string: %256s", tst_string, lp);
147	else
148		tst_resm(TPASS, "expected string received.");
149}
150
151static void setup(void)
152{
153	tst_require_root();
154
155#if !defined(__NR_process_vm_readv)
156	tst_brkm(TCONF, NULL, "process_vm_readv does not exist "
157		 "on your system");
158#endif
159	semid = init_sem(1);
160
161	TEST_PAUSE;
162}
163
164static void cleanup(void)
165{
166	clean_sem(semid);
167}
168