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 | .. | |
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 |