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 exiting 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)) < (long)sizeof(bytes)) {
155 perror("child: write to pipe failed");
156 }
157 close(pipefd[1]); /* EOF */
158 exit(3);
159 }
160 exit(0); /* success */
161 }
162
163 /* parent */
164 SAFE_WAITPID(cleanup, pid, &status, 0);
165
166 switch (WEXITSTATUS(status)) {
167 case 0:
168 tst_resm(TPASS, "RLIMIT_FSIZE test PASSED");
169 break;
170 case 1:
171 tst_resm(TFAIL, "setrlimit failed to set "
172 "RLIMIT_FSIZE, errno = %d", errno);
173 break;
174 case 2:
175 tst_resm(TFAIL, "creating testfile failed");
176 break;
177 case 3:
178 close(pipefd[1]); /* close unused write end */
179 if (read(pipefd[0], &bytes, sizeof(bytes)) < (long)sizeof(bytes))
180 tst_resm(TFAIL, "parent: reading pipe failed");
181
182 close(pipefd[0]);
183 tst_resm(TFAIL, "setrlimit failed, expected "
184 "10 got %d", bytes);
185 break;
186 default:
187 tst_resm(TFAIL, "child returned bad exit status");
188 }
189 }
190
191 /*
192 * test3 - Test for RLIMIT_NPROC
193 */
test3(void)194 static void test3(void)
195 {
196 SAFE_GETRLIMIT(cleanup, RLIMIT_NPROC, &save_rlim);
197
198 rlim.rlim_cur = 10;
199 rlim.rlim_max = 10;
200
201 TEST(setrlimit(RLIMIT_NPROC, &rlim));
202
203 if (TEST_RETURN == -1) {
204 tst_resm(TFAIL, "setrlimit failed to set "
205 "RLIMIT_NPROC, errno = %d", errno);
206 return;
207 }
208
209 if ((getrlimit(RLIMIT_NPROC, &rlim1)) == -1) {
210 tst_brkm(TBROK, cleanup, "getrlimit failed to get "
211 "values for RLIMIT_NPROC, errno = %d", errno);
212 }
213
214 if ((rlim1.rlim_cur != 10) && (rlim1.rlim_max != 10)) {
215 tst_resm(TFAIL, "setrlimit did not set the proc "
216 "limit correctly");
217 return;
218 }
219
220 for (i = 0; i < 20; i++) {
221 pid = FORK_OR_VFORK();
222 if (pid == -1) {
223 if (errno != EAGAIN) {
224 tst_resm(TWARN, "Expected EAGAIN got %d",
225 errno);
226 }
227 } else if (pid == 0) {
228 exit(0);
229 }
230 }
231 waitpid(pid, &status, 0);
232 if (WEXITSTATUS(status) != 0)
233 tst_resm(TFAIL, "RLIMIT_NPROC functionality is not correct");
234 else
235 tst_resm(TPASS, "RLIMIT_NPROC functionality is correct");
236 }
237
238 /*
239 * test4() - Test for RLIMIT_CORE by forking a child and
240 * having it cause a segfault
241 */
test4(void)242 static void test4(void)
243 {
244 rlim.rlim_cur = 10;
245 rlim.rlim_max = 10;
246
247 TEST(setrlimit(RLIMIT_CORE, &rlim));
248
249 if (TEST_RETURN == -1) {
250 tst_resm(TFAIL | TTERRNO, "setrlimit failed to set RLIMIT_CORE");
251 return;
252 }
253
254 pid = FORK_OR_VFORK();
255 if (pid == -1)
256 tst_brkm(TBROK, cleanup, "fork() failed");
257
258 if (pid == 0) { /* child */
259 char *testbuf = NULL;
260 strcpy(testbuf, "abcd");
261 exit(0);
262 }
263 wait(&status);
264
265 if (access("core", F_OK) == 0) {
266 tst_resm(TFAIL, "core dump dumped unexpectedly");
267 return;
268 } else if (errno != ENOENT) {
269 tst_resm(TFAIL | TERRNO, "access failed unexpectedly");
270 return;
271 }
272
273 tst_resm(TPASS, "RLIMIT_CORE functionality is correct");
274 }
275
276 /*
277 * sighandler() - catch sigsegv when generated by child in test #4
278 */
sighandler(int sig)279 static void sighandler(int sig)
280 {
281 if (sig != SIGSEGV && sig != SIGXFSZ && sig != SIGTERM)
282 tst_brkm(TBROK, NULL, "caught unexpected signal: %d", sig);
283
284 _exit(0);
285 }
286
setup(void)287 static void setup(void)
288 {
289 tst_require_root();
290
291 umask(0);
292
293 tst_sig(FORK, sighandler, cleanup);
294
295 TEST_PAUSE;
296
297 tst_tmpdir();
298
299 sprintf(filename, "setrlimit1.%d", getpid());
300 }
301
cleanup(void)302 static void cleanup(void)
303 {
304 unlink(filename);
305 tst_rmdir();
306 }
307