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