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