17 \h'-\w'\\$1\ 'u'\\$1\ \c
22 .TH mdup 3 "4 January" "Straylight/Edgeware" "mLib utilities library"
24 mdup \- renumber file descriptors
26 .B "#include <mLib/mdup.h>"
28 .BI "int mdup(mdup_fd *" v ", size_t " n ");"
32 function renumbers file descriptors, using the
36 system calls. Its arguments are a pointer
40 structures, and the length
42 of this vector, in elements. The
44 structure is defined as
46 typedef struct mdup_fd {
51 Each `slot' (element) in the vector
53 represents a file. The slot's
55 member names the current file descriptor for this file; the
57 member is the file descriptor to move it to. In order to keep a file
58 alive when you don't care which descriptor it ends up with, set
60 = \-1. Several slots may specify the same
62 descriptor; but they all have to declare different
64 (except that several slots may have
68 On successful exit, the function will have rearranged the file
69 descriptors as requested. To reflect this, the
71 members will all be set to match the
73 members (except where the latter are \-1).
75 If there is a failure, then some rearrangement may have been performed
78 members are set to reflect which file descriptors are to be used.
80 The old file descriptors are
82 This is different from usual
84 behaviour, of course, but essential for reliable error handling. If you
85 want to keep a particular source file descriptor open as well as make a
86 new copy then specify two slots with the same
90 and one with the desired output descriptor.
94 function is capable of arbitrary file descriptor remappings. In
95 particular, it works correctly even if the desired remappings contain
97 .SS "Background: the problem that mdup solves"
100 function is intended to be used to adjust file descriptors prior to
103 system calls. The standard use of
105 to establish the child process's standard input/output/error files is
106 prone to errors in the case where the newly opened file in fact already
107 has one of the relevant file descriptors.
109 Consider the case where we want to run a process with separate pipes
110 attached to each of the standard descriptors. Typical code looks like
113 #define P_INIT { \-1, \-1 }
114 int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
118 if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
119 if ((kid = fork()) < 0) goto error;
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 #define P_INIT { \-1, \-1 }
151 int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
156 if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
157 if ((kid = fork()) < 0) goto error;
159 if (close(p_in[1] || close(p_out[0]) || close(p_err[0]))
161 md[0].cur = p_in[0]; md[0].want = STDIN_FILENO;
162 md[1].cur = p_out[1]; md[1].want = STDOUT_FILENO;
163 md[2].cur = p_err[1]; md[2].want = STDERR_FILENO;
164 if (mdup(md, 3)) _exit(127);
165 execvp("/bin/sh", "sh", "-c", "...", (char *)0);
169 One can see that, not only is the resulting program more correct, it's
170 also simpler. Note that we close the unwanted ends of the pipes
174 Closing them afterwards risks interfering with the newly assigned
175 descriptors which are meant to be passed to the child process. Note
178 has taken responsibility for closing the other descriptors for the
179 wanted ends of the pipes.
185 Mark Wooding, <mdw@distorted.org.uk>