@@@ much mess, mostly manpages
[mLib] / sys / mdup.3.in
CommitLineData
b317b99d 1.\" -*-nroff-*-
c4ccbbf9
MW
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.\"--------------------------------------------------------------------------
b317b99d
MW
35.SH NAME
36mdup \- renumber file descriptors
c4ccbbf9
MW
37.
38.\"--------------------------------------------------------------------------
b317b99d 39.SH SYNOPSIS
c4ccbbf9 40.
8128dcdf 41.nf
b317b99d 42.B "#include <mLib/mdup.h>"
d056fbdf 43.PP
adec5584 44.ta 2n
4729aa69 45.B "typedef struct {"
adec5584
MW
46.B " int cur;"
47.B " int want;"
4729aa69 48.B "} mdup_fd;"
d056fbdf 49.PP
b317b99d 50.BI "int mdup(mdup_fd *" v ", size_t " n ");"
8128dcdf 51.fi
c4ccbbf9
MW
52.
53.\"--------------------------------------------------------------------------
b317b99d 54.SH DESCRIPTION
c4ccbbf9 55.
b317b99d
MW
56The
57.B mdup
58function renumbers file descriptors, using the
59.BR dup (2)
60and
61.BR dup2 (2)
62system calls. Its arguments are a pointer
63.I v
64to a vector of
65.B mdup_fd
66structures, and the length
67.I n
4729aa69 68of this vector, in elements. Each `slot' (element) in the vector
b317b99d
MW
69.I v
70represents a file. The slot's
71.B cur
72member names the current file descriptor for this file; the
73.B want
74member is the file descriptor to move it to. In order to keep a file
75alive 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
79descriptor; 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
85On successful exit, the function will have rearranged the file
86descriptors as requested. To reflect this, the
87.B cur
88members will all be set to match the
89.B want
90members (except where the latter are \-1).
91.PP
92If there is a failure, then some rearrangement may have been performed
93and some not; the
94.B cur
95members are set to reflect which file descriptors are to be used.
96.PP
97The old file descriptors are
98.IR closed .
99This is different from usual
100.BR dup (2)
101behaviour, of course, but essential for reliable error handling. If you
102want to keep a particular source file descriptor open as well as make a
103new copy then specify two slots with the same
104.BR cur ,
105one with
106.B want " = " cur
107and one with the desired output descriptor.
108.PP
109The
110.B mdup
111function is capable of arbitrary file descriptor remappings. In
112particular, it works correctly even if the desired remappings contain
113cycles.
c4ccbbf9 114.
b317b99d
MW
115.SS "Background: the problem that mdup solves"
116The
117.B mdup
118function is intended to be used to adjust file descriptors prior to
119invoking one of the
120.B exec
121system calls. The standard use of
122.BR dup (2)
123to establish the child process's standard input/output/error files is
124prone to errors in the case where the newly opened file in fact already
125has one of the relevant file descriptors.
126.PP
127Consider the case where we want to run a process with separate pipes
128attached to each of the standard descriptors. Typical code looks like
129this.
130.VS
131#define P_INIT { \-1, \-1 }
132int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
133pid_t kid = -1;
134int i;
d056fbdf 135.VP
b317b99d
MW
136if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
137if ((kid = fork()) < 0) goto error;
138if (!kid) {
adec5584
MW
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);
b317b99d
MW
147}
148\&...
149.VE
150Now suppose that, in the parent process, the standard input, output and
151error descriptors are all initially closed. After the calls to
152.BR pipe (2),
153descriptors 0, 1, and 2 refer to
154.BR p_in[0] ,
155.BR p_in[1] ,
156and
157.B p_out[0]
158respectively. In the child process, the calls to
159.BR dup2 (2)
160rearrange these. But then the
161.BR close (2)
162calls will immediately close all three descriptors, before
163.BR exec ing
164the child.
165.PP
166Here's how to rewrite the above function using
167.BR mdup .
168.VS
adec5584 169.ta 2n 4n 2n+\w'\fBmd[0].cur = p_out[1]; 'u
b317b99d
MW
170#define P_INIT { \-1, \-1 }
171int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
172pid_t kid = -1;
173mdup_fd md[3];
174int i;
d056fbdf 175.VP
b317b99d
MW
176if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
177if ((kid = fork()) < 0) goto error;
178if (!kid) {
adec5584
MW
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);
b317b99d
MW
186}
187\&...
188.VE
189One can see that, not only is the resulting program more correct, it's
190also simpler. Note that we close the unwanted ends of the pipes
191.I before
192invoking
193.BR mdup .
194Closing them afterwards risks interfering with the newly assigned
195descriptors which are meant to be passed to the child process. Note
196also that
197.B mdup
198has taken responsibility for closing the other descriptors for the
199wanted ends of the pipes.
c4ccbbf9
MW
200.
201.\"--------------------------------------------------------------------------
b317b99d 202.SH "SEE ALSO"
c4ccbbf9 203.
b317b99d
MW
204.BR dup (2),
205.BR dup2 (2),
206.BR mLib (3).
c4ccbbf9
MW
207.
208.\"--------------------------------------------------------------------------
b317b99d 209.SH AUTHOR
c4ccbbf9 210.
b317b99d 211Mark Wooding, <mdw@distorted.org.uk>
c4ccbbf9
MW
212.
213.\"----- That's all, folks --------------------------------------------------