Commit 0bab00f30f798bd8ae4366a4516d2149174aa714
1 parent
3532fa74
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
Showing
2 changed files
with
426 additions
and
9 deletions
qemu-doc.texi
@@ -496,8 +496,14 @@ Debug/Expert options: | @@ -496,8 +496,14 @@ Debug/Expert options: | ||
496 | @table @option | 496 | @table @option |
497 | 497 | ||
498 | @item -serial dev | 498 | @item -serial dev |
499 | -Redirect the virtual serial port to host device @var{dev}. Available | ||
500 | -devices are: | 499 | +Redirect the virtual serial port to host character device |
500 | +@var{dev}. The default device is @code{vc} in graphical mode and | ||
501 | +@code{stdio} in non graphical mode. | ||
502 | + | ||
503 | +This option can be used several times to simulate up to 4 serials | ||
504 | +ports. | ||
505 | + | ||
506 | +Available character devices are: | ||
501 | @table @code | 507 | @table @code |
502 | @item vc | 508 | @item vc |
503 | Virtual console | 509 | Virtual console |
@@ -516,13 +522,47 @@ Write output to filename. No character can be read. | @@ -516,13 +522,47 @@ Write output to filename. No character can be read. | ||
516 | @item stdio | 522 | @item stdio |
517 | [Unix only] standard input/output | 523 | [Unix only] standard input/output |
518 | @item pipe:filename | 524 | @item pipe:filename |
519 | -[Unix only] name pipe @var{filename} | 525 | +name pipe @var{filename} |
526 | +@item COMn | ||
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. | ||
540 | + | ||
541 | +If you plan to send characters back via netconsole or you want to stop | ||
542 | +and start qemu a lot of times, you should have qemu use the same | ||
543 | +source port each time by using something like @code{-serial | ||
544 | +udp:4556:localhost:4555} to qemu. Another approach is to use a patched | ||
545 | +version of netcat which can listen to a TCP port and send and receive | ||
546 | +characters via udp. If you have a patched version of netcat which | ||
547 | +activates telnet remote echo and single char transfer, then you can | ||
548 | +use the following options to step up a netcat redirector to allow | ||
549 | +telnet on port 5555 to access the qemu port. | ||
550 | +@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 | ||
520 | @end table | 555 | @end table |
521 | -The default device is @code{vc} in graphical mode and @code{stdio} in | ||
522 | -non graphical mode. | ||
523 | 556 | ||
524 | -This option can be used several times to simulate up to 4 serials | ||
525 | -ports. | 557 | + |
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 | +@end table | ||
526 | 566 | ||
527 | @item -parallel dev | 567 | @item -parallel dev |
528 | Redirect the virtual parallel port to host device @var{dev} (same | 568 | Redirect the virtual parallel port to host device @var{dev} (same |
vl.c
@@ -2130,6 +2130,373 @@ CharDriverState *qemu_chr_open_win_file_out(const char *file_out) | @@ -2130,6 +2130,373 @@ CharDriverState *qemu_chr_open_win_file_out(const char *file_out) | ||
2130 | } | 2130 | } |
2131 | #endif | 2131 | #endif |
2132 | 2132 | ||
2133 | +/***********************************************************/ | ||
2134 | +/* UDP Net console */ | ||
2135 | + | ||
2136 | +typedef struct { | ||
2137 | + IOCanRWHandler *fd_can_read; | ||
2138 | + IOReadHandler *fd_read; | ||
2139 | + void *fd_opaque; | ||
2140 | + int fd; | ||
2141 | + struct sockaddr_in daddr; | ||
2142 | + char buf[1024]; | ||
2143 | + int bufcnt; | ||
2144 | + int bufptr; | ||
2145 | + int max_size; | ||
2146 | +} NetCharDriver; | ||
2147 | + | ||
2148 | +static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) | ||
2149 | +{ | ||
2150 | + NetCharDriver *s = chr->opaque; | ||
2151 | + | ||
2152 | + return sendto(s->fd, buf, len, 0, | ||
2153 | + (struct sockaddr *)&s->daddr, sizeof(struct sockaddr_in)); | ||
2154 | +} | ||
2155 | + | ||
2156 | +static int udp_chr_read_poll(void *opaque) | ||
2157 | +{ | ||
2158 | + CharDriverState *chr = opaque; | ||
2159 | + NetCharDriver *s = chr->opaque; | ||
2160 | + | ||
2161 | + s->max_size = s->fd_can_read(s->fd_opaque); | ||
2162 | + | ||
2163 | + /* If there were any stray characters in the queue process them | ||
2164 | + * first | ||
2165 | + */ | ||
2166 | + while (s->max_size > 0 && s->bufptr < s->bufcnt) { | ||
2167 | + s->fd_read(s->fd_opaque, &s->buf[s->bufptr], 1); | ||
2168 | + s->bufptr++; | ||
2169 | + s->max_size = s->fd_can_read(s->fd_opaque); | ||
2170 | + } | ||
2171 | + return s->max_size; | ||
2172 | +} | ||
2173 | + | ||
2174 | +static void udp_chr_read(void *opaque) | ||
2175 | +{ | ||
2176 | + CharDriverState *chr = opaque; | ||
2177 | + NetCharDriver *s = chr->opaque; | ||
2178 | + | ||
2179 | + if (s->max_size == 0) | ||
2180 | + return; | ||
2181 | + s->bufcnt = recv(s->fd, s->buf, sizeof(s->buf), 0); | ||
2182 | + s->bufptr = s->bufcnt; | ||
2183 | + if (s->bufcnt <= 0) | ||
2184 | + return; | ||
2185 | + | ||
2186 | + s->bufptr = 0; | ||
2187 | + while (s->max_size > 0 && s->bufptr < s->bufcnt) { | ||
2188 | + s->fd_read(s->fd_opaque, &s->buf[s->bufptr], 1); | ||
2189 | + s->bufptr++; | ||
2190 | + s->max_size = s->fd_can_read(s->fd_opaque); | ||
2191 | + } | ||
2192 | +} | ||
2193 | + | ||
2194 | +static void udp_chr_add_read_handler(CharDriverState *chr, | ||
2195 | + IOCanRWHandler *fd_can_read, | ||
2196 | + IOReadHandler *fd_read, void *opaque) | ||
2197 | +{ | ||
2198 | + NetCharDriver *s = chr->opaque; | ||
2199 | + | ||
2200 | + if (s->fd >= 0) { | ||
2201 | + s->fd_can_read = fd_can_read; | ||
2202 | + s->fd_read = fd_read; | ||
2203 | + s->fd_opaque = opaque; | ||
2204 | + qemu_set_fd_handler2(s->fd, udp_chr_read_poll, | ||
2205 | + udp_chr_read, NULL, chr); | ||
2206 | + } | ||
2207 | +} | ||
2208 | + | ||
2209 | +int parse_host_port(struct sockaddr_in *saddr, const char *str); | ||
2210 | + | ||
2211 | +CharDriverState *qemu_chr_open_udp(const char *def) | ||
2212 | +{ | ||
2213 | + CharDriverState *chr = NULL; | ||
2214 | + NetCharDriver *s = NULL; | ||
2215 | + int fd = -1; | ||
2216 | + int con_type; | ||
2217 | + struct sockaddr_in addr; | ||
2218 | + const char *p, *r; | ||
2219 | + int port; | ||
2220 | + | ||
2221 | + chr = qemu_mallocz(sizeof(CharDriverState)); | ||
2222 | + if (!chr) | ||
2223 | + goto return_err; | ||
2224 | + s = qemu_mallocz(sizeof(NetCharDriver)); | ||
2225 | + if (!s) | ||
2226 | + goto return_err; | ||
2227 | + | ||
2228 | + fd = socket(PF_INET, SOCK_DGRAM, 0); | ||
2229 | + if (fd < 0) { | ||
2230 | + perror("socket(PF_INET, SOCK_DGRAM)"); | ||
2231 | + goto return_err; | ||
2232 | + } | ||
2233 | + | ||
2234 | + /* There are three types of port definitions | ||
2235 | + * 1) udp:remote_port | ||
2236 | + * Juse use 0.0.0.0 for the IP and send to remote | ||
2237 | + * 2) udp:remote_host:port | ||
2238 | + * Use a IP and send traffic to remote | ||
2239 | + * 3) udp:local_port:remote_host:remote_port | ||
2240 | + * Use local_port as the originator + #2 | ||
2241 | + */ | ||
2242 | + con_type = 0; | ||
2243 | + p = def; | ||
2244 | + while ((p = strchr(p, ':'))) { | ||
2245 | + p++; | ||
2246 | + con_type++; | ||
2247 | + } | ||
2248 | + | ||
2249 | + p = def; | ||
2250 | + memset(&addr,0,sizeof(addr)); | ||
2251 | + addr.sin_family = AF_INET; | ||
2252 | + addr.sin_addr.s_addr = htonl(INADDR_ANY); | ||
2253 | + s->daddr.sin_family = AF_INET; | ||
2254 | + s->daddr.sin_addr.s_addr = htonl(INADDR_ANY); | ||
2255 | + | ||
2256 | + switch (con_type) { | ||
2257 | + case 0: | ||
2258 | + port = strtol(p, (char **)&r, 0); | ||
2259 | + if (r == p) { | ||
2260 | + fprintf(stderr, "Error parsing port number\n"); | ||
2261 | + goto return_err; | ||
2262 | + } | ||
2263 | + s->daddr.sin_port = htons((short)port); | ||
2264 | + break; | ||
2265 | + case 2: | ||
2266 | + port = strtol(p, (char **)&r, 0); | ||
2267 | + if (r == p) { | ||
2268 | + fprintf(stderr, "Error parsing port number\n"); | ||
2269 | + goto return_err; | ||
2270 | + } | ||
2271 | + addr.sin_port = htons((short)port); | ||
2272 | + p = r + 1; | ||
2273 | + /* Fall through to case 1 now that we have the local port */ | ||
2274 | + case 1: | ||
2275 | + if (parse_host_port(&s->daddr, p) < 0) { | ||
2276 | + fprintf(stderr, "Error parsing host name and port\n"); | ||
2277 | + goto return_err; | ||
2278 | + } | ||
2279 | + break; | ||
2280 | + default: | ||
2281 | + fprintf(stderr, "Too many ':' characters\n"); | ||
2282 | + goto return_err; | ||
2283 | + } | ||
2284 | + | ||
2285 | + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) | ||
2286 | + { | ||
2287 | + perror("bind"); | ||
2288 | + goto return_err; | ||
2289 | + } | ||
2290 | + | ||
2291 | + s->fd = fd; | ||
2292 | + s->bufcnt = 0; | ||
2293 | + s->bufptr = 0; | ||
2294 | + chr->opaque = s; | ||
2295 | + chr->chr_write = udp_chr_write; | ||
2296 | + chr->chr_add_read_handler = udp_chr_add_read_handler; | ||
2297 | + return chr; | ||
2298 | + | ||
2299 | +return_err: | ||
2300 | + if (chr) | ||
2301 | + free(chr); | ||
2302 | + if (s) | ||
2303 | + free(s); | ||
2304 | + if (fd >= 0) | ||
2305 | + closesocket(fd); | ||
2306 | + return NULL; | ||
2307 | +} | ||
2308 | + | ||
2309 | +/***********************************************************/ | ||
2310 | +/* TCP Net console */ | ||
2311 | + | ||
2312 | +typedef struct { | ||
2313 | + IOCanRWHandler *fd_can_read; | ||
2314 | + IOReadHandler *fd_read; | ||
2315 | + void *fd_opaque; | ||
2316 | + int fd, listen_fd; | ||
2317 | + int connected; | ||
2318 | + int max_size; | ||
2319 | +} TCPCharDriver; | ||
2320 | + | ||
2321 | +static void tcp_chr_accept(void *opaque); | ||
2322 | + | ||
2323 | +static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) | ||
2324 | +{ | ||
2325 | + TCPCharDriver *s = chr->opaque; | ||
2326 | + if (s->connected) { | ||
2327 | + return send_all(s->fd, buf, len); | ||
2328 | + } else { | ||
2329 | + /* XXX: indicate an error ? */ | ||
2330 | + return len; | ||
2331 | + } | ||
2332 | +} | ||
2333 | + | ||
2334 | +static int tcp_chr_read_poll(void *opaque) | ||
2335 | +{ | ||
2336 | + CharDriverState *chr = opaque; | ||
2337 | + TCPCharDriver *s = chr->opaque; | ||
2338 | + if (!s->connected) | ||
2339 | + return 0; | ||
2340 | + s->max_size = s->fd_can_read(s->fd_opaque); | ||
2341 | + return s->max_size; | ||
2342 | +} | ||
2343 | + | ||
2344 | +static void tcp_chr_read(void *opaque) | ||
2345 | +{ | ||
2346 | + CharDriverState *chr = opaque; | ||
2347 | + TCPCharDriver *s = chr->opaque; | ||
2348 | + uint8_t buf[1024]; | ||
2349 | + int len, size; | ||
2350 | + | ||
2351 | + if (!s->connected || s->max_size <= 0) | ||
2352 | + return; | ||
2353 | + len = sizeof(buf); | ||
2354 | + if (len > s->max_size) | ||
2355 | + len = s->max_size; | ||
2356 | + size = recv(s->fd, buf, len, 0); | ||
2357 | + if (size == 0) { | ||
2358 | + /* connection closed */ | ||
2359 | + s->connected = 0; | ||
2360 | + if (s->listen_fd >= 0) { | ||
2361 | + qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); | ||
2362 | + } | ||
2363 | + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); | ||
2364 | + closesocket(s->fd); | ||
2365 | + s->fd = -1; | ||
2366 | + } else if (size > 0) { | ||
2367 | + s->fd_read(s->fd_opaque, buf, size); | ||
2368 | + } | ||
2369 | +} | ||
2370 | + | ||
2371 | +static void tcp_chr_add_read_handler(CharDriverState *chr, | ||
2372 | + IOCanRWHandler *fd_can_read, | ||
2373 | + IOReadHandler *fd_read, void *opaque) | ||
2374 | +{ | ||
2375 | + TCPCharDriver *s = chr->opaque; | ||
2376 | + | ||
2377 | + s->fd_can_read = fd_can_read; | ||
2378 | + s->fd_read = fd_read; | ||
2379 | + s->fd_opaque = opaque; | ||
2380 | +} | ||
2381 | + | ||
2382 | +static void tcp_chr_connect(void *opaque) | ||
2383 | +{ | ||
2384 | + CharDriverState *chr = opaque; | ||
2385 | + TCPCharDriver *s = chr->opaque; | ||
2386 | + | ||
2387 | + s->connected = 1; | ||
2388 | + qemu_set_fd_handler2(s->fd, tcp_chr_read_poll, | ||
2389 | + tcp_chr_read, NULL, chr); | ||
2390 | +} | ||
2391 | + | ||
2392 | +static void tcp_chr_accept(void *opaque) | ||
2393 | +{ | ||
2394 | + CharDriverState *chr = opaque; | ||
2395 | + TCPCharDriver *s = chr->opaque; | ||
2396 | + struct sockaddr_in saddr; | ||
2397 | + socklen_t len; | ||
2398 | + int fd; | ||
2399 | + | ||
2400 | + for(;;) { | ||
2401 | + len = sizeof(saddr); | ||
2402 | + fd = accept(s->listen_fd, (struct sockaddr *)&saddr, &len); | ||
2403 | + if (fd < 0 && errno != EINTR) { | ||
2404 | + return; | ||
2405 | + } else if (fd >= 0) { | ||
2406 | + break; | ||
2407 | + } | ||
2408 | + } | ||
2409 | + socket_set_nonblock(fd); | ||
2410 | + s->fd = fd; | ||
2411 | + qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); | ||
2412 | + tcp_chr_connect(chr); | ||
2413 | +} | ||
2414 | + | ||
2415 | +static void tcp_chr_close(CharDriverState *chr) | ||
2416 | +{ | ||
2417 | + TCPCharDriver *s = chr->opaque; | ||
2418 | + if (s->fd >= 0) | ||
2419 | + closesocket(s->fd); | ||
2420 | + if (s->listen_fd >= 0) | ||
2421 | + closesocket(s->listen_fd); | ||
2422 | + qemu_free(s); | ||
2423 | +} | ||
2424 | + | ||
2425 | +static CharDriverState *qemu_chr_open_tcp(const char *host_str, | ||
2426 | + int is_listen) | ||
2427 | +{ | ||
2428 | + CharDriverState *chr = NULL; | ||
2429 | + TCPCharDriver *s = NULL; | ||
2430 | + int fd = -1, ret, err, val; | ||
2431 | + struct sockaddr_in saddr; | ||
2432 | + | ||
2433 | + if (parse_host_port(&saddr, host_str) < 0) | ||
2434 | + goto fail; | ||
2435 | + | ||
2436 | + chr = qemu_mallocz(sizeof(CharDriverState)); | ||
2437 | + if (!chr) | ||
2438 | + goto fail; | ||
2439 | + s = qemu_mallocz(sizeof(TCPCharDriver)); | ||
2440 | + if (!s) | ||
2441 | + goto fail; | ||
2442 | + | ||
2443 | + fd = socket(PF_INET, SOCK_STREAM, 0); | ||
2444 | + if (fd < 0) | ||
2445 | + goto fail; | ||
2446 | + socket_set_nonblock(fd); | ||
2447 | + | ||
2448 | + s->connected = 0; | ||
2449 | + s->fd = -1; | ||
2450 | + s->listen_fd = -1; | ||
2451 | + if (is_listen) { | ||
2452 | + /* allow fast reuse */ | ||
2453 | + val = 1; | ||
2454 | + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); | ||
2455 | + | ||
2456 | + ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)); | ||
2457 | + if (ret < 0) | ||
2458 | + goto fail; | ||
2459 | + ret = listen(fd, 0); | ||
2460 | + if (ret < 0) | ||
2461 | + goto fail; | ||
2462 | + s->listen_fd = fd; | ||
2463 | + qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); | ||
2464 | + } else { | ||
2465 | + for(;;) { | ||
2466 | + ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); | ||
2467 | + if (ret < 0) { | ||
2468 | + err = socket_error(); | ||
2469 | + if (err == EINTR || err == EWOULDBLOCK) { | ||
2470 | + } else if (err == EINPROGRESS) { | ||
2471 | + break; | ||
2472 | + } else { | ||
2473 | + goto fail; | ||
2474 | + } | ||
2475 | + } else { | ||
2476 | + s->connected = 1; | ||
2477 | + break; | ||
2478 | + } | ||
2479 | + } | ||
2480 | + s->fd = fd; | ||
2481 | + if (s->connected) | ||
2482 | + tcp_chr_connect(chr); | ||
2483 | + else | ||
2484 | + qemu_set_fd_handler(s->fd, NULL, tcp_chr_connect, chr); | ||
2485 | + } | ||
2486 | + | ||
2487 | + chr->opaque = s; | ||
2488 | + chr->chr_write = tcp_chr_write; | ||
2489 | + chr->chr_add_read_handler = tcp_chr_add_read_handler; | ||
2490 | + chr->chr_close = tcp_chr_close; | ||
2491 | + return chr; | ||
2492 | + fail: | ||
2493 | + if (fd >= 0) | ||
2494 | + closesocket(fd); | ||
2495 | + qemu_free(s); | ||
2496 | + qemu_free(chr); | ||
2497 | + return NULL; | ||
2498 | +} | ||
2499 | + | ||
2133 | CharDriverState *qemu_chr_open(const char *filename) | 2500 | CharDriverState *qemu_chr_open(const char *filename) |
2134 | { | 2501 | { |
2135 | const char *p; | 2502 | const char *p; |
@@ -2139,6 +2506,15 @@ CharDriverState *qemu_chr_open(const char *filename) | @@ -2139,6 +2506,15 @@ CharDriverState *qemu_chr_open(const char *filename) | ||
2139 | } else if (!strcmp(filename, "null")) { | 2506 | } else if (!strcmp(filename, "null")) { |
2140 | return qemu_chr_open_null(); | 2507 | return qemu_chr_open_null(); |
2141 | } else | 2508 | } else |
2509 | + if (strstart(filename, "tcp:", &p)) { | ||
2510 | + return qemu_chr_open_tcp(p, 0); | ||
2511 | + } else | ||
2512 | + if (strstart(filename, "tcpl:", &p)) { | ||
2513 | + return qemu_chr_open_tcp(p, 1); | ||
2514 | + } else | ||
2515 | + if (strstart(filename, "udp:", &p)) { | ||
2516 | + return qemu_chr_open_udp(p); | ||
2517 | + } else | ||
2142 | #ifndef _WIN32 | 2518 | #ifndef _WIN32 |
2143 | if (strstart(filename, "file:", &p)) { | 2519 | if (strstart(filename, "file:", &p)) { |
2144 | return qemu_chr_open_file_out(p); | 2520 | return qemu_chr_open_file_out(p); |
@@ -2844,7 +3220,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) | @@ -2844,7 +3220,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) | ||
2844 | socket_set_nonblock(fd); | 3220 | socket_set_nonblock(fd); |
2845 | return fd; | 3221 | return fd; |
2846 | fail: | 3222 | fail: |
2847 | - if (fd>=0) close(fd); | 3223 | + if (fd >= 0) |
3224 | + closesocket(fd); | ||
2848 | return -1; | 3225 | return -1; |
2849 | } | 3226 | } |
2850 | 3227 | ||
@@ -2972,7 +3349,7 @@ static void net_socket_accept(void *opaque) | @@ -2972,7 +3349,7 @@ static void net_socket_accept(void *opaque) | ||
2972 | } | 3349 | } |
2973 | s1 = net_socket_fd_init(s->vlan, fd, 1); | 3350 | s1 = net_socket_fd_init(s->vlan, fd, 1); |
2974 | if (!s1) { | 3351 | if (!s1) { |
2975 | - close(fd); | 3352 | + closesocket(fd); |
2976 | } else { | 3353 | } else { |
2977 | snprintf(s1->vc->info_str, sizeof(s1->vc->info_str), | 3354 | snprintf(s1->vc->info_str, sizeof(s1->vc->info_str), |
2978 | "socket: connection from %s:%d", | 3355 | "socket: connection from %s:%d", |