]> jfr.im git - irc/rqf/shadowircd.git/blob - libratbox/src/linebuf.c
Copied libratbox and related stuff from shadowircd upstream.
[irc/rqf/shadowircd.git] / libratbox / src / linebuf.c
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * linebuf.c: Maintains linebuffers.
4 *
5 * Copyright (C) 2001-2002 Adrian Chadd <adrian@creative.net.au>
6 * Copyright (C) 2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22 * USA
23 *
24 * $Id: linebuf.c 26092 2008-09-19 15:13:52Z androsyn $
25 */
26
27 #include <libratbox_config.h>
28 #include <ratbox_lib.h>
29 #include <commio-int.h>
30
31 #ifndef NOBALLOC
32 static rb_bh *rb_linebuf_heap;
33 #endif
34
35 static int bufline_count = 0;
36
37 #ifndef LINEBUF_HEAP_SIZE
38 #define LINEBUF_HEAP_SIZE 2048
39 #endif
40
41 /*
42 * rb_linebuf_init
43 *
44 * Initialise the linebuf mechanism
45 */
46
47 void
48 rb_linebuf_init(size_t heap_size)
49 {
50 rb_linebuf_heap = rb_bh_create(sizeof(buf_line_t), heap_size, "librb_linebuf_heap");
51 }
52
53 static buf_line_t *
54 rb_linebuf_allocate(void)
55 {
56 buf_line_t *t;
57 t = rb_bh_alloc(rb_linebuf_heap);
58 return (t);
59
60 }
61
62 static void
63 rb_linebuf_free(buf_line_t * p)
64 {
65 rb_bh_free(rb_linebuf_heap, p);
66 }
67
68 /*
69 * rb_linebuf_new_line
70 *
71 * Create a new line, and link it to the given linebuf.
72 * It will be initially empty.
73 */
74 static buf_line_t *
75 rb_linebuf_new_line(buf_head_t * bufhead)
76 {
77 buf_line_t *bufline;
78 rb_dlink_node *node;
79
80 bufline = rb_linebuf_allocate();
81 if(bufline == NULL)
82 return NULL;
83 ++bufline_count;
84
85
86 node = rb_make_rb_dlink_node();
87
88 /* Stick it at the end of the buf list */
89 rb_dlinkAddTail(bufline, node, &bufhead->list);
90 bufline->refcount++;
91
92 /* And finally, update the allocated size */
93 bufhead->alloclen++;
94 bufhead->numlines++;
95
96 return bufline;
97 }
98
99
100 /*
101 * rb_linebuf_done_line
102 *
103 * We've finished with the given line, so deallocate it
104 */
105 static void
106 rb_linebuf_done_line(buf_head_t * bufhead, buf_line_t * bufline, rb_dlink_node *node)
107 {
108 /* Remove it from the linked list */
109 rb_dlinkDestroy(node, &bufhead->list);
110
111 /* Update the allocated size */
112 bufhead->alloclen--;
113 bufhead->len -= bufline->len;
114 lrb_assert(bufhead->len >= 0);
115 bufhead->numlines--;
116
117 bufline->refcount--;
118 lrb_assert(bufline->refcount >= 0);
119
120 if(bufline->refcount == 0)
121 {
122 /* and finally, deallocate the buf */
123 --bufline_count;
124 lrb_assert(bufline_count >= 0);
125 rb_linebuf_free(bufline);
126 }
127 }
128
129
130 /*
131 * skip to end of line or the crlfs, return the number of bytes ..
132 */
133 static inline int
134 rb_linebuf_skip_crlf(char *ch, int len)
135 {
136 int orig_len = len;
137
138 /* First, skip until the first non-CRLF */
139 for(; len; len--, ch++)
140 {
141 if(*ch == '\r')
142 break;
143 else if(*ch == '\n')
144 break;
145 }
146
147 /* Then, skip until the last CRLF */
148 for(; len; len--, ch++)
149 {
150 if((*ch != '\r') && (*ch != '\n'))
151 break;
152 }
153 lrb_assert(orig_len > len);
154 return (orig_len - len);
155 }
156
157
158
159 /*
160 * rb_linebuf_newbuf
161 *
162 * Initialise the new buffer
163 */
164 void
165 rb_linebuf_newbuf(buf_head_t * bufhead)
166 {
167 /* not much to do right now :) */
168 memset(bufhead, 0, sizeof(buf_head_t));
169 }
170
171 /*
172 * rb_linebuf_donebuf
173 *
174 * Flush all the lines associated with this buffer
175 */
176 void
177 rb_linebuf_donebuf(buf_head_t * bufhead)
178 {
179 while(bufhead->list.head != NULL)
180 {
181 rb_linebuf_done_line(bufhead, (buf_line_t *) bufhead->list.head->data,
182 bufhead->list.head);
183 }
184 }
185
186 /*
187 * rb_linebuf_copy_line
188 *
189 * Okay..this functions comments made absolutely no sense.
190 *
191 * Basically what we do is this. Find the first chunk of text
192 * and then scan for a CRLF. If we didn't find it, but we didn't
193 * overflow our buffer..we wait for some more data.
194 * If we found a CRLF, we replace them with a \0 character.
195 * If we overflowed, we copy the most our buffer can handle, terminate
196 * it with a \0 and return.
197 *
198 * The return value is the amount of data we consumed. This could
199 * be different than the size of the linebuffer, as when we discard
200 * the overflow, we don't want to process it again.
201 *
202 * This still sucks in my opinion, but it seems to work.
203 *
204 * -Aaron
205 */
206 static int
207 rb_linebuf_copy_line(buf_head_t * bufhead, buf_line_t * bufline, char *data, int len)
208 {
209 int cpylen = 0; /* how many bytes we've copied */
210 char *ch = data; /* Pointer to where we are in the read data */
211 char *bufch = bufline->buf + bufline->len;
212 int clen = 0; /* how many bytes we've processed,
213 and don't ever want to see again.. */
214
215 /* If its full or terminated, ignore it */
216
217 bufline->raw = 0;
218 lrb_assert(bufline->len < BUF_DATA_SIZE);
219 if(bufline->terminated == 1)
220 return 0;
221
222 clen = cpylen = rb_linebuf_skip_crlf(ch, len);
223 if(clen == -1)
224 return -1;
225
226 /* This is the ~overflow case..This doesn't happen often.. */
227 if(cpylen > (BUF_DATA_SIZE - bufline->len - 1))
228 {
229 memcpy(bufch, ch, (BUF_DATA_SIZE - bufline->len - 1));
230 bufline->buf[BUF_DATA_SIZE - 1] = '\0';
231 bufch = bufline->buf + BUF_DATA_SIZE - 2;
232 while(cpylen && (*bufch == '\r' || *bufch == '\n'))
233 {
234 *bufch = '\0';
235 cpylen--;
236 bufch--;
237 }
238 bufline->terminated = 1;
239 bufline->len = BUF_DATA_SIZE - 1;
240 bufhead->len += BUF_DATA_SIZE - 1;
241 return clen;
242 }
243
244 memcpy(bufch, ch, cpylen);
245 bufch += cpylen;
246 *bufch = '\0';
247 bufch--;
248
249 if(*bufch != '\r' && *bufch != '\n')
250 {
251 /* No linefeed, bail for the next time */
252 bufhead->len += cpylen;
253 bufline->len += cpylen;
254 bufline->terminated = 0;
255 return clen;
256 }
257
258 /* Yank the CRLF off this, replace with a \0 */
259 while(cpylen && (*bufch == '\r' || *bufch == '\n'))
260 {
261 *bufch = '\0';
262 cpylen--;
263 bufch--;
264 }
265
266 bufline->terminated = 1;
267 bufhead->len += cpylen;
268 bufline->len += cpylen;
269 return clen;
270 }
271
272 /*
273 * rb_linebuf_copy_raw
274 *
275 * Copy as much data as possible directly into a linebuf,
276 * splitting at \r\n, but without altering any data.
277 *
278 */
279 static int
280 rb_linebuf_copy_raw(buf_head_t * bufhead, buf_line_t * bufline, char *data, int len)
281 {
282 int cpylen = 0; /* how many bytes we've copied */
283 char *ch = data; /* Pointer to where we are in the read data */
284 char *bufch = bufline->buf + bufline->len;
285 int clen = 0; /* how many bytes we've processed,
286 and don't ever want to see again.. */
287
288 /* If its full or terminated, ignore it */
289
290 bufline->raw = 1;
291 lrb_assert(bufline->len < BUF_DATA_SIZE);
292 if(bufline->terminated == 1)
293 return 0;
294
295 clen = cpylen = rb_linebuf_skip_crlf(ch, len);
296 if(clen == -1)
297 return -1;
298
299 /* This is the overflow case..This doesn't happen often.. */
300 if(cpylen > (BUF_DATA_SIZE - bufline->len - 1))
301 {
302 clen = BUF_DATA_SIZE - bufline->len - 1;
303 memcpy(bufch, ch, clen);
304 bufline->buf[BUF_DATA_SIZE - 1] = '\0';
305 bufch = bufline->buf + BUF_DATA_SIZE - 2;
306 bufline->terminated = 1;
307 bufline->len = BUF_DATA_SIZE - 1;
308 bufhead->len += BUF_DATA_SIZE - 1;
309 return clen;
310 }
311
312 memcpy(bufch, ch, cpylen);
313 bufch += cpylen;
314 *bufch = '\0';
315 bufch--;
316
317 if(*bufch != '\r' && *bufch != '\n')
318 {
319 /* No linefeed, bail for the next time */
320 bufhead->len += cpylen;
321 bufline->len += cpylen;
322 bufline->terminated = 0;
323 return clen;
324 }
325
326 bufline->terminated = 1;
327 bufhead->len += cpylen;
328 bufline->len += cpylen;
329 return clen;
330 }
331
332
333 /*
334 * rb_linebuf_parse
335 *
336 * Take a given buffer and break out as many buffers as we can.
337 * If we find a CRLF, we terminate that buffer and create a new one.
338 * If we don't find a CRLF whilst parsing a buffer, we don't mark it
339 * 'finished', so the next loop through we can continue appending ..
340 *
341 * A few notes here, which you'll need to understand before continuing.
342 *
343 * - right now I'm only dealing with single sized buffers. Later on,
344 * I might consider chaining buffers together to get longer "lines"
345 * but seriously, I don't see the advantage right now.
346 *
347 * - This *is* designed to turn into a reference-counter-protected setup
348 * to dodge copious copies.
349 */
350 int
351 rb_linebuf_parse(buf_head_t * bufhead, char *data, int len, int raw)
352 {
353 buf_line_t *bufline;
354 int cpylen;
355 int linecnt = 0;
356
357 /* First, if we have a partial buffer, try to squeze data into it */
358 if(bufhead->list.tail != NULL)
359 {
360 /* Check we're doing the partial buffer thing */
361 bufline = bufhead->list.tail->data;
362 /* just try, the worst it could do is *reject* us .. */
363 if(!raw)
364 cpylen = rb_linebuf_copy_line(bufhead, bufline, data, len);
365 else
366 cpylen = rb_linebuf_copy_raw(bufhead, bufline, data, len);
367
368 if(cpylen == -1)
369 return -1;
370
371 linecnt++;
372 /* If we've copied the same as what we've got, quit now */
373 if(cpylen == len)
374 return linecnt; /* all the data done so soon? */
375
376 /* Skip the data and update len .. */
377 len -= cpylen;
378 lrb_assert(len >= 0);
379 data += cpylen;
380 }
381
382 /* Next, the loop */
383 while(len > 0)
384 {
385 /* We obviously need a new buffer, so .. */
386 bufline = rb_linebuf_new_line(bufhead);
387
388 /* And parse */
389 if(!raw)
390 cpylen = rb_linebuf_copy_line(bufhead, bufline, data, len);
391 else
392 cpylen = rb_linebuf_copy_raw(bufhead, bufline, data, len);
393
394 if(cpylen == -1)
395 return -1;
396
397 len -= cpylen;
398 lrb_assert(len >= 0);
399 data += cpylen;
400 linecnt++;
401 }
402 return linecnt;
403 }
404
405
406 /*
407 * rb_linebuf_get
408 *
409 * get the next buffer from our line. For the time being it will copy
410 * data into the given buffer and free the underlying linebuf.
411 */
412 int
413 rb_linebuf_get(buf_head_t * bufhead, char *buf, int buflen, int partial, int raw)
414 {
415 buf_line_t *bufline;
416 int cpylen;
417 char *start, *ch;
418
419 /* make sure we have a line */
420 if(bufhead->list.head == NULL)
421 return 0; /* Obviously not.. hrm. */
422
423 bufline = bufhead->list.head->data;
424
425 /* make sure that the buffer was actually *terminated */
426 if(!(partial || bufline->terminated))
427 return 0; /* Wait for more data! */
428
429 if(buflen < bufline->len)
430 cpylen = buflen - 1;
431 else
432 cpylen = bufline->len;
433
434 /* Copy it */
435 start = bufline->buf;
436
437 /* if we left extraneous '\r\n' characters in the string,
438 * and we don't want to read the raw data, clean up the string.
439 */
440 if(bufline->raw && !raw)
441 {
442 /* skip leading EOL characters */
443 while(cpylen && (*start == '\r' || *start == '\n'))
444 {
445 start++;
446 cpylen--;
447 }
448 /* skip trailing EOL characters */
449 ch = &start[cpylen - 1];
450 while(cpylen && (*ch == '\r' || *ch == '\n'))
451 {
452 ch--;
453 cpylen--;
454 }
455 }
456
457 memcpy(buf, start, cpylen);
458
459 /* convert CR/LF to NULL */
460 if(!raw)
461 buf[cpylen] = '\0';
462
463 lrb_assert(cpylen >= 0);
464
465 /* Deallocate the line */
466 rb_linebuf_done_line(bufhead, bufline, bufhead->list.head);
467
468 /* return how much we copied */
469 return cpylen;
470 }
471
472 /*
473 * rb_linebuf_attach
474 *
475 * attach the lines in a buf_head_t to another buf_head_t
476 * without copying the data (using refcounts).
477 */
478 void
479 rb_linebuf_attach(buf_head_t * bufhead, buf_head_t * new)
480 {
481 rb_dlink_node *ptr;
482 buf_line_t *line;
483
484 RB_DLINK_FOREACH(ptr, new->list.head)
485 {
486 line = ptr->data;
487 rb_dlinkAddTailAlloc(line, &bufhead->list);
488
489 /* Update the allocated size */
490 bufhead->alloclen++;
491 bufhead->len += line->len;
492 bufhead->numlines++;
493
494 line->refcount++;
495 }
496 }
497
498
499
500 /*
501 * rb_linebuf_putmsg
502 *
503 * Similar to rb_linebuf_put, but designed for use by send.c.
504 *
505 * prefixfmt is used as a format for the varargs, and is inserted first.
506 * Then format/va_args is appended to the buffer.
507 */
508 void
509 rb_linebuf_putmsg(buf_head_t * bufhead, const char *format, va_list * va_args,
510 const char *prefixfmt, ...)
511 {
512 buf_line_t *bufline;
513 int len = 0;
514 va_list prefix_args;
515
516 /* make sure the previous line is terminated */
517 #ifndef NDEBUG
518 if(bufhead->list.tail)
519 {
520 bufline = bufhead->list.tail->data;
521 lrb_assert(bufline->terminated);
522 }
523 #endif
524 /* Create a new line */
525 bufline = rb_linebuf_new_line(bufhead);
526
527 if(prefixfmt != NULL)
528 {
529 va_start(prefix_args, prefixfmt);
530 len = rb_vsnprintf(bufline->buf, BUF_DATA_SIZE, prefixfmt, prefix_args);
531 va_end(prefix_args);
532 }
533
534 if(va_args != NULL)
535 {
536 len += rb_vsnprintf((bufline->buf + len), (BUF_DATA_SIZE - len), format, *va_args);
537 }
538
539 bufline->terminated = 1;
540
541 /* Truncate the data if required */
542 if(rb_unlikely(len > 510))
543 {
544 len = 510;
545 bufline->buf[len++] = '\r';
546 bufline->buf[len++] = '\n';
547 }
548 else if(rb_unlikely(len == 0))
549 {
550 bufline->buf[len++] = '\r';
551 bufline->buf[len++] = '\n';
552 bufline->buf[len] = '\0';
553 }
554 else
555 {
556 /* Chop trailing CRLF's .. */
557 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n')
558 || (bufline->buf[len] == '\0'))
559 {
560 len--;
561 }
562
563 bufline->buf[++len] = '\r';
564 bufline->buf[++len] = '\n';
565 bufline->buf[++len] = '\0';
566 }
567
568 bufline->len = len;
569 bufhead->len += len;
570 }
571
572 void
573 rb_linebuf_putbuf(buf_head_t * bufhead, const char *buffer)
574 {
575 buf_line_t *bufline;
576 int len = 0;
577
578 /* make sure the previous line is terminated */
579 #ifndef NDEBUG
580 if(bufhead->list.tail)
581 {
582 bufline = bufhead->list.tail->data;
583 lrb_assert(bufline->terminated);
584 }
585 #endif
586 /* Create a new line */
587 bufline = rb_linebuf_new_line(bufhead);
588
589 if(rb_unlikely(buffer != NULL))
590 len = rb_strlcpy(bufline->buf, buffer, BUF_DATA_SIZE);
591
592 bufline->terminated = 1;
593
594 /* Truncate the data if required */
595 if(rb_unlikely(len > 510))
596 {
597 len = 510;
598 bufline->buf[len++] = '\r';
599 bufline->buf[len++] = '\n';
600 }
601 else if(rb_unlikely(len == 0))
602 {
603 bufline->buf[len++] = '\r';
604 bufline->buf[len++] = '\n';
605 bufline->buf[len] = '\0';
606 }
607 else
608 {
609 /* Chop trailing CRLF's .. */
610 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n')
611 || (bufline->buf[len] == '\0'))
612 {
613 len--;
614 }
615
616 bufline->buf[++len] = '\r';
617 bufline->buf[++len] = '\n';
618 bufline->buf[++len] = '\0';
619 }
620
621 bufline->len = len;
622 bufhead->len += len;
623
624
625 }
626
627 void
628 rb_linebuf_put(buf_head_t * bufhead, const char *format, ...)
629 {
630 buf_line_t *bufline;
631 int len = 0;
632 va_list args;
633
634 /* make sure the previous line is terminated */
635 #ifndef NDEBUG
636 if(bufhead->list.tail)
637 {
638 bufline = bufhead->list.tail->data;
639 lrb_assert(bufline->terminated);
640 }
641 #endif
642 /* Create a new line */
643 bufline = rb_linebuf_new_line(bufhead);
644
645 if(rb_unlikely(format != NULL))
646 {
647 va_start(args, format);
648 len = rb_vsnprintf(bufline->buf, BUF_DATA_SIZE, format, args);
649 va_end(args);
650 }
651
652 bufline->terminated = 1;
653
654 /* Truncate the data if required */
655 if(rb_unlikely(len > 510))
656 {
657 len = 510;
658 bufline->buf[len++] = '\r';
659 bufline->buf[len++] = '\n';
660 }
661 else if(rb_unlikely(len == 0))
662 {
663 bufline->buf[len++] = '\r';
664 bufline->buf[len++] = '\n';
665 bufline->buf[len] = '\0';
666 }
667 else
668 {
669 /* Chop trailing CRLF's .. */
670 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n')
671 || (bufline->buf[len] == '\0'))
672 {
673 len--;
674 }
675
676 bufline->buf[++len] = '\r';
677 bufline->buf[++len] = '\n';
678 bufline->buf[++len] = '\0';
679 }
680
681 bufline->len = len;
682 bufhead->len += len;
683 }
684
685
686
687 /*
688 * rb_linebuf_flush
689 *
690 * Flush data to the buffer. It tries to write as much data as possible
691 * to the given socket. Any return values are passed straight through.
692 * If there is no data in the socket, EWOULDBLOCK is set as an errno
693 * rather than returning 0 (which would map to an EOF..)
694 *
695 * Notes: XXX We *should* have a clue here when a non-full buffer is arrived.
696 * and tag it so that we don't re-schedule another write until
697 * we have a CRLF.
698 */
699 int
700 rb_linebuf_flush(rb_fde_t *F, buf_head_t * bufhead)
701 {
702 buf_line_t *bufline;
703 int retval;
704
705 /*
706 * autoconf checks for this..but really just want to use it if we have a
707 * native version even if libircd provides a fake version...
708 */
709 #ifdef HAVE_WRITEV
710 if(!rb_fd_ssl(F))
711 {
712 rb_dlink_node *ptr;
713 int x = 0, y;
714 int xret;
715 static struct rb_iovec vec[RB_UIO_MAXIOV];
716
717 memset(vec, 0, sizeof(vec));
718 /* Check we actually have a first buffer */
719 if(bufhead->list.head == NULL)
720 {
721 /* nope, so we return none .. */
722 errno = EWOULDBLOCK;
723 return -1;
724 }
725
726 ptr = bufhead->list.head;
727
728 bufline = ptr->data;
729 if(!bufline->terminated)
730 {
731 errno = EWOULDBLOCK;
732 return -1;
733
734 }
735
736 vec[x].iov_base = bufline->buf + bufhead->writeofs;
737 vec[x++].iov_len = bufline->len - bufhead->writeofs;
738 ptr = ptr->next;
739
740 do
741 {
742 if(ptr == NULL)
743 break;
744
745 bufline = ptr->data;
746 if(!bufline->terminated)
747 break;
748
749 vec[x].iov_base = bufline->buf;
750 vec[x].iov_len = bufline->len;
751 ptr = ptr->next;
752
753 }
754 while(++x < RB_UIO_MAXIOV);
755
756 if(x == 0)
757 {
758 errno = EWOULDBLOCK;
759 return -1;
760 }
761
762 xret = retval = rb_writev(F, vec, x);
763 if(retval <= 0)
764 return retval;
765
766 ptr = bufhead->list.head;
767
768 for(y = 0; y < x; y++)
769 {
770 bufline = ptr->data;
771
772 if(xret >= bufline->len - bufhead->writeofs)
773 {
774 xret -= bufline->len - bufhead->writeofs;
775 ptr = ptr->next;
776 rb_linebuf_done_line(bufhead, bufline, bufhead->list.head);
777 bufhead->writeofs = 0;
778 }
779 else
780 {
781 bufhead->writeofs += xret;
782 break;
783 }
784 }
785
786 return retval;
787 }
788 #endif
789
790 /* this is the non-writev case */
791
792 /* Check we actually have a first buffer */
793 if(bufhead->list.head == NULL)
794 {
795 /* nope, so we return none .. */
796 errno = EWOULDBLOCK;
797 return -1;
798 }
799
800 bufline = bufhead->list.head->data;
801
802 /* And that its actually full .. */
803 if(!bufline->terminated)
804 {
805 errno = EWOULDBLOCK;
806 return -1;
807 }
808
809 /* Now, try writing data */
810 retval = rb_write(F, bufline->buf + bufhead->writeofs, bufline->len - bufhead->writeofs);
811
812 if(retval <= 0)
813 return retval;
814
815 /* we've got data, so update the write offset */
816 bufhead->writeofs += retval;
817
818 /* if we've written everything *and* the CRLF, deallocate and update
819 bufhead */
820 if(bufhead->writeofs == bufline->len)
821 {
822 bufhead->writeofs = 0;
823 lrb_assert(bufhead->len >= 0);
824 rb_linebuf_done_line(bufhead, bufline, bufhead->list.head);
825 }
826
827 /* Return line length */
828 return retval;
829 }
830
831
832
833 /*
834 * count linebufs for stats z
835 */
836
837 void
838 rb_count_rb_linebuf_memory(size_t *count, size_t *rb_linebuf_memory_used)
839 {
840 rb_bh_usage(rb_linebuf_heap, count, NULL, rb_linebuf_memory_used, NULL);
841 }