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", | ... | ... |