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