Annotation of embedaddon/quagga/lib/buffer.c, revision 1.1.1.2
1.1 misho 1: /*
2: * Buffering of output and input.
3: * Copyright (C) 1998 Kunihiro Ishiguro
4: *
5: * This file is part of GNU Zebra.
6: *
7: * GNU Zebra is free software; you can redistribute it and/or modify
8: * it under the terms of the GNU General Public License as published
9: * by the Free Software Foundation; either version 2, or (at your
10: * option) any later version.
11: *
12: * GNU Zebra is distributed in the hope that it will be useful, but
13: * WITHOUT ANY WARRANTY; without even the implied warranty of
14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15: * General Public License for more details.
16: *
17: * You should have received a copy of the GNU General Public License
18: * along with GNU Zebra; see the file COPYING. If not, write to the
19: * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20: * Boston, MA 02111-1307, USA.
21: */
22:
23: #include <zebra.h>
24:
25: #include "memory.h"
26: #include "buffer.h"
27: #include "log.h"
28: #include "network.h"
29: #include <stddef.h>
30:
31:
32:
33: /* Buffer master. */
34: struct buffer
35: {
36: /* Data list. */
37: struct buffer_data *head;
38: struct buffer_data *tail;
39:
40: /* Size of each buffer_data chunk. */
41: size_t size;
42: };
43:
44: /* Data container. */
45: struct buffer_data
46: {
47: struct buffer_data *next;
48:
49: /* Location to add new data. */
50: size_t cp;
51:
52: /* Pointer to data not yet flushed. */
53: size_t sp;
54:
55: /* Actual data stream (variable length). */
56: unsigned char data[]; /* real dimension is buffer->size */
57: };
58:
59: /* It should always be true that: 0 <= sp <= cp <= size */
60:
61: /* Default buffer size (used if none specified). It is rounded up to the
62: next page boundery. */
63: #define BUFFER_SIZE_DEFAULT 4096
64:
65:
66: #define BUFFER_DATA_FREE(D) XFREE(MTYPE_BUFFER_DATA, (D))
67:
68: /* Make new buffer. */
69: struct buffer *
70: buffer_new (size_t size)
71: {
72: struct buffer *b;
73:
74: b = XCALLOC (MTYPE_BUFFER, sizeof (struct buffer));
75:
76: if (size)
77: b->size = size;
78: else
79: {
80: static size_t default_size;
81: if (!default_size)
82: {
83: long pgsz = sysconf(_SC_PAGESIZE);
84: default_size = ((((BUFFER_SIZE_DEFAULT-1)/pgsz)+1)*pgsz);
85: }
86: b->size = default_size;
87: }
88:
89: return b;
90: }
91:
92: /* Free buffer. */
93: void
94: buffer_free (struct buffer *b)
95: {
96: buffer_reset(b);
97: XFREE (MTYPE_BUFFER, b);
98: }
99:
100: /* Make string clone. */
101: char *
102: buffer_getstr (struct buffer *b)
103: {
104: size_t totlen = 0;
105: struct buffer_data *data;
106: char *s;
107: char *p;
108:
109: for (data = b->head; data; data = data->next)
110: totlen += data->cp - data->sp;
111: if (!(s = XMALLOC(MTYPE_TMP, totlen+1)))
112: return NULL;
113: p = s;
114: for (data = b->head; data; data = data->next)
115: {
116: memcpy(p, data->data + data->sp, data->cp - data->sp);
117: p += data->cp - data->sp;
118: }
119: *p = '\0';
120: return s;
121: }
122:
123: /* Return 1 if buffer is empty. */
124: int
125: buffer_empty (struct buffer *b)
126: {
127: return (b->head == NULL);
128: }
129:
130: /* Clear and free all allocated data. */
131: void
132: buffer_reset (struct buffer *b)
133: {
134: struct buffer_data *data;
135: struct buffer_data *next;
136:
137: for (data = b->head; data; data = next)
138: {
139: next = data->next;
140: BUFFER_DATA_FREE(data);
141: }
142: b->head = b->tail = NULL;
143: }
144:
145: /* Add buffer_data to the end of buffer. */
146: static struct buffer_data *
147: buffer_add (struct buffer *b)
148: {
149: struct buffer_data *d;
150:
151: d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(struct buffer_data, data[b->size]));
152: d->cp = d->sp = 0;
153: d->next = NULL;
154:
155: if (b->tail)
156: b->tail->next = d;
157: else
158: b->head = d;
159: b->tail = d;
160:
161: return d;
162: }
163:
164: /* Write data to buffer. */
165: void
166: buffer_put(struct buffer *b, const void *p, size_t size)
167: {
168: struct buffer_data *data = b->tail;
169: const char *ptr = p;
170:
171: /* We use even last one byte of data buffer. */
172: while (size)
173: {
174: size_t chunk;
175:
176: /* If there is no data buffer add it. */
177: if (data == NULL || data->cp == b->size)
178: data = buffer_add (b);
179:
180: chunk = ((size <= (b->size - data->cp)) ? size : (b->size - data->cp));
181: memcpy ((data->data + data->cp), ptr, chunk);
182: size -= chunk;
183: ptr += chunk;
184: data->cp += chunk;
185: }
186: }
187:
188: /* Insert character into the buffer. */
189: void
190: buffer_putc (struct buffer *b, u_char c)
191: {
192: buffer_put(b, &c, 1);
193: }
194:
195: /* Put string to the buffer. */
196: void
197: buffer_putstr (struct buffer *b, const char *c)
198: {
199: buffer_put(b, c, strlen(c));
200: }
201:
202: /* Keep flushing data to the fd until the buffer is empty or an error is
203: encountered or the operation would block. */
204: buffer_status_t
205: buffer_flush_all (struct buffer *b, int fd)
206: {
207: buffer_status_t ret;
208: struct buffer_data *head;
209: size_t head_sp;
210:
211: if (!b->head)
212: return BUFFER_EMPTY;
213: head_sp = (head = b->head)->sp;
214: /* Flush all data. */
215: while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING)
216: {
217: if ((b->head == head) && (head_sp == head->sp) && (errno != EINTR))
218: /* No data was flushed, so kernel buffer must be full. */
219: return ret;
220: head_sp = (head = b->head)->sp;
221: }
222:
223: return ret;
224: }
225:
226: /* Flush enough data to fill a terminal window of the given scene (used only
227: by vty telnet interface). */
228: buffer_status_t
229: buffer_flush_window (struct buffer *b, int fd, int width, int height,
230: int erase_flag, int no_more_flag)
231: {
232: int nbytes;
233: int iov_alloc;
234: int iov_index;
235: struct iovec *iov;
236: struct iovec small_iov[3];
237: char more[] = " --More-- ";
238: char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
239: ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
240: 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
241: struct buffer_data *data;
242: int column;
243:
244: if (!b->head)
245: return BUFFER_EMPTY;
246:
247: if (height < 1)
248: {
249: zlog_warn("%s called with non-positive window height %d, forcing to 1",
250: __func__, height);
251: height = 1;
252: }
253: else if (height >= 2)
254: height--;
255: if (width < 1)
256: {
257: zlog_warn("%s called with non-positive window width %d, forcing to 1",
258: __func__, width);
259: width = 1;
260: }
261:
262: /* For erase and more data add two to b's buffer_data count.*/
263: if (b->head->next == NULL)
264: {
1.1.1.2 ! misho 265: iov_alloc = array_size(small_iov);
1.1 misho 266: iov = small_iov;
267: }
268: else
269: {
270: iov_alloc = ((height*(width+2))/b->size)+10;
271: iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
272: }
273: iov_index = 0;
274:
275: /* Previously print out is performed. */
276: if (erase_flag)
277: {
278: iov[iov_index].iov_base = erase;
279: iov[iov_index].iov_len = sizeof erase;
280: iov_index++;
281: }
282:
283: /* Output data. */
284: column = 1; /* Column position of next character displayed. */
285: for (data = b->head; data && (height > 0); data = data->next)
286: {
287: size_t cp;
288:
289: cp = data->sp;
290: while ((cp < data->cp) && (height > 0))
291: {
292: /* Calculate lines remaining and column position after displaying
293: this character. */
294: if (data->data[cp] == '\r')
295: column = 1;
296: else if ((data->data[cp] == '\n') || (column == width))
297: {
298: column = 1;
299: height--;
300: }
301: else
302: column++;
303: cp++;
304: }
305: iov[iov_index].iov_base = (char *)(data->data + data->sp);
306: iov[iov_index++].iov_len = cp-data->sp;
307: data->sp = cp;
308:
309: if (iov_index == iov_alloc)
310: /* This should not ordinarily happen. */
311: {
312: iov_alloc *= 2;
313: if (iov != small_iov)
314: {
315: zlog_warn("%s: growing iov array to %d; "
316: "width %d, height %d, size %lu",
317: __func__, iov_alloc, width, height, (u_long)b->size);
318: iov = XREALLOC(MTYPE_TMP, iov, iov_alloc*sizeof(*iov));
319: }
320: else
321: {
322: /* This should absolutely never occur. */
323: zlog_err("%s: corruption detected: iov_small overflowed; "
324: "head %p, tail %p, head->next %p",
325: __func__, b->head, b->tail, b->head->next);
326: iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
327: memcpy(iov, small_iov, sizeof(small_iov));
328: }
329: }
330: }
331:
332: /* In case of `more' display need. */
333: if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag)
334: {
335: iov[iov_index].iov_base = more;
336: iov[iov_index].iov_len = sizeof more;
337: iov_index++;
338: }
339:
340:
341: #ifdef IOV_MAX
342: /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
343: example: Solaris2.6 are defined IOV_MAX size at 16. */
344: {
345: struct iovec *c_iov = iov;
346: nbytes = 0; /* Make sure it's initialized. */
347:
348: while (iov_index > 0)
349: {
350: int iov_size;
351:
352: iov_size = ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
353: if ((nbytes = writev(fd, c_iov, iov_size)) < 0)
354: {
355: zlog_warn("%s: writev to fd %d failed: %s",
356: __func__, fd, safe_strerror(errno));
357: break;
358: }
359:
360: /* move pointer io-vector */
361: c_iov += iov_size;
362: iov_index -= iov_size;
363: }
364: }
365: #else /* IOV_MAX */
366: if ((nbytes = writev (fd, iov, iov_index)) < 0)
367: zlog_warn("%s: writev to fd %d failed: %s",
368: __func__, fd, safe_strerror(errno));
369: #endif /* IOV_MAX */
370:
371: /* Free printed buffer data. */
372: while (b->head && (b->head->sp == b->head->cp))
373: {
374: struct buffer_data *del;
375: if (!(b->head = (del = b->head)->next))
376: b->tail = NULL;
377: BUFFER_DATA_FREE(del);
378: }
379:
380: if (iov != small_iov)
381: XFREE (MTYPE_TMP, iov);
382:
383: return (nbytes < 0) ? BUFFER_ERROR :
384: (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
385: }
386:
387: /* This function (unlike other buffer_flush* functions above) is designed
388: to work with non-blocking sockets. It does not attempt to write out
389: all of the queued data, just a "big" chunk. It returns 0 if it was
390: able to empty out the buffers completely, 1 if more flushing is
391: required later, or -1 on a fatal write error. */
392: buffer_status_t
393: buffer_flush_available(struct buffer *b, int fd)
394: {
395:
396: /* These are just reasonable values to make sure a significant amount of
397: data is written. There's no need to go crazy and try to write it all
398: in one shot. */
399: #ifdef IOV_MAX
400: #define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
401: #else
402: #define MAX_CHUNKS 16
403: #endif
404: #define MAX_FLUSH 131072
405:
406: struct buffer_data *d;
407: size_t written;
408: struct iovec iov[MAX_CHUNKS];
409: size_t iovcnt = 0;
410: size_t nbyte = 0;
411:
412: for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
413: d = d->next, iovcnt++)
414: {
415: iov[iovcnt].iov_base = d->data+d->sp;
416: nbyte += (iov[iovcnt].iov_len = d->cp-d->sp);
417: }
418:
419: if (!nbyte)
420: /* No data to flush: should we issue a warning message? */
421: return BUFFER_EMPTY;
422:
423: /* only place where written should be sign compared */
424: if ((ssize_t)(written = writev(fd,iov,iovcnt)) < 0)
425: {
426: if (ERRNO_IO_RETRY(errno))
427: /* Calling code should try again later. */
428: return BUFFER_PENDING;
429: zlog_warn("%s: write error on fd %d: %s",
430: __func__, fd, safe_strerror(errno));
431: return BUFFER_ERROR;
432: }
433:
434: /* Free printed buffer data. */
435: while (written > 0)
436: {
437: struct buffer_data *d;
438: if (!(d = b->head))
439: {
440: zlog_err("%s: corruption detected: buffer queue empty, "
441: "but written is %lu", __func__, (u_long)written);
442: break;
443: }
444: if (written < d->cp-d->sp)
445: {
446: d->sp += written;
447: return BUFFER_PENDING;
448: }
449:
450: written -= (d->cp-d->sp);
451: if (!(b->head = d->next))
452: b->tail = NULL;
453: BUFFER_DATA_FREE(d);
454: }
455:
456: return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
457:
458: #undef MAX_CHUNKS
459: #undef MAX_FLUSH
460: }
461:
462: buffer_status_t
463: buffer_write(struct buffer *b, int fd, const void *p, size_t size)
464: {
465: ssize_t nbytes;
466:
467: #if 0
468: /* Should we attempt to drain any previously buffered data? This could help
469: reduce latency in pushing out the data if we are stuck in a long-running
470: thread that is preventing the main select loop from calling the flush
471: thread... */
472: if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
473: return BUFFER_ERROR;
474: #endif
475: if (b->head)
476: /* Buffer is not empty, so do not attempt to write the new data. */
477: nbytes = 0;
478: else if ((nbytes = write(fd, p, size)) < 0)
479: {
480: if (ERRNO_IO_RETRY(errno))
481: nbytes = 0;
482: else
483: {
484: zlog_warn("%s: write error on fd %d: %s",
485: __func__, fd, safe_strerror(errno));
486: return BUFFER_ERROR;
487: }
488: }
489: /* Add any remaining data to the buffer. */
490: {
491: size_t written = nbytes;
492: if (written < size)
493: buffer_put(b, ((const char *)p)+written, size-written);
494: }
495: return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
496: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>