2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu>

* buffer.h: Make the struct buffer and struct buffer_data structures
	  private by moving them inside buffer.c.  Add comments for all
	  functions.  Rename buffer_write as buffer_put (to be more consistent
	  with the buffer_putc and buffer_putstr functions).  Declare a new
	  buffer_write function that is used to write data to a file descriptor
	  and/or add it to the buffer queue.  Remove unused function
	  buffer_flush_vty_all.  Create a new enum typedef buffer_status_t
	  to be used as the return code for all buffer_flush* functions
	  and buffer_write.
	* buffer.c: The struct buffer and struct buffer_data declarations
	  are now private to this file.  In conjunction with that, remove
	  some unnecessary fields: struct buffer (alloc, unused_head,
	  unused_tail, length), struct buffer_data (prev).
	  (buffer_data_new) Removed: functionality incorporated into buffer_add.
	  (buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead.
	  (buffer_new) Use calloc instead of malloc + memset(zero).
	  Supply an appropriate default size if the specified size is 0.
	  (buffer_free) Eliminate code duplication by calling buffer_reset to
	  free the contents of the buffer (and remove unused code related
	  to unused_head).
	  (buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more
	  compact).
	  (buffer_reset) Use macro BUFFER_DATA_FREE.  No need to set
	  alloc and length to 0 (these fields have been removed).
	  (buffer_add) Fix scope to be static.  Call XMALLOC directly instead
	  of calling removed buffer_data_new function.  Simplify the logic
	  (since it's now a singly-linked list instead of doubly-linked).
	  (buffer_write) Renamed to buffer_put.  Change to void, since return
	  code of 1 was meaningless.  No need to adjust length field, since
	  it has been removed.
	  (buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty)
	  Remove unused functions.
	  (buffer_flush_all) Rewrite using buffer_flush_available to eliminate
	  a possible failure mode if IOV_MAX is less than the number of buffers
	  on the queue.
	  (buffer_flush_window) Incorporate logic from buffer_flush_vty.
	  Log an error message if there is a writev error.
	  (buffer_flush_available) Be more paranoid: check for case where
	  buffer is already empty.  Use new ERRNO_IO_RETRY macro, and use
	  new enum for return codes.  Simplify deletion logic (since it's
	  now a singly-linked list).
	  (buffer_write) New function for use with non-blocking I/O.
	* vty.h: Replace the struct vty sb_buffer field with a fixed-size
	  (5-character) sb_buf field and an sb_len field, since using
	  a struct buffer was inappropriate for this task.  Add some useful
	  comments about telnet window size negotiation.
	* vty.c: Include <arpa/telnet.h> (no longer included by zebra.h).
	  Remove VTY_OBUF_SIZE (instead use buffer_new default size).
	  Make telnet_backward_char and telnet_space_char static const.
	  (vty_out) Replace buffer_write with buffer_put.
	  (vty_log_out) Check for I/O errors.  If fatal, close the vty session.
	  Consolidate 3 separate writes into a single write call.
	  (vty_will_echo,vty_command,vty_next_line,vty_previous_line,
	  vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo,
	  vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout,
	  vty_config_write,vty_save_cwd) Fix scope to static.
	  (vty_new) Let buffer_new use its default buffer size.
	  (vty_write) Fix signature: 2nd arg should be const char *.
	  Replaced buffer_write with buffer_put.
	  (vty_telnet_option) Fix minor bug (window height or width greater than
	  255 was broken).  Use sb_buf and sb_len instead of removed sb_buffer
	  (which was being used improperly).
	  (vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal.
	  If the error is fatal, call buffer_reset so vty_close does not attempt
	  to flush the data.  Use new sb_buf and sb_len instead of sb_buffer
	  to store the SB negotiation string.
	  (vty_flush) When vty->lines is 0, call buffer_flush_available instead
	  of buffer_flush_window.  Look at the return code from buffer_flush
	  to detect I/O errors (and in that case, log an error message and
	  close the vty).
	  (vty_create) Fix scope to static.  Initialize sb_len to 0 instead
	  of creating sb_buffer.
	  (vty_accept) Set socket nonblocking.
	  (vtysh_accept) Use new set_nonblocking function instead of calling
	  fcntl directly.
	  (vtysh_flush) New function called from vtysh_read (after command
	  execution) and from vtysh_write.  This flushes the buffer
	  and reacts appropriately to the return code (by closing the vty
	  or scheduling further flushes).
	  (vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY.
	  If not, just try again later.  Otherwise, call buffer_reset before
	  calling vty_close (to avoid trying to flush the buffer in vty_close).
	  Fix logic to allow case where a command does not arrive atomically
	  in a single read call by checking for the terminating NUL char.
	  (vtysh_write) Use new vtysh_flush helper function.
	  (vty_close) No need to call buffer_empty, just call buffer_flush_all
	  in any case (it will check whether the buffer is empty).
	  Do not free sb_buffer (since it has been removed).
	  (vty_log_fixed) Use writev instead of write.
	* zebra.h: Do not include <arpa/telnet.h>, since this is used only
	  by lib/vty.c.
This commit is contained in:
ajs 2005-02-23 15:12:34 +00:00
parent 338b342415
commit 9fc7ebf10b
6 changed files with 615 additions and 587 deletions

View File

@ -1,3 +1,97 @@
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
* buffer.h: Make the struct buffer and struct buffer_data structures
private by moving them inside buffer.c. Add comments for all
functions. Rename buffer_write as buffer_put (to be more consistent
with the buffer_putc and buffer_putstr functions). Declare a new
buffer_write function that is used to write data to a file descriptor
and/or add it to the buffer queue. Remove unused function
buffer_flush_vty_all. Create a new enum typedef buffer_status_t
to be used as the return code for all buffer_flush* functions
and buffer_write.
* buffer.c: The struct buffer and struct buffer_data declarations
are now private to this file. In conjunction with that, remove
some unnecessary fields: struct buffer (alloc, unused_head,
unused_tail, length), struct buffer_data (prev).
(buffer_data_new) Removed: functionality incorporated into buffer_add.
(buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead.
(buffer_new) Use calloc instead of malloc + memset(zero).
Supply an appropriate default size if the specified size is 0.
(buffer_free) Eliminate code duplication by calling buffer_reset to
free the contents of the buffer (and remove unused code related
to unused_head).
(buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more
compact).
(buffer_reset) Use macro BUFFER_DATA_FREE. No need to set
alloc and length to 0 (these fields have been removed).
(buffer_add) Fix scope to be static. Call XMALLOC directly instead
of calling removed buffer_data_new function. Simplify the logic
(since it's now a singly-linked list instead of doubly-linked).
(buffer_write) Renamed to buffer_put. Change to void, since return
code of 1 was meaningless. No need to adjust length field, since
it has been removed.
(buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty)
Remove unused functions.
(buffer_flush_all) Rewrite using buffer_flush_available to eliminate
a possible failure mode if IOV_MAX is less than the number of buffers
on the queue.
(buffer_flush_window) Incorporate logic from buffer_flush_vty.
Log an error message if there is a writev error.
(buffer_flush_available) Be more paranoid: check for case where
buffer is already empty. Use new ERRNO_IO_RETRY macro, and use
new enum for return codes. Simplify deletion logic (since it's
now a singly-linked list).
(buffer_write) New function for use with non-blocking I/O.
* vty.h: Replace the struct vty sb_buffer field with a fixed-size
(5-character) sb_buf field and an sb_len field, since using
a struct buffer was inappropriate for this task. Add some useful
comments about telnet window size negotiation.
* vty.c: Include <arpa/telnet.h> (no longer included by zebra.h).
Remove VTY_OBUF_SIZE (instead use buffer_new default size).
Make telnet_backward_char and telnet_space_char static const.
(vty_out) Replace buffer_write with buffer_put.
(vty_log_out) Check for I/O errors. If fatal, close the vty session.
Consolidate 3 separate writes into a single write call.
(vty_will_echo,vty_command,vty_next_line,vty_previous_line,
vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo,
vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout,
vty_config_write,vty_save_cwd) Fix scope to static.
(vty_new) Let buffer_new use its default buffer size.
(vty_write) Fix signature: 2nd arg should be const char *.
Replaced buffer_write with buffer_put.
(vty_telnet_option) Fix minor bug (window height or width greater than
255 was broken). Use sb_buf and sb_len instead of removed sb_buffer
(which was being used improperly).
(vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal.
If the error is fatal, call buffer_reset so vty_close does not attempt
to flush the data. Use new sb_buf and sb_len instead of sb_buffer
to store the SB negotiation string.
(vty_flush) When vty->lines is 0, call buffer_flush_available instead
of buffer_flush_window. Look at the return code from buffer_flush
to detect I/O errors (and in that case, log an error message and
close the vty).
(vty_create) Fix scope to static. Initialize sb_len to 0 instead
of creating sb_buffer.
(vty_accept) Set socket nonblocking.
(vtysh_accept) Use new set_nonblocking function instead of calling
fcntl directly.
(vtysh_flush) New function called from vtysh_read (after command
execution) and from vtysh_write. This flushes the buffer
and reacts appropriately to the return code (by closing the vty
or scheduling further flushes).
(vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY.
If not, just try again later. Otherwise, call buffer_reset before
calling vty_close (to avoid trying to flush the buffer in vty_close).
Fix logic to allow case where a command does not arrive atomically
in a single read call by checking for the terminating NUL char.
(vtysh_write) Use new vtysh_flush helper function.
(vty_close) No need to call buffer_empty, just call buffer_flush_all
in any case (it will check whether the buffer is empty).
Do not free sb_buffer (since it has been removed).
(vty_log_fixed) Use writev instead of write.
* zebra.h: Do not include <arpa/telnet.h>, since this is used only
by lib/vty.c.
2005-02-21 Vincenzo Eramo <eramo at infocom.ing.uniroma1.it> 2005-02-21 Vincenzo Eramo <eramo at infocom.ing.uniroma1.it>
* pqueue.[ch]: Introduce "update" function to meet ospf spf needs. It * pqueue.[ch]: Introduce "update" function to meet ospf spf needs. It

View File

@ -25,24 +25,45 @@
#include "memory.h" #include "memory.h"
#include "buffer.h" #include "buffer.h"
#include "log.h" #include "log.h"
#include "network.h"
#include <stddef.h> #include <stddef.h>
/* Make buffer data. */
static struct buffer_data *
buffer_data_new (size_t size)
{
struct buffer_data *d;
d = XMALLOC (MTYPE_BUFFER_DATA, offsetof(struct buffer_data,data[size]));
d->cp = d->sp = 0;
return d;
}
static void /* Buffer master. */
buffer_data_free (struct buffer_data *d) struct buffer
{ {
XFREE (MTYPE_BUFFER_DATA, d); /* Data list. */
} struct buffer_data *head;
struct buffer_data *tail;
/* Size of each buffer_data chunk. */
size_t size;
};
/* Data container. */
struct buffer_data
{
struct buffer_data *next;
/* Location to add new data. */
size_t cp;
/* Pointer to data not yet flushed. */
size_t sp;
/* Actual data stream (variable length). */
unsigned char data[0]; /* real dimension is buffer->size */
};
/* It should always be true that: 0 <= sp <= cp <= size */
/* Default buffer size (used if none specified). It is rounded up to the
next page boundery. */
#define BUFFER_SIZE_DEFAULT 4096
#define BUFFER_DATA_FREE(D) XFREE(MTYPE_BUFFER_DATA, (D))
/* Make new buffer. */ /* Make new buffer. */
struct buffer * struct buffer *
@ -50,10 +71,20 @@ buffer_new (size_t size)
{ {
struct buffer *b; struct buffer *b;
b = XMALLOC (MTYPE_BUFFER, sizeof (struct buffer)); b = XCALLOC (MTYPE_BUFFER, sizeof (struct buffer));
memset (b, 0, sizeof (struct buffer));
b->size = size; if (size)
b->size = size;
else
{
static size_t default_size;
if (!default_size)
{
long pgsz = sysconf(_SC_PAGESIZE);
default_size = ((((BUFFER_SIZE_DEFAULT-1)/pgsz)+1)*pgsz);
}
b->size = default_size;
}
return b; return b;
} }
@ -62,25 +93,7 @@ buffer_new (size_t size)
void void
buffer_free (struct buffer *b) buffer_free (struct buffer *b)
{ {
struct buffer_data *d; buffer_reset(b);
struct buffer_data *next;
d = b->head;
while (d)
{
next = d->next;
buffer_data_free (d);
d = next;
}
d = b->unused_head;
while (d)
{
next = d->next;
buffer_data_free (d);
d = next;
}
XFREE (MTYPE_BUFFER, b); XFREE (MTYPE_BUFFER, b);
} }
@ -111,10 +124,7 @@ buffer_getstr (struct buffer *b)
int int
buffer_empty (struct buffer *b) buffer_empty (struct buffer *b)
{ {
if (b->tail == NULL || b->tail->cp == b->tail->sp) return (b->head == NULL);
return 1;
else
return 0;
} }
/* Clear and free all allocated data. */ /* Clear and free all allocated data. */
@ -127,48 +137,36 @@ buffer_reset (struct buffer *b)
for (data = b->head; data; data = next) for (data = b->head; data; data = next)
{ {
next = data->next; next = data->next;
buffer_data_free (data); BUFFER_DATA_FREE(data);
} }
b->head = b->tail = NULL; b->head = b->tail = NULL;
b->alloc = 0;
b->length = 0;
} }
/* Add buffer_data to the end of buffer. */ /* Add buffer_data to the end of buffer. */
void static struct buffer_data *
buffer_add (struct buffer *b) buffer_add (struct buffer *b)
{ {
struct buffer_data *d; struct buffer_data *d;
d = buffer_data_new (b->size); d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(struct buffer_data, data[b->size]));
d->cp = d->sp = 0;
d->next = NULL;
if (b->tail == NULL) if (b->tail)
{ b->tail->next = d;
d->prev = NULL;
d->next = NULL;
b->head = d;
b->tail = d;
}
else else
{ b->head = d;
d->prev = b->tail; b->tail = d;
d->next = NULL;
b->tail->next = d; return d;
b->tail = d;
}
b->alloc++;
} }
/* Write data to buffer. */ /* Write data to buffer. */
int void
buffer_write (struct buffer *b, const void *p, size_t size) buffer_put(struct buffer *b, const void *p, size_t size)
{ {
struct buffer_data *data; struct buffer_data *data = b->tail;
const char *ptr = p; const char *ptr = p;
data = b->tail;
b->length += size;
/* We use even last one byte of data buffer. */ /* We use even last one byte of data buffer. */
while (size) while (size)
@ -177,10 +175,7 @@ buffer_write (struct buffer *b, const void *p, size_t size)
/* If there is no data buffer add it. */ /* If there is no data buffer add it. */
if (data == NULL || data->cp == b->size) if (data == NULL || data->cp == b->size)
{ data = buffer_add (b);
buffer_add (b);
data = b->tail;
}
chunk = ((size <= (b->size - data->cp)) ? size : (b->size - data->cp)); chunk = ((size <= (b->size - data->cp)) ? size : (b->size - data->cp));
memcpy ((data->data + data->cp), ptr, chunk); memcpy ((data->data + data->cp), ptr, chunk);
@ -188,127 +183,54 @@ buffer_write (struct buffer *b, const void *p, size_t size)
ptr += chunk; ptr += chunk;
data->cp += chunk; data->cp += chunk;
} }
return 1;
} }
/* Insert character into the buffer. */ /* Insert character into the buffer. */
int void
buffer_putc (struct buffer *b, u_char c) buffer_putc (struct buffer *b, u_char c)
{ {
buffer_write (b, &c, 1); buffer_put(b, &c, 1);
return 1;
}
/* Insert word (2 octets) into ther buffer. */
int
buffer_putw (struct buffer *b, u_short c)
{
buffer_write (b, (char *)&c, 2);
return 1;
} }
/* Put string to the buffer. */ /* Put string to the buffer. */
int void
buffer_putstr (struct buffer *b, const char *c) buffer_putstr (struct buffer *b, const char *c)
{ {
size_t size; buffer_put(b, c, strlen(c));
size = strlen (c);
buffer_write (b, (void *) c, size);
return 1;
} }
/* Flush specified size to the fd. */ /* Keep flushing data to the fd until the buffer is empty or an error is
void encountered or the operation would block. */
buffer_flush (struct buffer *b, int fd, size_t size) buffer_status_t
{
int iov_index;
struct iovec *iovec;
struct buffer_data *data;
struct buffer_data *out;
struct buffer_data *next;
iovec = malloc (sizeof (struct iovec) * b->alloc);
iov_index = 0;
for (data = b->head; data; data = data->next)
{
iovec[iov_index].iov_base = (char *)(data->data + data->sp);
if (size <= (data->cp - data->sp))
{
iovec[iov_index++].iov_len = size;
data->sp += size;
b->length -= size;
if (data->sp == data->cp)
data = data->next;
break;
}
else
{
iovec[iov_index++].iov_len = data->cp - data->sp;
b->length -= (data->cp - data->sp);
size -= data->cp - data->sp;
data->sp = data->cp;
}
}
/* Write buffer to the fd. */
writev (fd, iovec, iov_index);
/* Free printed buffer data. */
for (out = b->head; out && out != data; out = next)
{
next = out->next;
if (next)
next->prev = NULL;
else
b->tail = next;
b->head = next;
buffer_data_free (out);
b->alloc--;
}
free (iovec);
}
/* Flush all buffer to the fd. */
int
buffer_flush_all (struct buffer *b, int fd) buffer_flush_all (struct buffer *b, int fd)
{ {
int ret; buffer_status_t ret;
struct buffer_data *d; struct buffer_data *head;
int iov_index; size_t head_sp;
struct iovec *iovec;
if (buffer_empty (b)) if (!b->head)
return 0; return BUFFER_EMPTY;
head_sp = (head = b->head)->sp;
iovec = malloc (sizeof (struct iovec) * b->alloc); /* Flush all data. */
iov_index = 0; while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING)
for (d = b->head; d; d = d->next)
{ {
iovec[iov_index].iov_base = (char *)(d->data + d->sp); if ((b->head == head) && (head_sp == head->sp) && (errno != EINTR))
iovec[iov_index].iov_len = d->cp - d->sp; /* No data was flushed, so kernel buffer must be full. */
iov_index++; return ret;
head_sp = (head = b->head)->sp;
} }
ret = writev (fd, iovec, iov_index);
free (iovec);
buffer_reset (b);
return ret; return ret;
} }
/* Flush all buffer to the fd. */ /* Flush enough data to fill a terminal window of the given scene (used only
int by vty telnet interface). */
buffer_flush_vty_all (struct buffer *b, int fd, int erase_flag, buffer_status_t
int no_more_flag) buffer_flush_window (struct buffer *b, int fd, int width, int height,
int erase_flag, int no_more_flag)
{ {
int nbytes; int nbytes;
int iov_alloc;
int iov_index; int iov_index;
struct iovec *iov; struct iovec *iov;
struct iovec small_iov[3]; struct iovec small_iov[3];
@ -317,16 +239,37 @@ buffer_flush_vty_all (struct buffer *b, int fd, int erase_flag,
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
struct buffer_data *data; struct buffer_data *data;
struct buffer_data *out; int column;
struct buffer_data *next;
if (!b->head)
return BUFFER_EMPTY;
if (height < 1)
{
zlog_warn("%s called with non-positive window height %d, forcing to 1",
__func__, height);
height = 1;
}
else if (height >= 2)
height--;
if (width < 1)
{
zlog_warn("%s called with non-positive window width %d, forcing to 1",
__func__, width);
width = 1;
}
/* For erase and more data add two to b's buffer_data count.*/ /* For erase and more data add two to b's buffer_data count.*/
if (b->alloc == 1) if (b->head->next == NULL)
iov = small_iov; {
iov_alloc = sizeof(small_iov)/sizeof(small_iov[0]);
iov = small_iov;
}
else else
iov = XCALLOC (MTYPE_TMP, sizeof (struct iovec) * (b->alloc + 2)); {
iov_alloc = ((height*(width+2))/b->size)+10;
data = b->head; iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
}
iov_index = 0; iov_index = 0;
/* Previously print out is performed. */ /* Previously print out is performed. */
@ -338,250 +281,115 @@ buffer_flush_vty_all (struct buffer *b, int fd, int erase_flag,
} }
/* Output data. */ /* Output data. */
for (data = b->head; data; data = data->next) column = 1; /* Column position of next character displayed. */
for (data = b->head; data && (height > 0); data = data->next)
{ {
size_t cp;
cp = data->sp;
while ((cp < data->cp) && (height > 0))
{
/* Calculate lines remaining and column position after displaying
this character. */
if (data->data[cp] == '\r')
column = 1;
else if ((data->data[cp] == '\n') || (column == width))
{
column = 1;
height--;
}
else
column++;
cp++;
}
iov[iov_index].iov_base = (char *)(data->data + data->sp); iov[iov_index].iov_base = (char *)(data->data + data->sp);
iov[iov_index].iov_len = data->cp - data->sp; iov[iov_index++].iov_len = cp-data->sp;
iov_index++; data->sp = cp;
if (iov_index == iov_alloc)
/* This should not ordinarily happen. */
{
iov_alloc *= 2;
if (iov != small_iov)
{
zlog_warn("%s: growing iov array to %d; "
"width %d, height %d, size %lu",
__func__, iov_alloc, width, height, (u_long)b->size);
iov = XREALLOC(MTYPE_TMP, iov, iov_alloc*sizeof(*iov));
}
else
{
/* This should absolutely never occur. */
zlog_err("%s: corruption detected: iov_small overflowed; "
"head %p, tail %p, head->next %p",
__func__, b->head, b->tail, b->head->next);
iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
memcpy(iov, small_iov, sizeof(small_iov));
}
}
} }
/* In case of `more' display need. */ /* In case of `more' display need. */
if (! buffer_empty (b) && !no_more_flag) if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag)
{ {
iov[iov_index].iov_base = more; iov[iov_index].iov_base = more;
iov[iov_index].iov_len = sizeof more; iov[iov_index].iov_len = sizeof more;
iov_index++; iov_index++;
} }
/* We use write or writev*/
nbytes = writev (fd, iov, iov_index);
/* Error treatment. */
if (nbytes < 0)
{
if (errno == EINTR)
;
if (errno == EWOULDBLOCK)
;
}
/* Free printed buffer data. */
for (out = b->head; out && out != data; out = next)
{
next = out->next;
if (next)
next->prev = NULL;
else
b->tail = next;
b->head = next;
b->length -= (out->cp-out->sp);
buffer_data_free (out);
b->alloc--;
}
if (iov != small_iov)
XFREE (MTYPE_TMP, iov);
return nbytes;
}
/* Flush buffer to the file descriptor. Mainly used from vty
interface. */
int
buffer_flush_vty (struct buffer *b, int fd, unsigned int size,
int erase_flag, int no_more_flag)
{
int nbytes;
int iov_index;
struct iovec *iov;
struct iovec small_iov[3];
char more[] = " --More-- ";
char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
struct buffer_data *data;
struct buffer_data *out;
struct buffer_data *next;
#ifdef IOV_MAX
int iov_size;
int total_size;
struct iovec *c_iov;
int c_nbytes;
#endif /* IOV_MAX */
/* For erase and more data add two to b's buffer_data count.*/
if (b->alloc == 1)
iov = small_iov;
else
iov = XCALLOC (MTYPE_TMP, sizeof (struct iovec) * (b->alloc + 2));
data = b->head;
iov_index = 0;
/* Previously print out is performed. */
if (erase_flag)
{
iov[iov_index].iov_base = erase;
iov[iov_index].iov_len = sizeof erase;
iov_index++;
}
/* Output data. */
for (data = b->head; data; data = data->next)
{
iov[iov_index].iov_base = (char *)(data->data + data->sp);
if (size <= (data->cp - data->sp))
{
iov[iov_index++].iov_len = size;
data->sp += size;
b->length -= size;
if (data->sp == data->cp)
data = data->next;
break;
}
else
{
iov[iov_index++].iov_len = data->cp - data->sp;
size -= (data->cp - data->sp);
b->length -= (data->cp - data->sp);
data->sp = data->cp;
}
}
/* In case of `more' display need. */
if (!buffer_empty (b) && !no_more_flag)
{
iov[iov_index].iov_base = more;
iov[iov_index].iov_len = sizeof more;
iov_index++;
}
/* We use write or writev*/
#ifdef IOV_MAX #ifdef IOV_MAX
/* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g. /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
example: Solaris2.6 are defined IOV_MAX size at 16. */ example: Solaris2.6 are defined IOV_MAX size at 16. */
c_iov = iov; {
total_size = iov_index; struct iovec *c_iov = iov;
nbytes = 0; nbytes = 0; /* Make sure it's initialized. */
while( total_size > 0 ) while (iov_index > 0)
{ {
/* initialize write vector size at once */ int iov_size;
iov_size = ( total_size > IOV_MAX ) ? IOV_MAX : total_size;
c_nbytes = writev (fd, c_iov, iov_size ); iov_size = ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
if ((nbytes = writev(fd, c_iov, iov_size)) < 0)
{
zlog_warn("%s: writev to fd %d failed: %s",
__func__, fd, safe_strerror(errno));
break;
}
if( c_nbytes < 0 ) /* move pointer io-vector */
{ c_iov += iov_size;
if(errno == EINTR) iov_index -= iov_size;
; }
; }
if(errno == EWOULDBLOCK)
;
;
nbytes = c_nbytes;
break;
}
nbytes += c_nbytes;
/* move pointer io-vector */
c_iov += iov_size;
total_size -= iov_size;
}
#else /* IOV_MAX */ #else /* IOV_MAX */
nbytes = writev (fd, iov, iov_index); if ((nbytes = writev (fd, iov, iov_index)) < 0)
zlog_warn("%s: writev to fd %d failed: %s",
/* Error treatment. */ __func__, fd, safe_strerror(errno));
if (nbytes < 0)
{
if (errno == EINTR)
;
if (errno == EWOULDBLOCK)
;
}
#endif /* IOV_MAX */ #endif /* IOV_MAX */
/* Free printed buffer data. */ /* Free printed buffer data. */
for (out = b->head; out && out != data; out = next) while (b->head && (b->head->sp == b->head->cp))
{ {
next = out->next; struct buffer_data *del;
if (next) if (!(b->head = (del = b->head)->next))
next->prev = NULL; b->tail = NULL;
else BUFFER_DATA_FREE(del);
b->tail = next;
b->head = next;
buffer_data_free (out);
b->alloc--;
} }
if (iov != small_iov) if (iov != small_iov)
XFREE (MTYPE_TMP, iov); XFREE (MTYPE_TMP, iov);
return nbytes; return (nbytes < 0) ? BUFFER_ERROR :
} (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
/* Calculate size of outputs then flush buffer to the file
descriptor. */
int
buffer_flush_window (struct buffer *b, int fd, int width, int height,
int erase, int no_more)
{
unsigned long cp;
unsigned long size;
int lp;
int lineno;
struct buffer_data *data;
if (height >= 2)
height--;
/* We have to calculate how many bytes should be written. */
lp = 0;
lineno = 0;
size = 0;
for (data = b->head; data; data = data->next)
{
cp = data->sp;
while (cp < data->cp)
{
if (data->data[cp] == '\n' || lp == width)
{
lineno++;
if (lineno == height)
{
cp++;
size++;
goto flush;
}
lp = 0;
}
cp++;
lp++;
size++;
}
}
/* Write data to the file descriptor. */
flush:
return buffer_flush_vty (b, fd, size, erase, no_more);
} }
/* This function (unlike other buffer_flush* functions above) is designed /* This function (unlike other buffer_flush* functions above) is designed
to work with non-blocking sockets. It does not attempt to write out to work with non-blocking sockets. It does not attempt to write out
all of the queued data, just a "big" chunk. It returns 0 if it was all of the queued data, just a "big" chunk. It returns 0 if it was
able to empty out the buffers completely, or 1 if more flushing is able to empty out the buffers completely, 1 if more flushing is
required later. */ required later, or -1 on a fatal write error. */
int buffer_status_t
buffer_flush_available(struct buffer *b, int fd) buffer_flush_available(struct buffer *b, int fd)
{ {
@ -596,7 +404,6 @@ in one shot. */
#define MAX_FLUSH 131072 #define MAX_FLUSH 131072
struct buffer_data *d; struct buffer_data *d;
struct buffer_data *next;
size_t written; size_t written;
struct iovec iov[MAX_CHUNKS]; struct iovec iov[MAX_CHUNKS];
size_t iovcnt = 0; size_t iovcnt = 0;
@ -609,40 +416,76 @@ in one shot. */
nbyte += (iov[iovcnt].iov_len = d->cp-d->sp); nbyte += (iov[iovcnt].iov_len = d->cp-d->sp);
} }
if (!nbyte)
/* No data to flush: should we issue a warning message? */
return BUFFER_EMPTY;
/* only place where written should be sign compared */ /* only place where written should be sign compared */
if ((ssize_t)(written = writev(fd,iov,iovcnt)) < 0) if ((ssize_t)(written = writev(fd,iov,iovcnt)) < 0)
{ {
if ((errno != EAGAIN) && (errno != EINTR)) if (ERRNO_IO_RETRY(errno))
zlog_warn("buffer_flush_available write error on fd %d: %s", /* Calling code should try again later. */
fd,safe_strerror(errno)); return BUFFER_PENDING;
return 1; zlog_warn("%s: write error on fd %d: %s",
__func__, fd, safe_strerror(errno));
return BUFFER_ERROR;
} }
/* Free printed buffer data. */ /* Free printed buffer data. */
for (d = b->head; (written > 0) && d; d = next) while (written > 0)
{ {
struct buffer_data *d;
if (!(d = b->head))
{
zlog_err("%s: corruption detected: buffer queue empty, "
"but written is %lu", __func__, (u_long)written);
break;
}
if (written < d->cp-d->sp) if (written < d->cp-d->sp)
{ {
d->sp += written; d->sp += written;
b->length -= written; return BUFFER_PENDING;
return 1;
} }
written -= (d->cp-d->sp); written -= (d->cp-d->sp);
next = d->next; if (!(b->head = d->next))
if (next) b->tail = NULL;
next->prev = NULL; BUFFER_DATA_FREE(d);
else
b->tail = next;
b->head = next;
b->length -= (d->cp-d->sp);
buffer_data_free (d);
b->alloc--;
} }
return (b->head != NULL); return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
#undef MAX_CHUNKS #undef MAX_CHUNKS
#undef MAX_FLUSH #undef MAX_FLUSH
} }
buffer_status_t
buffer_write(struct buffer *b, int fd, const void *p, size_t size)
{
ssize_t nbytes;
/* Attempt to drain the previously buffered data? */
if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
return BUFFER_ERROR;
if (b->head)
/* Buffer still not empty. */
nbytes = 0;
else if ((nbytes = write(fd, p, size)) < 0)
{
if (ERRNO_IO_RETRY(errno))
nbytes = 0;
else
{
zlog_warn("%s: write error on fd %d: %s",
__func__, fd, safe_strerror(errno));
return BUFFER_ERROR;
}
}
/* Add any remaining data to the buffer. */
{
size_t written = nbytes;
if (written < size)
buffer_put(b, ((const char *)p)+written, size-written);
}
return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
}

