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