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