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