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