]>
git.proxmox.com Git - mirror_frr.git/blob - lib/buffer.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Buffering of output and input.
4 * Copyright (C) 1998 Kunihiro Ishiguro
13 #include "lib_errors.h"
17 DEFINE_MTYPE_STATIC(LIB
, BUFFER
, "Buffer");
18 DEFINE_MTYPE_STATIC(LIB
, BUFFER_DATA
, "Buffer data");
23 struct buffer_data
*head
;
24 struct buffer_data
*tail
;
26 /* Size of each buffer_data chunk. */
32 struct buffer_data
*next
;
34 /* Location to add new data. */
37 /* Pointer to data not yet flushed. */
40 /* Actual data stream (variable length). */
41 unsigned char data
[]; /* real dimension is buffer->size */
44 /* It should always be true that: 0 <= sp <= cp <= size */
46 /* Default buffer size (used if none specified). It is rounded up to the
47 next page boundary. */
48 #define BUFFER_SIZE_DEFAULT 4096
50 #define BUFFER_DATA_FREE(D) XFREE(MTYPE_BUFFER_DATA, (D))
52 /* Make new buffer. */
53 struct buffer
*buffer_new(size_t size
)
57 b
= XCALLOC(MTYPE_BUFFER
, sizeof(struct buffer
));
62 static size_t default_size
;
64 long pgsz
= sysconf(_SC_PAGESIZE
);
65 default_size
= ((((BUFFER_SIZE_DEFAULT
- 1) / pgsz
) + 1)
68 b
->size
= default_size
;
75 void buffer_free(struct buffer
*b
)
78 XFREE(MTYPE_BUFFER
, b
);
81 /* Make string clone. */
82 char *buffer_getstr(struct buffer
*b
)
85 struct buffer_data
*data
;
89 for (data
= b
->head
; data
; data
= data
->next
)
90 totlen
+= data
->cp
- data
->sp
;
91 if (!(s
= XMALLOC(MTYPE_TMP
, totlen
+ 1)))
94 for (data
= b
->head
; data
; data
= data
->next
) {
95 memcpy(p
, data
->data
+ data
->sp
, data
->cp
- data
->sp
);
96 p
+= data
->cp
- data
->sp
;
102 /* Clear and free all allocated data. */
103 void buffer_reset(struct buffer
*b
)
105 struct buffer_data
*data
;
106 struct buffer_data
*next
;
108 for (data
= b
->head
; data
; data
= next
) {
110 BUFFER_DATA_FREE(data
);
112 b
->head
= b
->tail
= NULL
;
115 /* Add buffer_data to the end of buffer. */
116 static struct buffer_data
*buffer_add(struct buffer
*b
)
118 struct buffer_data
*d
;
120 d
= XMALLOC(MTYPE_BUFFER_DATA
,
121 offsetof(struct buffer_data
, data
) + b
->size
);
134 /* Write data to buffer. */
135 void buffer_put(struct buffer
*b
, const void *p
, size_t size
)
137 struct buffer_data
*data
= b
->tail
;
140 /* We use even last one byte of data buffer. */
144 /* If there is no data buffer add it. */
145 if (data
== NULL
|| data
->cp
== b
->size
)
146 data
= buffer_add(b
);
148 chunk
= ((size
<= (b
->size
- data
->cp
)) ? size
149 : (b
->size
- data
->cp
));
150 memcpy((data
->data
+ data
->cp
), ptr
, chunk
);
157 /* Insert character into the buffer. */
158 void buffer_putc(struct buffer
*b
, uint8_t c
)
160 buffer_put(b
, &c
, 1);
163 /* Put string to the buffer. */
164 void buffer_putstr(struct buffer
*b
, const char *c
)
166 buffer_put(b
, c
, strlen(c
));
169 /* Expand \n to \r\n */
170 void buffer_put_crlf(struct buffer
*b
, const void *origp
, size_t origsize
)
172 struct buffer_data
*data
= b
->tail
;
173 const char *p
= origp
, *end
= p
+ origsize
, *lf
;
176 lf
= memchr(p
, '\n', end
- p
);
178 /* We use even last one byte of data buffer. */
182 /* If there is no data buffer add it. */
183 if (data
== NULL
|| data
->cp
== b
->size
)
184 data
= buffer_add(b
);
186 size
= (lf
? lf
: end
) - p
;
187 avail
= b
->size
- data
->cp
;
189 chunk
= (size
<= avail
) ? size
: avail
;
190 memcpy(data
->data
+ data
->cp
, p
, chunk
);
195 if (lf
&& size
<= avail
) {
196 /* we just copied up to (including) a '\n' */
197 if (data
->cp
== b
->size
)
198 data
= buffer_add(b
);
199 data
->data
[data
->cp
++] = '\r';
200 if (data
->cp
== b
->size
)
201 data
= buffer_add(b
);
202 data
->data
[data
->cp
++] = '\n';
205 lf
= memchr(p
, '\n', end
- p
);
210 /* Keep flushing data to the fd until the buffer is empty or an error is
211 encountered or the operation would block. */
212 buffer_status_t
buffer_flush_all(struct buffer
*b
, int fd
)
215 struct buffer_data
*head
;
220 head_sp
= (head
= b
->head
)->sp
;
221 /* Flush all data. */
222 while ((ret
= buffer_flush_available(b
, fd
)) == BUFFER_PENDING
) {
223 if ((b
->head
== head
) && (head_sp
== head
->sp
)
225 /* No data was flushed, so kernel buffer must be full.
228 head_sp
= (head
= b
->head
)->sp
;
234 /* Flush enough data to fill a terminal window of the given scene (used only
235 by vty telnet interface). */
236 buffer_status_t
buffer_flush_window(struct buffer
*b
, int fd
, int width
,
237 int height
, int erase_flag
,
244 struct iovec small_iov
[3];
245 char more
[] = " --More-- ";
246 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 struct buffer_data
*data
;
258 else if (height
>= 2)
263 /* For erase and more data add two to b's buffer_data count.*/
264 if (b
->head
->next
== NULL
) {
265 iov_alloc
= array_size(small_iov
);
268 iov_alloc
= ((height
* (width
+ 2)) / b
->size
) + 10;
269 iov
= XMALLOC(MTYPE_TMP
, iov_alloc
* sizeof(*iov
));
273 /* Previously print out is performed. */
275 iov
[iov_index
].iov_base
= erase
;
276 iov
[iov_index
].iov_len
= sizeof(erase
);
281 column
= 1; /* Column position of next character displayed. */
282 for (data
= b
->head
; data
&& (height
> 0); data
= data
->next
) {
286 while ((cp
< data
->cp
) && (height
> 0)) {
287 /* Calculate lines remaining and column position after
290 if (data
->data
[cp
] == '\r')
292 else if ((data
->data
[cp
] == '\n')
293 || (column
== width
)) {
300 iov
[iov_index
].iov_base
= (char *)(data
->data
+ data
->sp
);
301 iov
[iov_index
++].iov_len
= cp
- data
->sp
;
304 if (iov_index
== iov_alloc
)
305 /* This should not ordinarily happen. */
308 if (iov
!= small_iov
) {
309 iov
= XREALLOC(MTYPE_TMP
, iov
,
310 iov_alloc
* sizeof(*iov
));
312 /* This should absolutely never occur. */
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 iov
= XMALLOC(MTYPE_TMP
,
319 iov_alloc
* sizeof(*iov
));
320 memcpy(iov
, small_iov
, sizeof(small_iov
));
325 /* In case of `more' display need. */
326 if (b
->tail
&& (b
->tail
->sp
< b
->tail
->cp
) && !no_more_flag
) {
327 iov
[iov_index
].iov_base
= more
;
328 iov
[iov_index
].iov_len
= sizeof(more
);
334 /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
335 example: Solaris2.6 are defined IOV_MAX size at 16. */
337 struct iovec
*c_iov
= iov
;
338 nbytes
= 0; /* Make sure it's initialized. */
340 while (iov_index
> 0) {
344 ((iov_index
> IOV_MAX
) ? IOV_MAX
: iov_index
);
345 nbytes
= writev(fd
, c_iov
, iov_size
);
347 flog_err(EC_LIB_SOCKET
,
348 "%s: writev to fd %d failed: %s",
349 __func__
, fd
, safe_strerror(errno
));
353 /* move pointer io-vector */
355 iov_index
-= iov_size
;
359 nbytes
= writev(fd
, iov
, iov_index
);
361 flog_err(EC_LIB_SOCKET
, "%s: writev to fd %d failed: %s",
362 __func__
, fd
, safe_strerror(errno
));
365 /* Free printed buffer data. */
366 while (b
->head
&& (b
->head
->sp
== b
->head
->cp
)) {
367 struct buffer_data
*del
;
368 if (!(b
->head
= (del
= b
->head
)->next
))
370 BUFFER_DATA_FREE(del
);
373 if (iov
!= small_iov
)
374 XFREE(MTYPE_TMP
, iov
);
376 return (nbytes
< 0) ? BUFFER_ERROR
377 : (b
->head
? BUFFER_PENDING
: BUFFER_EMPTY
);
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 buffer_status_t
buffer_flush_available(struct buffer
*b
, int fd
)
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
392 #define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
394 #define MAX_CHUNKS 16
396 #define MAX_FLUSH 131072
398 struct buffer_data
*d
;
400 struct iovec iov
[MAX_CHUNKS
];
407 for (d
= b
->head
; d
&& (iovcnt
< MAX_CHUNKS
) && (nbyte
< MAX_FLUSH
);
408 d
= d
->next
, iovcnt
++) {
409 iov
[iovcnt
].iov_base
= d
->data
+ d
->sp
;
410 nbyte
+= (iov
[iovcnt
].iov_len
= d
->cp
- d
->sp
);
414 /* No data to flush: should we issue a warning message? */
417 /* only place where written should be sign compared */
418 if ((ssize_t
)(written
= writev(fd
, iov
, iovcnt
)) < 0) {
419 if (ERRNO_IO_RETRY(errno
))
420 /* Calling code should try again later. */
421 return BUFFER_PENDING
;
422 flog_err(EC_LIB_SOCKET
, "%s: write error on fd %d: %s",
423 __func__
, fd
, safe_strerror(errno
));
427 /* Free printed buffer data. */
428 while (written
> 0) {
429 if (!(d
= b
->head
)) {
432 "%s: corruption detected: buffer queue empty, but written is %lu",
433 __func__
, (unsigned long)written
);
436 if (written
< d
->cp
- d
->sp
) {
438 return BUFFER_PENDING
;
441 written
-= (d
->cp
- d
->sp
);
442 if (!(b
->head
= d
->next
))
447 return b
->head
? BUFFER_PENDING
: BUFFER_EMPTY
;
453 buffer_status_t
buffer_write(struct buffer
*b
, int fd
, const void *p
,
459 /* Buffer is not empty, so do not attempt to write the new data.
463 nbytes
= write(fd
, p
, size
);
465 if (ERRNO_IO_RETRY(errno
))
468 flog_err(EC_LIB_SOCKET
,
469 "%s: write error on fd %d: %s",
470 __func__
, fd
, safe_strerror(errno
));
475 /* Add any remaining data to the buffer. */
477 size_t written
= nbytes
;
479 buffer_put(b
, ((const char *)p
) + written
,
482 return b
->head
? BUFFER_PENDING
: BUFFER_EMPTY
;