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 | |
8128dcdf | 26 | .nf |
b317b99d MW |
27 | .B "#include <mLib/mdup.h>" |
28 | ||
adec5584 | 29 | .ta 2n |
4729aa69 | 30 | .B "typedef struct {" |
adec5584 MW |
31 | .B " int cur;" |
32 | .B " int want;" | |
4729aa69 MW |
33 | .B "} mdup_fd;" |
34 | ||
b317b99d | 35 | .BI "int mdup(mdup_fd *" v ", size_t " n ");" |
8128dcdf | 36 | .fi |
b317b99d MW |
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 | |
4729aa69 | 50 | of this vector, in elements. Each `slot' (element) in the vector |
b317b99d MW |
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) { | |
adec5584 MW |
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); | |
b317b99d MW |
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 | |
adec5584 | 150 | .ta 2n 4n 2n+\w'\fBmd[0].cur = p_out[1]; 'u |
b317b99d MW |
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) { | |
adec5584 MW |
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); | |
b317b99d MW |
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 |