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 41char *TCID = "setrlimit01"; 42int TST_TOTAL = 1; 43 44static void setup(void); 45static void cleanup(void); 46static void test1(void); 47static void test2(void); 48static void test3(void); 49static void test4(void); 50static void sighandler(int); 51 52static char filename[40] = ""; 53static struct rlimit save_rlim, rlim, rlim1; 54static int nofiles, fd, bytes, i, status; 55static char *buf = "abcdefghijklmnopqrstuvwxyz"; 56static pid_t pid; 57 58int 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 */ 87static 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 */ 114static 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 */ 194static 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 */ 242static 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 */ 279static 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 287static 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 302static void cleanup(void) 303{ 304 unlink(filename); 305 tst_rmdir(); 306} 307