Project

General

Profile

Download (11.1 KB) Statistics
| Branch: | Tag: | Revision:
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 <stdio.h>
24
#include <stdlib.h>
25
#include <unistd.h>
26
#include <string.h>
27
#include <errno.h>
28
#include <stddef.h>
29
#include <sys/uio.h>
30

    
31
#include "buffer.h"
32
#include "vty.h"
33

    
34
/* Buffer master. */
35
struct buffer {
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
	struct buffer_data *next;
47

    
48
	/* Location to add new data. */
49
	size_t cp;
50

    
51
	/* Pointer to data not yet flushed. */
52
	size_t sp;
53

    
54
	/* Actual data stream (variable length). */
55
	unsigned char data[0];	/* real dimension is buffer->size */
56
};
57

    
58
/* It should always be true that: 0 <= sp <= cp <= size */
59

    
60
/* Default buffer size (used if none specified).  It is rounded up to the
61
   next page boundery. */
62
#define BUFFER_SIZE_DEFAULT		4096
63

    
64
#define BUFFER_DATA_FREE(D) free((D))
65

    
66
/* Make new buffer. */
67
struct buffer *buffer_new(size_t size)
68
{
69
	struct buffer *b;
70

    
71
	b = calloc(1, sizeof(struct buffer));
72

    
73
	if (size)
74
		b->size = size;
75
	else {
76
		static size_t default_size;
77
		if (!default_size) {
78
			long pgsz = sysconf(_SC_PAGESIZE);
79
			default_size =
80
			    ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz);
81
		}
82
		b->size = default_size;
83
	}
84

    
85
	return b;
86
}
87

    
88
/* Free buffer. */
89
void buffer_free(struct buffer *b)
90
{
91
	buffer_reset(b);
92
	free(b);
93
}
94

    
95
/* Make string clone. */
96
char *buffer_getstr(struct buffer *b)
97
{
98
	size_t totlen = 0;
99
	struct buffer_data *data;
100
	char *s;
101
	char *p;
102

    
103
	for (data = b->head; data; data = data->next)
104
		totlen += data->cp - data->sp;
105
	if (!(s = malloc(totlen + 1)))
106
		return NULL;
107
	p = s;
108
	for (data = b->head; data; data = data->next) {
109
		memcpy(p, data->data + data->sp, data->cp - data->sp);
110
		p += data->cp - data->sp;
111
	}
112
	*p = '\0';
113
	return s;
114
}
115

    
116
/* Return 1 if buffer is empty. */
117
int buffer_empty(struct buffer *b)
118
{
119
	return (b->head == NULL);
120
}
121

    
122
/* Clear and free all allocated data. */
123
void buffer_reset(struct buffer *b)
124
{
125
	struct buffer_data *data;
126
	struct buffer_data *next;
127

    
128
	for (data = b->head; data; data = next) {
129
		next = data->next;
130
		BUFFER_DATA_FREE(data);
131
	}
132
	b->head = b->tail = NULL;
133
}
134

    
135
/* Add buffer_data to the end of buffer. */
136
static struct buffer_data *buffer_add(struct buffer *b)
137
{
138
	struct buffer_data *d;
139

    
140
	d = malloc(offsetof(struct buffer_data, data[b->size]));
141
	if (!d)
142
		return NULL;
143
	d->cp = d->sp = 0;
144
	d->next = NULL;
145

    
146
	if (b->tail)
147
		b->tail->next = d;
148
	else
149
		b->head = d;
150
	b->tail = d;
151

    
152
	return d;
153
}
154

    
155
/* Write data to buffer. */
156
void buffer_put(struct buffer *b, const void *p, size_t size)
157
{
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 =
170
		    ((size <=
171
		      (b->size - data->cp)) ? size : (b->size - data->cp));
172
		memcpy((data->data + data->cp), ptr, chunk);
173
		size -= chunk;
174
		ptr += chunk;
175
		data->cp += chunk;
176
	}
177
}
178

    
179
/* Insert character into the buffer. */
180
void buffer_putc(struct buffer *b, u_char c)
181
{
182
	buffer_put(b, &c, 1);
183
}
184

    
185
/* Put string to the buffer. */
186
void buffer_putstr(struct buffer *b, const char *c)
187
{
188
	buffer_put(b, c, strlen(c));
189
}
190

    
191
/* Keep flushing data to the fd until the buffer is empty or an error is
192
   encountered or the operation would block. */
