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