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