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