3 .\" Manual for descriptor juggling
5 .\" (c) 2009, 2023, 2024 Straylight/Edgeware
8 .\"----- Licensing notice ---------------------------------------------------
10 .\" This file is part of the mLib utilities library.
12 .\" mLib is free software: you can redistribute it and/or modify it under
13 .\" the terms of the GNU Library General Public License as published by
14 .\" the Free Software Foundation; either version 2 of the License, or (at
15 .\" your option) any later version.
17 .\" mLib is distributed in the hope that it will be useful, but WITHOUT
18 .\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 .\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 .\" License for more details.
22 .\" You should have received a copy of the GNU Library General Public
23 .\" License along with mLib. If not, write to the Free Software
24 .\" Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
27 .\"--------------------------------------------------------------------------
28 .so ../defs.man \" @@@PRE@@@
30 .\"--------------------------------------------------------------------------
31 .TH mdup 3mLib "4 January" "Straylight/Edgeware" "mLib utilities library"
34 .\"--------------------------------------------------------------------------
36 mdup \- renumber file descriptors
38 .\"--------------------------------------------------------------------------
42 .B "#include <mLib/mdup.h>"
50 .BI "int mdup(mdup_fd *" v ", size_t " n ");"
53 .\"--------------------------------------------------------------------------
58 function renumbers file descriptors, using the
62 system calls. Its arguments are a pointer
66 structures, and the length
68 of this vector, in elements. Each `slot' (element) in the vector
70 represents a file. The slot's
72 member names the current file descriptor for this file; the
74 member is the file descriptor to move it to. In order to keep a file
75 alive when you don't care which descriptor it ends up with, set
77 = \-1. Several slots may specify the same
79 descriptor; but they all have to declare different
81 (except that several slots may have
85 On successful exit, the function will have rearranged the file
86 descriptors as requested. To reflect this, the
88 members will all be set to match the
90 members (except where the latter are \-1).
92 If there is a failure, then some rearrangement may have been performed
95 members are set to reflect which file descriptors are to be used.
97 The old file descriptors are
99 This is different from usual
101 behaviour, of course, but essential for reliable error handling. If you
102 want to keep a particular source file descriptor open as well as make a
103 new copy then specify two slots with the same
107 and one with the desired output descriptor.
111 function is capable of arbitrary file descriptor remappings. In
112 particular, it works correctly even if the desired remappings contain
115 .SS "Background: the problem that mdup solves"
118 function is intended to be used to adjust file descriptors prior to
121 system calls. The standard use of
123 to establish the child process's standard input/output/error files is
124 prone to errors in the case where the newly opened file in fact already
125 has one of the relevant file descriptors.
127 Consider the case where we want to run a process with separate pipes
128 attached to each of the standard descriptors. Typical code looks like
131 #define P_INIT { \-1, \-1 }
132 int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
136 if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
137 if ((kid = fork()) < 0) goto error;
139 .ta 2n 4n 2n+\w'\fBif ('u
140 if (dup2(p_in[0], STDIN_FILENO) < 0 ||
141 dup2(p_out[1], STDOUT_FILENO) < 0 ||
142 dup2(p_err[2], STDERR_FILENO) < 0 ||
143 close(p_in[0]) || close(p_out[0]) || close(p_err[0]) ||
144 close(p_in[1]) || close(p_out[1]) || close(p_err[1]))
146 execvp("/bin/sh", "sh", "-c", "...", (char *)0);
150 Now suppose that, in the parent process, the standard input, output and
151 error descriptors are all initially closed. After the calls to
153 descriptors 0, 1, and 2 refer to
158 respectively. In the child process, the calls to
160 rearrange these. But then the
162 calls will immediately close all three descriptors, before
166 Here's how to rewrite the above function using
169 .ta 2n 4n 2n+\w'\fBmd[0].cur = p_out[1]; 'u
170 #define P_INIT { \-1, \-1 }
171 int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
176 if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
177 if ((kid = fork()) < 0) goto error;
179 if (close(p_in[1] || close(p_out[0]) || close(p_err[0]))
181 md[0].cur = p_in[0]; md[0].want = STDIN_FILENO;
182 md[1].cur = p_out[1]; md[1].want = STDOUT_FILENO;
183 md[2].cur = p_err[1]; md[2].want = STDERR_FILENO;
184 if (mdup(md, 3)) _exit(127);
185 execvp("/bin/sh", "sh", "-c", "...", (char *)0);
189 One can see that, not only is the resulting program more correct, it's
190 also simpler. Note that we close the unwanted ends of the pipes
194 Closing them afterwards risks interfering with the newly assigned
195 descriptors which are meant to be passed to the child process. Note
198 has taken responsibility for closing the other descriptors for the
199 wanted ends of the pipes.
201 .\"--------------------------------------------------------------------------
208 .\"--------------------------------------------------------------------------
211 Mark Wooding, <mdw@distorted.org.uk>
213 .\"----- That's all, folks --------------------------------------------------