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