Home | History | Annotate | Download | only in setrlimit
      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