Line data Source code
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 143 : buffer_new (size_t size)
71 : {
72 : struct buffer *b;
73 :
74 143 : b = XCALLOC (MTYPE_BUFFER, sizeof (struct buffer));
75 :
76 143 : if (size)
77 0 : b->size = size;
78 : else
79 : {
80 : static size_t default_size;
81 143 : if (!default_size)
82 : {
83 46 : long pgsz = sysconf(_SC_PAGESIZE);
84 46 : default_size = ((((BUFFER_SIZE_DEFAULT-1)/pgsz)+1)*pgsz);
85 : }
86 143 : b->size = default_size;
87 : }
88 :
89 143 : return b;
90 : }
91 :
92 : /* Free buffer. */
93 : void
94 91 : buffer_free (struct buffer *b)
95 : {
96 91 : buffer_reset(b);
97 91 : XFREE (MTYPE_BUFFER, b);
98 91 : }
99 :
100 : /* Make string clone. */
101 : char *
102 0 : buffer_getstr (struct buffer *b)
103 : {
104 0 : size_t totlen = 0;
105 : struct buffer_data *data;
106 : char *s;
107 : char *p;
108 :
109 0 : for (data = b->head; data; data = data->next)
110 0 : totlen += data->cp - data->sp;
111 0 : if (!(s = XMALLOC(MTYPE_TMP, totlen+1)))
112 0 : return NULL;
113 0 : p = s;
114 0 : for (data = b->head; data; data = data->next)
115 : {
116 0 : memcpy(p, data->data + data->sp, data->cp - data->sp);
117 0 : p += data->cp - data->sp;
118 : }
119 0 : *p = '\0';
120 0 : return s;
121 : }
122 :
123 : /* Return 1 if buffer is empty. */
124 : int
125 0 : buffer_empty (struct buffer *b)
126 : {
127 0 : return (b->head == NULL);
128 : }
129 :
130 : /* Clear and free all allocated data. */
131 : void
132 93 : buffer_reset (struct buffer *b)
133 : {
134 : struct buffer_data *data;
135 : struct buffer_data *next;
136 :
137 93 : for (data = b->head; data; data = next)
138 : {
139 0 : next = data->next;
140 0 : BUFFER_DATA_FREE(data);
141 : }
142 93 : b->head = b->tail = NULL;
143 93 : }
144 :
145 : /* Add buffer_data to the end of buffer. */
146 : static struct buffer_data *
147 628 : buffer_add (struct buffer *b)
148 : {
149 : struct buffer_data *d;
150 :
151 628 : d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(struct buffer_data, data[b->size]));
152 628 : d->cp = d->sp = 0;
153 628 : d->next = NULL;
154 :
155 628 : if (b->tail)
156 2 : b->tail->next = d;
157 : else
158 626 : b->head = d;
159 628 : b->tail = d;
160 :
161 628 : return d;
162 : }
163 :
164 : /* Write data to buffer. */
165 : void
166 14124 : buffer_put(struct buffer *b, const void *p, size_t size)
167 : {
168 14124 : struct buffer_data *data = b->tail;
169 14124 : const char *ptr = p;
170 :
171 : /* We use even last one byte of data buffer. */
172 42374 : while (size)
173 : {
174 : size_t chunk;
175 :
176 : /* If there is no data buffer add it. */
177 14126 : if (data == NULL || data->cp == b->size)
178 628 : data = buffer_add (b);
179 :
180 14126 : chunk = ((size <= (b->size - data->cp)) ? size : (b->size - data->cp));
181 14126 : memcpy ((data->data + data->cp), ptr, chunk);
182 14126 : size -= chunk;
183 14126 : ptr += chunk;
184 14126 : data->cp += chunk;
185 : }
186 14124 : }
187 :
188 : /* Insert character into the buffer. */
189 : void
190 0 : buffer_putc (struct buffer *b, u_char c)
191 : {
192 0 : buffer_put(b, &c, 1);
193 0 : }
194 :
195 : /* Put string to the buffer. */
196 : void
197 0 : buffer_putstr (struct buffer *b, const char *c)
198 : {
199 0 : buffer_put(b, c, strlen(c));
200 0 : }
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 47 : 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 47 : if (!b->head)
212 47 : return BUFFER_EMPTY;
213 0 : head_sp = (head = b->head)->sp;
214 : /* Flush all data. */
215 0 : while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING)
216 : {
217 0 : if ((b->head == head) && (head_sp == head->sp) && (errno != EINTR))
218 : /* No data was flushed, so kernel buffer must be full. */
219 0 : return ret;
220 0 : head_sp = (head = b->head)->sp;
221 : }
222 :
223 0 : 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 0 : 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 0 : char more[] = " --More-- ";
238 0 : 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 0 : if (!b->head)
245 0 : return BUFFER_EMPTY;
246 :
247 0 : if (height < 1)
248 : {
249 0 : zlog_warn("%s called with non-positive window height %d, forcing to 1",
250 : __func__, height);
251 0 : height = 1;
252 : }
253 0 : else if (height >= 2)
254 0 : height--;
255 0 : if (width < 1)
256 : {
257 0 : zlog_warn("%s called with non-positive window width %d, forcing to 1",
258 : __func__, width);
259 0 : width = 1;
260 : }
261 :
262 : /* For erase and more data add two to b's buffer_data count.*/
263 0 : if (b->head->next == NULL)
264 : {
265 0 : iov_alloc = array_size(small_iov);
266 0 : iov = small_iov;
267 : }
268 : else
269 : {
270 0 : iov_alloc = ((height*(width+2))/b->size)+10;
271 0 : iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
272 : }
273 0 : iov_index = 0;
274 :
275 : /* Previously print out is performed. */
276 0 : if (erase_flag)
277 : {
278 0 : iov[iov_index].iov_base = erase;
279 0 : iov[iov_index].iov_len = sizeof erase;
280 0 : iov_index++;
281 : }
282 :
283 : /* Output data. */
284 0 : column = 1; /* Column position of next character displayed. */
285 0 : for (data = b->head; data && (height > 0); data = data->next)
286 : {
287 : size_t cp;
288 :
289 0 : cp = data->sp;
290 0 : while ((cp < data->cp) && (height > 0))
291 : {
292 : /* Calculate lines remaining and column position after displaying
293 : this character. */
294 0 : if (data->data[cp] == '\r')
295 0 : column = 1;
296 0 : else if ((data->data[cp] == '\n') || (column == width))
297 : {
298 0 : column = 1;
299 0 : height--;
300 : }
301 : else
302 0 : column++;
303 0 : cp++;
304 : }
305 0 : iov[iov_index].iov_base = (char *)(data->data + data->sp);
306 0 : iov[iov_index++].iov_len = cp-data->sp;
307 0 : data->sp = cp;
308 :
309 0 : if (iov_index == iov_alloc)
310 : /* This should not ordinarily happen. */
311 : {
312 0 : iov_alloc *= 2;
313 0 : if (iov != small_iov)
314 : {
315 0 : zlog_warn("%s: growing iov array to %d; "
316 : "width %d, height %d, size %lu",
317 0 : __func__, iov_alloc, width, height, (u_long)b->size);
318 0 : iov = XREALLOC(MTYPE_TMP, iov, iov_alloc*sizeof(*iov));
319 : }
320 : else
321 : {
322 : /* This should absolutely never occur. */
323 0 : zlog_err("%s: corruption detected: iov_small overflowed; "
324 : "head %p, tail %p, head->next %p",
325 0 : __func__, b->head, b->tail, b->head->next);
326 0 : iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
327 0 : memcpy(iov, small_iov, sizeof(small_iov));
328 : }
329 : }
330 : }
331 :
332 : /* In case of `more' display need. */
333 0 : if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag)
334 : {
335 0 : iov[iov_index].iov_base = more;
336 0 : iov[iov_index].iov_len = sizeof more;
337 0 : 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 0 : struct iovec *c_iov = iov;
346 0 : nbytes = 0; /* Make sure it's initialized. */
347 :
348 0 : while (iov_index > 0)
349 : {
350 : int iov_size;
351 :
352 0 : iov_size = ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
353 0 : if ((nbytes = writev(fd, c_iov, iov_size)) < 0)
354 : {
355 0 : zlog_warn("%s: writev to fd %d failed: %s",
356 0 : __func__, fd, safe_strerror(errno));
357 0 : break;
358 : }
359 :
360 : /* move pointer io-vector */
361 0 : c_iov += iov_size;
362 0 : 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 0 : while (b->head && (b->head->sp == b->head->cp))
373 : {
374 : struct buffer_data *del;
375 0 : if (!(b->head = (del = b->head)->next))
376 0 : b->tail = NULL;
377 0 : BUFFER_DATA_FREE(del);
378 : }
379 :
380 0 : if (iov != small_iov)
381 0 : XFREE (MTYPE_TMP, iov);
382 :
383 0 : return (nbytes < 0) ? BUFFER_ERROR :
384 0 : (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 626 : 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 626 : size_t iovcnt = 0;
410 626 : size_t nbyte = 0;
411 :
412 1880 : for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
413 628 : d = d->next, iovcnt++)
414 : {
415 628 : iov[iovcnt].iov_base = d->data+d->sp;
416 628 : nbyte += (iov[iovcnt].iov_len = d->cp-d->sp);
417 : }
418 :
419 626 : if (!nbyte)
420 : /* No data to flush: should we issue a warning message? */
421 0 : return BUFFER_EMPTY;
422 :
423 : /* only place where written should be sign compared */
424 626 : if ((ssize_t)(written = writev(fd,iov,iovcnt)) < 0)
425 : {
426 0 : if (ERRNO_IO_RETRY(errno))
427 : /* Calling code should try again later. */
428 0 : return BUFFER_PENDING;
429 0 : zlog_warn("%s: write error on fd %d: %s",
430 0 : __func__, fd, safe_strerror(errno));
431 0 : return BUFFER_ERROR;
432 : }
433 :
434 : /* Free printed buffer data. */
435 1880 : while (written > 0)
436 : {
437 : struct buffer_data *d;
438 628 : if (!(d = b->head))
439 : {
440 0 : zlog_err("%s: corruption detected: buffer queue empty, "
441 : "but written is %lu", __func__, (u_long)written);
442 0 : break;
443 : }
444 628 : if (written < d->cp-d->sp)
445 : {
446 0 : d->sp += written;
447 0 : return BUFFER_PENDING;
448 : }
449 :
450 628 : written -= (d->cp-d->sp);
451 628 : if (!(b->head = d->next))
452 626 : b->tail = NULL;
453 628 : BUFFER_DATA_FREE(d);
454 : }
455 :
456 626 : return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
457 :
458 : #undef MAX_CHUNKS
459 : #undef MAX_FLUSH
460 : }
461 :
462 : buffer_status_t
463 0 : 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 0 : if (b->head)
476 : /* Buffer is not empty, so do not attempt to write the new data. */
477 0 : nbytes = 0;
478 0 : else if ((nbytes = write(fd, p, size)) < 0)
479 : {
480 0 : if (ERRNO_IO_RETRY(errno))
481 0 : nbytes = 0;
482 : else
483 : {
484 0 : zlog_warn("%s: write error on fd %d: %s",
485 0 : __func__, fd, safe_strerror(errno));
486 0 : return BUFFER_ERROR;
487 : }
488 : }
489 : /* Add any remaining data to the buffer. */
490 : {
491 0 : size_t written = nbytes;
492 0 : if (written < size)
493 0 : buffer_put(b, ((const char *)p)+written, size-written);
494 : }
495 0 : return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
496 : }
|