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