chiark / gitweb /
@@@ fltfmt fettling
[mLib] / sys / mdup.3.in
1 .\" -*-nroff-*-
2 .\"
3 .\" Manual for descriptor juggling
4 .\"
5 .\" (c) 2009, 2023, 2024 Straylight/Edgeware
6 .\"
7 .
8 .\"----- Licensing notice ---------------------------------------------------
9 .\"
10 .\" This file is part of the mLib utilities library.
11 .\"
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.
16 .\"
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.
21 .\"
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,
25 .\" USA.
26 .
27 .\"--------------------------------------------------------------------------
28 .so ../defs.man \" @@@PRE@@@
29 .
30 .\"--------------------------------------------------------------------------
31 .TH mdup 3mLib "4 January" "Straylight/Edgeware" "mLib utilities library"
32 .\" @mdup
33 .
34 .\"--------------------------------------------------------------------------
35 .SH NAME
36 mdup \- renumber file descriptors
37 .
38 .\"--------------------------------------------------------------------------
39 .SH SYNOPSIS
40 .
41 .nf
42 .B "#include <mLib/mdup.h>"
43 .PP
44 .ta 2n
45 .B "typedef struct {"
46 .B "    int cur;"
47 .B "    int want;"
48 .B "} mdup_fd;"
49 .PP
50 .BI "int mdup(mdup_fd *" v ", size_t " n ");"
51 .fi
52 .
53 .\"--------------------------------------------------------------------------
54 .SH DESCRIPTION
55 .
56 The
57 .B mdup
58 function renumbers file descriptors, using the
59 .BR dup (2)
60 and
61 .BR dup2 (2)
62 system calls.  Its arguments are a pointer
63 .I v
64 to a vector of
65 .B mdup_fd
66 structures, and the length
67 .I n
68 of this vector, in elements.  Each `slot' (element) in the vector
69 .I v
70 represents a file.  The slot's
71 .B cur
72 member names the current file descriptor for this file; the
73 .B want
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
76 .I want
77 = \-1.  Several slots may specify the same
78 .B cur
79 descriptor; but they all have to declare different
80 .BR want s
81 (except that several slots may have
82 .I want
83 = \-1.
84 .PP
85 On successful exit, the function will have rearranged the file
86 descriptors as requested.  To reflect this, the
87 .B cur
88 members will all be set to match the
89 .B want
90 members (except where the latter are \-1).
91 .PP
92 If there is a failure, then some rearrangement may have been performed
93 and some not; the
94 .B cur
95 members are set to reflect which file descriptors are to be used.
96 .PP
97 The old file descriptors are
98 .IR closed .
99 This is different from usual
100 .BR dup (2)
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
104 .BR cur ,
105 one with
106 .B want " = " cur
107 and one with the desired output descriptor.
108 .PP
109 The
110 .B mdup
111 function is capable of arbitrary file descriptor remappings.  In
112 particular, it works correctly even if the desired remappings contain
113 cycles.
114 .
115 .SS "Background: the problem that mdup solves"
116 The
117 .B mdup
118 function is intended to be used to adjust file descriptors prior to
119 invoking one of the
120 .B exec
121 system calls.  The standard use of
122 .BR dup (2)
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.
126 .PP
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
129 this.
130 .VS
131 #define P_INIT { \-1, \-1 }
132 int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
133 pid_t kid = -1;
134 int i;
135 .VP
136 if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
137 if ((kid = fork()) < 0) goto error;
138 if (!kid) {
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]))
145                 _exit(127);
146         execvp("/bin/sh", "sh", "-c", "...", (char *)0);
147 }
148 \&...
149 .VE
150 Now suppose that, in the parent process, the standard input, output and
151 error descriptors are all initially closed.  After the calls to
152 .BR pipe (2),
153 descriptors 0, 1, and 2 refer to
154 .BR p_in[0] ,
155 .BR p_in[1] ,
156 and
157 .B p_out[0]
158 respectively.  In the child process, the calls to
159 .BR dup2 (2)
160 rearrange these.  But then the
161 .BR close (2)
162 calls will immediately close all three descriptors, before
163 .BR exec ing
164 the child.
165 .PP
166 Here's how to rewrite the above function using
167 .BR mdup .
168 .VS
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;
172 pid_t kid = -1;
173 mdup_fd md[3];
174 int i;
175 .VP
176 if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
177 if ((kid = fork()) < 0) goto error;
178 if (!kid) {
179         if (close(p_in[1] || close(p_out[0]) || close(p_err[0]))
180                 goto _exit(127);
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);
186 }
187 \&...
188 .VE
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
191 .I before
192 invoking
193 .BR mdup .
194 Closing them afterwards risks interfering with the newly assigned
195 descriptors which are meant to be passed to the child process.  Note
196 also that
197 .B mdup
198 has taken responsibility for closing the other descriptors for the
199 wanted ends of the pipes.
200 .
201 .\"--------------------------------------------------------------------------
202 .SH "SEE ALSO"
203 .
204 .BR dup (2),
205 .BR dup2 (2),
206 .BR mLib (3).
207 .
208 .\"--------------------------------------------------------------------------
209 .SH AUTHOR
210 .
211 Mark Wooding, <mdw@distorted.org.uk>
212 .
213 .\"----- That's all, folks --------------------------------------------------