#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <stdio.h>

#ifdef __DragonFly__
#ifdef RLIMIT_FORK
#define	RLIMIT_FKDEPTH	RLIMIT_FORK
#else
#define	RLIMIT_FKDEPTH	12
#define RLIM_NLIMITS	13
#endif /* RLIMIT_FORK */
#else
#define RLIMIT_FKDEPTH	11
#define RLIM_NLIMITS	12
#endif /* __DragonFly__ */

#define DEPTH 5

int
main(void)
{
	pid_t new;
	struct rlimit rlim;
	int i, ret, err, err_count=0;

	/* Check default depth */
	printf("Checking default depth\n");
	getrlimit(RLIMIT_FKDEPTH, &rlim);
	printf(" -Default depth: %d(soft) %d(hard)\n", rlim.rlim_cur,
	       rlim.rlim_max);
	
	/* Check children of a process with default depth */
	printf("Checking children of a process with default depth...\n");
	err = 0;
	new = fork();
	if (new == 0) {
		getrlimit(RLIMIT_FKDEPTH, &rlim);
		printf(" -Childen of default depth: %d(soft) %d(hard)\n",
		       rlim.rlim_cur, rlim.rlim_max);
		return 0;
	}
	else if (new == -1) {
		printf(" -Error creating first child\n");
		err = 1;
		err_count++;
	}
	if (!err) printf("Check OK\n\n");
	
	/* Check reaching max depth */
	printf("Checking reaching max depth...\n");
	err = 0;
	printf(" Max depth is set to %d\n", DEPTH);
	rlim.rlim_cur = DEPTH; 
	rlim.rlim_max = DEPTH + 5;
	ret = setrlimit(RLIMIT_FKDEPTH, &rlim);

	if (ret == -1) {
		printf(" -Error calling setrlimit\n");
		err = 1;
		err_count++;
	}
	for (i = 1; i <= 10; i++) {
		new=fork();
		if (new == -1) {
			if (i >= DEPTH)
				printf(" -OK: cannot create child no %d\n", i);
			else {
				err = 1;
				err_count++;
				printf(" -Unexpected error creating child no. "
				       "%d\n", i);
			}
		}
		else if(new == 0){
			getrlimit(RLIMIT_FKDEPTH, &rlim);
			printf(" -Process no. %d rlimit: %d(soft) %d(hard)\n",
			       i, rlim.rlim_cur, rlim.rlim_max);
		}
		else return 0;
	}
	if (!err) printf("Check OK\n\n");
		
	/* Check disabled fork */
	printf("Checking disabled fork...\n");
	err = 0;
	rlim.rlim_cur = 0;
	rlim.rlim_max = 0;
	ret = setrlimit(RLIMIT_FKDEPTH, &rlim);
	if (ret == -1) {
		printf(" -Error calling setrlimit\n");
		err = 1;
		err_count++;
	}
	new = fork();
	if (new == 0) {
		printf(" -Unexpected error, fork successful\n");
		err = 1;
		err_count++;
	}
	else if (new == -1)
		printf(" -OK: unable to fork\n");
	if (!err) printf("Check OK\n\n");

	/*
	 * Test if when rlim_cur != RLIM_INFINITY and rlim_max == RLIM_INFINITY
	 * only rlim_cur is decremented.
	 *
	 * XXX Note: this test should fail with the current Linux patch.
	 */
	printf("Checking rlim_max when it equals infinity...\n");
	rlim.rlim_cur = 1;
	rlim.rlim_max = RLIM_INFINITY;
	ret = setrlimit(RLIMIT_FKDEPTH, &rlim);
	if (ret == -1) {
		printf(" -Error calling setrlimit\n");
		err = 1;
		err_count++;
	}
	new = fork();
	if (new == -1) {
		printf(" -Error in fork\n");
		err = 1;
		err_count++;
	} else if (new == 0) {
		getrlimit(RLIMIT_FKDEPTH, &rlim);
		if (rlim.rlim_cur != 0) {
			printf(" -Error: rlim_cur is %d, should be 0\n",
			       rlim.rlim_cur);
			err = 1;
			err_count++;
		}
		if (rlim.rlim_max != RLIM_INFINITY) {
			printf(" -Error: rlim_max is %d, should be "
			       "RLIM_INFINITY\n", rlim.rlim_cur);
			err = 1;
			err_count++;
		} else {
			printf(" -OK: rlim_max was kept untouched\n");
		}
	}
	if (!err) printf("Check OK\n");

	if (!err_count) printf("Test successful\n");
	else if (err_count == 1) printf("1 error occured\n");
	else printf("%d errors occured\n", err_count);
	
	return 0;
}
