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 40char *TCID = "setrlimit01"; 41int TST_TOTAL = 1; 42 43static void setup(void); 44static void cleanup(void); 45static void test1(void); 46static void test2(void); 47static void test3(void); 48static void test4(void); 49static void sighandler(int); 50 51static char filename[40] = ""; 52static struct rlimit save_rlim, rlim, rlim1; 53static int nofiles, fd, bytes, i, status; 54static char *buf = "abcdefghijklmnopqrstuvwxyz"; 55static pid_t pid; 56 57int 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 */ 86static 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 */ 113static 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 */ 196static 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 */ 245static 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 */ 282static 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 290static 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 305static void cleanup(void) 306{ 307 unlink(filename); 308 tst_rmdir(); 309} 310