Commit 951f13516a50383cd462c9150e643cc9f9566267
1 parent
1f6e24e7
telnet protocol and more consistent syntax (Jason Wessel)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2030 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
191 additions
and
82 deletions
qemu-doc.texi
... | ... | @@ -525,43 +525,60 @@ Write output to filename. No character can be read. |
525 | 525 | name pipe @var{filename} |
526 | 526 | @item COMn |
527 | 527 | [Windows only] Use host serial port @var{n} |
528 | -@item udp:remote_port | |
529 | -UDP Net Console sent to locahost at remote_port | |
530 | -@item udp:remote_host:remote_port | |
531 | -UDP Net Console sent to remote_host at remote_port | |
532 | -@item udp:src_port:remote_host:remote_port | |
533 | -UDP Net Console sent from src_port to remote_host at the remote_port. | |
534 | - | |
535 | -The udp:* sub options are primary intended for netconsole. If you | |
536 | -just want a simple readonly console you can use @code{netcat} or | |
537 | -@code{nc}, by starting qemu with: @code{-serial udp:4555} and nc as: | |
538 | -@code{nc -u -l -p 4555}. Any time qemu writes something to that port | |
539 | -it will appear in the netconsole session. | |
528 | +@item udp:[remote_host]:remote_port[@@[src_ip]:src_port] | |
529 | +This implements UDP Net Console. When @var{remote_host} or @var{src_ip} are not specified they default to @code{0.0.0.0}. When not using a specifed @var{src_port} a random port is automatically chosen. | |
530 | + | |
531 | +If you just want a simple readonly console you can use @code{netcat} or | |
532 | +@code{nc}, by starting qemu with: @code{-serial udp::4555} and nc as: | |
533 | +@code{nc -u -l -p 4555}. Any time qemu writes something to that port it | |
534 | +will appear in the netconsole session. | |
540 | 535 | |
541 | 536 | If you plan to send characters back via netconsole or you want to stop |
542 | 537 | and start qemu a lot of times, you should have qemu use the same |
543 | 538 | source port each time by using something like @code{-serial |
544 | -udp:4556:localhost:4555} to qemu. Another approach is to use a patched | |
539 | +udp::4555@@:4556} to qemu. Another approach is to use a patched | |
545 | 540 | version of netcat which can listen to a TCP port and send and receive |
546 | 541 | characters via udp. If you have a patched version of netcat which |
547 | 542 | activates telnet remote echo and single char transfer, then you can |
548 | 543 | use the following options to step up a netcat redirector to allow |
549 | 544 | telnet on port 5555 to access the qemu port. |
550 | 545 | @table @code |
551 | -@item Qemu Options | |
552 | --serial udp:4556:localhost:4555 | |
553 | -@item netcat options | |
554 | --u -P 4555 -L localhost:4556 -t -p 5555 -I -T | |
546 | +@item Qemu Options: | |
547 | +-serial udp::4555@@:4556 | |
548 | +@item netcat options: | |
549 | +-u -P 4555 -L 0.0.0.0:4556 -t -p 5555 -I -T | |
550 | +@item telnet options: | |
551 | +localhost 5555 | |
552 | +@end table | |
553 | + | |
554 | + | |
555 | +@item tcp:[host]:port[,server][,nowait] | |
556 | +The TCP Net Console has two modes of operation. It can send the serial | |
557 | +I/O to a location or wait for a connection from a location. By default | |
558 | +the TCP Net Console is sent to @var{host} at the @var{port}. If you use | |
559 | +the @var{,server} option QEMU will wait for a client socket application | |
560 | +to connect to the port before continuing, unless the @code{,nowait} | |
561 | +option was specified. If @var{host} is omitted, 0.0.0.0 is assumed. Only | |
562 | +one TCP connection at a time is accepted. You can use @code{telnet} to | |
563 | +connect to the corresponding character device. | |
564 | +@table @code | |
565 | +@item Example to send tcp console to 192.168.0.2 port 4444 | |
566 | +-serial tcp:192.168.0.2:4444 | |
567 | +@item Example to listen and wait on port 4444 for connection | |
568 | +-serial tcp::4444,server | |
569 | +@item Example to not wait and listen on ip 192.168.0.100 port 4444 | |
570 | +-serial tcp:192.168.0.100:4444,server,nowait | |
555 | 571 | @end table |
556 | 572 | |
573 | +@item telnet:host:port[,server][,nowait] | |
574 | +The telnet protocol is used instead of raw tcp sockets. The options | |
575 | +work the same as if you had specified @code{-serial tcp}. The | |
576 | +difference is that the port acts like a telnet server or client using | |
577 | +telnet option negotiation. This will also allow you to send the | |
578 | +MAGIC_SYSRQ sequence if you use a telnet that supports sending the break | |
579 | +sequence. Typically in unix telnet you do it with Control-] and then | |
580 | +type "send break" followed by pressing the enter key. | |
557 | 581 | |
558 | -@item tcp:remote_host:remote_port | |
559 | -TCP Net Console sent to remote_host at the remote_port | |
560 | -@item tcpl:host:port | |
561 | -TCP Net Console: wait for connection on @var{host} on the local port | |
562 | -@var{port}. If host is omitted, 0.0.0.0 is assumed. Only one TCP | |
563 | -connection at a time is accepted. You can use @code{telnet} to connect | |
564 | -to the corresponding character device. | |
565 | 582 | @end table |
566 | 583 | |
567 | 584 | @item -parallel dev | ... | ... |
vl.c
... | ... | @@ -2203,16 +2203,16 @@ static void udp_chr_add_read_handler(CharDriverState *chr, |
2203 | 2203 | } |
2204 | 2204 | |
2205 | 2205 | int parse_host_port(struct sockaddr_in *saddr, const char *str); |
2206 | +int parse_host_src_port(struct sockaddr_in *haddr, | |
2207 | + struct sockaddr_in *saddr, | |
2208 | + const char *str); | |
2206 | 2209 | |
2207 | 2210 | CharDriverState *qemu_chr_open_udp(const char *def) |
2208 | 2211 | { |
2209 | 2212 | CharDriverState *chr = NULL; |
2210 | 2213 | NetCharDriver *s = NULL; |
2211 | 2214 | int fd = -1; |
2212 | - int con_type; | |
2213 | - struct sockaddr_in addr; | |
2214 | - const char *p, *r; | |
2215 | - int port; | |
2215 | + struct sockaddr_in saddr; | |
2216 | 2216 | |
2217 | 2217 | chr = qemu_mallocz(sizeof(CharDriverState)); |
2218 | 2218 | if (!chr) |
... | ... | @@ -2227,58 +2227,12 @@ CharDriverState *qemu_chr_open_udp(const char *def) |
2227 | 2227 | goto return_err; |
2228 | 2228 | } |
2229 | 2229 | |
2230 | - /* There are three types of port definitions | |
2231 | - * 1) udp:remote_port | |
2232 | - * Juse use 0.0.0.0 for the IP and send to remote | |
2233 | - * 2) udp:remote_host:port | |
2234 | - * Use a IP and send traffic to remote | |
2235 | - * 3) udp:local_port:remote_host:remote_port | |
2236 | - * Use local_port as the originator + #2 | |
2237 | - */ | |
2238 | - con_type = 0; | |
2239 | - p = def; | |
2240 | - while ((p = strchr(p, ':'))) { | |
2241 | - p++; | |
2242 | - con_type++; | |
2243 | - } | |
2244 | - | |
2245 | - p = def; | |
2246 | - memset(&addr,0,sizeof(addr)); | |
2247 | - addr.sin_family = AF_INET; | |
2248 | - addr.sin_addr.s_addr = htonl(INADDR_ANY); | |
2249 | - s->daddr.sin_family = AF_INET; | |
2250 | - s->daddr.sin_addr.s_addr = htonl(INADDR_ANY); | |
2251 | - | |
2252 | - switch (con_type) { | |
2253 | - case 0: | |
2254 | - port = strtol(p, (char **)&r, 0); | |
2255 | - if (r == p) { | |
2256 | - fprintf(stderr, "Error parsing port number\n"); | |
2257 | - goto return_err; | |
2258 | - } | |
2259 | - s->daddr.sin_port = htons((short)port); | |
2260 | - break; | |
2261 | - case 2: | |
2262 | - port = strtol(p, (char **)&r, 0); | |
2263 | - if (r == p) { | |
2264 | - fprintf(stderr, "Error parsing port number\n"); | |
2265 | - goto return_err; | |
2266 | - } | |
2267 | - addr.sin_port = htons((short)port); | |
2268 | - p = r + 1; | |
2269 | - /* Fall through to case 1 now that we have the local port */ | |
2270 | - case 1: | |
2271 | - if (parse_host_port(&s->daddr, p) < 0) { | |
2272 | - fprintf(stderr, "Error parsing host name and port\n"); | |
2273 | - goto return_err; | |
2274 | - } | |
2275 | - break; | |
2276 | - default: | |
2277 | - fprintf(stderr, "Too many ':' characters\n"); | |
2278 | - goto return_err; | |
2230 | + if (parse_host_src_port(&s->daddr, &saddr, def) < 0) { | |
2231 | + printf("Could not parse: %s\n", def); | |
2232 | + goto return_err; | |
2279 | 2233 | } |
2280 | 2234 | |
2281 | - if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) | |
2235 | + if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) | |
2282 | 2236 | { |
2283 | 2237 | perror("bind"); |
2284 | 2238 | goto return_err; |
... | ... | @@ -2312,6 +2266,7 @@ typedef struct { |
2312 | 2266 | int fd, listen_fd; |
2313 | 2267 | int connected; |
2314 | 2268 | int max_size; |
2269 | + int do_telnetopt; | |
2315 | 2270 | } TCPCharDriver; |
2316 | 2271 | |
2317 | 2272 | static void tcp_chr_accept(void *opaque); |
... | ... | @@ -2337,6 +2292,56 @@ static int tcp_chr_read_poll(void *opaque) |
2337 | 2292 | return s->max_size; |
2338 | 2293 | } |
2339 | 2294 | |
2295 | +#define IAC 255 | |
2296 | +#define IAC_BREAK 243 | |
2297 | +static void tcp_chr_process_IAC_bytes(CharDriverState *chr, | |
2298 | + TCPCharDriver *s, | |
2299 | + char *buf, int *size) | |
2300 | +{ | |
2301 | + /* Handle any telnet client's basic IAC options to satisfy char by | |
2302 | + * char mode with no echo. All IAC options will be removed from | |
2303 | + * the buf and the do_telnetopt variable will be used to track the | |
2304 | + * state of the width of the IAC information. | |
2305 | + * | |
2306 | + * IAC commands come in sets of 3 bytes with the exception of the | |
2307 | + * "IAC BREAK" command and the double IAC. | |
2308 | + */ | |
2309 | + | |
2310 | + int i; | |
2311 | + int j = 0; | |
2312 | + | |
2313 | + for (i = 0; i < *size; i++) { | |
2314 | + if (s->do_telnetopt > 1) { | |
2315 | + if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) { | |
2316 | + /* Double IAC means send an IAC */ | |
2317 | + if (j != i) | |
2318 | + buf[j] = buf[i]; | |
2319 | + j++; | |
2320 | + s->do_telnetopt = 1; | |
2321 | + } else { | |
2322 | + if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) { | |
2323 | + /* Handle IAC break commands by sending a serial break */ | |
2324 | + chr->chr_event(s->fd_opaque, CHR_EVENT_BREAK); | |
2325 | + s->do_telnetopt++; | |
2326 | + } | |
2327 | + s->do_telnetopt++; | |
2328 | + } | |
2329 | + if (s->do_telnetopt >= 4) { | |
2330 | + s->do_telnetopt = 1; | |
2331 | + } | |
2332 | + } else { | |
2333 | + if ((unsigned char)buf[i] == IAC) { | |
2334 | + s->do_telnetopt = 2; | |
2335 | + } else { | |
2336 | + if (j != i) | |
2337 | + buf[j] = buf[i]; | |
2338 | + j++; | |
2339 | + } | |
2340 | + } | |
2341 | + } | |
2342 | + *size = j; | |
2343 | +} | |
2344 | + | |
2340 | 2345 | static void tcp_chr_read(void *opaque) |
2341 | 2346 | { |
2342 | 2347 | CharDriverState *chr = opaque; |
... | ... | @@ -2360,7 +2365,10 @@ static void tcp_chr_read(void *opaque) |
2360 | 2365 | closesocket(s->fd); |
2361 | 2366 | s->fd = -1; |
2362 | 2367 | } else if (size > 0) { |
2363 | - s->fd_read(s->fd_opaque, buf, size); | |
2368 | + if (s->do_telnetopt) | |
2369 | + tcp_chr_process_IAC_bytes(chr, s, buf, &size); | |
2370 | + if (size > 0) | |
2371 | + s->fd_read(s->fd_opaque, buf, size); | |
2364 | 2372 | } |
2365 | 2373 | } |
2366 | 2374 | |
... | ... | @@ -2385,6 +2393,21 @@ static void tcp_chr_connect(void *opaque) |
2385 | 2393 | tcp_chr_read, NULL, chr); |
2386 | 2394 | } |
2387 | 2395 | |
2396 | +#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c; | |
2397 | +static void tcp_chr_telnet_init(int fd) | |
2398 | +{ | |
2399 | + char buf[3]; | |
2400 | + /* Send the telnet negotion to put telnet in binary, no echo, single char mode */ | |
2401 | + IACSET(buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */ | |
2402 | + send(fd, (char *)buf, 3, 0); | |
2403 | + IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */ | |
2404 | + send(fd, (char *)buf, 3, 0); | |
2405 | + IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */ | |
2406 | + send(fd, (char *)buf, 3, 0); | |
2407 | + IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */ | |
2408 | + send(fd, (char *)buf, 3, 0); | |
2409 | +} | |
2410 | + | |
2388 | 2411 | static void tcp_chr_accept(void *opaque) |
2389 | 2412 | { |
2390 | 2413 | CharDriverState *chr = opaque; |
... | ... | @@ -2399,6 +2422,8 @@ static void tcp_chr_accept(void *opaque) |
2399 | 2422 | if (fd < 0 && errno != EINTR) { |
2400 | 2423 | return; |
2401 | 2424 | } else if (fd >= 0) { |
2425 | + if (s->do_telnetopt) | |
2426 | + tcp_chr_telnet_init(fd); | |
2402 | 2427 | break; |
2403 | 2428 | } |
2404 | 2429 | } |
... | ... | @@ -2419,16 +2444,34 @@ static void tcp_chr_close(CharDriverState *chr) |
2419 | 2444 | } |
2420 | 2445 | |
2421 | 2446 | static CharDriverState *qemu_chr_open_tcp(const char *host_str, |
2422 | - int is_listen) | |
2447 | + int is_telnet) | |
2423 | 2448 | { |
2424 | 2449 | CharDriverState *chr = NULL; |
2425 | 2450 | TCPCharDriver *s = NULL; |
2426 | 2451 | int fd = -1, ret, err, val; |
2452 | + int is_listen = 0; | |
2453 | + int is_waitconnect = 1; | |
2454 | + const char *ptr; | |
2427 | 2455 | struct sockaddr_in saddr; |
2428 | 2456 | |
2429 | 2457 | if (parse_host_port(&saddr, host_str) < 0) |
2430 | 2458 | goto fail; |
2431 | 2459 | |
2460 | + ptr = host_str; | |
2461 | + while((ptr = strchr(ptr,','))) { | |
2462 | + ptr++; | |
2463 | + if (!strncmp(ptr,"server",6)) { | |
2464 | + is_listen = 1; | |
2465 | + } else if (!strncmp(ptr,"nowait",6)) { | |
2466 | + is_waitconnect = 0; | |
2467 | + } else { | |
2468 | + printf("Unknown option: %s\n", ptr); | |
2469 | + goto fail; | |
2470 | + } | |
2471 | + } | |
2472 | + if (!is_listen) | |
2473 | + is_waitconnect = 0; | |
2474 | + | |
2432 | 2475 | chr = qemu_mallocz(sizeof(CharDriverState)); |
2433 | 2476 | if (!chr) |
2434 | 2477 | goto fail; |
... | ... | @@ -2439,7 +2482,9 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, |
2439 | 2482 | fd = socket(PF_INET, SOCK_STREAM, 0); |
2440 | 2483 | if (fd < 0) |
2441 | 2484 | goto fail; |
2442 | - socket_set_nonblock(fd); | |
2485 | + | |
2486 | + if (!is_waitconnect) | |
2487 | + socket_set_nonblock(fd); | |
2443 | 2488 | |
2444 | 2489 | s->connected = 0; |
2445 | 2490 | s->fd = -1; |
... | ... | @@ -2457,6 +2502,8 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, |
2457 | 2502 | goto fail; |
2458 | 2503 | s->listen_fd = fd; |
2459 | 2504 | qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); |
2505 | + if (is_telnet) | |
2506 | + s->do_telnetopt = 1; | |
2460 | 2507 | } else { |
2461 | 2508 | for(;;) { |
2462 | 2509 | ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); |
... | ... | @@ -2484,6 +2531,12 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, |
2484 | 2531 | chr->chr_write = tcp_chr_write; |
2485 | 2532 | chr->chr_add_read_handler = tcp_chr_add_read_handler; |
2486 | 2533 | chr->chr_close = tcp_chr_close; |
2534 | + if (is_listen && is_waitconnect) { | |
2535 | + printf("QEMU waiting for connection on: %s\n", host_str); | |
2536 | + tcp_chr_accept(chr); | |
2537 | + socket_set_nonblock(s->listen_fd); | |
2538 | + } | |
2539 | + | |
2487 | 2540 | return chr; |
2488 | 2541 | fail: |
2489 | 2542 | if (fd >= 0) |
... | ... | @@ -2505,7 +2558,7 @@ CharDriverState *qemu_chr_open(const char *filename) |
2505 | 2558 | if (strstart(filename, "tcp:", &p)) { |
2506 | 2559 | return qemu_chr_open_tcp(p, 0); |
2507 | 2560 | } else |
2508 | - if (strstart(filename, "tcpl:", &p)) { | |
2561 | + if (strstart(filename, "telnet:", &p)) { | |
2509 | 2562 | return qemu_chr_open_tcp(p, 1); |
2510 | 2563 | } else |
2511 | 2564 | if (strstart(filename, "udp:", &p)) { |
... | ... | @@ -2618,6 +2671,45 @@ static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) |
2618 | 2671 | return 0; |
2619 | 2672 | } |
2620 | 2673 | |
2674 | +int parse_host_src_port(struct sockaddr_in *haddr, | |
2675 | + struct sockaddr_in *saddr, | |
2676 | + const char *input_str) | |
2677 | +{ | |
2678 | + char *str = strdup(input_str); | |
2679 | + char *host_str = str; | |
2680 | + char *src_str; | |
2681 | + char *ptr; | |
2682 | + | |
2683 | + /* | |
2684 | + * Chop off any extra arguments at the end of the string which | |
2685 | + * would start with a comma, then fill in the src port information | |
2686 | + * if it was provided else use the "any address" and "any port". | |
2687 | + */ | |
2688 | + if ((ptr = strchr(str,','))) | |
2689 | + *ptr = '\0'; | |
2690 | + | |
2691 | + if ((src_str = strchr(input_str,'@'))) { | |
2692 | + *src_str = '\0'; | |
2693 | + src_str++; | |
2694 | + } | |
2695 | + | |
2696 | + if (parse_host_port(haddr, host_str) < 0) | |
2697 | + goto fail; | |
2698 | + | |
2699 | + if (!src_str || *src_str == '\0') | |
2700 | + src_str = ":0"; | |
2701 | + | |
2702 | + if (parse_host_port(saddr, src_str) < 0) | |
2703 | + goto fail; | |
2704 | + | |
2705 | + free(str); | |
2706 | + return(0); | |
2707 | + | |
2708 | +fail: | |
2709 | + free(str); | |
2710 | + return -1; | |
2711 | +} | |
2712 | + | |
2621 | 2713 | int parse_host_port(struct sockaddr_in *saddr, const char *str) |
2622 | 2714 | { |
2623 | 2715 | char buf[512]; | ... | ... |