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 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 303 static void cleanup(void) 304 { 305 unlink(filename); 306 tst_rmdir(); 307 } 308