1/*	$OpenBSD: dup2_self.c,v 1.3 2003/07/31 21:48:08 deraadt Exp $	*/
2/*
3 *	Written by Artur Grabowski <art@openbsd.org> 2002 Public Domain.
4 */
5#include <stdio.h>
6#include <stdlib.h>
7#include <unistd.h>
8#include <err.h>
9#include <fcntl.h>
10
11/*
12 * We're testing a small tweak in dup2 semantics. Normally dup and dup2
13 * will clear the close-on-exec flag on the new fd (which appears to be
14 * an implementation mistake from start and not some planned behavior).
15 * In todays implementations of dup and dup2 we have to make an effort
16 * to really clear that flag. But all tested implementations of dup2 have
17 * another tweak. If we dup2(old, new) when old == new, the syscall
18 * short-circuits and returns early (because there is no need to do all
19 * the work (and there is a risk for serious mistakes)). So although the
20 * docs say that dup2 should "take 'old', close 'new' perform a dup(2) of
21 * 'old' into 'new'" the docs are not really followed because close-on-exec
22 * is not cleared on 'new'.
23 *
24 * Since everyone has this bug, we pretend that this is the way it is
25 * supposed to be and test here that it really works that way.
26 *
27 * This is a fine example on where two separate implementation fuckups
28 * take out each other and make the end-result the way it was meant to be.
29 */
30
31int
32main(int argc, char *argv[])
33{
34	int orgfd, fd1, fd2;
35	char temp[] = "/tmp/dup2XXXXXXXXX";
36
37	if ((orgfd = mkstemp(temp)) < 0)
38		err(1, "mkstemp");
39	remove(temp);
40
41	if (ftruncate(orgfd, 1024) != 0)
42		err(1, "ftruncate");
43
44	if ((fd1 = dup(orgfd)) < 0)
45		err(1, "dup");
46
47	/* Set close-on-exec */
48	if (fcntl(fd1, F_SETFD, 1) != 0)
49		err(1, "fcntl(F_SETFD)");
50
51	if ((fd2 = dup2(fd1, fd1)) < 0)
52		err(1, "dup2");
53
54	/* Test 1: Do we get the right fd? */
55	if (fd2 != fd1)
56		errx(1, "dup2 didn't give us the right fd");
57
58	/* Test 2: Was close-on-exec cleared? */
59	if (fcntl(fd2, F_GETFD) == 0)
60		errx(1, "dup2 cleared close-on-exec");
61
62	return 0;
63}
64