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