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