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
40 char *TCID = "setrlimit01";
41 int TST_TOTAL = 1;
42
43 static void setup(void);
44 static void cleanup(void);
45 static void test1(void);
46 static void test2(void);
47 static void test3(void);
48 static void test4(void);
49 static void sighandler(int);
50
51 static char filename[40] = "";
52 static struct rlimit save_rlim, rlim, rlim1;
53 static int nofiles, fd, bytes, i, status;
54 static char *buf = "abcdefghijklmnopqrstuvwxyz";
55 static pid_t pid;
56
main(int ac,char ** av)57 int main(int ac, char **av)
58 {
59 int lc;
60
61 tst_parse_opts(ac, av, NULL, NULL);
62
63 setup();
64
65 for (lc = 0; TEST_LOOPING(lc); lc++) {
66 tst_count = 0;
67
68 test1();
69 test2();
70 test3();
71 /* reset saved conditions */
72 if ((setrlimit(RLIMIT_NPROC, &save_rlim)) == -1) {
73 tst_brkm(TBROK, cleanup, "setrlimit failed to reset "
74 "RLIMIT_NPROC, errno = %d", errno);
75 }
76 test4();
77 }
78
79 cleanup();
80 tst_exit();
81 }
82
83 /*
84 * test1 - Test for RLIMIT_NOFILE
85 */
test1(void)86 static void test1(void)
87 {
88 rlim.rlim_cur = 100;
89 rlim.rlim_max = 100;
90
91 TEST(setrlimit(RLIMIT_NOFILE, &rlim));
92
93 if (TEST_RETURN == -1) {
94 tst_resm(TFAIL, "setrlimit failed to set "
95 "RLIMIT_NOFILE, errno = %d", errno);
96 return;
97 }
98
99 nofiles = getdtablesize();
100
101 if (nofiles != 100) {
102 tst_resm(TFAIL, "setrlimit failed, expected "
103 "100, got %d", nofiles);
104 return;
105 }
106
107 tst_resm(TPASS, "RLIMIT_NOFILE functionality is correct");
108 }
109
110 /*
111 * test2 - Test for RLIMIT_FSIZE
112 */
test2(void)113 static void test2(void)
114 {
115 /*
116 * Since we would be altering the filesize in the child,
117 * we need to "sync", ie. fflush the parent's write buffers
118 * here. This is because the child will inherit the parent's
119 * write buffer, and while exitting it would try to fflush it.
120 * Since its filesize is truncated to only 10 bytes, the
121 * fflush attempt would fail, and the child would exit with
122 * an wired value! So, it is essential to fflush the parent's
123 * write buffer HERE
124 */
125 int pipefd[2];
126 fflush(stdout);
127 if (pipe(pipefd) == -1)
128 tst_brkm(TBROK | TERRNO, NULL, "pipe creation failed");
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 if (waitpid(pid, &status, 0) == -1)
166 tst_brkm(TBROK, cleanup, "waitpid() failed");
167
168 switch (WEXITSTATUS(status)) {
169 case 0:
170 tst_resm(TPASS, "RLIMIT_FSIZE test PASSED");
171 break;
172 case 1:
173 tst_resm(TFAIL, "setrlimit failed to set "
174 "RLIMIT_FSIZE, errno = %d", errno);
175 break;
176 case 2:
177 tst_resm(TFAIL, "creating testfile failed");
178 break;
179 case 3:
180 close(pipefd[1]); /* close unused write end */
181 if (read(pipefd[0], &bytes, sizeof(bytes)) < sizeof(bytes))
182 tst_resm(TFAIL, "parent: reading pipe failed");
183
184 close(pipefd[0]);
185 tst_resm(TFAIL, "setrlimit failed, expected "
186 "10 got %d", bytes);
187 break;
188 default:
189 tst_resm(TFAIL, "child returned bad exit status");
190 }
191 }
192
193 /*
194 * test3 - Test for RLIMIT_NPROC
195 */
test3(void)196 static void test3(void)
197 {
198 if (getrlimit(RLIMIT_NPROC, &save_rlim) < 0)
199 tst_brkm(TBROK, cleanup, "getrlimit failed, errno: %d", errno);
200
201 rlim.rlim_cur = 10;
202 rlim.rlim_max = 10;
203
204 TEST(setrlimit(RLIMIT_NPROC, &rlim));
205
206 if (TEST_RETURN == -1) {
207 tst_resm(TFAIL, "setrlimit failed to set "
208 "RLIMIT_NPROC, errno = %d", errno);
209 return;
210 }
211
212 if ((getrlimit(RLIMIT_NPROC, &rlim1)) == -1) {
213 tst_brkm(TBROK, cleanup, "getrlimit failed to get "
214 "values for RLIMIT_NPROC, errno = %d", errno);
215 }
216
217 if ((rlim1.rlim_cur != 10) && (rlim1.rlim_max != 10)) {
218 tst_resm(TFAIL, "setrlimit did not set the proc "
219 "limit correctly");
220 return;
221 }
222
223 for (i = 0; i < 20; i++) {
224 pid = FORK_OR_VFORK();
225 if (pid == -1) {
226 if (errno != EAGAIN) {
227 tst_resm(TWARN, "Expected EAGAIN got %d",
228 errno);
229 }
230 } else if (pid == 0) {
231 exit(0);
232 }
233 }
234 waitpid(pid, &status, 0);
235 if (WEXITSTATUS(status) != 0)
236 tst_resm(TFAIL, "RLIMIT_NPROC functionality is not correct");
237 else
238 tst_resm(TPASS, "RLIMIT_NPROC functionality is correct");
239 }
240
241 /*
242 * test4() - Test for RLIMIT_CORE by forking a child and
243 * having it cause a segfault
244 */
test4(void)245 static void test4(void)
246 {
247 rlim.rlim_cur = 10;
248 rlim.rlim_max = 10;
249
250 TEST(setrlimit(RLIMIT_CORE, &rlim));
251
252 if (TEST_RETURN == -1) {
253 tst_resm(TFAIL | TERRNO, "setrlimit failed to set RLIMIT_CORE");
254 return;
255 }
256
257 pid = FORK_OR_VFORK();
258 if (pid == -1)
259 tst_brkm(TBROK, cleanup, "fork() failed");
260
261 if (pid == 0) { /* child */
262 char *testbuf = NULL;
263 strcpy(testbuf, "abcd");
264 exit(0);
265 }
266 wait(&status);
267
268 if (access("core", F_OK) == 0) {
269 tst_resm(TFAIL, "core dump dumped unexpectedly");
270 return;
271 } else if (errno != ENOENT) {
272 tst_resm(TFAIL | TERRNO, "access failed unexpectedly");
273 return;
274 }
275
276 tst_resm(TPASS, "RLIMIT_CORE functionality is correct");
277 }
278
279 /*
280 * sighandler() - catch sigsegv when generated by child in test #4
281 */
sighandler(int sig)282 static void sighandler(int sig)
283 {
284 if (sig != SIGSEGV && sig != SIGXFSZ && sig != SIGTERM)
285 tst_brkm(TBROK, NULL, "caught unexpected signal: %d", sig);
286
287 _exit(0);
288 }
289
setup(void)290 static void setup(void)
291 {
292 tst_require_root();
293
294 umask(0);
295
296 tst_sig(FORK, sighandler, cleanup);
297
298 TEST_PAUSE;
299
300 tst_tmpdir();
301
302 sprintf(filename, "setrlimit1.%d", getpid());
303 }
304
cleanup(void)305 static void cleanup(void)
306 {
307 unlink(filename);
308 tst_rmdir();
309 }
310