193
buffer_status_t buffer_flush_all(struct buffer *b, int fd)
194
{
195
	buffer_status_t ret;
196
	struct buffer_data *head;
197
	size_t head_sp;
198

    
199
	if (!b->head)
200
		return BUFFER_EMPTY;
201
	head_sp = (head = b->head)->sp;
202
	/* Flush all data. */
203
	while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) {
204
		if ((b->head == head) && (head_sp == head->sp)
205
		    && (errno != EINTR))
206
			/* No data was flushed, so kernel buffer must be full. */
207
			return ret;
208
		head_sp = (head = b->head)->sp;
209
	}
210

    
211
	return ret;
212
}
213

    
214
#if 0
215
/* Flush enough data to fill a terminal window of the given scene (used only
216
   by vty telnet interface). */
217
buffer_status_t
218
buffer_flush_window(struct buffer * b, int fd, int width, int height,
219
		    int erase_flag, int no_more_flag)
220
{
221
	int nbytes;
222
	int iov_alloc;
223
	int iov_index;
224
	struct iovec *iov;
225
	struct iovec small_iov[3];
226
	char more[] = " --More-- ";
227
	char erase[] =
228
	    { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
229
		' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
230
		0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
231
	};
232
	struct buffer_data *data;
233
	int column;
234

    
235
	if (!b->head)
236
		return BUFFER_EMPTY;
237

    
238
	if (height < 1) {
239
		zlog_warn
240
		    ("%s called with non-positive window height %d, forcing to 1",
241
		     __func__, height);
242
		height = 1;
243
	} else if (height >= 2)
244
		height--;
245
	if (width < 1) {
246
		zlog_warn
247
		    ("%s called with non-positive window width %d, forcing to 1",
248
		     __func__, width);
249
		width = 1;
250
	}
251

    
252
	/* For erase and more data add two to b's buffer_data count. */
253
	if (b->head->next == NULL) {
254
		iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]);
255
		iov = small_iov;
256
	} else {
257
		iov_alloc = ((height * (width + 2)) / b->size) + 10;
258
		iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov));
259
	}
260
	iov_index = 0;
261

    
262
	/* Previously print out is performed. */
263
	if (erase_flag) {
264
		iov[iov_index].iov_base = erase;
265
		iov[iov_index].iov_len = sizeof erase;
266
		iov_index++;
267
	}
268

    
269
	/* Output data. */
270
	column = 1;		/* Column position of next character displayed. */
271
	for (data = b->head; data && (height > 0); data = data->next) {
272
		size_t cp;
273

    
274
		cp = data->sp;
275
		while ((cp < data->cp) && (height > 0)) {
276
			/* Calculate lines remaining and column position after displaying
277
			   this character. */
278
			if (data->data[cp] == '\r')
279
				column = 1;
280
			else if ((data->data[cp] == '\n') || (column == width)) {
281
				column = 1;
282
				height--;
283
			} else
284
				column++;
285
			cp++;
286
		}
287
		iov[iov_index].iov_base = (char *)(data->data + data->sp);
288
		iov[iov_index++].iov_len = cp - data->sp;
289
		data->sp = cp;
290

    
291
		if (iov_index == iov_alloc)
292
			/* This should not ordinarily happen. */
293
		{
294
			iov_alloc *= 2;
295
			if (iov != small_iov) {
296
				zlog_warn("%s: growing iov array to %d; "
297
					  "width %d, height %d, size %lu",
298
					  __func__, iov_alloc, width, height,
299
					  (u_long) b->size);
300
				iov =
301
				    XREALLOC(MTYPE_TMP, iov,
302
					     iov_alloc * sizeof(*iov));
303
			} else {
304
				/* This should absolutely never occur. */
305
				zlog_err
306
				    ("%s: corruption detected: iov_small overflowed; "
307
				     "head %p, tail %p, head->next %p",
308
				     __func__, b->head, b->tail, b->head->next);
309
				iov =
310
				    XMALLOC(MTYPE_TMP,
311
					    iov_alloc * sizeof(*iov));
312
				memcpy(iov, small_iov, sizeof(small_iov));
313
			}
314
		}
315
	}
316

    
317
	/* In case of `more' display need. */
318
	if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) {
319
		iov[iov_index].iov_base = more;
320
		iov[iov_index].iov_len = sizeof more;
321
		iov_index++;
322
	}
323
#ifdef IOV_MAX
324
	/* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
325
	   example: Solaris2.6 are defined IOV_MAX size at 16.     */
