1 /*
2 ** fdbuf.c - Buffering functions for file descriptors
3 **
4 ** Copyright (c) 1997-2000 Peter Eriksson <pen@lysator.liu.se>
5 **
6 ** This program is free software; you can redistribute it and/or
7 ** modify it as you wish - as long as you don't claim that you wrote
8 ** it.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 */
14
15 #include "plib/config.h"
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <stdarg.h>
21 #include <string.h>
22
23 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/ioctl.h>
30
31 #ifdef HAVE_SYS_FILIO_H
32 #include <sys/filio.h>
33 #endif
34
35 #include <arpa/telnet.h>
36
37
38 #include "plib/safeio.h"
39 #include "plib/safestr.h"
40 #include "plib/fdbuf.h"
41 #include "plib/aalloc.h"
42
43 #define inline
44
45
46 /*
47 ** Setup a new buffer. Never fails.
48 */
49 FDBUF *
fd_create(int fd,int flags)50 fd_create(int fd,
51 int flags)
52 {
53 FDBUF *fdp;
54
55
56 A_NEW(fdp);
57
58 pthread_mutex_init(&fdp->refcnt_lock, NULL);
59 fdp->refcnt = 1;
60
61 fdp->flags = flags;
62 fdp->fd = fd;
63
64 pthread_mutex_init(&fdp->in_lock, NULL);
65 fdp->ungetc = -1;
66
67 fdp->in_start = 0;
68 fdp->in_end = 0;
69 fdp->inbufsize = FDBUF_INBUFSIZE;
70 fdp->inbuf = a_malloc(fdp->inbufsize, "FDBUF inbuf");
71
72 pthread_mutex_init(&fdp->out_lock, NULL);
73 fdp->lastc = -1;
74
75 fdp->outbuflen = 0;
76 fdp->outbufsize = FDBUF_OUTBUFSIZE;
77 fdp->outbuf = a_malloc(fdp->outbufsize, "FDBUF outbuf");
78
79 return fdp;
80 }
81
82
83 /*
84 ** Free and destroy the buffer. Never fails.
85 */
86 void
fd_destroy(FDBUF * fdp)87 fd_destroy(FDBUF *fdp)
88 {
89 if (fdp == NULL)
90 return;
91
92 fd_flush(fdp);
93
94 pthread_mutex_lock(&fdp->refcnt_lock);
95 fdp->refcnt--;
96 if (fdp->refcnt > 0)
97 {
98 pthread_mutex_unlock(&fdp->refcnt_lock);
99 return;
100 }
101
102 a_free(fdp->outbuf);
103 a_free(fdp->inbuf);
104 a_free(fdp);
105 }
106
107
108
109 /*
110 ** Load more data into the buffer
111 */
112 static inline int
_fd_fill(FDBUF * fdp)113 _fd_fill(FDBUF *fdp)
114 {
115 int maxlen, len, avail, err;
116
117
118 if (fdp->in_start == fdp->in_end)
119 fdp->in_start = fdp->in_end = 0;
120
121 /* Free space in the input buffer */
122 maxlen = fdp->inbufsize - fdp->in_end;
123 if (maxlen == 0)
124 return 0;
125
126 avail = 0;
127 err = ioctl(fdp->fd, FIONREAD, &avail);
128
129 if (err < 0 || avail == 0)
130 avail = 1;
131
132 if (avail > maxlen)
133 avail = maxlen;
134
135 len = s_read(fdp->fd, fdp->inbuf+fdp->in_start, avail);
136
137 if (len == 0)
138 return -1;
139 else if (len < 0)
140 {
141 return errno;
142 }
143
144 fdp->in_end += len;
145 return 0;
146 }
147
148 int
fd_fill(FDBUF * fdp)149 fd_fill(FDBUF *fdp)
150 {
151 int err;
152
153 pthread_mutex_lock(&fdp->in_lock);
154 err = _fd_fill(fdp);
155 pthread_mutex_unlock(&fdp->in_lock);
156
157 return err;
158 }
159
160
161 /*
162 ** Send any buffered data
163 */
164 static inline int
_fd_flush(FDBUF * fdp)165 _fd_flush(FDBUF *fdp)
166 {
167 int len, rest;
168
169
170 if (fdp->outbuflen == 0)
171 return 0;
172
173 len = s_write(fdp->fd, fdp->outbuf, fdp->outbuflen);
174 if (len < 0)
175 return errno;
176
177 if (len == 0)
178 return 0;
179
180 rest = fdp->outbuflen - len;
181 if (rest > 0)
182 memcpy(fdp->outbuf, fdp->outbuf+len, rest);
183 fdp->outbuflen -= len;
184 return 0;
185 }
186
187
188 int
fd_flush(FDBUF * fdp)189 fd_flush(FDBUF *fdp)
190 {
191 int err;
192
193
194 pthread_mutex_lock(&fdp->out_lock);
195 err = _fd_flush(fdp);
196 pthread_mutex_unlock(&fdp->out_lock);
197
198 return err;
199 }
200
201
202 void
fd_purge(FDBUF * fdp)203 fd_purge(FDBUF *fdp)
204 {
205 pthread_mutex_lock(&fdp->out_lock);
206 fdp->outbuflen = 0;
207 fdp->lastc = -1;
208 pthread_mutex_unlock(&fdp->out_lock);
209 }
210
211
212 /*
213 ** Send one character
214 */
215 static inline int
_fd_putc(FDBUF * fdp,int c)216 _fd_putc(FDBUF *fdp,
217 int c)
218 {
219 int err;
220
221
222 if ((fdp->flags & FDF_CRLF) && c == '\n')
223 if ((err = _fd_putc(fdp, '\r')) != 0)
224 return err;
225
226 if (fdp->outbuflen == fdp->outbufsize)
227 {
228 _fd_flush(fdp);
229
230 if (fdp->outbuflen == fdp->outbufsize)
231 return EAGAIN;
232 }
233
234 fdp->outbuf[fdp->outbuflen++] = c;
235 if ((fdp->flags & FDF_LINEBUF) && c == '\n')
236 _fd_flush(fdp);
237
238 return 0;
239 }
240
241 int
fd_putc(FDBUF * fdp,int c)242 fd_putc(FDBUF *fdp,
243 int c)
244 {
245 int err;
246
247 pthread_mutex_lock(&fdp->out_lock);
248 err = _fd_putc(fdp, c);
249 pthread_mutex_unlock(&fdp->out_lock);
250
251 return err;
252 }
253
254
255 /*
256 ** Send a string
257 */
258 int
fd_puts(FDBUF * fdp,const char * str)259 fd_puts(FDBUF *fdp,
260 const char *str)
261 {
262 int err = 0;
263
264
265 pthread_mutex_lock(&fdp->out_lock);
266 while (*str && err == 0)
267 err = _fd_putc(fdp, *str++);
268 pthread_mutex_unlock(&fdp->out_lock);
269
270 return err;
271 }
272
273
274
275 /*
276 ** Send a formatted string
277 */
278 int
fd_printf(FDBUF * fdp,const char * fmt,...)279 fd_printf(FDBUF *fdp,
280 const char *fmt,
281 ...)
282 {
283 va_list ap;
284 char buf[8192];
285 int ecode;
286
287
288 va_start(ap, fmt);
289 ecode = s_vsnprintf(buf, sizeof(buf), fmt, ap);
290 va_end(ap);
291
292 if (ecode < 0 || ecode >= sizeof(buf))
293 return EINVAL;
294
295 return fd_puts(fdp, buf);
296 }
297
298
299
300 /*
301 ** Unget a character
302 */
303 int
fd_ungetc(FDBUF * fdp,int c)304 fd_ungetc(FDBUF *fdp,
305 int c)
306 {
307 if (fdp->ungetc != -1)
308 return ENOMEM;
309
310 fdp->ungetc = c;
311 return 0;
312 }
313
314 /*
315 ** Get a character from the buffer
316 */
317 static inline int
_fd_rgetc(FDBUF * fdp)318 _fd_rgetc(FDBUF *fdp)
319 {
320 if (fdp->in_start == fdp->in_end && _fd_fill(fdp))
321 return -1;
322
323 return fdp->inbuf[fdp->in_start++];
324 }
325
326 int
fd_rgetc(FDBUF * fdp)327 fd_rgetc(FDBUF *fdp)
328 {
329 int c;
330
331 pthread_mutex_lock(&fdp->in_lock);
332 c = _fd_rgetc(fdp);
333 pthread_mutex_unlock(&fdp->in_lock);
334 return c;
335 }
336
337
338 static inline int
_fd_getc(FDBUF * fdp)339 _fd_getc(FDBUF *fdp)
340 {
341 int c;
342
343
344 if (fdp->ungetc != -1)
345 {
346 c = fdp->ungetc;
347 fdp->ungetc = -1;
348 return c;
349 }
350
351 Again:
352 c = _fd_rgetc(fdp);
353 if (c == EOF)
354 return EOF;
355
356 /* Simple, basic TELNET protocol handling */
357 if ((fdp->flags & FDF_TELNET) && c == IAC)
358 {
359 c = _fd_rgetc(fdp);
360 switch (c)
361 {
362 case EOF:
363 return EOF;
364
365 case AO:
366 fd_purge(fdp);
367 goto Again;
368
369 case AYT:
370 fd_puts(fdp, "\n[Yes]\n");
371 fd_flush(fdp);
372 goto Again;
373
374 case NOP:
375 goto Again;
376
377 case IP: /* Interrupt process */
378 /* XXX: What should we do? */
379 goto Again;
380
381 case SYNCH: /* Synchronize */
382 return -2;
383
384 case WILL:
385 case WONT:
386 c = _fd_rgetc(fdp);
387 if (c == EOF)
388 return EOF;
389 pthread_mutex_lock(&fdp->out_lock);
390 _fd_putc(fdp, IAC);
391 _fd_putc(fdp, DONT);
392 _fd_putc(fdp, c);
393 _fd_flush(fdp);
394 pthread_mutex_unlock(&fdp->out_lock);
395 goto Again;
396
397 case DO:
398 case DONT:
399 c = _fd_rgetc(fdp);
400 if (c == EOF)
401 return EOF;
402 pthread_mutex_lock(&fdp->out_lock);
403 _fd_putc(fdp, IAC);
404 _fd_putc(fdp, WONT);
405 _fd_putc(fdp, c);
406 _fd_flush(fdp);
407 pthread_mutex_unlock(&fdp->out_lock);
408 goto Again;
409
410 default:
411 goto Again;
412 }
413 }
414
415 if ((fdp->flags & FDF_CRLF) && fdp->lastc == '\r' && c == '\n')
416 goto Again;
417
418 fdp->lastc = c;
419 if ((fdp->flags & FDF_CRLF) && c == '\r')
420 c = '\n';
421
422 return c;
423 }
424
425
426 int
fd_getc(FDBUF * fdp)427 fd_getc(FDBUF *fdp)
428 {
429 int c;
430
431
432 pthread_mutex_lock(&fdp->in_lock);
433
434 while ((c = _fd_getc(fdp)) == -2)
435 ;
436
437 pthread_mutex_unlock(&fdp->in_lock);
438
439 return c;
440 }
441
442
443 /*
444 ** Get a line from the buffer
445 */
446 int
fd_gets(FDBUF * fdp,char * buf,int bufsize)447 fd_gets(FDBUF *fdp,
448 char *buf,
449 int bufsize)
450 {
451 int c, i;
452
453
454 if (bufsize == 0 || !buf)
455 return -1;
456
457 pthread_mutex_lock(&fdp->in_lock);
458
459 i = 0;
460 while ((c = _fd_getc(fdp)) != -1 &&
461 c != '\n' && c != '\r' &&
462 (i < (bufsize-1)))
463 {
464 if (c == -2) /* TELNET SYNCH */
465 i = 0;
466 else
467 buf[i++] = c;
468 }
469 buf[i] = '\0';
470
471 pthread_mutex_unlock(&fdp->in_lock);
472
473 if (i == 0 && c == -1)
474 return -1;
475
476 if (i == (bufsize-1) && !(c == '\n' || c == '\r' || c == -1))
477 return -1;
478
479 return 0;
480 }
481