3 * Generic endpoint abstraction
5 * (c) 1999 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the `fw' port forwarder.
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.
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.
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.
27 /*----- Header files ------------------------------------------------------*/
36 #include <sys/types.h>
40 #include <mLib/alloc.h>
45 /*----- Data structures ---------------------------------------------------*/
47 /* --- Pairs of channels --- *
49 * Channels are always allocated and freed in pairs. It makes sense to keep
50 * the pair together. (It also wastes less memory.)
53 typedef struct chanpair
{
54 struct chanpair
*next
;
58 /* --- The `private data structure' --- *
60 * This is called a @tango@ because it takes two (endpoints).
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 */
73 /*----- Static variables --------------------------------------------------*/
75 static chanpair
*chans
= 0; /* List of spare channel pairs */
76 static tango
*tangos
= 0; /* List of tangos */
78 /*----- Main code ---------------------------------------------------------*/
80 /* --- @doneab@, @doneba@ --- *
82 * Arguments: @void *p@ = pointer to a tango block
86 * Use: Handles completion of a channel.
89 static void doneab(void *p
)
93 t
->b
->ops
->wclose(t
->b
);
98 static void doneba(void *p
)
102 t
->a
->ops
->wclose(t
->a
);
107 /* --- @endpt_kill@ --- *
109 * Arguments: @endpt *a@ = an endpoint
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).
118 void endpt_kill(endpt
*a
)
123 /* --- If the endpont is unconnected, just close it --- */
133 /* --- See whether to close channels --- *
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.
139 if (a
->f
& b
->f
& EPF_FILE
) {
141 chan_close(&t
->c
->ab
);
142 if (!(b
->f
& EPF_PENDING
) && (t
->s
& EPS_BA
))
143 chan_close(&t
->c
->ba
);
148 /* --- Now just throw the endpoints and tango block away --- */
153 t
->next
->prev
= t
->prev
;
155 t
->prev
->next
= t
->next
;
161 /* --- @endpt_killall@ --- *
167 * Use: Destroys all current endpoint connections. Used when
171 void endpt_killall(void)
182 /* --- @endpt_join@ --- *
184 * Arguments: @endpt *a@ = pointer to first endpoint
185 * @endpt *b@ = pointer to second endpoint
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.
195 void endpt_join(endpt
*a
, endpt
*b
)
199 /* --- If there is no tango yet, create one --- */
208 t
->s
= EPS_AB
| EPS_BA
;
215 /* --- If both endpoints are unfinished, leave them for a while --- */
217 if (a
->f
& b
->f
& EPF_PENDING
)
220 /* --- At least one endpoint is ready --- *
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.
227 if ((a
->f
| b
->f
) & EPF_PENDING
) {
229 /* --- Only be interested if both things are files --- */
231 if (!t
->c
&& (a
->f
& b
->f
& EPF_FILE
)) {
233 /* --- Allocate a pair of channels --- */
236 t
->c
= xmalloc(sizeof(*t
->c
));
242 /* --- Make @a@ the endpoint which is ready --- */
244 if (a
->f
& EPF_PENDING
) {
249 /* --- Open one channel to read from @a@ --- */
251 chan_open(&t
->c
->ab
, a
->in
->fd
, -1, doneab
, t
);
256 /* --- Both endpoints are now ready --- *
258 * There are four cases to consider here.
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
268 if (!((a
->f
| b
->f
) & EPF_FILE
)) {
270 reffd
*rab
, *wab
, *rba
, *wba
;
272 /* --- Create the pipes --- */
274 if (pipe(pab
)) goto tidy_nofile_0
;
275 if (pipe(pba
)) goto tidy_nofile_1
;
277 /* --- Attach reference counters --- */
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
);
287 /* --- Tidy up after a mess --- */
290 close(pab
[0]); close(pab
[1]);
295 t
->next
->prev
= t
->prev
;
297 t
->prev
->next
= t
->next
;
306 * One endpoint is a file, the other isn't. I just attach the other
307 * endpoint to the file and stand well back.
310 if ((a
->f
^ b
->f
) & EPF_FILE
) {
312 /* --- Let @a@ be the endpoint with the file --- */
314 if (b
->f
& EPF_FILE
) {
319 /* --- Attach the non-file endpoint to the file and run away --- *
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.
325 b
->ops
->attach(b
, a
->in
, a
->out
);
328 t
->next
->prev
= t
->prev
;
330 t
->prev
->next
= t
->next
;
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
347 chan_open(&t
->c
->ba
, b
->in
->fd
, a
->out
->fd
, doneba
, t
);
348 chan_dest(&t
->c
->ab
, b
->out
->fd
);
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.
359 t
->c
= xmalloc(sizeof(*t
->c
));
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
);
369 /*----- That's all, folks -------------------------------------------------*/