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: {
265: iov_alloc = array_size(small_iov);
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__, (void *)b->head, (void *)b->tail,
326: (void *)b->head->next);
327: iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
328: memcpy(iov, small_iov, sizeof(small_iov));
329: }
330: }
331: }
332:
333: /* In case of `more' display need. */
334: if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag)
335: {
336: iov[iov_index].iov_base = more;
337: iov[iov_index].iov_len = sizeof more;
338: iov_index++;
339: }
340:
341:
342: #ifdef IOV_MAX
343: /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
344: example: Solaris2.6 are defined IOV_MAX size at 16. */
345: {
346: struct iovec *c_iov = iov;
347: nbytes = 0; /* Make sure it's initialized. */
348:
349: while (iov_index > 0)
350: {
351: int iov_size;
352:
353: iov_size = ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
354: if ((nbytes = writev(fd, c_iov, iov_size)) < 0)
355: {
356: zlog_warn("%s: writev to fd %d failed: %s",
357: __func__, fd, safe_strerror(errno));
358: break;
359: }
360:
361: /* move pointer io-vector */
362: c_iov += iov_size;
363: iov_index -= iov_size;
364: }
365: }
366: #else /* IOV_MAX */
367: if ((nbytes = writev (fd, iov, iov_index)) < 0)
368: zlog_warn("%s: writev to fd %d failed: %s",
369: __func__, fd, safe_strerror(errno));
370: #endif /* IOV_MAX */
371:
372: /* Free printed buffer data. */
373: while (b->head && (b->head->sp == b->head->cp))
374: {
375: struct buffer_data *del;
376: if (!(b->head = (del = b->head)->next))
377: b->tail = NULL;
378: BUFFER_DATA_FREE(del);
379: }
380:
381: if (iov != small_iov)
382: XFREE (MTYPE_TMP, iov);
383:
384: return (nbytes < 0) ? BUFFER_ERROR :
385: (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
386: }
387:
388: /* This function (unlike other buffer_flush* functions above) is designed
389: to work with non-blocking sockets. It does not attempt to write out
390: all of the queued data, just a "big" chunk. It returns 0 if it was
391: able to empty out the buffers completely, 1 if more flushing is
392: required later, or -1 on a fatal write error. */
393: buffer_status_t
394: buffer_flush_available(struct buffer *b, int fd)
395: {
396:
397: /* These are just reasonable values to make sure a significant amount of
398: data is written. There's no need to go crazy and try to write it all
399: in one shot. */
400: #ifdef IOV_MAX
401: #define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
402: #else
403: #define MAX_CHUNKS 16
404: #endif
405: #define MAX_FLUSH 131072
406:
407: struct buffer_data *d;
408: size_t written;
409: struct iovec iov[MAX_CHUNKS];
410: size_t iovcnt = 0;
411: size_t nbyte = 0;
412:
413: for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
414: d = d->next, iovcnt++)
415: {
416: iov[iovcnt].iov_base = d->data+d->sp;
417: nbyte += (iov[iovcnt].iov_len = d->cp-d->sp);
418: }
419:
420: if (!nbyte)
421: /* No data to flush: should we issue a warning message? */
422: return BUFFER_EMPTY;
423:
424: /* only place where written should be sign compared */
425: if ((ssize_t)(written = writev(fd,iov,iovcnt)) < 0)
426: {
427: if (ERRNO_IO_RETRY(errno))
428: /* Calling code should try again later. */
429: return BUFFER_PENDING;
430: zlog_warn("%s: write error on fd %d: %s",
431: __func__, fd, safe_strerror(errno));
432: return BUFFER_ERROR;
433: }
434:
435: /* Free printed buffer data. */
436: while (written > 0)
437: {
438: struct buffer_data *d;
439: if (!(d = b->head))
440: {
441: zlog_err("%s: corruption detected: buffer queue empty, "
442: "but written is %lu", __func__, (u_long)written);
443: break;
444: }
445: if (written < d->cp-d->sp)
446: {
447: d->sp += written;
448: return BUFFER_PENDING;
449: }
450:
451: written -= (d->cp-d->sp);
452: if (!(b->head = d->next))
453: b->tail = NULL;
454: BUFFER_DATA_FREE(d);
455: }
456:
457: return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
458:
459: #undef MAX_CHUNKS
460: #undef MAX_FLUSH
461: }
462:
463: buffer_status_t
464: buffer_write(struct buffer *b, int fd, const void *p, size_t size)
465: {
466: ssize_t nbytes;
467:
468: #if 0
469: /* Should we attempt to drain any previously buffered data? This could help
470: reduce latency in pushing out the data if we are stuck in a long-running
471: thread that is preventing the main select loop from calling the flush
472: thread... */
473: if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
474: return BUFFER_ERROR;
475: #endif
476: if (b->head)
477: /* Buffer is not empty, so do not attempt to write the new data. */
478: nbytes = 0;
479: else if ((nbytes = write(fd, p, size)) < 0)
480: {
481: if (ERRNO_IO_RETRY(errno))
482: nbytes = 0;
483: else
484: {
485: zlog_warn("%s: write error on fd %d: %s",
486: __func__, fd, safe_strerror(errno));
487: return BUFFER_ERROR;
488: }
489: }
490: /* Add any remaining data to the buffer. */
491: {
492: size_t written = nbytes;
493: if (written < size)
494: buffer_put(b, ((const char *)p)+written, size-written);
495: }
496: return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
497: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>