Expunge revision histories in files.
[fwd] / endpt.c
1 /* -*-c-*-
2 *
3 * $Id: endpt.c,v 1.4 2004/04/08 01:36:25 mdw Exp $
4 *
5 * Generic endpoint abstraction
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10 /*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of the `fw' port forwarder.
13 *
14 * `fw' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * `fw' is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with `fw'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 /*----- Header files ------------------------------------------------------*/
30
31 #include "config.h"
32
33 #include <errno.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41
42 #include <mLib/alloc.h>
43
44 #include "chan.h"
45 #include "endpt.h"
46
47 /*----- Data structures ---------------------------------------------------*/
48
49 /* --- Pairs of channels --- *
50 *
51 * Channels are always allocated and freed in pairs. It makes sense to keep
52 * the pair together. (It also wastes less memory.)
53 */
54
55 typedef struct chanpair {
56 struct chanpair *next;
57 chan ab, ba;
58 } chanpair;
59
60 /* --- The `private data structure' --- *
61 *
62 * This is called a @tango@ because it takes two (endpoints).
63 */
64
65 typedef struct tango {
66 struct tango *next, *prev; /* A big list of all tangos */
67 endpt *a, *b; /* The two endpoints */
68 unsigned s; /* State of the connection */
69 chanpair *c; /* The pair of channels */
70 } tango;
71
72 #define EPS_AB 1u
73 #define EPS_BA 2u
74
75 /*----- Static variables --------------------------------------------------*/
76
77 static chanpair *chans = 0; /* List of spare channel pairs */
78 static tango *tangos = 0; /* List of tangos */
79
80 /*----- Main code ---------------------------------------------------------*/
81
82 /* --- @doneab@, @doneba@ --- *
83 *
84 * Arguments: @void *p@ = pointer to a tango block
85 *
86 * Returns: ---
87 *
88 * Use: Handles completion of a channel.
89 */
90
91 static void doneab(void *p)
92 {
93 tango *t = p;
94 t->s &= ~EPS_AB;
95 t->b->ops->wclose(t->b);
96 if (!t->s)
97 endpt_kill(t->a);
98 }
99
100 static void doneba(void *p)
101 {
102 tango *t = p;
103 t->s &= ~EPS_BA;
104 t->a->ops->wclose(t->a);
105 if (!t->s)
106 endpt_kill(t->a);
107 }
108
109 /* --- @endpt_kill@ --- *
110 *
111 * Arguments: @endpt *a@ = an endpoint
112 *
113 * Returns: ---
114 *
115 * Use: Kills an endpoint. If the endpoint is joined to another, the
116 * other endpoint is also killed, as is the connection between
117 * them (and that's the tricky bit).
118 */
119
120 void endpt_kill(endpt *a)
121 {
122 tango *t;
123 endpt *b;
124
125 /* --- If the endpont is unconnected, just close it --- */
126
127 if (!a->t) {
128 a->ops->close(a);
129 return;
130 }
131 t = a->t;
132 a = t->a;
133 b = t->b;
134
135 /* --- See whether to close channels --- *
136 *
137 * I'll only have opened channels if both things are files. Also, the
138 * channel from @b@ to @a@ will only be open if @b@ is not pending.
139 */
140
141 if (a->f & b->f & EPF_FILE) {
142 if (t->s & EPS_AB)
143 chan_close(&t->c->ab);
144 if (!(b->f & EPF_PENDING) && (t->s & EPS_BA))
145 chan_close(&t->c->ba);
146 t->c->next = chans;
147 chans = t->c;
148 }
149
150 /* --- Now just throw the endpoints and tango block away --- */
151
152 a->ops->close(a);
153 b->ops->close(b);
154 if (t->next)
155 t->next->prev = t->prev;
156 if (t->prev)
157 t->prev->next = t->next;
158 else
159 tangos = t->next;
160 DESTROY(t);
161 }
162
163 /* --- @endpt_killall@ --- *
164 *
165 * Arguments: ---
166 *
167 * Returns: ---
168 *
169 * Use: Destroys all current endpoint connections. Used when
170 * shutting down.
171 */
172
173 void endpt_killall(void)
174 {
175 tango *t = tangos;
176 while (t) {
177 tango *tt = t;
178 t = t->next;
179 endpt_kill(tt->a);
180 }
181 tangos = 0;
182 }
183
184 /* --- @endpt_join@ --- *
185 *
186 * Arguments: @endpt *a@ = pointer to first endpoint
187 * @endpt *b@ = pointer to second endpoint
188 *
189 * Returns: ---
190 *
191 * Use: Joins two endpoints together. It's OK to join endpoints
192 * which are already joined; in fact, the the right thing to do
193 * when your endpoint decides that it's not pending any more is
194 * to join it to its partner again.
195 */
196
197 void endpt_join(endpt *a, endpt *b)
198 {
199 tango *t = a->t;
200
201 /* --- If there is no tango yet, create one --- */
202
203 if (!t) {
204 t = CREATE(tango);
205 t->next = tangos;
206 t->prev = 0;
207 t->a = b->other = a;
208 t->b = a->other = b;
209 a->t = b->t = t;
210 t->s = EPS_AB | EPS_BA;
211 t->c = 0;
212 if (tangos)
213 tangos->prev = t;
214 tangos = t;
215 }
216
217 /* --- If both endpoints are unfinished, leave them for a while --- */
218
219 if (a->f & b->f & EPF_PENDING)
220 return;
221
222 /* --- At least one endpoint is ready --- *
223 *
224 * If both endpoints are files then I can speculatively create the
225 * channels, which will let data start flowing a bit. Otherwise, I'll just
226 * have to wait some more.
227 */
228
229 if ((a->f | b->f) & EPF_PENDING) {
230
231 /* --- Only be interested if both things are files --- */
232
233 if (!t->c && (a->f & b->f & EPF_FILE)) {
234
235 /* --- Allocate a pair of channels --- */
236
237 if (!chans)
238 t->c = xmalloc(sizeof(*t->c));
239 else {
240 t->c = chans;
241 chans = chans->next;
242 }
243
244 /* --- Make @a@ the endpoint which is ready --- */
245
246 if (a->f & EPF_PENDING) {
247 t->b = a; t->a = b;
248 a = t->a; b = t->b;
249 }
250
251 /* --- Open one channel to read from @a@ --- */
252
253 chan_open(&t->c->ab, a->in->fd, -1, doneab, t);
254 }
255 return;
256 }
257
258 /* --- Both endpoints are now ready --- *
259 *
260 * There are four cases to consider here.
261 */
262
263 /* --- Case 1 --- *
264 *
265 * Neither endpoint is a file. I need to make a pair of pipes and tell
266 * both endpoints to attach to them. I can then close the pipes and
267 * discard the tango.
268 */
269
270 if (!((a->f | b->f) & EPF_FILE)) {
271 int pab[2], pba[2];
272 reffd *rab, *wab, *rba, *wba;
273
274 /* --- Create the pipes --- */
275
276 if (pipe(pab)) goto tidy_nofile_0;
277 if (pipe(pba)) goto tidy_nofile_1;
278
279 /* --- Attach reference counters --- */
280
281 rab = reffd_init(pab[0]); wab = reffd_init(pab[1]);
282 rba = reffd_init(pba[0]); wba = reffd_init(pba[1]);
283 a->ops->attach(a, rba, wab);
284 b->ops->attach(b, rab, wba);
285 REFFD_DEC(rab); REFFD_DEC(wab);
286 REFFD_DEC(rba); REFFD_DEC(wba);
287 goto tidy_nofile_0;
288
289 /* --- Tidy up after a mess --- */
290
291 tidy_nofile_1:
292 close(pab[0]); close(pab[1]);
293 tidy_nofile_0:
294 a->ops->close(a);
295 b->ops->close(b);
296 if (t->next)
297 t->next->prev = t->prev;
298 if (t->prev)
299 t->prev->next = t->next;
300 else
301 tangos = t->next;
302 DESTROY(t);
303 return;
304 }
305
306 /* --- Case 2 --- *
307 *
308 * One endpoint is a file, the other isn't. I just attach the other
309 * endpoint to the file and stand well back.
310 */
311
312 if ((a->f ^ b->f) & EPF_FILE) {
313
314 /* --- Let @a@ be the endpoint with the file --- */
315
316 if (b->f & EPF_FILE) {
317 endpt *e;
318 e = a; a = b; b = e;
319 }
320
321 /* --- Attach the non-file endpoint to the file and run away --- *
322 *
323 * Leave it as the non-file's responsibility to close the other endpoint
324 * when it's ready. It should also close itself at that time.
325 */
326
327 b->ops->attach(b, a->in, a->out);
328 b->ops->file(b, a);
329 if (t->next)
330 t->next->prev = t->prev;
331 if (t->prev)
332 t->prev->next = t->next;
333 else
334 tangos = t->next;
335 DESTROY(t);
336 return;
337 }
338
339 /* --- Case 3 --- *
340 *
341 * Both endpoints are files and I have a partially created channel pair. I
342 * need to create the other channel and add a destination to the first one.
343 * In this case, @t->b@ is the endpoint which has just finished setting
344 * itself up.
345 */
346
347 if (t->c) {
348 a = t->a; b = t->b;
349 chan_open(&t->c->ba, b->in->fd, a->out->fd, doneba, t);
350 chan_dest(&t->c->ab, b->out->fd);
351 return;
352 }
353
354 /* --- Case 4 --- *
355 *
356 * Both endpoints are files and I don't have a partially created channel
357 * pair. I need to allocate a channel pair and create both channels.
358 */
359
360 if (!chans)
361 t->c = xmalloc(sizeof(*t->c));
362 else {
363 t->c = chans;
364 chans = chans->next;
365 }
366 chan_open(&t->c->ab, a->in->fd, b->out->fd, doneab, t);
367 chan_open(&t->c->ba, b->in->fd, a->out->fd, doneba, t);
368 return;
369 }
370
371 /*----- That's all, folks -------------------------------------------------*/