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 | 496 | @table @option |
| 497 | 497 | |
| 498 | 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 | 507 | @table @code |
| 502 | 508 | @item vc |
| 503 | 509 | Virtual console |
| ... | ... | @@ -516,13 +522,47 @@ Write output to filename. No character can be read. |
| 516 | 522 | @item stdio |
| 517 | 523 | [Unix only] standard input/output |
| 518 | 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 | 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 | 567 | @item -parallel dev |
| 528 | 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 | 2130 | } |
| 2131 | 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 | 2500 | CharDriverState *qemu_chr_open(const char *filename) |
| 2134 | 2501 | { |
| 2135 | 2502 | const char *p; |
| ... | ... | @@ -2139,6 +2506,15 @@ CharDriverState *qemu_chr_open(const char *filename) |
| 2139 | 2506 | } else if (!strcmp(filename, "null")) { |
| 2140 | 2507 | return qemu_chr_open_null(); |
| 2141 | 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 | 2518 | #ifndef _WIN32 |
| 2143 | 2519 | if (strstart(filename, "file:", &p)) { |
| 2144 | 2520 | return qemu_chr_open_file_out(p); |
| ... | ... | @@ -2844,7 +3220,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) |
| 2844 | 3220 | socket_set_nonblock(fd); |
| 2845 | 3221 | return fd; |
| 2846 | 3222 | fail: |
| 2847 | - if (fd>=0) close(fd); | |
| 3223 | + if (fd >= 0) | |
| 3224 | + closesocket(fd); | |
| 2848 | 3225 | return -1; |
| 2849 | 3226 | } |
| 2850 | 3227 | |
| ... | ... | @@ -2972,7 +3349,7 @@ static void net_socket_accept(void *opaque) |
| 2972 | 3349 | } |
| 2973 | 3350 | s1 = net_socket_fd_init(s->vlan, fd, 1); |
| 2974 | 3351 | if (!s1) { |
| 2975 | - close(fd); | |
| 3352 | + closesocket(fd); | |
| 2976 | 3353 | } else { |
| 2977 | 3354 | snprintf(s1->vc->info_str, sizeof(s1->vc->info_str), |
| 2978 | 3355 | "socket: connection from %s:%d", | ... | ... |