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