• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *	Testcase to check the basic functionality of the setrlimit system call.
22  *	Use the different commands like RLIMIT_NOFILE, RLIMIT_CORE,
23  *	RLIMIT_FSIZE, and, RLIMIT_NOFILE, and test for different test
24  *	conditions.
25  *
26  *	07/2001 Ported by Wayne Boyer
27  */
28 
29 #include <sys/types.h>
30 #include <sys/resource.h>
31 #include <sys/stat.h>
32 #include <sys/time.h>
33 #include <sys/wait.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include "test.h"
39 #include "safe_macros.h"
40 
41 char *TCID = "setrlimit01";
42 int TST_TOTAL = 1;
43 
44 static void setup(void);
45 static void cleanup(void);
46 static void test1(void);
47 static void test2(void);
48 static void test3(void);
49 static void test4(void);
50 static void sighandler(int);
51 
52 static char filename[40] = "";
53 static struct rlimit save_rlim, rlim, rlim1;
54 static int nofiles, fd, bytes, i, status;
55 static char *buf = "abcdefghijklmnopqrstuvwxyz";
56 static pid_t pid;
57 
main(int ac,char ** av)58 int main(int ac, char **av)
59 {
60 	int lc;
61 
62 	tst_parse_opts(ac, av, NULL, NULL);
63 
64 	setup();
65 
66 	for (lc = 0; TEST_LOOPING(lc); lc++) {
67 		tst_count = 0;
68 
69 		test1();
70 		test2();
71 		test3();
72 		/* reset saved conditions */
73 		if ((setrlimit(RLIMIT_NPROC, &save_rlim)) == -1) {
74 			tst_brkm(TBROK, cleanup, "setrlimit failed to reset "
75 				 "RLIMIT_NPROC, errno = %d", errno);
76 		}
77 		test4();
78 	}
79 
80 	cleanup();
81 	tst_exit();
82 }
83 
84 /*
85  * test1 - Test for RLIMIT_NOFILE
86  */
test1(void)87 static void test1(void)
88 {
89 	rlim.rlim_cur = 100;
90 	rlim.rlim_max = 100;
91 
92 	TEST(setrlimit(RLIMIT_NOFILE, &rlim));
93 
94 	if (TEST_RETURN == -1) {
95 		tst_resm(TFAIL, "setrlimit failed to set "
96 			 "RLIMIT_NOFILE, errno = %d", errno);
97 		return;
98 	}
99 
100 	nofiles = getdtablesize();
101 
102 	if (nofiles != 100) {
103 		tst_resm(TFAIL, "setrlimit failed, expected "
104 			 "100, got %d", nofiles);
105 		return;
106 	}
107 
108 	tst_resm(TPASS, "RLIMIT_NOFILE functionality is correct");
109 }
110 
111 /*
112  * test2 - Test for RLIMIT_FSIZE
113  */
test2(void)114 static void test2(void)
115 {
116 	/*
117 	 * Since we would be altering the filesize in the child,
118 	 * we need to "sync", ie. fflush the parent's write buffers
119 	 * here.  This is because the child will inherit the parent's
120 	 * write buffer, and while exitting it would try to fflush it.
121 	 * Since its filesize is truncated to only 10 bytes, the
122 	 * fflush attempt would fail, and the child would exit with
123 	 * an wired value!  So, it is essential to fflush the parent's
124 	 * write buffer HERE
125 	 */
126 	int pipefd[2];
127 	fflush(stdout);
128 	SAFE_PIPE(NULL, pipefd);
129 
130 	/*
131 	 * Spawn a child process, and reduce the filesize to
132 	 * 10 by calling setrlimit(). We can't do this in the
133 	 * parent, because the parent needs a bigger filesize as its
134 	 * output will be saved to the logfile (instead of stdout)
135 	 * when the testcase (parent) is run from the driver.
136 	 */
137 	pid = FORK_OR_VFORK();
138 	if (pid == -1)
139 		tst_brkm(TBROK, cleanup, "fork() failed");
140 
141 	if (pid == 0) {
142 		close(pipefd[0]);	/* close unused read end */
143 		rlim.rlim_cur = 10;
144 		rlim.rlim_max = 10;
145 		if ((setrlimit(RLIMIT_FSIZE, &rlim)) == -1)
146 			exit(1);
147 
148 		fd = creat(filename, 0644);
149 		if (fd < 0)
150 			exit(2);
151 
152 		bytes = write(fd, buf, 26);
153 		if (bytes != 10) {
154 			if (write(pipefd[1], &bytes, sizeof(bytes))
155 			    < sizeof(bytes)) {
156 				perror("child: write to pipe failed");
157 			}
158 			close(pipefd[1]);	/* EOF */
159 			exit(3);
160 		}
161 		exit(0);	/* success */
162 	}
163 
164 	/* parent */
165 	SAFE_WAITPID(cleanup, pid, &status, 0);
166 
167 	switch (WEXITSTATUS(status)) {
168 	case 0:
169 		tst_resm(TPASS, "RLIMIT_FSIZE test PASSED");
170 		break;
171 	case 1:
172 		tst_resm(TFAIL, "setrlimit failed to set "
173 			 "RLIMIT_FSIZE, errno = %d", errno);
174 		break;
175 	case 2:
176 		tst_resm(TFAIL, "creating testfile failed");
177 		break;
178 	case 3:
179 		close(pipefd[1]);	/* close unused write end */
180 		if (read(pipefd[0], &bytes, sizeof(bytes)) < sizeof(bytes))
181 			tst_resm(TFAIL, "parent: reading pipe failed");
182 
183 		close(pipefd[0]);
184 		tst_resm(TFAIL, "setrlimit failed, expected "
185 			 "10 got %d", bytes);
186 		break;
187 	default:
188 		tst_resm(TFAIL, "child returned bad exit status");
189 	}
190 }
191 
192 /*
193  * test3 - Test for RLIMIT_NPROC
194  */
test3(void)195 static void test3(void)
196 {
197 	SAFE_GETRLIMIT(cleanup, RLIMIT_NPROC, &save_rlim);
198 
199 	rlim.rlim_cur = 10;
200 	rlim.rlim_max = 10;
201 
202 	TEST(setrlimit(RLIMIT_NPROC, &rlim));
203 
204 	if (TEST_RETURN == -1) {
205 		tst_resm(TFAIL, "setrlimit failed to set "
206 			 "RLIMIT_NPROC, errno = %d", errno);
207 		return;
208 	}
209 
210 	if ((getrlimit(RLIMIT_NPROC, &rlim1)) == -1) {
211 		tst_brkm(TBROK, cleanup, "getrlimit failed to get "
212 			 "values for RLIMIT_NPROC, errno = %d", errno);
213 	}
214 
215 	if ((rlim1.rlim_cur != 10) && (rlim1.rlim_max != 10)) {
216 		tst_resm(TFAIL, "setrlimit did not set the proc "
217 			 "limit correctly");
218 		return;
219 	}
220 
221 	for (i = 0; i < 20; i++) {
222 		pid = FORK_OR_VFORK();
223 		if (pid == -1) {
224 			if (errno != EAGAIN) {
225 				tst_resm(TWARN, "Expected EAGAIN got %d",
226 					 errno);
227 			}
228 		} else if (pid == 0) {
229 			exit(0);
230 		}
231 	}
232 	waitpid(pid, &status, 0);
233 	if (WEXITSTATUS(status) != 0)
234 		tst_resm(TFAIL, "RLIMIT_NPROC functionality is not correct");
235 	else
236 		tst_resm(TPASS, "RLIMIT_NPROC functionality is correct");
237 }
238 
239 /*
240  * test4() - Test for RLIMIT_CORE by forking a child and
241  *           having it cause a segfault
242  */
test4(void)243 static void test4(void)
244 {
245 	rlim.rlim_cur = 10;
246 	rlim.rlim_max = 10;
247 
248 	TEST(setrlimit(RLIMIT_CORE, &rlim));
249 
250 	if (TEST_RETURN == -1) {
251 		tst_resm(TFAIL | TERRNO, "setrlimit failed to set RLIMIT_CORE");
252 		return;
253 	}
254 
255 	pid = FORK_OR_VFORK();
256 	if (pid == -1)
257 		tst_brkm(TBROK, cleanup, "fork() failed");
258 
259 	if (pid == 0) {		/* child */
260 		char *testbuf = NULL;
261 		strcpy(testbuf, "abcd");
262 		exit(0);
263 	}
264 	wait(&status);
265 
266 	if (access("core", F_OK) == 0) {
267 		tst_resm(TFAIL, "core dump dumped unexpectedly");
268 		return;
269 	} else if (errno != ENOENT) {
270 		tst_resm(TFAIL | TERRNO, "access failed unexpectedly");
271 		return;
272 	}
273 
274 	tst_resm(TPASS, "RLIMIT_CORE functionality is correct");
275 }
276 
277 /*
278  * sighandler() - catch sigsegv when generated by child in test #4
279  */
sighandler(int sig)280 static void sighandler(int sig)
281 {
282 	if (sig != SIGSEGV && sig != SIGXFSZ && sig != SIGTERM)
283 		tst_brkm(TBROK, NULL, "caught unexpected signal: %d", sig);
284 
285 	_exit(0);
286 }
287 
setup(void)288 static void setup(void)
289 {
290 	tst_require_root();
291 
292 	umask(0);
293 
294 	tst_sig(FORK, sighandler, cleanup);
295 
296 	TEST_PAUSE;
297 
298 	tst_tmpdir();
299 
300 	sprintf(filename, "setrlimit1.%d", getpid());
301 }
302 
cleanup(void)303 static void cleanup(void)
304 {
305 	unlink(filename);
306 	tst_rmdir();
307 }
308