Loading...
#include <sys/resource.h>
#include <sys/wait.h>

#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <unistd.h>
#include <util.h>

#include <darwintest.h>
#include <TargetConditionals.h>

T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));

T_DECL(forkpty_forkfail,
    "Check for fd leak when fork() fails",
    T_META_CHECK_LEAKS(false),
    T_META_ENABLED(TARGET_OS_OSX))
{
	struct rlimit orl, nrl;
	struct passwd *pwd;
	pid_t pid;
	int prevfd, fd, pty;

	T_SETUPBEGIN;
	if (geteuid() == 0) {
		/* the setrlimit() trick won't work if we're root */
		T_ASSERT_NOTNULL(pwd = getpwnam("nobody"), NULL);
		T_ASSERT_POSIX_SUCCESS(setgid(pwd->pw_gid), NULL);
		T_ASSERT_POSIX_SUCCESS(setuid(pwd->pw_uid), NULL);
	}
	T_ASSERT_POSIX_SUCCESS(getrlimit(RLIMIT_NPROC, &orl), NULL);
	nrl = orl;
	nrl.rlim_cur = 1;
	T_ASSERT_POSIX_SUCCESS(setrlimit(RLIMIT_NPROC, &nrl), NULL);
	T_ASSERT_POSIX_SUCCESS(fd = dup(0), NULL);
	T_ASSERT_POSIX_SUCCESS(close(fd), NULL);
	T_SETUPEND;
	pid = forkpty(&pty, NULL, NULL, NULL);
	if (pid == 0) {
		/* child - fork() unexpectedly succeeded */
		_exit(0);
	}
	T_EXPECT_POSIX_FAILURE(pid, EAGAIN, "expected fork() to fail");
	if (pid > 0) {
		/* parent - fork() unexpectedly succeeded */
		(void)waitpid(pid, NULL, 0);
	}
	prevfd = fd;
	T_ASSERT_POSIX_SUCCESS(fd = dup(0), NULL);
	T_EXPECT_EQ(fd, prevfd, "expected same fd as previously");
}