17 \h'-\w'\\$1\ 'u'\\$1\ \c
22 .TH mdup 3 "4 January" "Straylight/Edgeware" "mLib utilities library"
24 mdup \- renumber file descriptors
27 .B "#include <mLib/mdup.h>"
35 .BI "int mdup(mdup_fd *" v ", size_t " n ");"
40 function renumbers file descriptors, using the
44 system calls. Its arguments are a pointer
48 structures, and the length
50 of this vector, in elements. Each `slot' (element) in the vector
52 represents a file. The slot's
54 member names the current file descriptor for this file; the
56 member is the file descriptor to move it to. In order to keep a file
57 alive when you don't care which descriptor it ends up with, set
59 = \-1. Several slots may specify the same
61 descriptor; but they all have to declare different
63 (except that several slots may have
67 On successful exit, the function will have rearranged the file
68 descriptors as requested. To reflect this, the
70 members will all be set to match the
72 members (except where the latter are \-1).
74 If there is a failure, then some rearrangement may have been performed
77 members are set to reflect which file descriptors are to be used.
79 The old file descriptors are
81 This is different from usual
83 behaviour, of course, but essential for reliable error handling. If you
84 want to keep a particular source file descriptor open as well as make a
85 new copy then specify two slots with the same
89 and one with the desired output descriptor.
93 function is capable of arbitrary file descriptor remappings. In
94 particular, it works correctly even if the desired remappings contain
96 .SS "Background: the problem that mdup solves"
99 function is intended to be used to adjust file descriptors prior to
102 system calls. The standard use of
104 to establish the child process's standard input/output/error files is
105 prone to errors in the case where the newly opened file in fact already
106 has one of the relevant file descriptors.
108 Consider the case where we want to run a process with separate pipes
109 attached to each of the standard descriptors. Typical code looks like
112 #define P_INIT { \-1, \-1 }
113 int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
117 if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
118 if ((kid = fork()) < 0) goto error;
120 .ta 2n 4n 2n+\w'\fBif ('u
121 if (dup2(p_in[0], STDIN_FILENO) < 0 ||
122 dup2(p_out[1], STDOUT_FILENO) < 0 ||
123 dup2(p_err[2], STDERR_FILENO) < 0 ||
124 close(p_in[0]) || close(p_out[0]) || close(p_err[0]) ||
125 close(p_in[1]) || close(p_out[1]) || close(p_err[1]))
127 execvp("/bin/sh", "sh", "-c", "...", (char *)0);
131 Now suppose that, in the parent process, the standard input, output and
132 error descriptors are all initially closed. After the calls to
134 descriptors 0, 1, and 2 refer to
139 respectively. In the child process, the calls to
141 rearrange these. But then the
143 calls will immediately close all three descriptors, before
147 Here's how to rewrite the above function using
150 .ta 2n 4n 2n+\w'\fBmd[0].cur = p_out[1]; 'u
151 #define P_INIT { \-1, \-1 }
152 int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
157 if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
158 if ((kid = fork()) < 0) goto error;
160 if (close(p_in[1] || close(p_out[0]) || close(p_err[0]))
162 md[0].cur = p_in[0]; md[0].want = STDIN_FILENO;
163 md[1].cur = p_out[1]; md[1].want = STDOUT_FILENO;
164 md[2].cur = p_err[1]; md[2].want = STDERR_FILENO;
165 if (mdup(md, 3)) _exit(127);
166 execvp("/bin/sh", "sh", "-c", "...", (char *)0);
170 One can see that, not only is the resulting program more correct, it's
171 also simpler. Note that we close the unwanted ends of the pipes
175 Closing them afterwards risks interfering with the newly assigned
176 descriptors which are meant to be passed to the child process. Note
179 has taken responsibility for closing the other descriptors for the
180 wanted ends of the pipes.
186 Mark Wooding, <mdw@distorted.org.uk>