View File

@ -23,68 +23,80 @@
#ifndef _ZEBRA_BUFFER_H #ifndef _ZEBRA_BUFFER_H
#define _ZEBRA_BUFFER_H #define _ZEBRA_BUFFER_H
/* Buffer master. */
struct buffer
{
/* Data list. */
struct buffer_data *head;
struct buffer_data *tail;
/* XXX: These unsigned longs should be size_t's */
/* Current allocated data. */
unsigned long alloc;
/* Size of each buffer_data chunk. */ /* Create a new buffer. Memory will be allocated in chunks of the given
unsigned long size; size. If the argument is 0, the library will supply a reasonable
default size suitable for buffering socket I/O. */
/* For allocation. */
struct buffer_data *unused_head;
struct buffer_data *unused_tail;
/* Current total length of this buffer. */
unsigned long length;
};
/* Data container. */
struct buffer_data
{
struct buffer_data *next;
struct buffer_data *prev;
/* Current pointer. */
unsigned long cp;
/* Start pointer. */
unsigned long sp;
/* Actual data stream (variable length). */
unsigned char data[0]; /* real dimension is buffer->size */
};
/* Buffer prototypes. */
struct buffer *buffer_new (size_t); struct buffer *buffer_new (size_t);
int buffer_write (struct buffer *, const void *, size_t);
/* Free all data in the buffer. */
void buffer_reset (struct buffer *);
/* This function first calls buffer_reset to release all buffered data.
Then it frees the struct buffer itself. */
void buffer_free (struct buffer *); void buffer_free (struct buffer *);
/* Add the given data to the end of the buffer. */
extern void buffer_put (struct buffer *, const void *, size_t);
/* Add a single character to the end of the buffer. */
extern void buffer_putc (struct buffer *, u_char);
/* Add a NUL-terminated string to the end of the buffer. */
extern void buffer_putstr (struct buffer *, const char *);
/* Combine all accumulated (and unflushed) data inside the buffer into a /* Combine all accumulated (and unflushed) data inside the buffer into a
single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note
that this function does not alter the state of the buffer, so the data that this function does not alter the state of the buffer, so the data
is still inside waiting to be flushed. */ is still inside waiting to be flushed. */
char *buffer_getstr (struct buffer *); char *buffer_getstr (struct buffer *);
int buffer_putc (struct buffer *, u_char); /* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */
int buffer_putstr (struct buffer *, const char *);
void buffer_reset (struct buffer *);
int buffer_flush_all (struct buffer *, int);
int buffer_flush_vty_all (struct buffer *, int, int, int);
int buffer_flush_window (struct buffer *, int, int, int, int, int);
int buffer_empty (struct buffer *); int buffer_empty (struct buffer *);
/* buffer_flush_available attempts to flush the queued data to the given typedef enum
file descriptor. It returns 0 if the buffers are now empty (after {
flushing), or 1 if more data remains on the buffer queue (must be flushed /* An I/O error occurred. The buffer should be destroyed and the
later). This function (unlike the other buffer_flush* functions) is file descriptor should be closed. */
designed to work with non-blocking file descriptors. */ BUFFER_ERROR = -1,
int buffer_flush_available(struct buffer *, int fd);
/* The data was written successfully, and the buffer is now empty
(there is no pending data waiting to be flushed). */
BUFFER_EMPTY = 0,
/* There is pending data in the buffer waiting to be flushed. Please
try flushing the buffer when select indicates that the file descriptor
is writeable. */
BUFFER_PENDING = 1
} buffer_status_t;
/* Try to write this data to the file descriptor. Any data that cannot
be written immediately is added to the buffer queue. */
extern buffer_status_t buffer_write(struct buffer *, int fd,
const void *, size_t);
/* This function attempts to flush some (but perhaps not all) of
the queued data to the given file descriptor. */
extern buffer_status_t buffer_flush_available(struct buffer *, int fd);
/* The following 2 functions (buffer_flush_all and buffer_flush_window)
are for use in lib/vty.c only. They should not be used elsewhere. */
/* Call buffer_flush_available repeatedly until either all data has been
flushed, or an I/O error has been encountered, or the operation would
block. */
extern buffer_status_t buffer_flush_all (struct buffer *, int fd);
/* Attempt to write enough data to the given fd to fill a window of the
given width and height (and remove the data written from the buffer).
If !no_more, then a message saying " --More-- " is appended.
If erase is true, then first overwrite the previous " --More-- " message
with spaces.
Any write error (including EAGAIN or EINTR) will cause this function
to return -1 (because the logic for handling the erase and more features
is too complicated to retry the write later).
*/
extern buffer_status_t buffer_flush_window (struct buffer *, int fd, int width,
int height, int erase, int no_more);
#endif /* _ZEBRA_BUFFER_H */ #endif /* _ZEBRA_BUFFER_H */

