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