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>"
29 .BI "int mdup(mdup_fd *" v ", size_t " n ");"
34 function renumbers file descriptors, using the
38 system calls. Its arguments are a pointer
42 structures, and the length
44 of this vector, in elements. The
46 structure is defined as
48 typedef struct mdup_fd {
53 Each `slot' (element) in the vector
55 represents a file. The slot's
57 member names the current file descriptor for this file; the
59 member is the file descriptor to move it to. In order to keep a file
60 alive when you don't care which descriptor it ends up with, set
62 = \-1. Several slots may specify the same
64 descriptor; but they all have to declare different
66 (except that several slots may have
70 On successful exit, the function will have rearranged the file
71 descriptors as requested. To reflect this, the
73 members will all be set to match the
75 members (except where the latter are \-1).
77 If there is a failure, then some rearrangement may have been performed
80 members are set to reflect which file descriptors are to be used.
82 The old file descriptors are
84 This is different from usual
86 behaviour, of course, but essential for reliable error handling. If you
87 want to keep a particular source file descriptor open as well as make a
88 new copy then specify two slots with the same
92 and one with the desired output descriptor.
96 function is capable of arbitrary file descriptor remappings. In
97 particular, it works correctly even if the desired remappings contain
99 .SS "Background: the problem that mdup solves"
102 function is intended to be used to adjust file descriptors prior to
105 system calls. The standard use of
107 to establish the child process's standard input/output/error files is
108 prone to errors in the case where the newly opened file in fact already
109 has one of the relevant file descriptors.
111 Consider the case where we want to run a process with separate pipes
112 attached to each of the standard descriptors. Typical code looks like
115 #define P_INIT { \-1, \-1 }
116 int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
120 if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
121 if ((kid = fork()) < 0) goto error;
123 if (dup2(p_in[0], STDIN_FILENO) < 0 ||
124 dup2(p_out[1], STDOUT_FILENO) < 0 ||
125 dup2(p_err[2], STDERR_FILENO) < 0 ||
126 close(p_in[0]) || close(p_out[0]) || close(p_err[0]) ||
127 close(p_in[1]) || close(p_out[1]) || close(p_err[1]))
129 execvp("/bin/sh", "sh", "-c", "...", (char *)0);
133 Now suppose that, in the parent process, the standard input, output and
134 error descriptors are all initially closed. After the calls to
136 descriptors 0, 1, and 2 refer to
141 respectively. In the child process, the calls to
143 rearrange these. But then the
145 calls will immediately close all three descriptors, before
149 Here's how to rewrite the above function using
152 #define P_INIT { \-1, \-1 }
153 int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
158 if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
159 if ((kid = fork()) < 0) goto error;
161 if (close(p_in[1] || close(p_out[0]) || close(p_err[0]))
163 md[0].cur = p_in[0]; md[0].want = STDIN_FILENO;
164 md[1].cur = p_out[1]; md[1].want = STDOUT_FILENO;
165 md[2].cur = p_err[1]; md[2].want = STDERR_FILENO;
166 if (mdup(md, 3)) _exit(127);
167 execvp("/bin/sh", "sh", "-c", "...", (char *)0);
171 One can see that, not only is the resulting program more correct, it's
172 also simpler. Note that we close the unwanted ends of the pipes
176 Closing them afterwards risks interfering with the newly assigned
177 descriptors which are meant to be passed to the child process. Note
180 has taken responsibility for closing the other descriptors for the
181 wanted ends of the pipes.
187 Mark Wooding, <mdw@distorted.org.uk>