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