mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-05 13:21:22 +00:00
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:
parent
338b342415
commit
9fc7ebf10b
@ -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>
|
||||
|
||||
* pqueue.[ch]: Introduce "update" function to meet ospf spf needs. It
|
||||
|
649
lib/buffer.c
649
lib/buffer.c
@ -25,24 +25,45 @@
|
||||
#include "memory.h"
|
||||
#include "buffer.h"
|
||||
#include "log.h"
|
||||
#include "network.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_data_free (struct buffer_data *d)
|
||||
/* Buffer master. */
|
||||
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. */
|
||||
struct buffer *
|
||||
@ -50,10 +71,20 @@ buffer_new (size_t size)
|
||||
{
|
||||
struct buffer *b;
|
||||
|
||||
b = XMALLOC (MTYPE_BUFFER, sizeof (struct buffer));
|
||||
memset (b, 0, sizeof (struct buffer));
|
||||
b = XCALLOC (MTYPE_BUFFER, 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;
|
||||
}
|
||||
@ -62,25 +93,7 @@ buffer_new (size_t size)
|
||||
void
|
||||
buffer_free (struct buffer *b)
|
||||
{
|
||||
struct buffer_data *d;
|
||||
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;
|
||||
}
|
||||
|
||||
buffer_reset(b);
|
||||
XFREE (MTYPE_BUFFER, b);
|
||||
}
|
||||
|
||||
@ -111,10 +124,7 @@ buffer_getstr (struct buffer *b)
|
||||
int
|
||||
buffer_empty (struct buffer *b)
|
||||
{
|
||||
if (b->tail == NULL || b->tail->cp == b->tail->sp)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
return (b->head == NULL);
|
||||
}
|
||||
|
||||
/* Clear and free all allocated data. */
|
||||
@ -127,48 +137,36 @@ buffer_reset (struct buffer *b)
|
||||
for (data = b->head; data; data = next)
|
||||
{
|
||||
next = data->next;
|
||||
buffer_data_free (data);
|
||||
BUFFER_DATA_FREE(data);
|
||||
}
|
||||
b->head = b->tail = NULL;
|
||||
b->alloc = 0;
|
||||
b->length = 0;
|
||||
}
|
||||
|
||||
/* Add buffer_data to the end of buffer. */
|
||||
void
|
||||
static struct buffer_data *
|
||||
buffer_add (struct buffer *b)
|
||||
{
|
||||
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)
|
||||
{
|
||||
d->prev = NULL;
|
||||
d->next = NULL;
|
||||
b->head = d;
|
||||
b->tail = d;
|
||||
}
|
||||
if (b->tail)
|
||||
b->tail->next = d;
|
||||
else
|
||||
{
|
||||
d->prev = b->tail;
|
||||
d->next = NULL;
|
||||
b->head = d;
|
||||
b->tail = d;
|
||||
|
||||
b->tail->next = d;
|
||||
b->tail = d;
|
||||
}
|
||||
|
||||
b->alloc++;
|
||||
return d;
|
||||
}
|
||||
|
||||
/* Write data to buffer. */
|
||||
int
|
||||
buffer_write (struct buffer *b, const void *p, size_t size)
|
||||
void
|
||||
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;
|
||||
data = b->tail;
|
||||
b->length += size;
|
||||
|
||||
/* We use even last one byte of data buffer. */
|
||||
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 (data == NULL || data->cp == b->size)
|
||||
{
|
||||
buffer_add (b);
|
||||
data = b->tail;
|
||||
}
|
||||
data = buffer_add (b);
|
||||
|
||||
chunk = ((size <= (b->size - data->cp)) ? size : (b->size - data->cp));
|
||||
memcpy ((data->data + data->cp), ptr, chunk);
|
||||
@ -188,127 +183,54 @@ buffer_write (struct buffer *b, const void *p, size_t size)
|
||||
ptr += chunk;
|
||||
data->cp += chunk;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Insert character into the buffer. */
|
||||
int
|
||||
void
|
||||
buffer_putc (struct buffer *b, u_char c)
|
||||
{
|
||||
buffer_write (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;
|
||||
buffer_put(b, &c, 1);
|
||||
}
|
||||
|
||||
/* Put string to the buffer. */
|
||||
int
|
||||
void
|
||||
buffer_putstr (struct buffer *b, const char *c)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
size = strlen (c);
|
||||
buffer_write (b, (void *) c, size);
|
||||
return 1;
|
||||
buffer_put(b, c, strlen(c));
|
||||
}
|
||||
|
||||
/* Flush specified size to the fd. */
|
||||
void
|
||||
buffer_flush (struct buffer *b, int fd, size_t size)
|
||||
{
|
||||
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
|
||||
/* Keep flushing data to the fd until the buffer is empty or an error is
|
||||
encountered or the operation would block. */
|
||||
buffer_status_t
|
||||
buffer_flush_all (struct buffer *b, int fd)
|
||||
{
|
||||
int ret;
|
||||
struct buffer_data *d;
|
||||
int iov_index;
|
||||
struct iovec *iovec;
|
||||
buffer_status_t ret;
|
||||
struct buffer_data *head;
|
||||
size_t head_sp;
|
||||
|
||||
if (buffer_empty (b))
|
||||
return 0;
|
||||
|
||||
iovec = malloc (sizeof (struct iovec) * b->alloc);
|
||||
iov_index = 0;
|
||||
|
||||
for (d = b->head; d; d = d->next)
|
||||
if (!b->head)
|
||||
return BUFFER_EMPTY;
|
||||
head_sp = (head = b->head)->sp;
|
||||
/* Flush all data. */
|
||||
while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING)
|
||||
{
|
||||
iovec[iov_index].iov_base = (char *)(d->data + d->sp);
|
||||
iovec[iov_index].iov_len = d->cp - d->sp;
|
||||
iov_index++;
|
||||
if ((b->head == head) && (head_sp == head->sp) && (errno != EINTR))
|
||||
/* No data was flushed, so kernel buffer must be full. */
|
||||
return ret;
|
||||
head_sp = (head = b->head)->sp;
|
||||
}
|
||||
ret = writev (fd, iovec, iov_index);
|
||||
|
||||
free (iovec);
|
||||
|
||||
buffer_reset (b);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Flush all buffer to the fd. */
|
||||
int
|
||||
buffer_flush_vty_all (struct buffer *b, int fd, int erase_flag,
|
||||
int no_more_flag)
|
||||
/* Flush enough data to fill a terminal window of the given scene (used only
|
||||
by vty telnet interface). */
|
||||
buffer_status_t
|
||||
buffer_flush_window (struct buffer *b, int fd, int width, int height,
|
||||
int erase_flag, int no_more_flag)
|
||||
{
|
||||
int nbytes;
|
||||
int iov_alloc;
|
||||
int iov_index;
|
||||
struct iovec *iov;
|
||||
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};
|
||||
struct buffer_data *data;
|
||||
struct buffer_data *out;
|
||||
struct buffer_data *next;
|
||||
int column;
|
||||
|
||||
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.*/
|
||||
if (b->alloc == 1)
|
||||
iov = small_iov;
|
||||
if (b->head->next == NULL)
|
||||
{
|
||||
iov_alloc = sizeof(small_iov)/sizeof(small_iov[0]);
|
||||
iov = small_iov;
|
||||
}
|
||||
else
|
||||
iov = XCALLOC (MTYPE_TMP, sizeof (struct iovec) * (b->alloc + 2));
|
||||
|
||||
data = b->head;
|
||||
{
|
||||
iov_alloc = ((height*(width+2))/b->size)+10;
|
||||
iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
|
||||
}
|
||||
iov_index = 0;
|
||||
|
||||
/* Previously print out is performed. */
|
||||
@ -338,250 +281,115 @@ buffer_flush_vty_all (struct buffer *b, int fd, int erase_flag,
|
||||
}
|
||||
|
||||
/* 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_len = data->cp - data->sp;
|
||||
iov_index++;
|
||||
iov[iov_index++].iov_len = cp-data->sp;
|
||||
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. */
|
||||
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_len = sizeof more;
|
||||
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
|
||||
/* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
|
||||
example: Solaris2.6 are defined IOV_MAX size at 16. */
|
||||
c_iov = iov;
|
||||
total_size = iov_index;
|
||||
nbytes = 0;
|
||||
{
|
||||
struct iovec *c_iov = iov;
|
||||
nbytes = 0; /* Make sure it's initialized. */
|
||||
|
||||
while( total_size > 0 )
|
||||
{
|
||||
/* initialize write vector size at once */
|
||||
iov_size = ( total_size > IOV_MAX ) ? IOV_MAX : total_size;
|
||||
while (iov_index > 0)
|
||||
{
|
||||
int iov_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 )
|
||||
{
|
||||
if(errno == EINTR)
|
||||
;
|
||||
;
|
||||
if(errno == EWOULDBLOCK)
|
||||
;
|
||||
;
|
||||
nbytes = c_nbytes;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
nbytes += c_nbytes;
|
||||
|
||||
/* move pointer io-vector */
|
||||
c_iov += iov_size;
|
||||
total_size -= iov_size;
|
||||
}
|
||||
/* move pointer io-vector */
|
||||
c_iov += iov_size;
|
||||
iov_index -= iov_size;
|
||||
}
|
||||
}
|
||||
#else /* IOV_MAX */
|
||||
nbytes = writev (fd, iov, iov_index);
|
||||
|
||||
/* Error treatment. */
|
||||
if (nbytes < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
;
|
||||
if (errno == EWOULDBLOCK)
|
||||
;
|
||||
}
|
||||
if ((nbytes = writev (fd, iov, iov_index)) < 0)
|
||||
zlog_warn("%s: writev to fd %d failed: %s",
|
||||
__func__, fd, safe_strerror(errno));
|
||||
#endif /* IOV_MAX */
|
||||
|
||||
/* 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;
|
||||
if (next)
|
||||
next->prev = NULL;
|
||||
else
|
||||
b->tail = next;
|
||||
b->head = next;
|
||||
|
||||
buffer_data_free (out);
|
||||
b->alloc--;
|
||||
struct buffer_data *del;
|
||||
if (!(b->head = (del = b->head)->next))
|
||||
b->tail = NULL;
|
||||
BUFFER_DATA_FREE(del);
|
||||
}
|
||||
|
||||
if (iov != small_iov)
|
||||
XFREE (MTYPE_TMP, iov);
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
return (nbytes < 0) ? BUFFER_ERROR :
|
||||
(b->head ? BUFFER_PENDING : BUFFER_EMPTY);
|
||||
}
|
||||
|
||||
/* This function (unlike other buffer_flush* functions above) is designed
|
||||
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
|
||||
able to empty out the buffers completely, or 1 if more flushing is
|
||||
required later. */
|
||||
int
|
||||
able to empty out the buffers completely, 1 if more flushing is
|
||||
required later, or -1 on a fatal write error. */
|
||||
buffer_status_t
|
||||
buffer_flush_available(struct buffer *b, int fd)
|
||||
{
|
||||
|
||||
@ -596,7 +404,6 @@ in one shot. */
|
||||
#define MAX_FLUSH 131072
|
||||
|
||||
struct buffer_data *d;
|
||||
struct buffer_data *next;
|
||||
size_t written;
|
||||
struct iovec iov[MAX_CHUNKS];
|
||||
size_t iovcnt = 0;
|
||||
@ -609,40 +416,76 @@ in one shot. */
|
||||
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 */
|
||||
if ((ssize_t)(written = writev(fd,iov,iovcnt)) < 0)
|
||||
{
|
||||
if ((errno != EAGAIN) && (errno != EINTR))
|
||||
zlog_warn("buffer_flush_available write error on fd %d: %s",
|
||||
fd,safe_strerror(errno));
|
||||
return 1;
|
||||
if (ERRNO_IO_RETRY(errno))
|
||||
/* Calling code should try again later. */
|
||||
return BUFFER_PENDING;
|
||||
zlog_warn("%s: write error on fd %d: %s",
|
||||
__func__, fd, safe_strerror(errno));
|
||||
return BUFFER_ERROR;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
d->sp += written;
|
||||
b->length -= written;
|
||||
return 1;
|
||||
return BUFFER_PENDING;
|
||||
}
|
||||
|
||||
written -= (d->cp-d->sp);
|
||||
next = d->next;
|
||||
if (next)
|
||||
next->prev = NULL;
|
||||
else
|
||||
b->tail = next;
|
||||
b->head = next;
|
||||
|
||||
b->length -= (d->cp-d->sp);
|
||||
buffer_data_free (d);
|
||||
b->alloc--;
|
||||
if (!(b->head = d->next))
|
||||
b->tail = NULL;
|
||||
BUFFER_DATA_FREE(d);
|
||||
}
|
||||
|
||||
return (b->head != NULL);
|
||||
return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
|
||||
|
||||
#undef MAX_CHUNKS
|
||||
#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;
|
||||
}
|
||||
|
114
lib/buffer.h
114
lib/buffer.h
@ -23,68 +23,80 @@
|
||||
#ifndef _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. */
|
||||
unsigned long size;
|
||||
|
||||
/* 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. */
|
||||
/* Create a new buffer. Memory will be allocated in chunks of the given
|
||||
size. If the argument is 0, the library will supply a reasonable
|
||||
default size suitable for buffering socket I/O. */
|
||||
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 *);
|
||||
|
||||
/* 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
|
||||
single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note
|
||||
that this function does not alter the state of the buffer, so the data
|
||||
is still inside waiting to be flushed. */
|
||||
char *buffer_getstr (struct buffer *);
|
||||
|
||||
int buffer_putc (struct buffer *, u_char);
|
||||
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);
|
||||
/* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */
|
||||
int buffer_empty (struct buffer *);
|
||||
|
||||
/* buffer_flush_available attempts to flush the queued data to the given
|
||||
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
|
||||
later). This function (unlike the other buffer_flush* functions) is
|
||||
designed to work with non-blocking file descriptors. */
|
||||
int buffer_flush_available(struct buffer *, int fd);
|
||||
typedef enum
|
||||
{
|
||||
/* An I/O error occurred. The buffer should be destroyed and the
|
||||
file descriptor should be closed. */
|
||||
BUFFER_ERROR = -1,
|
||||
|
||||
/* 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 */
|
||||
|
329
lib/vty.c
329
lib/vty.c
@ -35,6 +35,9 @@
|
||||
#include "filter.h"
|
||||
#include "vty.h"
|
||||
#include "privs.h"
|
||||
#include "network.h"
|
||||
|
||||
#include <arpa/telnet.h>
|
||||
|
||||
/* Vty events */
|
||||
enum event
|
||||
@ -50,9 +53,6 @@ enum event
|
||||
#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 *);
|
||||
|
||||
/* Extern host structure from command.c */
|
||||
@ -137,7 +137,7 @@ vty_out (struct vty *vty, const char *format, ...)
|
||||
p = buf;
|
||||
|
||||
/* 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 != buf)
|
||||
@ -151,24 +151,38 @@ static int
|
||||
vty_log_out (struct vty *vty, const char *level, const char *proto_str,
|
||||
const char *format, va_list va)
|
||||
{
|
||||
int ret;
|
||||
int len;
|
||||
char buf[1024];
|
||||
|
||||
if (level)
|
||||
snprintf (buf, sizeof buf, "%s: %s: ", level, proto_str);
|
||||
len = snprintf(buf, sizeof(buf), "%s: %s: ", level, proto_str);
|
||||
else
|
||||
snprintf (buf, sizeof buf, "%s: ", proto_str);
|
||||
write (vty->fd, buf, strlen (buf));
|
||||
|
||||
len = vsnprintf (buf, sizeof buf, format, va);
|
||||
if (len < 0)
|
||||
len = snprintf(buf, sizeof(buf), "%s: ", proto_str);
|
||||
if ((len < 0) || ((size_t)len >= sizeof(buf)))
|
||||
return -1;
|
||||
write (vty->fd, (u_char *)buf, len);
|
||||
|
||||
snprintf (buf, sizeof buf, "\r\n");
|
||||
write (vty->fd, buf, 2);
|
||||
if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) ||
|
||||
((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. */
|
||||
@ -226,7 +240,7 @@ vty_prompt (struct vty *vty)
|
||||
}
|
||||
|
||||
/* Send WILL TELOPT_ECHO to remote server. */
|
||||
void
|
||||
static void
|
||||
vty_will_echo (struct vty *vty)
|
||||
{
|
||||
unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
|
||||
@ -272,12 +286,10 @@ struct vty *
|
||||
vty_new ()
|
||||
{
|
||||
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->max = VTY_BUFSIZ;
|
||||
new->sb_buffer = NULL;
|
||||
|
||||
return new;
|
||||
}
|
||||
@ -349,7 +361,7 @@ vty_auth (struct vty *vty, char *buf)
|
||||
}
|
||||
|
||||
/* Command execution over the vty interface. */
|
||||
int
|
||||
static int
|
||||
vty_command (struct vty *vty, char *buf)
|
||||
{
|
||||
int ret;
|
||||
@ -385,18 +397,18 @@ vty_command (struct vty *vty, char *buf)
|
||||
return ret;
|
||||
}
|
||||
|
||||
char telnet_backward_char = 0x08;
|
||||
char telnet_space_char = ' ';
|
||||
static const char telnet_backward_char = 0x08;
|
||||
static const char telnet_space_char = ' ';
|
||||
|
||||
/* Basic function to write buffer to vty. */
|
||||
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))
|
||||
return;
|
||||
|
||||
/* 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. */
|
||||
@ -517,7 +529,7 @@ vty_history_print (struct vty *vty)
|
||||
}
|
||||
|
||||
/* Show next command line history. */
|
||||
void
|
||||
static void
|
||||
vty_next_line (struct vty *vty)
|
||||
{
|
||||
int try_index;
|
||||
@ -542,7 +554,7 @@ vty_next_line (struct vty *vty)
|
||||
}
|
||||
|
||||
/* Show previous command line history. */
|
||||
void
|
||||
static void
|
||||
vty_previous_line (struct vty *vty)
|
||||
{
|
||||
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. */
|
||||
void
|
||||
static void
|
||||
vty_end_config (struct vty *vty)
|
||||
{
|
||||
vty_out (vty, "%s", VTY_NEWLINE);
|
||||
@ -855,7 +867,7 @@ vty_complete_command (struct vty *vty)
|
||||
vector_only_index_free (matched);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
vty_describe_fold (struct vty *vty, int cmd_width,
|
||||
unsigned int desc_width, struct desc *desc)
|
||||
{
|
||||
@ -1012,7 +1024,7 @@ vty_describe_command (struct vty *vty)
|
||||
vty_redraw_line (vty);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
vty_clear_buf (struct vty *vty)
|
||||
{
|
||||
memset (vty->buf, 0, vty->max);
|
||||
@ -1146,36 +1158,41 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
|
||||
switch (buf[0])
|
||||
{
|
||||
case SB:
|
||||
buffer_reset(vty->sb_buffer);
|
||||
vty->sb_len = 0;
|
||||
vty->iac_sb_in_progress = 1;
|
||||
return 0;
|
||||
break;
|
||||
case SE:
|
||||
{
|
||||
char *buffer;
|
||||
int length;
|
||||
|
||||
if (!vty->iac_sb_in_progress)
|
||||
return 0;
|
||||
|
||||
buffer = (char *)vty->sb_buffer->head->data;
|
||||
length = vty->sb_buffer->length;
|
||||
|
||||
if (buffer == NULL)
|
||||
return 0;
|
||||
|
||||
if (buffer[0] == '\0')
|
||||
if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
|
||||
{
|
||||
vty->iac_sb_in_progress = 0;
|
||||
return 0;
|
||||
}
|
||||
switch (buffer[0])
|
||||
switch (vty->sb_buf[0])
|
||||
{
|
||||
case TELOPT_NAWS:
|
||||
if (length < 5)
|
||||
break;
|
||||
vty->width = buffer[2];
|
||||
vty->height = vty->lines >= 0 ? vty->lines : buffer[4];
|
||||
if (vty->sb_len != TELNET_NAWS_SB_LEN)
|
||||
zlog_warn("RFC 1073 violation detected: telnet NAWS option "
|
||||
"should send %d characters, but we received %lu",
|
||||
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;
|
||||
}
|
||||
vty->iac_sb_in_progress = 0;
|
||||
@ -1272,9 +1289,21 @@ vty_read (struct thread *thread)
|
||||
vty->t_read = NULL;
|
||||
|
||||
/* Read raw data from socket */
|
||||
nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ);
|
||||
if (nbytes <= 0)
|
||||
vty->status = VTY_CLOSE;
|
||||
if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
@ -1293,7 +1322,9 @@ vty_read (struct thread *thread)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1459,58 +1490,64 @@ static int
|
||||
vty_flush (struct thread *thread)
|
||||
{
|
||||
int erase;
|
||||
buffer_status_t flushrc;
|
||||
int vty_sock = THREAD_FD (thread);
|
||||
struct vty *vty = THREAD_ARG (thread);
|
||||
|
||||
vty->t_write = NULL;
|
||||
|
||||
/* Tempolary disable read thread. */
|
||||
if (vty->lines == 0)
|
||||
if (vty->t_read)
|
||||
{
|
||||
thread_cancel (vty->t_read);
|
||||
vty->t_read = NULL;
|
||||
}
|
||||
if ((vty->lines == 0) && vty->t_read)
|
||||
{
|
||||
thread_cancel (vty->t_read);
|
||||
vty->t_read = NULL;
|
||||
}
|
||||
|
||||
/* Function execution continue. */
|
||||
if (vty->status == VTY_MORE || vty->status == VTY_MORELINE)
|
||||
erase = 1;
|
||||
else
|
||||
erase = 0;
|
||||
erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
|
||||
|
||||
if (vty->lines == 0)
|
||||
buffer_flush_window (vty->obuf, vty->fd, vty->width, 25, 0, 1);
|
||||
else if (vty->status == VTY_MORELINE)
|
||||
buffer_flush_window (vty->obuf, vty->fd, vty->width, 1, erase, 0);
|
||||
else
|
||||
buffer_flush_window (vty->obuf, vty->fd, vty->width,
|
||||
vty->lines >= 0 ? vty->lines : vty->height,
|
||||
erase, 0);
|
||||
|
||||
if (buffer_empty (vty->obuf))
|
||||
{
|
||||
if (vty->status == VTY_CLOSE)
|
||||
vty_close (vty);
|
||||
else
|
||||
{
|
||||
vty->status = VTY_NORMAL;
|
||||
|
||||
if (vty->lines == 0)
|
||||
vty_event (VTY_READ, vty_sock, vty);
|
||||
}
|
||||
}
|
||||
/* N.B. if width is 0, that means we don't know the window size. */
|
||||
if ((vty->lines == 0) || (vty->width == 0))
|
||||
flushrc = buffer_flush_available(vty->obuf, vty->fd);
|
||||
else if (vty->status == VTY_MORELINE)
|
||||
flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width,
|
||||
1, erase, 0);
|
||||
else
|
||||
flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width,
|
||||
vty->lines >= 0 ? vty->lines :
|
||||
vty->height,
|
||||
erase, 0);
|
||||
switch (flushrc)
|
||||
{
|
||||
case BUFFER_ERROR:
|
||||
zlog_warn("buffer_flush failed on vty client fd %d, closing",
|
||||
vty->fd);
|
||||
buffer_reset(vty->obuf);
|
||||
vty_close(vty);
|
||||
return 0;
|
||||
case BUFFER_EMPTY:
|
||||
if (vty->status == VTY_CLOSE)
|
||||
vty_close (vty);
|
||||
else
|
||||
{
|
||||
vty->status = VTY_MORE;
|
||||
|
||||
vty->status = VTY_NORMAL;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Create new vty structure. */
|
||||
struct vty *
|
||||
static struct vty *
|
||||
vty_create (int vty_sock, union sockunion *su)
|
||||
{
|
||||
struct vty *vty;
|
||||
@ -1545,7 +1582,7 @@ vty_create (int vty_sock, union sockunion *su)
|
||||
vty->lines = -1;
|
||||
vty->iac = 0;
|
||||
vty->iac_sb_in_progress = 0;
|
||||
vty->sb_buffer = buffer_new (1024);
|
||||
vty->sb_len = 0;
|
||||
|
||||
if (! no_password_check)
|
||||
{
|
||||
@ -1608,6 +1645,7 @@ vty_accept (struct thread *thread)
|
||||
zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
set_nonblocking(vty_sock);
|
||||
|
||||
p = sockunion2hostprefix (&su);
|
||||
|
||||
@ -1670,7 +1708,7 @@ vty_accept (struct thread *thread)
|
||||
}
|
||||
|
||||
#if defined(HAVE_IPV6) && !defined(NRL)
|
||||
void
|
||||
static void
|
||||
vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
|
||||
{
|
||||
int ret;
|
||||
@ -1736,7 +1774,7 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
|
||||
#endif /* HAVE_IPV6 && ! NRL */
|
||||
|
||||
/* Make vty server socket. */
|
||||
void
|
||||
static void
|
||||
vty_serv_sock_family (const char* addr, unsigned short port, int family)
|
||||
{
|
||||
int ret;
|
||||
@ -1805,7 +1843,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
|
||||
#include <sys/un.h>
|
||||
|
||||
/* VTY shell UNIX domain socket. */
|
||||
void
|
||||
static void
|
||||
vty_serv_un (const char *path)
|
||||
{
|
||||
int ret;
|
||||
@ -1879,7 +1917,6 @@ vtysh_accept (struct thread *thread)
|
||||
int accept_sock;
|
||||
int sock;
|
||||
int client_len;
|
||||
int flags;
|
||||
struct sockaddr_un client;
|
||||
struct vty *vty;
|
||||
|
||||
@ -1899,12 +1936,10 @@ vtysh_accept (struct thread *thread)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set to non-blocking*/
|
||||
if ( ((flags = fcntl (sock, F_GETFL)) == -1)
|
||||
|| (fcntl (sock, F_SETFL, flags|O_NONBLOCK) == -1) )
|
||||
if (set_nonblocking(sock) < 0)
|
||||
{
|
||||
zlog_warn ("vtysh_accept: could not set vty socket to non-blocking,"
|
||||
" %s, closing", safe_strerror (errno));
|
||||
zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking,"
|
||||
" %s, closing", sock, safe_strerror (errno));
|
||||
close (sock);
|
||||
return -1;
|
||||
}
|
||||
@ -1923,6 +1958,26 @@ vtysh_accept (struct thread *thread)
|
||||
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
|
||||
vtysh_read (struct thread *thread)
|
||||
{
|
||||
@ -1931,15 +1986,26 @@ vtysh_read (struct thread *thread)
|
||||
int nbytes;
|
||||
struct vty *vty;
|
||||
unsigned char buf[VTY_READ_BUFSIZ];
|
||||
unsigned char *p;
|
||||
u_char header[4] = {0, 0, 0, 0};
|
||||
|
||||
sock = THREAD_FD (thread);
|
||||
vty = THREAD_ARG (thread);
|
||||
vty->t_read = NULL;
|
||||
|
||||
nbytes = read (sock, buf, VTY_READ_BUFSIZ);
|
||||
if (nbytes <= 0)
|
||||
if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 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);
|
||||
#ifdef VTYSH_DEBUG
|
||||
printf ("close vtysh\n");
|
||||
@ -1948,28 +2014,35 @@ vtysh_read (struct thread *thread)
|
||||
}
|
||||
|
||||
#ifdef VTYSH_DEBUG
|
||||
printf ("line: %s\n", buf);
|
||||
printf ("line: %.*s\n", nbytes, buf);
|
||||
#endif /* VTYSH_DEBUG */
|
||||
|
||||
vty_ensure (vty, nbytes);
|
||||
memcpy (vty->buf, buf, nbytes);
|
||||
|
||||
/* Pass this line to parser. */
|
||||
ret = vty_execute (vty);
|
||||
for (p = buf; p < buf+nbytes; p++)
|
||||
{
|
||||
vty_ensure(vty, vty->length+1);
|
||||
vty->buf[vty->length++] = *p;
|
||||
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
|
||||
printf ("result: %d\n", ret);
|
||||
printf ("vtysh node: %d\n", vty->node);
|
||||
printf ("result: %d\n", ret);
|
||||
printf ("vtysh node: %d\n", vty->node);
|
||||
#endif /* VTYSH_DEBUG */
|
||||
|
||||
header[3] = ret;
|
||||
buffer_write(vty->obuf, header, 4);
|
||||
header[3] = ret;
|
||||
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);
|
||||
|
||||
return 0;
|
||||
@ -1981,8 +2054,7 @@ vtysh_write (struct thread *thread)
|
||||
struct vty *vty = THREAD_ARG (thread);
|
||||
|
||||
vty->t_write = NULL;
|
||||
if (buffer_flush_available(vty->obuf, vty->fd))
|
||||
vty_event (VTYSH_WRITE, vty->fd, vty);
|
||||
vtysh_flush(vty);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2028,16 +2100,11 @@ vty_close (struct vty *vty)
|
||||
thread_cancel (vty->t_timeout);
|
||||
|
||||
/* Flush buffer. */
|
||||
if (! buffer_empty (vty->obuf))
|
||||
buffer_flush_all (vty->obuf, vty->fd);
|
||||
buffer_flush_all (vty->obuf, vty->fd);
|
||||
|
||||
/* Free input buffer. */
|
||||
buffer_free (vty->obuf);
|
||||
|
||||
/* Free SB buffer. */
|
||||
if (vty->sb_buffer)
|
||||
buffer_free (vty->sb_buffer);
|
||||
|
||||
/* Free command history. */
|
||||
for (i = 0; i < VTY_MAXHIST; i++)
|
||||
if (vty->hist[i])
|
||||
@ -2118,7 +2185,7 @@ vty_read_file (FILE *confp)
|
||||
vty_close (vty);
|
||||
}
|
||||
|
||||
FILE *
|
||||
static FILE *
|
||||
vty_use_backup_config (char *fullpath)
|
||||
{
|
||||
char *fullpath_sav, *fullpath_tmp;
|
||||
@ -2298,16 +2365,20 @@ void
|
||||
vty_log_fixed (const char *buf, size_t len)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
struct vty *vty;
|
||||
if ((vty = vector_slot (vtyvec, i)) != NULL)
|
||||
if (vty->monitor)
|
||||
{
|
||||
write(vty->fd, buf, len);
|
||||
write(vty->fd, "\r\n", 2);
|
||||
}
|
||||
if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
|
||||
/* N.B. We don't care about the return code, since process is
|
||||
most likely just about to die anyway. */
|
||||
writev(vty->fd, iov, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2417,7 +2488,7 @@ DEFUN (line_vty,
|
||||
}
|
||||
|
||||
/* Set time out value. */
|
||||
int
|
||||
static int
|
||||
exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
|
||||
{
|
||||
unsigned long timeout = 0;
|
||||
@ -2635,7 +2706,7 @@ DEFUN (show_history,
|
||||
}
|
||||
|
||||
/* Display current configuration. */
|
||||
int
|
||||
static int
|
||||
vty_config_write (struct vty *vty)
|
||||
{
|
||||
vty_out (vty, "line vty%s", VTY_NEWLINE);
|
||||
@ -2749,8 +2820,8 @@ vty_finish ()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vty_save_cwd ()
|
||||
static void
|
||||
vty_save_cwd (void)
|
||||
{
|
||||
char cwd[MAXPATHLEN];
|
||||
char *c;
|
||||
|
15
lib/vty.h
15
lib/vty.h
@ -81,12 +81,21 @@ struct vty
|
||||
/* Current vty 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;
|
||||
|
||||
/* IAC SB handling */
|
||||
/* IAC SB (option subnegotiation) handling */
|
||||
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. */
|
||||
int width;
|
||||
|
@ -161,7 +161,6 @@ typedef int socklen_t;
|
||||
#endif /* HAVE_NETDB_H */
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/telnet.h>
|
||||
|
||||
#ifdef HAVE_INET_ND_H
|
||||
#include <inet/nd.h>
|
||||
|
Loading…
Reference in New Issue
Block a user