1 /*
2  * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it would be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * Further, this software is distributed without any warranty that it is
13  * free of the rightful claim of any third person regarding infringement
14  * or the like.  Any license provided herein, whether implied or
15  * otherwise, applies only to this software file.  Patent licenses, if
16  * any, provided herein do not apply to combinations of this program with
17  * other software, or any other product whatsoever.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24  * Mountain View, CA  94043, or:
25  *
26  * http://www.sgi.com
27  *
28  * For further information regarding this notice, see:
29  *
30  * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
31  *
32  *
33  *    OS Test - Silicon Graphics, Inc.
34  *    TEST IDENTIFIER	: fork04
35  *    TEST TITLE	: Child inheritance of Environment Variables after fork()
36  *    PARENT DOCUMENT	: frktds01
37  *    TEST CASE TOTAL	: 3
38  *    WALL CLOCK TIME	: 1
39  *    CPU TYPES		: ALL
40  *    AUTHOR		: Kathy Olmsted
41  *    CO-PILOT		: Steve Shaw
42  *    DATE STARTED	: 06/17/92
43  *    INITIAL RELEASE	: UNICOS 7.0
44  *
45  *    TEST CASES
46  *       Test these environment variables correctly inherited by child:
47  *       1. TERM
48  *       2. NoTSetzWq
49  *       3. TESTPROG
50  *
51  *    INPUT SPECIFICATIONS
52  * 	The standard options for system call tests are accepted.
53  *	(See the parse_opts(3) man page).
54  *
55  *    DURATION
56  * 	Terminates - with frequency and infinite modes.
57  *
58  *    SIGNALS
59  * 	Uses SIGUSR1 to pause before test if option set.
60  * 	(See the parse_opts(3) man page).
61  *
62  *    ENVIRONMENTAL NEEDS
63  *      No run-time environmental needs.
64  *
65  *    DETAILED DESCRIPTION
66  *
67  * 	Setup:
68  * 	  Setup signal handling.
69  *        Make and change to a temporary directory.
70  *	  Pause for SIGUSR1 if option specified.
71  *        Add TESTPROG variable to the environment
72  *
73  * 	Test:
74  *	 Loop if the proper options are given.
75  *	 fork()
76  *	 Check return code, if system call failed (return=-1)
77  *		Log the errno
78  *	   CHILD:
79  *              open a temp file
80  *		Determine environment values and write to file
81  *		close file containing test values.
82  *		exit.
83  *	    PARENT:
84  *		Wait for child to exit.
85  *              Verify exit status
86  *		Open file containing test values.
87  *		For each test case:
88  *			Read the value from the file.
89  *			Determine and report PASS/FAIL result.
90  *
91  * 	Cleanup:
92  * 	  Print errno log and/or timing stats if options given
93  *        Remove the temporary directory and exit.
94  */
95 
96 #include <stdlib.h>
97 #include <sys/types.h>
98 #include <sys/wait.h>
99 #include <unistd.h>
100 #include <fcntl.h>
101 #include <string.h>
102 #include <sys/param.h>
103 #include <signal.h>
104 #include <errno.h>
105 #include "test.h"
106 #include "safe_macros.h"
107 
108 char *TCID = "fork04";
109 
110 #define	KIDEXIT	42
111 #define MAX_LINE_LENGTH 256
112 #define OUTPUT_FILE  "env.out"
113 #define ENV_NOT_SET  "getenv() does not find variable set"
114 
115 /* list of environment variables to test */
116 char *environ_list[] = { "TERM", "NoTSetzWq", "TESTPROG" };
117 
118 #define NUMBER_OF_ENVIRON (sizeof(environ_list)/sizeof(char *))
119 int TST_TOTAL = NUMBER_OF_ENVIRON;
120 
121 static void cleanup(void)
122 {
123 	tst_rmdir();
124 }
125 
126 static void setup(void)
127 {
128 
129 	tst_sig(FORK, DEF_HANDLER, cleanup);
130 	TEST_PAUSE;
131 	tst_tmpdir();
132 
133 	/* add a variable to the environment */
134 	putenv("TESTPROG=FRKTCS04");
135 }
136 
137 static void child_environment(void)
138 {
139 
140 	int fildes;
141 	int index;
142 	char msg[MAX_LINE_LENGTH];
143 	char *var;
144 
145 	fildes = creat(OUTPUT_FILE, 0700);
146 
147 	for (index = 0; index < NUMBER_OF_ENVIRON; index++) {
148 		memset(msg, 0, MAX_LINE_LENGTH);
149 
150 		var = getenv(environ_list[index]);
151 		if (var == NULL)
152 			(void)sprintf(msg, "%s:%s", environ_list[index],
153 				      ENV_NOT_SET);
154 		else
155 			(void)sprintf(msg, "%s:%s", environ_list[index], var);
156 		/* includes extra null chars */
157 		write(fildes, msg, sizeof(msg));
158 	}
159 
160 	close(fildes);
161 }
162 
163 /*
164  * Compare parent env string to child's string.
165  * Each string is in the format:  <env var>:<value>
166  */
167 static int cmp_env_strings(char *pstring, char *cstring)
168 {
169 	char *penv, *cenv, *pvalue, *cvalue;
170 
171 	/*
172 	 * Break pstring into env and value
173 	 */
174 	penv = pstring;
175 	pvalue = strchr(pstring, ':');
176 	if (pvalue == NULL) {
177 		tst_resm(TBROK,
178 			 "internal error - parent's env string not in correct format:'%s'",
179 			 pstring);
180 		return -1;
181 	} else {
182 		*pvalue = '\0';
183 		pvalue++;
184 		if (*pvalue == '\0') {
185 			tst_resm(TBROK,
186 				 "internal error - missing parent's env value");
187 			return -1;
188 		}
189 	}
190 
191 	/*
192 	 * Break cstring into env and value
193 	 */
194 	cenv = cstring;
195 	cvalue = strchr(cstring, ':');
196 	if (cvalue == NULL) {
197 		tst_resm(TBROK,
198 			 "internal error - parent's env string not in correct format:'%s'",
199 			 cstring);
200 		return -1;
201 	} else {
202 		*cvalue = '\0';
203 		cvalue++;
204 		if (*cvalue == '\0') {
205 			tst_resm(TBROK,
206 				 "internal error - missing child's env value");
207 			return -1;
208 		}
209 	}
210 
211 	if (strcmp(penv, cenv) != 0) {
212 		tst_resm(TBROK, "internal error - parent(%s) != child (%s) env",
213 			 penv, cenv);
214 		return -1;
215 	}
216 
217 	if (strcmp(pvalue, cvalue) != 0) {
218 		tst_resm(TFAIL,
219 			 "Env var %s changed after fork(), parent's %s, child's %s",
220 			 penv, pvalue, cvalue);
221 	} else {
222 		tst_resm(TPASS, "Env var %s unchanged after fork(): %s",
223 			 penv, cvalue);
224 	}
225 	return 0;
226 
227 }
228 
229 /***************************************************************
230  * parent_environment - the parent side of the environment tests
231  *        determine values for the variables
232  *        read the values determined by the child
233  *        compare values
234  ***************************************************************/
235 void parent_environment(void)
236 {
237 
238 	int fildes;
239 	char tmp_line[MAX_LINE_LENGTH];
240 	char parent_value[MAX_LINE_LENGTH];
241 	int index;
242 	int ret;
243 	char *var;
244 
245 	fildes = SAFE_OPEN(cleanup, OUTPUT_FILE, O_RDWR);
246 	for (index = 0; index < NUMBER_OF_ENVIRON; index++) {
247 		ret = read(fildes, tmp_line, MAX_LINE_LENGTH);
248 		if (ret == 0) {
249 			tst_resm(TBROK,
250 				 "fork() test. parent_environment: failed to read from file with %d (%s)",
251 				 errno, strerror(errno));
252 		} else {
253 
254 			var = getenv(environ_list[index]);
255 			if (var == NULL)
256 				sprintf(parent_value, "%s:%s",
257 					environ_list[index], ENV_NOT_SET);
258 			else
259 				sprintf(parent_value, "%s:%s",
260 					environ_list[index], var);
261 
262 			cmp_env_strings(parent_value, tmp_line);
263 
264 		}
265 	}
266 
267 	close(fildes);
268 }
269 
270 int main(int ac, char **av)
271 {
272 	int lc;
273 	int kid_status;
274 	int wait_status;
275 	int fails;
276 
277 	tst_parse_opts(ac, av, NULL, NULL);
278 
279 	setup();
280 
281 	for (lc = 0; TEST_LOOPING(lc); lc++) {
282 		tst_count = 0;
283 		fails = 0;
284 
285 		TEST(fork());
286 
287 		if (TEST_RETURN == -1) {
288 			/* fork failed */
289 			tst_brkm(TFAIL, cleanup,
290 				 "fork() failed with %d (%s)",
291 				 TEST_ERRNO, strerror(TEST_ERRNO));
292 		} else if (TEST_RETURN == 0) {
293 			/* child */
294 			/* determine environment variables */
295 			child_environment();
296 			/* exit with known value */
297 			exit(KIDEXIT);
298 		} else {
299 			/* parent of successful fork */
300 			/* wait for the child to complete */
301 			wait_status = waitpid(TEST_RETURN, &kid_status, 0);
302 			/* validate the child exit status */
303 			if (wait_status == TEST_RETURN) {
304 				if (kid_status != KIDEXIT << 8) {
305 					tst_brkm(TBROK, cleanup,
306 						 "fork(): Incorrect child status returned on wait(): %d",
307 						 kid_status);
308 					fails++;
309 				}
310 			} else {
311 				tst_brkm(TBROK, cleanup,
312 					 "fork(): wait() for child status failed with %d errno: %d : %s",
313 					 wait_status, errno,
314 					 strerror(errno));
315 				fails++;
316 			}
317 
318 			if (fails == 0) {
319 				/* verification tests */
320 				parent_environment();
321 			}
322 		}
323 
324 	}
325 
326 	cleanup();
327 	tst_exit();
328 }
329