chiark / gitweb /
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 -------------------------------------------------*/