chiark / gitweb /
buf.c: Step over terminating null byte.
[mLib] / 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
26.B "#include <mLib/mdup.h>"
27
28.BI "int mdup(mdup_fd *" v ", size_t " n ");"
29.SH DESCRIPTION
30The
31.B mdup
32function renumbers file descriptors, using the
33.BR dup (2)
34and
35.BR dup2 (2)
36system calls. Its arguments are a pointer
37.I v
38to a vector of
39.B mdup_fd
40structures, and the length
41.I n
42of this vector, in elements. The
43.B mdup_fd
44structure is defined as
45.VS
46typedef struct mdup_fd {
47 int cur;
48 int want;
49} mdup_fd;
50.VE
51Each `slot' (element) in the vector
52.I v
53represents a file. The slot's
54.B cur
55member names the current file descriptor for this file; the
56.B want
57member is the file descriptor to move it to. In order to keep a file
58alive when you don't care which descriptor it ends up with, set
59.I want
60= \-1. Several slots may specify the same
61.B cur
62descriptor; but they all have to declare different
63.BR want s
64(except that several slots may have
65.I want
66= \-1.
67.PP
68On successful exit, the function will have rearranged the file
69descriptors as requested. To reflect this, the
70.B cur
71members will all be set to match the
72.B want
73members (except where the latter are \-1).
74.PP
75If there is a failure, then some rearrangement may have been performed
76and some not; the
77.B cur
78members are set to reflect which file descriptors are to be used.
79.PP
80The old file descriptors are
81.IR closed .
82This is different from usual
83.BR dup (2)
84behaviour, of course, but essential for reliable error handling. If you
85want to keep a particular source file descriptor open as well as make a
86new copy then specify two slots with the same
87.BR cur ,
88one with
89.B want " = " cur
90and one with the desired output descriptor.
91.PP
92The
93.B mdup
94function is capable of arbitrary file descriptor remappings. In
95particular, it works correctly even if the desired remappings contain
96cycles.
97.SS "Background: the problem that mdup solves"
98The
99.B mdup
100function is intended to be used to adjust file descriptors prior to
101invoking one of the
102.B exec
103system calls. The standard use of
104.BR dup (2)
105to establish the child process's standard input/output/error files is
106prone to errors in the case where the newly opened file in fact already
107has one of the relevant file descriptors.
108.PP
109Consider the case where we want to run a process with separate pipes
110attached to each of the standard descriptors. Typical code looks like
111this.
112.VS
113#define P_INIT { \-1, \-1 }
114int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
115pid_t kid = -1;
116int i;
117
118if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
119if ((kid = fork()) < 0) goto error;
120if (!kid) {
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);
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
150#define P_INIT { \-1, \-1 }
151int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
152pid_t kid = -1;
153mdup_fd md[3];
154int i;
155
156if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
157if ((kid = fork()) < 0) goto error;
158if (!kid) {
159 if (close(p_in[1] || close(p_out[0]) || close(p_err[0]))
160 goto _exit(127);
161 md[0].cur = p_in[0]; md[0].want = STDIN_FILENO;
162 md[1].cur = p_out[1]; md[1].want = STDOUT_FILENO;
163 md[2].cur = p_err[1]; md[2].want = STDERR_FILENO;
164 if (mdup(md, 3)) _exit(127);
165 execvp("/bin/sh", "sh", "-c", "...", (char *)0);
166}
167\&...
168.VE
169One can see that, not only is the resulting program more correct, it's
170also simpler. Note that we close the unwanted ends of the pipes
171.I before
172invoking
173.BR mdup .
174Closing them afterwards risks interfering with the newly assigned
175descriptors which are meant to be passed to the child process. Note
176also that
177.B mdup
178has taken responsibility for closing the other descriptors for the
179wanted ends of the pipes.
180.SH "SEE ALSO"
181.BR dup (2),
182.BR dup2 (2),
183.BR mLib (3).
184.SH AUTHOR
185Mark Wooding, <mdw@distorted.org.uk>
186