1 | /* |
---|
2 | * fdflag test suite. |
---|
3 | * |
---|
4 | * Written by Russ Allbery <rra@stanford.edu> |
---|
5 | * Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University |
---|
6 | * |
---|
7 | * See LICENSE for licensing terms. |
---|
8 | */ |
---|
9 | |
---|
10 | #include <config.h> |
---|
11 | #include <portable/system.h> |
---|
12 | #include <portable/socket.h> |
---|
13 | |
---|
14 | #include <errno.h> |
---|
15 | #include <sys/wait.h> |
---|
16 | |
---|
17 | #include <tests/tap/basic.h> |
---|
18 | #include <util/util.h> |
---|
19 | |
---|
20 | |
---|
21 | int |
---|
22 | main(void) |
---|
23 | { |
---|
24 | int master, data, out1, out2; |
---|
25 | socklen_t size; |
---|
26 | ssize_t status; |
---|
27 | struct sockaddr_in sin; |
---|
28 | pid_t child; |
---|
29 | char buffer[] = "D"; |
---|
30 | |
---|
31 | plan(8); |
---|
32 | |
---|
33 | /* Parent will create the socket first to get the port number. */ |
---|
34 | memset(&sin, '\0', sizeof(sin)); |
---|
35 | sin.sin_family = AF_INET; |
---|
36 | master = socket(AF_INET, SOCK_STREAM, 0); |
---|
37 | if (master == -1) |
---|
38 | sysbail("socket creation failed"); |
---|
39 | if (bind(master, (struct sockaddr *) &sin, sizeof(sin)) < 0) |
---|
40 | sysbail("bind failed"); |
---|
41 | size = sizeof(sin); |
---|
42 | if (getsockname(master, (struct sockaddr *) &sin, &size) < 0) |
---|
43 | sysbail("getsockname failed"); |
---|
44 | if (listen(master, 1) < 0) |
---|
45 | sysbail("listen failed"); |
---|
46 | |
---|
47 | /* Duplicate standard output to test close-on-exec. */ |
---|
48 | out1 = 8; |
---|
49 | out2 = 9; |
---|
50 | if (dup2(fileno(stdout), out1) < 0) |
---|
51 | sysbail("cannot dup stdout to fd 8"); |
---|
52 | if (dup2(fileno(stdout), out2) < 0) |
---|
53 | sysbail("cannot dup stdout to fd 9"); |
---|
54 | ok(fdflag_close_exec(out1, true), "set fd 8 to close-on-exec"); |
---|
55 | ok(fdflag_close_exec(out2, true), "set fd 9 to close-on-exec"); |
---|
56 | ok(fdflag_close_exec(out2, false), "set fd 9 back to regular"); |
---|
57 | |
---|
58 | /* |
---|
59 | * Fork, child closes the open socket and then tries to connect, parent |
---|
60 | * calls listen() and accept() on it. Parent will then set the socket |
---|
61 | * non-blocking and try to read from it to see what happens, then write to |
---|
62 | * the socket and close it, triggering the child close and exit. |
---|
63 | * |
---|
64 | * Before the child exits, it will exec a shell that will print "no" to |
---|
65 | * the duplicate of stdout that the parent created and then the ok to |
---|
66 | * regular stdout. |
---|
67 | */ |
---|
68 | child = fork(); |
---|
69 | if (child < 0) { |
---|
70 | sysbail("fork failed"); |
---|
71 | } else if (child != 0) { |
---|
72 | size = sizeof(sin); |
---|
73 | data = accept(master, (struct sockaddr *) &sin, &size); |
---|
74 | close(master); |
---|
75 | if (data < 0) |
---|
76 | sysbail("accept failed"); |
---|
77 | ok(fdflag_nonblocking(data, true), "set socket non-blocking"); |
---|
78 | status = read(data, buffer, sizeof(buffer)); |
---|
79 | is_int(-1, status, "got -1 from non-blocking read"); |
---|
80 | is_int(EAGAIN, errno, "...with EAGAIN errno"); |
---|
81 | write(data, buffer, sizeof(buffer)); |
---|
82 | close(data); |
---|
83 | } else { |
---|
84 | data = socket(AF_INET, SOCK_STREAM, 0); |
---|
85 | if (data < 0) |
---|
86 | sysbail("child socket failed"); |
---|
87 | if (connect(data, (struct sockaddr *) &sin, sizeof(sin)) < 0) |
---|
88 | sysbail("child connect failed"); |
---|
89 | read(data, buffer, sizeof(buffer)); |
---|
90 | fclose(stderr); |
---|
91 | execlp("sh", "sh", "-c", |
---|
92 | "printf 'not ' >&8; echo ok 7; echo 'ok 8' >&9", (char *) 0); |
---|
93 | sysbail("exec failed"); |
---|
94 | } |
---|
95 | waitpid(child, NULL, 0); |
---|
96 | exit(0); |
---|
97 | } |
---|