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