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