identify.c: Stash a copy of the caller's description string.
[fwd] / endpt.c
CommitLineData
07d71f34 1/* -*-c-*-
2 *
07d71f34 3 * Generic endpoint abstraction
4 *
5 * (c) 1999 Straylight/Edgeware
6 */
7
206212ca 8/*----- Licensing notice --------------------------------------------------*
07d71f34 9 *
9155ea97 10 * This file is part of the `fwd' port forwarder.
07d71f34 11 *
9155ea97 12 * `fwd' is free software; you can redistribute it and/or modify
07d71f34 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.
206212ca 16 *
9155ea97 17 * `fwd' is distributed in the hope that it will be useful,
07d71f34 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.
206212ca 21 *
07d71f34 22 * You should have received a copy of the GNU General Public License
9155ea97 23 * along with `fwd'; if not, write to the Free Software Foundation,
07d71f34 24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
9155ea97 27#include "fwd.h"
07d71f34 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
37typedef 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
47typedef struct tango {
48 struct tango *next, *prev; /* A big list of all tangos */
49 endpt *a, *b; /* The two endpoints */
86c9bc4b 50 char *desc; /* Description of the connection */
07d71f34 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
60static chanpair *chans = 0; /* List of spare channel pairs */
61static 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
74static 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
83static 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
103void 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) {
86c9bc4b 125 if (t->c->ab.err)
d935f68b 126 fw_log(NOW, "[%s] error: %s", t->desc, strerror(t->c->ab.err));
86c9bc4b 127 else if (t->c->ba.err)
d935f68b 128 fw_log(NOW, "[%s] error: %s", t->desc, strerror(t->c->ba.err));
07d71f34 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;
86c9bc4b
MW
147 if (t->desc)
148 xfree(t->desc);
07d71f34 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
162void 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
86c9bc4b 177 * @const char *desc@ = description of connection
07d71f34 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.
86c9bc4b
MW
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.
07d71f34 189 */
190
86c9bc4b 191void endpt_join(endpt *a, endpt *b, const char *desc)
07d71f34 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;
86c9bc4b 206 t->desc = xstrdup(desc);
07d71f34 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
28d2517c 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 */
07d71f34 321
322 b->ops->attach(b, a->in, a->out);
28d2517c 323 b->ops->file(b, a);
07d71f34 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;
206212ca 364}
07d71f34 365
366/*----- That's all, folks -------------------------------------------------*/