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