e82f7154 |
1 | /* -*-c-*- |
2 | * |
3eb9f39e |
3 | * $Id: chan.c,v 1.4 1999/08/31 17:42:49 mdw Exp $ |
e82f7154 |
4 | * |
5 | * Channel management |
6 | * |
e5398e09 |
7 | * (c) 1999 Straylight/Edgeware |
e82f7154 |
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 | /*----- Revision history --------------------------------------------------* |
30 | * |
31 | * $Log: chan.c,v $ |
3eb9f39e |
32 | * Revision 1.4 1999/08/31 17:42:49 mdw |
33 | * Use `sel_force' to avoid a `select' call between reads and writes. |
34 | * |
e0ce9d38 |
35 | * Revision 1.3 1999/07/27 18:30:53 mdw |
36 | * Various minor portability fixes. |
37 | * |
e5398e09 |
38 | * Revision 1.2 1999/07/26 23:27:52 mdw |
39 | * Minor modifications for new design. |
40 | * |
41 | * Revision 1.1.1.1 1999/07/01 08:56:23 mdw |
42 | * Initial revision. |
e82f7154 |
43 | * |
44 | */ |
45 | |
46 | /*----- Header files ------------------------------------------------------*/ |
47 | |
48 | #include "config.h" |
49 | |
50 | #include <errno.h> |
51 | #include <stdio.h> |
52 | #include <stdlib.h> |
53 | #include <string.h> |
54 | |
55 | #include <sys/types.h> |
56 | #include <sys/time.h> |
57 | #include <unistd.h> |
58 | #include <sys/uio.h> |
59 | |
e82f7154 |
60 | #include <mLib/alloc.h> |
61 | #include <mLib/conn.h> |
62 | #include <mLib/sel.h> |
63 | |
64 | #include "chan.h" |
65 | #include "fw.h" |
66 | |
67 | /*----- Main code ---------------------------------------------------------*/ |
68 | |
69 | /* --- @writechan@ --- * |
70 | * |
71 | * Arguments: @int fd@ = file descriptor to write to |
72 | * @unsigned mode@ = what the descriptor is ready for |
73 | * @void *vp@ = pointer to channel block |
74 | * |
75 | * Returns: --- |
76 | * |
77 | * Use: Writes to a channel. |
78 | */ |
79 | |
80 | static void writechan(int fd, unsigned mode, void *vp) |
81 | { |
82 | chan *c = vp; |
83 | int w; |
84 | unsigned base = c->base; |
85 | unsigned len = c->len; |
86 | |
87 | /* --- Write data from my buffer --- */ |
88 | |
89 | if (len) { |
90 | |
91 | /* --- Do the write --- */ |
92 | |
93 | if (base + len <= CHAN_BUFSZ) |
94 | w = write(fd, c->buf + base, len); |
95 | else { |
96 | struct iovec iov[2]; |
97 | iov[0].iov_base = c->buf + base; |
98 | iov[0].iov_len = CHAN_BUFSZ - base; |
99 | iov[1].iov_base = c->buf; |
100 | iov[1].iov_len = len - iov[0].iov_len; |
101 | w = writev(fd, iov, 2); |
102 | } |
103 | |
104 | /* --- Sift through the results --- */ |
105 | |
106 | if (w < 0) { |
107 | if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) |
108 | return; |
109 | goto close; |
110 | } |
111 | else if (w == 0) |
112 | goto close; |
113 | else if (c->len == CHAN_BUFSZ && !(c->f & CHANF_CLOSE)) |
114 | sel_addfile(&c->r); |
115 | c->len -= w; |
116 | } |
117 | if (c->len == 0) |
118 | sel_rmfile(&c->w); |
119 | |
120 | /* --- Close the output end if necessary --- */ |
121 | |
e5398e09 |
122 | if (c->len == 0 && (c->f & CHANF_CLOSE)) |
e82f7154 |
123 | c->func(c->p); |
e82f7154 |
124 | return; |
125 | |
126 | /* --- Force a close if an error occurred --- */ |
127 | |
128 | close: |
129 | chan_close(c); |
130 | c->func(c->p); |
131 | } |
132 | |
133 | /* --- @readchan@ --- * |
134 | * |
135 | * Arguments: @int fd@ = file descriptor to read from |
136 | * @unsigned mode@ = what the descriptor is ready for |
137 | * @void *vp@ = pointer to channel block |
138 | * |
139 | * Returns: --- |
140 | * |
141 | * Use: Reads from a channel. |
142 | */ |
143 | |
144 | static void readchan(int fd, unsigned mode, void *vp) |
145 | { |
146 | chan *c = vp; |
147 | int r; |
148 | unsigned base = (c->base + c->len) & (CHAN_BUFSZ - 1); |
149 | unsigned len = CHAN_BUFSZ - c->len; |
150 | |
151 | /* --- Do the read --- */ |
152 | |
153 | if (base + len <= CHAN_BUFSZ) |
154 | r = read(fd, c->buf + base, len); |
155 | else { |
156 | struct iovec iov[2]; |
157 | iov[0].iov_base = c->buf + base; |
158 | iov[0].iov_len = CHAN_BUFSZ - base; |
159 | iov[1].iov_base = c->buf; |
160 | iov[1].iov_len = len - iov[0].iov_len; |
161 | r = readv(fd, iov, 2); |
162 | } |
163 | |
164 | /* --- Sift through the results --- */ |
165 | |
166 | if (r < 0) { |
167 | if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) |
168 | return; |
169 | goto close; |
170 | } |
171 | else if (r == 0) |
172 | goto close; |
3eb9f39e |
173 | else if (c->len == 0 && (c->f & CHANF_READY)) { |
e82f7154 |
174 | sel_addfile(&c->w); |
3eb9f39e |
175 | sel_force(&c->w); |
176 | } |
e82f7154 |
177 | c->len += r; |
178 | if (c->len == CHAN_BUFSZ) |
179 | sel_rmfile(&c->r); |
180 | return; |
181 | |
182 | /* --- Close the read end of the channel --- */ |
183 | |
184 | close: |
185 | c->f |= CHANF_CLOSE; |
3eb9f39e |
186 | if (!c->len && (c->f & CHANF_READY)) { |
e82f7154 |
187 | sel_addfile(&c->w); |
3eb9f39e |
188 | sel_force(&c->w); |
189 | } |
e82f7154 |
190 | sel_rmfile(&c->r); |
191 | } |
192 | |
193 | /* --- @chan_close@ --- * |
194 | * |
195 | * Arguments: @chan *c@ = pointer to channel |
196 | * |
197 | * Returns: --- |
198 | * |
199 | * Use: Closes down a channel prematurely. |
200 | */ |
201 | |
202 | void chan_close(chan *c) |
203 | { |
204 | if (!(c->f & CHANF_CLOSE) && c->len != CHAN_BUFSZ) |
205 | sel_rmfile(&c->r); |
206 | if ((c->f & CHANF_READY) && c->len != 0) |
207 | sel_rmfile(&c->w); |
208 | } |
209 | |
210 | /* --- @chan_dest@ --- * |
211 | * |
212 | * Arguments: @chan *c@ = pointer to channel |
213 | * @int fd@ = destination file descriptor for channel |
214 | * |
215 | * Returns: --- |
216 | * |
217 | * Use: Sets the channel's destination so it knows where to put |
218 | * data. |
219 | */ |
220 | |
221 | void chan_dest(chan *c, int fd) |
222 | { |
223 | if (c->f & CHANF_READY) |
224 | return; |
225 | sel_initfile(sel, &c->w, fd, SEL_WRITE, writechan, c); |
3eb9f39e |
226 | if (c->len || (c->f & CHANF_CLOSE)) { |
e82f7154 |
227 | sel_addfile(&c->w); |
3eb9f39e |
228 | sel_force(&c->w); |
229 | } |
e5398e09 |
230 | c->f |= CHANF_READY; |
e82f7154 |
231 | } |
232 | |
233 | /* --- @chan_open@ --- * |
234 | * |
235 | * Arguments: @chan *c@ = pointer to channel to open |
236 | * @int from, to@ = source and destination file descriptors |
237 | * @void (*func)(void *p)@ = function to call on closure |
238 | * @void *p@ = argument to pass to function |
239 | * |
240 | * Returns: --- |
241 | * |
242 | * Use: Opens a channel. Data is copied from the source to the |
243 | * destination. The @to@ argument may be @-1@ if the file |
244 | * descriptor isn't known yet. |
245 | */ |
246 | |
247 | void chan_open(chan *c, int from, int to, |
248 | void (*func)(void */*p*/), void *p) |
249 | { |
250 | c->func = func; |
251 | c->p = p; |
252 | |
253 | c->base = 0; |
254 | c->len = 0; |
255 | c->f = 0; |
256 | |
257 | sel_initfile(sel, &c->r, from, SEL_READ, readchan, c); |
258 | sel_addfile(&c->r); |
259 | |
260 | if (to != -1) |
261 | chan_dest(c, to); |
262 | } |
263 | |
264 | /*----- That's all, folks -------------------------------------------------*/ |