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