326
	{
327
		struct iovec *c_iov = iov;
328
		nbytes = 0;	/* Make sure it's initialized. */
329

    
330
		while (iov_index > 0) {
331
			int iov_size;
332

    
333
			iov_size =
334
			    ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
335
			if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
336
				zlog_warn("%s: writev to fd %d failed: %s",
337
					  __func__, fd, safe_strerror(errno));
338
				break;
339
			}
340

    
341
			/* move pointer io-vector */
342
			c_iov += iov_size;
343
			iov_index -= iov_size;
344
		}
345
	}
346
#else				/* IOV_MAX */
347
	if ((nbytes = writev(fd, iov, iov_index)) < 0)
348
		zlog_warn("%s: writev to fd %d failed: %s",
349
			  __func__, fd, safe_strerror(errno));
350
#endif				/* IOV_MAX */
351

    
352
	/* Free printed buffer data. */
353
	while (b->head && (b->head->sp == b->head->cp)) {
354
		struct buffer_data *del;
355
		if (!(b->head = (del = b->head)->next))
356
			b->tail = NULL;
357
		BUFFER_DATA_FREE(del);
358
	}
359

    
360
	if (iov != small_iov)
361
		XFREE(MTYPE_TMP, iov);
362

    
363
	return (nbytes < 0) ? BUFFER_ERROR :
364
	    (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
365
}
366
#endif
367

    
368
/* This function (unlike other buffer_flush* functions above) is designed
369
to work with non-blocking sockets.  It does not attempt to write out
370
all of the queued data, just a "big" chunk.  It returns 0 if it was
371
able to empty out the buffers completely, 1 if more flushing is
372
required later, or -1 on a fatal write error. */
373
buffer_status_t buffer_flush_available(struct buffer * b, int fd)
374
{
375

    
376
/* These are just reasonable values to make sure a significant amount of
377
data is written.  There's no need to go crazy and try to write it all
378
in one shot. */
379
#ifdef IOV_MAX
380
#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
381
#else
382
#define MAX_CHUNKS 16
383
#endif
384
#define MAX_FLUSH 131072
385

    
386
	struct buffer_data *d;
387
	size_t written;
388
	struct iovec iov[MAX_CHUNKS];
389
	size_t iovcnt = 0;
390
	size_t nbyte = 0;
391

    
392
	for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
393
	     d = d->next, iovcnt++) {
394
		iov[iovcnt].iov_base = d->data + d->sp;
395
		nbyte += (iov[iovcnt].iov_len = d->cp - d->sp);
396
	}
397

    
398
	if (!nbyte)
399
		/* No data to flush: should we issue a warning message? */
400
		return BUFFER_EMPTY;
401

    
402
	/* only place where written should be sign compared */
403
	if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) {
404
		if (ERRNO_IO_RETRY(errno))
405
			/* Calling code should try again later. */
406
			return BUFFER_PENDING;
407
		return BUFFER_ERROR;
408
	}
409

    
410
	/* Free printed buffer data. */
411
	while (written > 0) {
412
		struct buffer_data *d;
413
		if (!(d = b->head))
414
			break;
415
		if (written < d->cp - d->sp) {
416
			d->sp += written;
417
			return BUFFER_PENDING;
418
		}
419

    
420
		written -= (d->cp - d->sp);
421
		if (!(b->head = d->next))
422
			b->tail = NULL;
423
		BUFFER_DATA_FREE(d);
424
	}
425

    
426
	return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
427

    
428
#undef MAX_CHUNKS
429
#undef MAX_FLUSH
430
}
431

    
432
buffer_status_t
433
buffer_write(struct buffer * b, int fd, const void *p, size_t size)
434
{
435
	ssize_t nbytes;
436

    
437
#if 0
438
	/* Should we attempt to drain any previously buffered data?  This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */
439

    
440
	if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
441
		return BUFFER_ERROR;
442
#endif
443
	if (b->head)
444
		/* Buffer is not empty, so do not attempt to write the new data. */
445
		nbytes = 0;
446
	else if ((nbytes = write(fd, p, size)) < 0) {
447
		if (ERRNO_IO_RETRY(errno))
448
			nbytes = 0;
449
		else
450
			return BUFFER_ERROR;
451
	}
452
	/* Add any remaining data to the buffer. */
453
	{
454
		size_t written = nbytes;
455
		if (written < size)
456
			buffer_put(b, ((const char *)p) + written,
457
				   size - written);
458
	}
459
	return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
460
}
(2-2/10)
Add picture from clipboard (Maximum size: 48.8 MB)