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