Commit 0bab00f3 authored by bellard's avatar bellard
Browse files

UDP char device (initial patch by Jason Wessel) - TCP char device


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2007 c046a42c-6fe2-441c-8c8c-71466251a162
parent 3532fa74
......@@ -496,8 +496,14 @@ Debug/Expert options:
@table @option
@item -serial dev
Redirect the virtual serial port to host device @var{dev}. Available
devices are:
Redirect the virtual serial port to host character device
@var{dev}. The default device is @code{vc} in graphical mode and
@code{stdio} in non graphical mode.
This option can be used several times to simulate up to 4 serials
ports.
Available character devices are:
@table @code
@item vc
Virtual console
......@@ -516,13 +522,47 @@ Write output to filename. No character can be read.
@item stdio
[Unix only] standard input/output
@item pipe:filename
[Unix only] name pipe @var{filename}
name pipe @var{filename}
@item COMn
[Windows only] Use host serial port @var{n}
@item udp:remote_port
UDP Net Console sent to locahost at remote_port
@item udp:remote_host:remote_port
UDP Net Console sent to remote_host at remote_port
@item udp:src_port:remote_host:remote_port
UDP Net Console sent from src_port to remote_host at the remote_port.
The udp:* sub options are primary intended for netconsole. If you
just want a simple readonly console you can use @code{netcat} or
@code{nc}, by starting qemu with: @code{-serial udp:4555} and nc as:
@code{nc -u -l -p 4555}. Any time qemu writes something to that port
it will appear in the netconsole session.
If you plan to send characters back via netconsole or you want to stop
and start qemu a lot of times, you should have qemu use the same
source port each time by using something like @code{-serial
udp:4556:localhost:4555} to qemu. Another approach is to use a patched
version of netcat which can listen to a TCP port and send and receive
characters via udp. If you have a patched version of netcat which
activates telnet remote echo and single char transfer, then you can
use the following options to step up a netcat redirector to allow
telnet on port 5555 to access the qemu port.
@table @code
@item Qemu Options
-serial udp:4556:localhost:4555
@item netcat options
-u -P 4555 -L localhost:4556 -t -p 5555 -I -T
@end table
The default device is @code{vc} in graphical mode and @code{stdio} in
non graphical mode.
This option can be used several times to simulate up to 4 serials
ports.
@item tcp:remote_host:remote_port
TCP Net Console sent to remote_host at the remote_port
@item tcpl:host:port
TCP Net Console: wait for connection on @var{host} on the local port
@var{port}. If host is omitted, 0.0.0.0 is assumed. Only one TCP
connection at a time is accepted. You can use @code{telnet} to connect
to the corresponding character device.
@end table
@item -parallel dev
Redirect the virtual parallel port to host device @var{dev} (same
......
......@@ -2130,6 +2130,373 @@ CharDriverState *qemu_chr_open_win_file_out(const char *file_out)
}
#endif
/***********************************************************/
/* UDP Net console */
typedef struct {
IOCanRWHandler *fd_can_read;
IOReadHandler *fd_read;
void *fd_opaque;
int fd;
struct sockaddr_in daddr;
char buf[1024];
int bufcnt;
int bufptr;
int max_size;
} NetCharDriver;
static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
NetCharDriver *s = chr->opaque;
return sendto(s->fd, buf, len, 0,
(struct sockaddr *)&s->daddr, sizeof(struct sockaddr_in));
}
static int udp_chr_read_poll(void *opaque)
{
CharDriverState *chr = opaque;
NetCharDriver *s = chr->opaque;
s->max_size = s->fd_can_read(s->fd_opaque);
/* If there were any stray characters in the queue process them
* first
*/
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
s->fd_read(s->fd_opaque, &s->buf[s->bufptr], 1);
s->bufptr++;
s->max_size = s->fd_can_read(s->fd_opaque);
}
return s->max_size;
}
static void udp_chr_read(void *opaque)
{
CharDriverState *chr = opaque;
NetCharDriver *s = chr->opaque;
if (s->max_size == 0)
return;
s->bufcnt = recv(s->fd, s->buf, sizeof(s->buf), 0);
s->bufptr = s->bufcnt;
if (s->bufcnt <= 0)
return;
s->bufptr = 0;
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
s->fd_read(s->fd_opaque, &s->buf[s->bufptr], 1);
s->bufptr++;
s->max_size = s->fd_can_read(s->fd_opaque);
}
}
static void udp_chr_add_read_handler(CharDriverState *chr,
IOCanRWHandler *fd_can_read,
IOReadHandler *fd_read, void *opaque)
{
NetCharDriver *s = chr->opaque;
if (s->fd >= 0) {
s->fd_can_read = fd_can_read;
s->fd_read = fd_read;
s->fd_opaque = opaque;
qemu_set_fd_handler2(s->fd, udp_chr_read_poll,
udp_chr_read, NULL, chr);
}
}
int parse_host_port(struct sockaddr_in *saddr, const char *str);
CharDriverState *qemu_chr_open_udp(const char *def)
{
CharDriverState *chr = NULL;
NetCharDriver *s = NULL;
int fd = -1;
int con_type;
struct sockaddr_in addr;
const char *p, *r;
int port;
chr = qemu_mallocz(sizeof(CharDriverState));
if (!chr)
goto return_err;
s = qemu_mallocz(sizeof(NetCharDriver));
if (!s)
goto return_err;
fd = socket(PF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
perror("socket(PF_INET, SOCK_DGRAM)");
goto return_err;
}
/* There are three types of port definitions
* 1) udp:remote_port
* Juse use 0.0.0.0 for the IP and send to remote
* 2) udp:remote_host:port
* Use a IP and send traffic to remote
* 3) udp:local_port:remote_host:remote_port
* Use local_port as the originator + #2
*/
con_type = 0;
p = def;
while ((p = strchr(p, ':'))) {
p++;
con_type++;
}
p = def;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
s->daddr.sin_family = AF_INET;
s->daddr.sin_addr.s_addr = htonl(INADDR_ANY);
switch (con_type) {
case 0:
port = strtol(p, (char **)&r, 0);
if (r == p) {
fprintf(stderr, "Error parsing port number\n");
goto return_err;
}
s->daddr.sin_port = htons((short)port);
break;
case 2:
port = strtol(p, (char **)&r, 0);
if (r == p) {
fprintf(stderr, "Error parsing port number\n");
goto return_err;
}
addr.sin_port = htons((short)port);
p = r + 1;
/* Fall through to case 1 now that we have the local port */
case 1:
if (parse_host_port(&s->daddr, p) < 0) {
fprintf(stderr, "Error parsing host name and port\n");
goto return_err;
}
break;
default:
fprintf(stderr, "Too many ':' characters\n");
goto return_err;
}
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("bind");
goto return_err;
}
s->fd = fd;
s->bufcnt = 0;
s->bufptr = 0;
chr->opaque = s;
chr->chr_write = udp_chr_write;
chr->chr_add_read_handler = udp_chr_add_read_handler;
return chr;
return_err:
if (chr)
free(chr);
if (s)
free(s);
if (fd >= 0)
closesocket(fd);
return NULL;
}
/***********************************************************/
/* TCP Net console */
typedef struct {
IOCanRWHandler *fd_can_read;
IOReadHandler *fd_read;
void *fd_opaque;
int fd, listen_fd;
int connected;
int max_size;
} TCPCharDriver;
static void tcp_chr_accept(void *opaque);
static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
TCPCharDriver *s = chr->opaque;
if (s->connected) {
return send_all(s->fd, buf, len);
} else {
/* XXX: indicate an error ? */
return len;
}
}
static int tcp_chr_read_poll(void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
if (!s->connected)
return 0;
s->max_size = s->fd_can_read(s->fd_opaque);
return s->max_size;
}
static void tcp_chr_read(void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
uint8_t buf[1024];
int len, size;
if (!s->connected || s->max_size <= 0)
return;
len = sizeof(buf);
if (len > s->max_size)
len = s->max_size;
size = recv(s->fd, buf, len, 0);
if (size == 0) {
/* connection closed */
s->connected = 0;
if (s->listen_fd >= 0) {
qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
}
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
closesocket(s->fd);
s->fd = -1;
} else if (size > 0) {
s->fd_read(s->fd_opaque, buf, size);
}
}
static void tcp_chr_add_read_handler(CharDriverState *chr,
IOCanRWHandler *fd_can_read,
IOReadHandler *fd_read, void *opaque)
{
TCPCharDriver *s = chr->opaque;
s->fd_can_read = fd_can_read;
s->fd_read = fd_read;
s->fd_opaque = opaque;
}
static void tcp_chr_connect(void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
s->connected = 1;
qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
tcp_chr_read, NULL, chr);
}
static void tcp_chr_accept(void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
struct sockaddr_in saddr;
socklen_t len;
int fd;
for(;;) {
len = sizeof(saddr);
fd = accept(s->listen_fd, (struct sockaddr *)&saddr, &len);
if (fd < 0 && errno != EINTR) {
return;
} else if (fd >= 0) {
break;
}
}
socket_set_nonblock(fd);
s->fd = fd;
qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
tcp_chr_connect(chr);
}
static void tcp_chr_close(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
if (s->fd >= 0)
closesocket(s->fd);
if (s->listen_fd >= 0)
closesocket(s->listen_fd);
qemu_free(s);
}
static CharDriverState *qemu_chr_open_tcp(const char *host_str,
int is_listen)
{
CharDriverState *chr = NULL;
TCPCharDriver *s = NULL;
int fd = -1, ret, err, val;
struct sockaddr_in saddr;
if (parse_host_port(&saddr, host_str) < 0)
goto fail;
chr = qemu_mallocz(sizeof(CharDriverState));
if (!chr)
goto fail;
s = qemu_mallocz(sizeof(TCPCharDriver));
if (!s)
goto fail;
fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0)
goto fail;
socket_set_nonblock(fd);
s->connected = 0;
s->fd = -1;
s->listen_fd = -1;
if (is_listen) {
/* allow fast reuse */
val = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
if (ret < 0)
goto fail;
ret = listen(fd, 0);
if (ret < 0)
goto fail;
s->listen_fd = fd;
qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
} else {
for(;;) {
ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
if (ret < 0) {
err = socket_error();
if (err == EINTR || err == EWOULDBLOCK) {
} else if (err == EINPROGRESS) {
break;
} else {
goto fail;
}
} else {
s->connected = 1;
break;
}
}
s->fd = fd;
if (s->connected)
tcp_chr_connect(chr);
else
qemu_set_fd_handler(s->fd, NULL, tcp_chr_connect, chr);
}
chr->opaque = s;
chr->chr_write = tcp_chr_write;
chr->chr_add_read_handler = tcp_chr_add_read_handler;
chr->chr_close = tcp_chr_close;
return chr;
fail:
if (fd >= 0)
closesocket(fd);
qemu_free(s);
qemu_free(chr);
return NULL;
}
CharDriverState *qemu_chr_open(const char *filename)
{
const char *p;
......@@ -2139,6 +2506,15 @@ CharDriverState *qemu_chr_open(const char *filename)
} else if (!strcmp(filename, "null")) {
return qemu_chr_open_null();
} else
if (strstart(filename, "tcp:", &p)) {
return qemu_chr_open_tcp(p, 0);
} else
if (strstart(filename, "tcpl:", &p)) {
return qemu_chr_open_tcp(p, 1);
} else
if (strstart(filename, "udp:", &p)) {
return qemu_chr_open_udp(p);
} else
#ifndef _WIN32
if (strstart(filename, "file:", &p)) {
return qemu_chr_open_file_out(p);
......@@ -2844,7 +3220,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr)
socket_set_nonblock(fd);
return fd;
fail:
if (fd>=0) close(fd);
if (fd >= 0)
closesocket(fd);
return -1;
}
......@@ -2972,7 +3349,7 @@ static void net_socket_accept(void *opaque)
}
s1 = net_socket_fd_init(s->vlan, fd, 1);
if (!s1) {
close(fd);
closesocket(fd);
} else {
snprintf(s1->vc->info_str, sizeof(s1->vc->info_str),
"socket: connection from %s:%d",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment