@@@ man wip
[mLib] / sys / mdup.3
CommitLineData
b317b99d
MW
1.\" -*-nroff-*-
2.de VS
3.sp 1
4.RS
5.nf
6.ft B
7..
8.de VE
9.ft R
10.fi
11.RE
12.sp 1
13..
14.de hP
15.IP
16.ft B
17\h'-\w'\\$1\ 'u'\\$1\ \c
18.ft P
19..
20.ie t .ds o \(bu
21.el .ds o o
22.TH mdup 3 "4 January" "Straylight/Edgeware" "mLib utilities library"
23.SH NAME
24mdup \- renumber file descriptors
25.SH SYNOPSIS
8128dcdf 26.nf
b317b99d
MW
27.B "#include <mLib/mdup.h>"
28
adec5584 29.ta 2n
4729aa69 30.B "typedef struct {"
adec5584
MW
31.B " int cur;"
32.B " int want;"
4729aa69
MW
33.B "} mdup_fd;"
34
b317b99d 35.BI "int mdup(mdup_fd *" v ", size_t " n ");"
8128dcdf 36.fi
b317b99d
MW
37.SH DESCRIPTION
38The
39.B mdup
40function renumbers file descriptors, using the
41.BR dup (2)
42and
43.BR dup2 (2)
44system calls. Its arguments are a pointer
45.I v
46to a vector of
47.B mdup_fd
48structures, and the length
49.I n
4729aa69 50of this vector, in elements. Each `slot' (element) in the vector
b317b99d
MW
51.I v
52represents a file. The slot's
53.B cur
54member names the current file descriptor for this file; the
55.B want
56member is the file descriptor to move it to. In order to keep a file
57alive when you don't care which descriptor it ends up with, set
58.I want
59= \-1. Several slots may specify the same
60.B cur
61descriptor; but they all have to declare different
62.BR want s
63(except that several slots may have
64.I want
65= \-1.
66.PP
67On successful exit, the function will have rearranged the file
68descriptors as requested. To reflect this, the
69.B cur
70members will all be set to match the
71.B want
72members (except where the latter are \-1).
73.PP
74If there is a failure, then some rearrangement may have been performed
75and some not; the
76.B cur
77members are set to reflect which file descriptors are to be used.
78.PP
79The old file descriptors are
80.IR closed .
81This is different from usual
82.BR dup (2)
83behaviour, of course, but essential for reliable error handling. If you
84want to keep a particular source file descriptor open as well as make a
85new copy then specify two slots with the same
86.BR cur ,
87one with
88.B want " = " cur
89and one with the desired output descriptor.
90.PP
91The
92.B mdup
93function is capable of arbitrary file descriptor remappings. In
94particular, it works correctly even if the desired remappings contain
95cycles.
96.SS "Background: the problem that mdup solves"
97The
98.B mdup
99function is intended to be used to adjust file descriptors prior to
100invoking one of the
101.B exec
102system calls. The standard use of
103.BR dup (2)
104to establish the child process's standard input/output/error files is
105prone to errors in the case where the newly opened file in fact already
106has one of the relevant file descriptors.
107.PP
108Consider the case where we want to run a process with separate pipes
109attached to each of the standard descriptors. Typical code looks like
110this.
111.VS
112#define P_INIT { \-1, \-1 }
113int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
114pid_t kid = -1;
115int i;
116
117if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
118if ((kid = fork()) < 0) goto error;
119if (!kid) {
adec5584
MW
120.ta 2n 4n 2n+\w'\fBif ('u
121 if (dup2(p_in[0], STDIN_FILENO) < 0 ||
122 dup2(p_out[1], STDOUT_FILENO) < 0 ||
123 dup2(p_err[2], STDERR_FILENO) < 0 ||
124 close(p_in[0]) || close(p_out[0]) || close(p_err[0]) ||
125 close(p_in[1]) || close(p_out[1]) || close(p_err[1]))
126 _exit(127);
127 execvp("/bin/sh", "sh", "-c", "...", (char *)0);
b317b99d
MW
128}
129\&...
130.VE
131Now suppose that, in the parent process, the standard input, output and
132error descriptors are all initially closed. After the calls to
133.BR pipe (2),
134descriptors 0, 1, and 2 refer to
135.BR p_in[0] ,
136.BR p_in[1] ,
137and
138.B p_out[0]
139respectively. In the child process, the calls to
140.BR dup2 (2)
141rearrange these. But then the
142.BR close (2)
143calls will immediately close all three descriptors, before
144.BR exec ing
145the child.
146.PP
147Here's how to rewrite the above function using
148.BR mdup .
149.VS
adec5584 150.ta 2n 4n 2n+\w'\fBmd[0].cur = p_out[1]; 'u
b317b99d
MW
151#define P_INIT { \-1, \-1 }
152int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
153pid_t kid = -1;
154mdup_fd md[3];
155int i;
156
157if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
158if ((kid = fork()) < 0) goto error;
159if (!kid) {
adec5584
MW
160 if (close(p_in[1] || close(p_out[0]) || close(p_err[0]))
161 goto _exit(127);
162 md[0].cur = p_in[0]; md[0].want = STDIN_FILENO;
163 md[1].cur = p_out[1]; md[1].want = STDOUT_FILENO;
164 md[2].cur = p_err[1]; md[2].want = STDERR_FILENO;
165 if (mdup(md, 3)) _exit(127);
166 execvp("/bin/sh", "sh", "-c", "...", (char *)0);
b317b99d
MW
167}
168\&...
169.VE
170One can see that, not only is the resulting program more correct, it's
171also simpler. Note that we close the unwanted ends of the pipes
172.I before
173invoking
174.BR mdup .
175Closing them afterwards risks interfering with the newly assigned
176descriptors which are meant to be passed to the child process. Note
177also that
178.B mdup
179has taken responsibility for closing the other descriptors for the
180wanted ends of the pipes.
181.SH "SEE ALSO"
182.BR dup (2),
183.BR dup2 (2),
184.BR mLib (3).
185.SH AUTHOR
186Mark Wooding, <mdw@distorted.org.uk>
187