329
lib/vty.c
View File

@ -35,6 +35,9 @@
#include "filter.h" #include "filter.h"
#include "vty.h" #include "vty.h"
#include "privs.h" #include "privs.h"
#include "network.h"
#include <arpa/telnet.h>
/* Vty events */ /* Vty events */
enum event enum event
@ -50,9 +53,6 @@ enum event
#endif /* VTYSH */ #endif /* VTYSH */
}; };
/* Minimum size of output buffers; to be rounded up to multiple of pagesize. */
#define VTY_OBUF_SIZE 4096
static void vty_event (enum event, int, struct vty *); static void vty_event (enum event, int, struct vty *);
/* Extern host structure from command.c */ /* Extern host structure from command.c */
@ -137,7 +137,7 @@ vty_out (struct vty *vty, const char *format, ...)
p = buf; p = buf;
/* Pointer p must point out buffer. */ /* Pointer p must point out buffer. */
buffer_write (vty->obuf, (u_char *) p, len); buffer_put (vty->obuf, (u_char *) p, len);
/* If p is not different with buf, it is allocated buffer. */ /* If p is not different with buf, it is allocated buffer. */
if (p != buf) if (p != buf)
@ -151,24 +151,38 @@ static int
vty_log_out (struct vty *vty, const char *level, const char *proto_str, vty_log_out (struct vty *vty, const char *level, const char *proto_str,
const char *format, va_list va) const char *format, va_list va)
{ {
int ret;
int len; int len;
char buf[1024]; char buf[1024];
if (level) if (level)
snprintf (buf, sizeof buf, "%s: %s: ", level, proto_str); len = snprintf(buf, sizeof(buf), "%s: %s: ", level, proto_str);
else else
snprintf (buf, sizeof buf, "%s: ", proto_str); len = snprintf(buf, sizeof(buf), "%s: ", proto_str);
write (vty->fd, buf, strlen (buf)); if ((len < 0) || ((size_t)len >= sizeof(buf)))
len = vsnprintf (buf, sizeof buf, format, va);
if (len < 0)
return -1; return -1;
write (vty->fd, (u_char *)buf, len);
snprintf (buf, sizeof buf, "\r\n"); if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) ||
write (vty->fd, buf, 2); ((size_t)((len += ret)+2) > sizeof(buf)))
return -1;
return len; buf[len++] = '\r';
buf[len++] = '\n';
if (write(vty->fd, buf, len) < 0)
{
if (ERRNO_IO_RETRY(errno))
/* Kernel buffer is full, probably too much debugging output, so just
drop the data and ignore. */
return -1;
/* Fatal I/O error. */
zlog_warn("%s: write failed to vty client fd %d, closing: %s",
__func__, vty->fd, safe_strerror(errno));
buffer_reset(vty->obuf);
vty_close(vty);
return -1;
}
return 0;
} }
/* Output current time to the vty. */ /* Output current time to the vty. */
@ -226,7 +240,7 @@ vty_prompt (struct vty *vty)
} }
/* Send WILL TELOPT_ECHO to remote server. */ /* Send WILL TELOPT_ECHO to remote server. */
void static void
vty_will_echo (struct vty *vty) vty_will_echo (struct vty *vty)
{ {
unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
@ -272,12 +286,10 @@ struct vty *
vty_new () vty_new ()
{ {
struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty)); struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty));
int pgsz = getpagesize();
new->obuf = (struct buffer *) buffer_new ((((VTY_OBUF_SIZE-1)/pgsz)+1)*pgsz); new->obuf = buffer_new(0); /* Use default buffer size. */
new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ); new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
new->max = VTY_BUFSIZ; new->max = VTY_BUFSIZ;
new->sb_buffer = NULL;
return new; return new;
} }
@ -349,7 +361,7 @@ vty_auth (struct vty *vty, char *buf)
} }
/* Command execution over the vty interface. */ /* Command execution over the vty interface. */
int static int
vty_command (struct vty *vty, char *buf) vty_command (struct vty *vty, char *buf)
{ {
int ret; int ret;
@ -385,18 +397,18 @@ vty_command (struct vty *vty, char *buf)
return ret; return ret;
} }
char telnet_backward_char = 0x08; static const char telnet_backward_char = 0x08;
char telnet_space_char = ' '; static const char telnet_space_char = ' ';
/* Basic function to write buffer to vty. */ /* Basic function to write buffer to vty. */
static void static void
vty_write (struct vty *vty, char *buf, size_t nbytes) vty_write (struct vty *vty, const char *buf, size_t nbytes)
{ {
if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
return; return;
/* Should we do buffering here ? And make vty_flush (vty) ? */ /* Should we do buffering here ? And make vty_flush (vty) ? */
buffer_write (vty->obuf, (u_char *)buf, nbytes); buffer_put (vty->obuf, buf, nbytes);
} }
/* Ensure length of input buffer. Is buffer is short, double it. */ /* Ensure length of input buffer. Is buffer is short, double it. */
@ -517,7 +529,7 @@ vty_history_print (struct vty *vty)
} }
/* Show next command line history. */ /* Show next command line history. */
void static void
vty_next_line (struct vty *vty) vty_next_line (struct vty *vty)
{ {
int try_index; int try_index;
@ -542,7 +554,7 @@ vty_next_line (struct vty *vty)
} }
/* Show previous command line history. */ /* Show previous command line history. */
void static void
vty_previous_line (struct vty *vty) vty_previous_line (struct vty *vty)
{ {
int try_index; int try_index;
@ -611,7 +623,7 @@ vty_down_level (struct vty *vty)
} }
/* When '^Z' is received from vty, move down to the enable mode. */ /* When '^Z' is received from vty, move down to the enable mode. */
void static void
vty_end_config (struct vty *vty) vty_end_config (struct vty *vty)
{ {
vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "%s", VTY_NEWLINE);
@ -855,7 +867,7 @@ vty_complete_command (struct vty *vty)
vector_only_index_free (matched); vector_only_index_free (matched);
} }
void static void
vty_describe_fold (struct vty *vty, int cmd_width, vty_describe_fold (struct vty *vty, int cmd_width,
unsigned int desc_width, struct desc *desc) unsigned int desc_width, struct desc *desc)
{ {
@ -1012,7 +1024,7 @@ vty_describe_command (struct vty *vty)
vty_redraw_line (vty); vty_redraw_line (vty);
} }
void static void
vty_clear_buf (struct vty *vty) vty_clear_buf (struct vty *vty)
{ {
memset (vty->buf, 0, vty->max); memset (vty->buf, 0, vty->max);
@ -1146,36 +1158,41 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
switch (buf[0]) switch (buf[0])
{ {
case SB: case SB:
buffer_reset(vty->sb_buffer); vty->sb_len = 0;
vty->iac_sb_in_progress = 1; vty->iac_sb_in_progress = 1;
return 0; return 0;
break; break;
case SE: case SE:
{ {
char *buffer;
int length;
if (!vty->iac_sb_in_progress) if (!vty->iac_sb_in_progress)
return 0; return 0;
buffer = (char *)vty->sb_buffer->head->data; if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
length = vty->sb_buffer->length;
if (buffer == NULL)
return 0;
if (buffer[0] == '\0')
{ {
vty->iac_sb_in_progress = 0; vty->iac_sb_in_progress = 0;
return 0; return 0;
} }
switch (buffer[0]) switch (vty->sb_buf[0])
{ {
case TELOPT_NAWS: case TELOPT_NAWS:
if (length < 5) if (vty->sb_len != TELNET_NAWS_SB_LEN)
break; zlog_warn("RFC 1073 violation detected: telnet NAWS option "
vty->width = buffer[2]; "should send %d characters, but we received %lu",
vty->height = vty->lines >= 0 ? vty->lines : buffer[4]; TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, "
"too small to handle the telnet NAWS option",
(u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
else
{
vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
#ifdef TELNET_OPTION_DEBUG
vty_out(vty, "TELNET NAWS window size negotiation completed: "
"width %d, height %d%s",
vty->width, vty->height, VTY_NEWLINE);
#endif
}
break; break;
} }
vty->iac_sb_in_progress = 0; vty->iac_sb_in_progress = 0;
@ -1272,9 +1289,21 @@ vty_read (struct thread *thread)
vty->t_read = NULL; vty->t_read = NULL;
/* Read raw data from socket */ /* Read raw data from socket */
nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ); if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
if (nbytes <= 0) {
vty->status = VTY_CLOSE; if (nbytes < 0)
{
if (ERRNO_IO_RETRY(errno))
{
vty_event (VTY_READ, vty_sock, vty);
return 0;
}
zlog_warn("%s: read error on vty client fd %d, closing: %s",
__func__, vty->fd, safe_strerror(errno));
}
buffer_reset(vty->obuf);
vty->status = VTY_CLOSE;
}
for (i = 0; i < nbytes; i++) for (i = 0; i < nbytes; i++)
{ {
@ -1293,7 +1322,9 @@ vty_read (struct thread *thread)
if (vty->iac_sb_in_progress && !vty->iac) if (vty->iac_sb_in_progress && !vty->iac)
{ {
buffer_putc(vty->sb_buffer, buf[i]); if (vty->sb_len < sizeof(vty->sb_buf))
vty->sb_buf[vty->sb_len] = buf[i];
vty->sb_len++;
continue; continue;
} }
@ -1459,58 +1490,64 @@ static int
vty_flush (struct thread *thread) vty_flush (struct thread *thread)
{ {
int erase; int erase;
buffer_status_t flushrc;
int vty_sock = THREAD_FD (thread); int vty_sock = THREAD_FD (thread);
struct vty *vty = THREAD_ARG (thread); struct vty *vty = THREAD_ARG (thread);
vty->t_write = NULL; vty->t_write = NULL;
/* Tempolary disable read thread. */ /* Tempolary disable read thread. */
if (vty->lines == 0) if ((vty->lines == 0) && vty->t_read)
if (vty->t_read) {
{ thread_cancel (vty->t_read);
thread_cancel (vty->t_read); vty->t_read = NULL;
vty->t_read = NULL; }
}
/* Function execution continue. */ /* Function execution continue. */
if (vty->status == VTY_MORE || vty->status == VTY_MORELINE) erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
erase = 1;
else
erase = 0;
if (vty->lines == 0) /* N.B. if width is 0, that means we don't know the window size. */
buffer_flush_window (vty->obuf, vty->fd, vty->width, 25, 0, 1); if ((vty->lines == 0) || (vty->width == 0))
else if (vty->status == VTY_MORELINE) flushrc = buffer_flush_available(vty->obuf, vty->fd);
buffer_flush_window (vty->obuf, vty->fd, vty->width, 1, erase, 0); else if (vty->status == VTY_MORELINE)
else flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width,
buffer_flush_window (vty->obuf, vty->fd, vty->width, 1, erase, 0);
vty->lines >= 0 ? vty->lines : vty->height, else
erase, 0); flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width,
vty->lines >= 0 ? vty->lines :
if (buffer_empty (vty->obuf)) vty->height,
{ erase, 0);
if (vty->status == VTY_CLOSE) switch (flushrc)
vty_close (vty); {
else case BUFFER_ERROR:
{ zlog_warn("buffer_flush failed on vty client fd %d, closing",
vty->status = VTY_NORMAL; vty->fd);
buffer_reset(vty->obuf);
if (vty->lines == 0) vty_close(vty);
vty_event (VTY_READ, vty_sock, vty); return 0;
} case BUFFER_EMPTY:
} if (vty->status == VTY_CLOSE)
vty_close (vty);
else else
{ {
vty->status = VTY_MORE; vty->status = VTY_NORMAL;
if (vty->lines == 0) if (vty->lines == 0)
vty_event (VTY_WRITE, vty_sock, vty); vty_event (VTY_READ, vty_sock, vty);
} }
break;
case BUFFER_PENDING:
/* There is more data waiting to be written. */
vty->status = VTY_MORE;
if (vty->lines == 0)
vty_event (VTY_WRITE, vty_sock, vty);
break;
}
return 0; return 0;
} }
/* Create new vty structure. */ /* Create new vty structure. */
struct vty * static struct vty *
vty_create (int vty_sock, union sockunion *su) vty_create (int vty_sock, union sockunion *su)
{ {
struct vty *vty; struct vty *vty;
@ -1545,7 +1582,7 @@ vty_create (int vty_sock, union sockunion *su)
vty->lines = -1; vty->lines = -1;
vty->iac = 0; vty->iac = 0;
vty->iac_sb_in_progress = 0; vty->iac_sb_in_progress = 0;
vty->sb_buffer = buffer_new (1024); vty->sb_len = 0;
if (! no_password_check) if (! no_password_check)
{ {
@ -1608,6 +1645,7 @@ vty_accept (struct thread *thread)
zlog_warn ("can't accept vty socket : %s", safe_strerror (errno)); zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
return -1; return -1;
} }
set_nonblocking(vty_sock);
p = sockunion2hostprefix (&su); p = sockunion2hostprefix (&su);
@ -1670,7 +1708,7 @@ vty_accept (struct thread *thread)
} }
#if defined(HAVE_IPV6) && !defined(NRL) #if defined(HAVE_IPV6) && !defined(NRL)
void static void
vty_serv_sock_addrinfo (const char *hostname, unsigned short port) vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
{ {
int ret; int ret;
@ -1736,7 +1774,7 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
#endif /* HAVE_IPV6 && ! NRL */ #endif /* HAVE_IPV6 && ! NRL */
/* Make vty server socket. */ /* Make vty server socket. */
void static void
vty_serv_sock_family (const char* addr, unsigned short port, int family) vty_serv_sock_family (const char* addr, unsigned short port, int family)
{ {
int ret; int ret;
@ -1805,7 +1843,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
#include <sys/un.h> #include <sys/un.h>
/* VTY shell UNIX domain socket. */ /* VTY shell UNIX domain socket. */
void static void
vty_serv_un (const char *path) vty_serv_un (const char *path)
{ {
int ret; int ret;
@ -1879,7 +1917,6 @@ vtysh_accept (struct thread *thread)
int accept_sock; int accept_sock;
int sock; int sock;
int client_len; int client_len;
int flags;
struct sockaddr_un client; struct sockaddr_un client;
struct vty *vty; struct vty *vty;
@ -1899,12 +1936,10 @@ vtysh_accept (struct thread *thread)
return -1; return -1;
} }
/* set to non-blocking*/ if (set_nonblocking(sock) < 0)
if ( ((flags = fcntl (sock, F_GETFL)) == -1)
|| (fcntl (sock, F_SETFL, flags|O_NONBLOCK) == -1) )
{ {
zlog_warn ("vtysh_accept: could not set vty socket to non-blocking," zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking,"
" %s, closing", safe_strerror (errno)); " %s, closing", sock, safe_strerror (errno));
close (sock); close (sock);
return -1; return -1;
} }
@ -1923,6 +1958,26 @@ vtysh_accept (struct thread *thread)
return 0; return 0;
} }
static int
vtysh_flush(struct vty *vty)
{
switch (buffer_flush_available(vty->obuf, vty->fd))
{
case BUFFER_PENDING:
vty_event(VTYSH_WRITE, vty->fd, vty);
break;
case BUFFER_ERROR:
zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd);
buffer_reset(vty->obuf);
vty_close(vty);
return -1;
break;
case BUFFER_EMPTY:
break;
}
return 0;
}
static int static int
vtysh_read (struct thread *thread) vtysh_read (struct thread *thread)
{ {
@ -1931,15 +1986,26 @@ vtysh_read (struct thread *thread)
int nbytes; int nbytes;
struct vty *vty; struct vty *vty;
unsigned char buf[VTY_READ_BUFSIZ]; unsigned char buf[VTY_READ_BUFSIZ];
unsigned char *p;
u_char header[4] = {0, 0, 0, 0}; u_char header[4] = {0, 0, 0, 0};
sock = THREAD_FD (thread); sock = THREAD_FD (thread);
vty = THREAD_ARG (thread); vty = THREAD_ARG (thread);
vty->t_read = NULL; vty->t_read = NULL;
nbytes = read (sock, buf, VTY_READ_BUFSIZ); if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)
if (nbytes <= 0)
{ {
if (nbytes < 0)
{
if (ERRNO_IO_RETRY(errno))
{
vty_event (VTYSH_READ, sock, vty);
return 0;
}
zlog_warn("%s: read failed on vtysh client fd %d, closing: %s",
__func__, sock, safe_strerror(errno));
}
buffer_reset(vty->obuf);
vty_close (vty); vty_close (vty);
#ifdef VTYSH_DEBUG #ifdef VTYSH_DEBUG
printf ("close vtysh\n"); printf ("close vtysh\n");
@ -1948,28 +2014,35 @@ vtysh_read (struct thread *thread)
} }
#ifdef VTYSH_DEBUG #ifdef VTYSH_DEBUG
printf ("line: %s\n", buf); printf ("line: %.*s\n", nbytes, buf);
#endif /* VTYSH_DEBUG */ #endif /* VTYSH_DEBUG */
vty_ensure (vty, nbytes); for (p = buf; p < buf+nbytes; p++)
memcpy (vty->buf, buf, nbytes); {
vty_ensure(vty, vty->length+1);
/* Pass this line to parser. */ vty->buf[vty->length++] = *p;
ret = vty_execute (vty); if (*p == '\0')
{
/* Pass this line to parser. */
ret = vty_execute (vty);
/* Note that vty_execute clears the command buffer and resets
vty->length to 0. */
vty_clear_buf (vty); /* Return result. */
/* Return result. */
#ifdef VTYSH_DEBUG #ifdef VTYSH_DEBUG
printf ("result: %d\n", ret); printf ("result: %d\n", ret);
printf ("vtysh node: %d\n", vty->node); printf ("vtysh node: %d\n", vty->node);
#endif /* VTYSH_DEBUG */ #endif /* VTYSH_DEBUG */
header[3] = ret; header[3] = ret;
buffer_write(vty->obuf, header, 4); buffer_put(vty->obuf, header, 4);
if (!vty->t_write && (vtysh_flush(vty) < 0))
/* Try to flush results; exit if a write error occurs. */
return 0;
}
}
if (!vty->t_write && buffer_flush_available(vty->obuf, vty->fd))
vty_event (VTYSH_WRITE, vty->fd, vty);
vty_event (VTYSH_READ, sock, vty); vty_event (VTYSH_READ, sock, vty);
return 0; return 0;
@ -1981,8 +2054,7 @@ vtysh_write (struct thread *thread)
struct vty *vty = THREAD_ARG (thread); struct vty *vty = THREAD_ARG (thread);
vty->t_write = NULL; vty->t_write = NULL;
if (buffer_flush_available(vty->obuf, vty->fd)) vtysh_flush(vty);
vty_event (VTYSH_WRITE, vty->fd, vty);
return 0; return 0;
} }
@ -2028,16 +2100,11 @@ vty_close (struct vty *vty)
thread_cancel (vty->t_timeout); thread_cancel (vty->t_timeout);
/* Flush buffer. */ /* Flush buffer. */
if (! buffer_empty (vty->obuf)) buffer_flush_all (vty->obuf, vty->fd);
buffer_flush_all (vty->obuf, vty->fd);
/* Free input buffer. */ /* Free input buffer. */
buffer_free (vty->obuf); buffer_free (vty->obuf);
/* Free SB buffer. */
if (vty->sb_buffer)
buffer_free (vty->sb_buffer);
/* Free command history. */ /* Free command history. */
for (i = 0; i < VTY_MAXHIST; i++) for (i = 0; i < VTY_MAXHIST; i++)
if (vty->hist[i]) if (vty->hist[i])
@ -2118,7 +2185,7 @@ vty_read_file (FILE *confp)
vty_close (vty); vty_close (vty);
} }
FILE * static FILE *
vty_use_backup_config (char *fullpath) vty_use_backup_config (char *fullpath)
{ {
char *fullpath_sav, *fullpath_tmp; char *fullpath_sav, *fullpath_tmp;
@ -2298,16 +2365,20 @@ void
vty_log_fixed (const char *buf, size_t len) vty_log_fixed (const char *buf, size_t len)
{ {
unsigned int i; unsigned int i;
struct iovec iov[2];
iov[0].iov_base = buf;
iov[0].iov_len = len;
iov[1].iov_base = "\r\n";
iov[1].iov_len = 2;
for (i = 0; i < vector_max (vtyvec); i++) for (i = 0; i < vector_max (vtyvec); i++)
{ {
struct vty *vty; struct vty *vty;
if ((vty = vector_slot (vtyvec, i)) != NULL) if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
if (vty->monitor) /* N.B. We don't care about the return code, since process is
{ most likely just about to die anyway. */
write(vty->fd, buf, len); writev(vty->fd, iov, 2);
write(vty->fd, "\r\n", 2);
}
} }
} }
@ -2417,7 +2488,7 @@ DEFUN (line_vty,
} }
/* Set time out value. */ /* Set time out value. */
int static int
exec_timeout (struct vty *vty, const char *min_str, const char *sec_str) exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
{ {
unsigned long timeout = 0; unsigned long timeout = 0;
@ -2635,7 +2706,7 @@ DEFUN (show_history,
} }
/* Display current configuration. */ /* Display current configuration. */
int static int
vty_config_write (struct vty *vty) vty_config_write (struct vty *vty)
{ {
vty_out (vty, "line vty%s", VTY_NEWLINE); vty_out (vty, "line vty%s", VTY_NEWLINE);
@ -2749,8 +2820,8 @@ vty_finish ()
} }
} }
void static void
vty_save_cwd () vty_save_cwd (void)
{ {
char cwd[MAXPATHLEN]; char cwd[MAXPATHLEN];
char *c; char *c;

View File

@ -81,12 +81,21 @@ struct vty
/* Current vty status. */ /* Current vty status. */
enum {VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE} status; enum {VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE} status;
/* IAC handling */ /* IAC handling: was the last character received the
IAC (interpret-as-command) escape character (and therefore the next
character will be the command code)? Refer to Telnet RFC 854. */
unsigned char iac; unsigned char iac;
/* IAC SB handling */ /* IAC SB (option subnegotiation) handling */
unsigned char iac_sb_in_progress; unsigned char iac_sb_in_progress;
struct buffer *sb_buffer; /* At the moment, we care only about the NAWS (window size) negotiation,
and that requires just a 5-character buffer (RFC 1073):
<NAWS char> <16-bit width> <16-bit height> */
#define TELNET_NAWS_SB_LEN 5
unsigned char sb_buf[TELNET_NAWS_SB_LEN];
/* How many subnegotiation characters have we received? We just drop
those that do not fit in the buffer. */
size_t sb_len;
/* Window width/height. */ /* Window width/height. */
int width; int width;

View File

@ -161,7 +161,6 @@ typedef int socklen_t;
#endif /* HAVE_NETDB_H */ #endif /* HAVE_NETDB_H */
#include <arpa/inet.h> #include <arpa/inet.h>
#include <arpa/telnet.h>
#ifdef HAVE_INET_ND_H #ifdef HAVE_INET_ND_H
#include <inet/nd.h> #include <inet/nd.h>