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