chiark / gitweb /
Update manual style.
[fwd] / endpt.c
1 /* -*-c-*-
2  *
3  * $Id: endpt.c,v 1.3 2000/08/01 17:58: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 /*----- Revision history --------------------------------------------------* 
30  *
31  * $Log: endpt.c,v $
32  * Revision 1.3  2000/08/01 17:58:25  mdw
33  * Include missing <mLib/alloc.h> header.
34  *
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  *
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
58 #include <mLib/alloc.h>
59
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
71 typedef 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
81 typedef 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
93 static chanpair *chans = 0;             /* List of spare channel pairs */
94 static 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
107 static 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
116 static 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
136 void 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
189 void 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
213 void 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
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      */
342
343     b->ops->attach(b, a->in, a->out);
344     b->ops->file(b, a);
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 -------------------------------------------------*/