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