1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
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/*
21 * Name: vfork01
22 *
23 * Test Description:
24 *  Fork a process using vfork() and verify that, the attribute values like
25 *  euid, ruid, suid, egid, rgid, sgid, umask, inode and device number of
26 *  root and current working directories are same as that of the parent
27 *  process.
28 * $
29 * Expected Result:
30 *  The attribute values like euid, ruid, suid, egid, rgid, sgid, umask, inode
31 *  and device number of root and current working directory of the parent and
32 *  child processes should be equal.
33 *
34 * Algorithm:
35 *  Setup:
36 *   Setup signal handling.
37 *   Pause for SIGUSR1 if option specified.
38 *
39 *  Test:
40 *   Loop if the proper options are given.
41 *   Execute system call
42 *   Check return code, if system call failed (return=-1)
43 *   	Log the errno and Issue a FAIL message.
44 *   Otherwise,
45 *   	Verify the Functionality of system call
46 *      if successful,
47 *      	Issue Functionality-Pass message.
48 *      Otherwise,
49 *		Issue Functionality-Fail message.
50 *  Cleanup:
51 *   Print errno log and/or timing stats if options given
52 *
53 * Usage:  <for command-line>
54 *  vfork01 [-c n] [-e] [-f] [-i n] [-I x] [-p x] [-t]
55 *	where,	-c n : Run n copies concurrently.
56 *		-e   : Turn on errno logging.
57 *		-f   : Turn off functionality Testing.
58 *		-i n : Execute test n times.
59 *		-I x : Execute test for x seconds.
60 *		-P x : Pause for x seconds between iterations.
61 *		-t   : Turn on syscall timing.
62 *
63 * History
64 *	07/2001 John George
65 *		-Ported
66 *
67 * Restrictions:
68 *  None.
69 *
70 */
71
72#define _GNU_SOURCE 1
73#include <stdio.h>
74#include <sys/types.h>
75#include <errno.h>
76#include <unistd.h>
77#include <fcntl.h>
78#include <string.h>
79#include <signal.h>
80#include <unistd.h>
81#include <sys/stat.h>
82#include <sys/wait.h>
83
84#include "test.h"
85
86char *TCID = "vfork01";
87int TST_TOTAL = 1;
88
89/* Variables to hold parent/child eff/real/saved uid/gid values */
90uid_t Peuid, Ceuid, Csuid, Psuid, Pruid, Cruid;
91gid_t Pegid, Cegid, Psgid, Csgid, Prgid, Crgid;
92mode_t Pumask, Cumask;
93
94char *Pcwd, *Ccwd;		/*
95				 * pathname of working directory of
96				 * child/parent process.
97				 */
98/* stat structure to hold directory/inode information for parent/child */
99struct stat StatPbuf;
100struct stat StatCbuf;
101struct stat Stat_cwd_Pbuf;
102struct stat Stat_cwd_Cbuf;
103
104void setup();			/* Main setup function of test */
105void cleanup();			/* cleanup function for the test */
106
107int main(int ac, char **av)
108{
109	int lc;
110	pid_t cpid;		/* process id of the child process */
111	int exit_status;	/* exit status of child process */
112
113	tst_parse_opts(ac, av, NULL, NULL);
114
115	setup();
116
117	for (lc = 0; TEST_LOOPING(lc); lc++) {
118
119		tst_count = 0;
120
121		/*
122		 * Call vfork(2) to create a child process without
123		 * fully copying the address space of parent.
124		 */
125		TEST(vfork());
126
127		if ((cpid = TEST_RETURN) == -1) {
128			tst_resm(TFAIL, "vfork() Failed, errno=%d : %s",
129				 TEST_ERRNO, strerror(TEST_ERRNO));
130		} else if (cpid == 0) {	/* Child process */
131			/*
132			 * Get the euid, ruid, egid, rgid, umask value
133			 * and the current working directory of the
134			 * child process
135			 */
136			if (getresuid(&Cruid, &Ceuid, &Csuid) < 0) {
137				tst_resm(TFAIL, "getresuid() fails to "
138					 "get real/eff./saved uid of "
139					 "child process");
140				_exit(1);
141			}
142
143			if (getresgid(&Crgid, &Cegid, &Csgid) < 0) {
144				tst_resm(TFAIL, "getresgid() fails to "
145					 "get real/eff./saved gid of "
146					 "child process");
147				_exit(1);
148			}
149
150			/*
151			 * Get the file mode creation mask value of
152			 * child process by setting value zero and
153			 * restore the previous mask value.
154			 */
155			Cumask = umask(0);
156
157			/*
158			 * Restore the process mask of child to
159			 * previous value.
160			 */
161			umask(Cumask);
162
163			/*
164			 * Get the pathname of current working
165			 * directory for the child process.
166			 */
167			if ((Ccwd = (char *)getcwd(NULL,
168						   BUFSIZ)) == NULL) {
169				tst_resm(TFAIL, "getcwd failed for the "
170					 "child process");
171				_exit(1);
172			}
173
174			/*
175			 * Get the device number and the inode
176			 * number of "/" directory for the child
177			 * process.
178			 */
179			if (stat("/", &StatCbuf) < 0) {
180				tst_resm(TFAIL, "stat(2) failed to get "
181					 "info. of'/' in the child "
182					 "process");
183				_exit(1);
184			}
185
186			/*
187			 * Get the device/inode number of "."
188			 * (working directory) for the child process.
189			 */
190			if (stat(Ccwd, &Stat_cwd_Cbuf) < 0) {
191				tst_resm(TFAIL, "stat(2) failed to get "
192					 "info. of working irectory in "
193					 "the child");
194				_exit(1);
195			}
196
197			/* Now, do the actual comparision */
198			if (Peuid != Ceuid || Pegid != Cegid ||
199			    Psuid != Csuid || Psgid != Csgid ||
200			    Pruid != Cruid || Prgid != Crgid ||
201			    Pumask != Cumask) {
202				tst_resm(TFAIL, "Attribute values of "
203					 "parent and child don't match");
204				_exit(1);
205			} else {
206				tst_resm(TINFO, "Attribute values of "
207					 "parent and child match");
208			}
209
210			/* Check for the same working directories */
211			if (strcmp(Pcwd, Ccwd) != 0) {
212				tst_resm(TFAIL, "Working directories "
213					 "of parent and child don't "
214					 "match");
215				_exit(1);
216			} else {
217				tst_resm(TINFO, "Working directories "
218					 "of parent and child match");
219			}
220
221			/*
222			 * Check for the same device/inode number of
223			 * '/' directory.
224			 */
225			if ((StatPbuf.st_ino != StatCbuf.st_ino) ||
226			    (StatPbuf.st_dev != StatCbuf.st_dev)) {
227				tst_resm(TFAIL, "Device/inode number "
228					 "of parent and childs '/' "
229					 " don't match");
230				_exit(1);
231			} else {
232				tst_resm(TINFO, "Device/inode number "
233					 "of parent and childs '/' "
234					 "match");
235			}
236
237			/*
238			 * Check for the same device and inode number
239			 *  of "." (current working directory.
240			 */
241			if ((Stat_cwd_Pbuf.st_ino !=
242			     Stat_cwd_Cbuf.st_ino) ||
243			    (Stat_cwd_Pbuf.st_dev !=
244			     Stat_cwd_Cbuf.st_dev)) {
245				tst_resm(TFAIL, "Device/inode number "
246					 "of parent and childs '.' "
247					 "don't match");
248				_exit(1);
249			} else {
250				tst_resm(TINFO, "Device/inode number "
251					 "of parent and childs '.' "
252					 "don't match");
253			}
254
255			/*
256			 * Exit with normal exit code if everything
257			 * fine
258			 */
259			_exit(0);
260
261		} else {	/* parent process */
262			/*
263			 * Let the parent process wait till child completes
264			 * its execution.
265			 */
266			wait(&exit_status);
267
268			/* Check for the exit status of child process */
269			if (WEXITSTATUS(exit_status) == 0) {
270				tst_resm(TPASS, "Call of vfork() successful");
271			} else if (WEXITSTATUS(exit_status) == 1) {
272				tst_resm(TFAIL,
273					 "Child process exited abnormally");
274			}
275		}
276		tst_count++;	/* incr. TEST_LOOP counter */
277	}
278
279	cleanup();
280	tst_exit();
281}
282
283/*
284 * void
285 * setup() - performs all ONE TIME setup for this test.
286 *  This function gets real/effective/saved uid/gid, umask, the device/inode
287 *  number of '/' and current working directory for the parent process.
288 */
289void setup(void)
290{
291
292	tst_sig(FORK, DEF_HANDLER, cleanup);
293
294	TEST_PAUSE;
295
296	/*
297	 * Get the euid, ruid, egid, rgid, umask value
298	 * and the current working directory of the parent process.
299	 */
300	if (getresuid(&Pruid, &Peuid, &Psuid) < 0) {
301		tst_brkm(TFAIL, cleanup, "getresuid() fails to get "
302			 "real/eff./saved uid of parent");
303	}
304
305	if (getresgid(&Prgid, &Pegid, &Psgid) < 0) {
306		tst_brkm(TFAIL, cleanup, "getresgid() fails to get "
307			 "real/eff./saved gid of parent");
308	}
309
310	/* Get the process file mode creation mask by setting value 0 */
311	Pumask = umask(0);
312	umask(Pumask);		/*
313				 * Restore the mask value of the
314				 * process.
315				 */
316	/*
317	 * Get the pathname of current working directory of the parent
318	 * process.
319	 */
320	if ((Pcwd = (char *)getcwd(NULL, BUFSIZ)) == NULL) {
321		tst_brkm(TFAIL, cleanup,
322			 "getcwd failed for the parent process");
323	}
324
325	/*
326	 * Get the device and inode number of root directory for the
327	 * parent process.
328	 */
329	if (stat("/", &StatPbuf) == -1) {
330		tst_brkm(TFAIL, cleanup, "stat(2) failed to get info. of '/' "
331			 "in parent process");
332	}
333
334	/*
335	 * Get the device number and the inode number of "." (current-
336	 * working directory) for the parent process.
337	 */
338	if (stat(Pcwd, &Stat_cwd_Pbuf) < 0) {
339		tst_brkm(TFAIL, cleanup, "stat(2) failed to get info. of "
340			 "working directory in parent process");
341	}
342}
343
344/*
345 * void
346 * cleanup() - performs all ONE TIME cleanup for this test at
347 *             completion or premature exit.
348 */
349void cleanup(void)
350{
351
352}
353