1 /* 2 * QEMU I/O channels sockets driver 3 * 4 * Copyright (c) 2015 Red Hat, Inc. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 #include "qemu/osdep.h" 22 #include "qapi/error.h" 23 #include "io/channel-socket.h" 24 #include "io/channel-watch.h" 25 #include "trace.h" 26 27 #define SOCKET_MAX_FDS 16 28 29 SocketAddress * 30 qio_channel_socket_get_local_address(QIOChannelSocket *ioc, 31 Error **errp) 32 { 33 return socket_sockaddr_to_address(&ioc->localAddr, 34 ioc->localAddrLen, 35 errp); 36 } 37 38 SocketAddress * 39 qio_channel_socket_get_remote_address(QIOChannelSocket *ioc, 40 Error **errp) 41 { 42 return socket_sockaddr_to_address(&ioc->remoteAddr, 43 ioc->remoteAddrLen, 44 errp); 45 } 46 47 QIOChannelSocket * 48 qio_channel_socket_new(void) 49 { 50 QIOChannelSocket *sioc; 51 QIOChannel *ioc; 52 53 sioc = QIO_CHANNEL_SOCKET(object_new(TYPE_QIO_CHANNEL_SOCKET)); 54 sioc->fd = -1; 55 56 ioc = QIO_CHANNEL(sioc); 57 ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN); 58 59 #ifdef WIN32 60 ioc->event = CreateEvent(NULL, FALSE, FALSE, NULL); 61 #endif 62 63 trace_qio_channel_socket_new(sioc); 64 65 return sioc; 66 } 67 68 69 static int 70 qio_channel_socket_set_fd(QIOChannelSocket *sioc, 71 int fd, 72 Error **errp) 73 { 74 if (sioc->fd != -1) { 75 error_setg(errp, "Socket is already open"); 76 return -1; 77 } 78 79 sioc->fd = fd; 80 sioc->remoteAddrLen = sizeof(sioc->remoteAddr); 81 sioc->localAddrLen = sizeof(sioc->localAddr); 82 83 84 if (getpeername(fd, (struct sockaddr *)&sioc->remoteAddr, 85 &sioc->remoteAddrLen) < 0) { 86 if (errno == ENOTCONN) { 87 memset(&sioc->remoteAddr, 0, sizeof(sioc->remoteAddr)); 88 sioc->remoteAddrLen = sizeof(sioc->remoteAddr); 89 } else { 90 error_setg_errno(errp, errno, 91 "Unable to query remote socket address"); 92 goto error; 93 } 94 } 95 96 if (getsockname(fd, (struct sockaddr *)&sioc->localAddr, 97 &sioc->localAddrLen) < 0) { 98 error_setg_errno(errp, errno, 99 "Unable to query local socket address"); 100 goto error; 101 } 102 103 #ifndef WIN32 104 if (sioc->localAddr.ss_family == AF_UNIX) { 105 QIOChannel *ioc = QIO_CHANNEL(sioc); 106 ioc->features |= (1 << QIO_CHANNEL_FEATURE_FD_PASS); 107 } 108 #endif /* WIN32 */ 109 110 return 0; 111 112 error: 113 sioc->fd = -1; /* Let the caller close FD on failure */ 114 return -1; 115 } 116 117 QIOChannelSocket * 118 qio_channel_socket_new_fd(int fd, 119 Error **errp) 120 { 121 QIOChannelSocket *ioc; 122 123 ioc = qio_channel_socket_new(); 124 if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { 125 object_unref(OBJECT(ioc)); 126 return NULL; 127 } 128 129 trace_qio_channel_socket_new_fd(ioc, fd); 130 131 return ioc; 132 } 133 134 135 int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, 136 SocketAddress *addr, 137 Error **errp) 138 { 139 int fd; 140 141 trace_qio_channel_socket_connect_sync(ioc, addr); 142 fd = socket_connect(addr, errp, NULL, NULL); 143 if (fd < 0) { 144 trace_qio_channel_socket_connect_fail(ioc); 145 return -1; 146 } 147 148 trace_qio_channel_socket_connect_complete(ioc, fd); 149 if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { 150 close(fd); 151 return -1; 152 } 153 154 return 0; 155 } 156 157 158 static int qio_channel_socket_connect_worker(QIOTask *task, 159 Error **errp, 160 gpointer opaque) 161 { 162 QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); 163 SocketAddress *addr = opaque; 164 int ret; 165 166 ret = qio_channel_socket_connect_sync(ioc, 167 addr, 168 errp); 169 170 object_unref(OBJECT(ioc)); 171 return ret; 172 } 173 174 175 void qio_channel_socket_connect_async(QIOChannelSocket *ioc, 176 SocketAddress *addr, 177 QIOTaskFunc callback, 178 gpointer opaque, 179 GDestroyNotify destroy) 180 { 181 QIOTask *task = qio_task_new( 182 OBJECT(ioc), callback, opaque, destroy); 183 SocketAddress *addrCopy; 184 185 qapi_copy_SocketAddress(&addrCopy, addr); 186 187 /* socket_connect() does a non-blocking connect(), but it 188 * still blocks in DNS lookups, so we must use a thread */ 189 trace_qio_channel_socket_connect_async(ioc, addr); 190 qio_task_run_in_thread(task, 191 qio_channel_socket_connect_worker, 192 addrCopy, 193 (GDestroyNotify)qapi_free_SocketAddress); 194 } 195 196 197 int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, 198 SocketAddress *addr, 199 Error **errp) 200 { 201 int fd; 202 203 trace_qio_channel_socket_listen_sync(ioc, addr); 204 fd = socket_listen(addr, errp); 205 if (fd < 0) { 206 trace_qio_channel_socket_listen_fail(ioc); 207 return -1; 208 } 209 210 trace_qio_channel_socket_listen_complete(ioc, fd); 211 if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { 212 close(fd); 213 return -1; 214 } 215 216 return 0; 217 } 218 219 220 static int qio_channel_socket_listen_worker(QIOTask *task, 221 Error **errp, 222 gpointer opaque) 223 { 224 QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); 225 SocketAddress *addr = opaque; 226 int ret; 227 228 ret = qio_channel_socket_listen_sync(ioc, 229 addr, 230 errp); 231 232 object_unref(OBJECT(ioc)); 233 return ret; 234 } 235 236 237 void qio_channel_socket_listen_async(QIOChannelSocket *ioc, 238 SocketAddress *addr, 239 QIOTaskFunc callback, 240 gpointer opaque, 241 GDestroyNotify destroy) 242 { 243 QIOTask *task = qio_task_new( 244 OBJECT(ioc), callback, opaque, destroy); 245 SocketAddress *addrCopy; 246 247 qapi_copy_SocketAddress(&addrCopy, addr); 248 249 /* socket_listen() blocks in DNS lookups, so we must use a thread */ 250 trace_qio_channel_socket_listen_async(ioc, addr); 251 qio_task_run_in_thread(task, 252 qio_channel_socket_listen_worker, 253 addrCopy, 254 (GDestroyNotify)qapi_free_SocketAddress); 255 } 256 257 258 int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc, 259 SocketAddress *localAddr, 260 SocketAddress *remoteAddr, 261 Error **errp) 262 { 263 int fd; 264 265 trace_qio_channel_socket_dgram_sync(ioc, localAddr, remoteAddr); 266 fd = socket_dgram(remoteAddr, localAddr, errp); 267 if (fd < 0) { 268 trace_qio_channel_socket_dgram_fail(ioc); 269 return -1; 270 } 271 272 trace_qio_channel_socket_dgram_complete(ioc, fd); 273 if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { 274 close(fd); 275 return -1; 276 } 277 278 return 0; 279 } 280 281 282 struct QIOChannelSocketDGramWorkerData { 283 SocketAddress *localAddr; 284 SocketAddress *remoteAddr; 285 }; 286 287 288 static void qio_channel_socket_dgram_worker_free(gpointer opaque) 289 { 290 struct QIOChannelSocketDGramWorkerData *data = opaque; 291 qapi_free_SocketAddress(data->localAddr); 292 qapi_free_SocketAddress(data->remoteAddr); 293 g_free(data); 294 } 295 296 static int qio_channel_socket_dgram_worker(QIOTask *task, 297 Error **errp, 298 gpointer opaque) 299 { 300 QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); 301 struct QIOChannelSocketDGramWorkerData *data = opaque; 302 int ret; 303 304 /* socket_dgram() blocks in DNS lookups, so we must use a thread */ 305 ret = qio_channel_socket_dgram_sync(ioc, 306 data->localAddr, 307 data->remoteAddr, 308 errp); 309 310 object_unref(OBJECT(ioc)); 311 return ret; 312 } 313 314 315 void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, 316 SocketAddress *localAddr, 317 SocketAddress *remoteAddr, 318 QIOTaskFunc callback, 319 gpointer opaque, 320 GDestroyNotify destroy) 321 { 322 QIOTask *task = qio_task_new( 323 OBJECT(ioc), callback, opaque, destroy); 324 struct QIOChannelSocketDGramWorkerData *data = g_new0( 325 struct QIOChannelSocketDGramWorkerData, 1); 326 327 qapi_copy_SocketAddress(&data->localAddr, localAddr); 328 qapi_copy_SocketAddress(&data->remoteAddr, remoteAddr); 329 330 trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr); 331 qio_task_run_in_thread(task, 332 qio_channel_socket_dgram_worker, 333 data, 334 qio_channel_socket_dgram_worker_free); 335 } 336 337 338 QIOChannelSocket * 339 qio_channel_socket_accept(QIOChannelSocket *ioc, 340 Error **errp) 341 { 342 QIOChannelSocket *cioc; 343 344 cioc = QIO_CHANNEL_SOCKET(object_new(TYPE_QIO_CHANNEL_SOCKET)); 345 cioc->fd = -1; 346 cioc->remoteAddrLen = sizeof(ioc->remoteAddr); 347 cioc->localAddrLen = sizeof(ioc->localAddr); 348 349 #ifdef WIN32 350 QIO_CHANNEL(cioc)->event = CreateEvent(NULL, FALSE, FALSE, NULL); 351 #endif 352 353 354 retry: 355 trace_qio_channel_socket_accept(ioc); 356 cioc->fd = qemu_accept(ioc->fd, (struct sockaddr *)&cioc->remoteAddr, 357 &cioc->remoteAddrLen); 358 if (cioc->fd < 0) { 359 trace_qio_channel_socket_accept_fail(ioc); 360 if (errno == EINTR) { 361 goto retry; 362 } 363 goto error; 364 } 365 366 if (getsockname(cioc->fd, (struct sockaddr *)&cioc->localAddr, 367 &cioc->localAddrLen) < 0) { 368 error_setg_errno(errp, errno, 369 "Unable to query local socket address"); 370 goto error; 371 } 372 373 #ifndef WIN32 374 if (cioc->localAddr.ss_family == AF_UNIX) { 375 QIO_CHANNEL(cioc)->features |= (1 << QIO_CHANNEL_FEATURE_FD_PASS); 376 } 377 #endif /* WIN32 */ 378 379 trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd); 380 return cioc; 381 382 error: 383 object_unref(OBJECT(cioc)); 384 return NULL; 385 } 386 387 static void qio_channel_socket_init(Object *obj) 388 { 389 QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj); 390 ioc->fd = -1; 391 } 392 393 static void qio_channel_socket_finalize(Object *obj) 394 { 395 QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj); 396 if (ioc->fd != -1) { 397 #ifdef WIN32 398 WSAEventSelect(ioc->fd, NULL, 0); 399 #endif 400 closesocket(ioc->fd); 401 ioc->fd = -1; 402 } 403 } 404 405 406 #ifndef WIN32 407 static void qio_channel_socket_copy_fds(struct msghdr *msg, 408 int **fds, size_t *nfds) 409 { 410 struct cmsghdr *cmsg; 411 412 *nfds = 0; 413 *fds = NULL; 414 415 for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { 416 int fd_size, i; 417 int gotfds; 418 419 if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) || 420 cmsg->cmsg_level != SOL_SOCKET || 421 cmsg->cmsg_type != SCM_RIGHTS) { 422 continue; 423 } 424 425 fd_size = cmsg->cmsg_len - CMSG_LEN(0); 426 427 if (!fd_size) { 428 continue; 429 } 430 431 gotfds = fd_size / sizeof(int); 432 *fds = g_renew(int, *fds, *nfds + gotfds); 433 memcpy(*fds + *nfds, CMSG_DATA(cmsg), fd_size); 434 435 for (i = 0; i < gotfds; i++) { 436 int fd = (*fds)[*nfds + i]; 437 if (fd < 0) { 438 continue; 439 } 440 441 /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ 442 qemu_set_block(fd); 443 444 #ifndef MSG_CMSG_CLOEXEC 445 qemu_set_cloexec(fd); 446 #endif 447 } 448 *nfds += gotfds; 449 } 450 } 451 452 453 static ssize_t qio_channel_socket_readv(QIOChannel *ioc, 454 const struct iovec *iov, 455 size_t niov, 456 int **fds, 457 size_t *nfds, 458 Error **errp) 459 { 460 QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); 461 ssize_t ret; 462 struct msghdr msg = { NULL, }; 463 char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)]; 464 int sflags = 0; 465 466 memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)); 467 468 #ifdef MSG_CMSG_CLOEXEC 469 sflags |= MSG_CMSG_CLOEXEC; 470 #endif 471 472 msg.msg_iov = (struct iovec *)iov; 473 msg.msg_iovlen = niov; 474 if (fds && nfds) { 475 msg.msg_control = control; 476 msg.msg_controllen = sizeof(control); 477 } 478 479 retry: 480 ret = recvmsg(sioc->fd, &msg, sflags); 481 if (ret < 0) { 482 if (errno == EAGAIN) { 483 return QIO_CHANNEL_ERR_BLOCK; 484 } 485 if (errno == EINTR) { 486 goto retry; 487 } 488 489 error_setg_errno(errp, errno, 490 "Unable to read from socket"); 491 return -1; 492 } 493 494 if (fds && nfds) { 495 qio_channel_socket_copy_fds(&msg, fds, nfds); 496 } 497 498 return ret; 499 } 500 501 static ssize_t qio_channel_socket_writev(QIOChannel *ioc, 502 const struct iovec *iov, 503 size_t niov, 504 int *fds, 505 size_t nfds, 506 Error **errp) 507 { 508 QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); 509 ssize_t ret; 510 struct msghdr msg = { NULL, }; 511 char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)]; 512 size_t fdsize = sizeof(int) * nfds; 513 struct cmsghdr *cmsg; 514 515 memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)); 516 517 msg.msg_iov = (struct iovec *)iov; 518 msg.msg_iovlen = niov; 519 520 if (nfds) { 521 if (nfds > SOCKET_MAX_FDS) { 522 error_setg_errno(errp, EINVAL, 523 "Only %d FDs can be sent, got %zu", 524 SOCKET_MAX_FDS, nfds); 525 return -1; 526 } 527 528 msg.msg_control = control; 529 msg.msg_controllen = CMSG_SPACE(sizeof(int) * nfds); 530 531 cmsg = CMSG_FIRSTHDR(&msg); 532 cmsg->cmsg_len = CMSG_LEN(fdsize); 533 cmsg->cmsg_level = SOL_SOCKET; 534 cmsg->cmsg_type = SCM_RIGHTS; 535 memcpy(CMSG_DATA(cmsg), fds, fdsize); 536 } 537 538 retry: 539 ret = sendmsg(sioc->fd, &msg, 0); 540 if (ret <= 0) { 541 if (errno == EAGAIN) { 542 return QIO_CHANNEL_ERR_BLOCK; 543 } 544 if (errno == EINTR) { 545 goto retry; 546 } 547 error_setg_errno(errp, errno, 548 "Unable to write to socket"); 549 return -1; 550 } 551 return ret; 552 } 553 #else /* WIN32 */ 554 static ssize_t qio_channel_socket_readv(QIOChannel *ioc, 555 const struct iovec *iov, 556 size_t niov, 557 int **fds, 558 size_t *nfds, 559 Error **errp) 560 { 561 QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); 562 ssize_t done = 0; 563 ssize_t i; 564 565 for (i = 0; i < niov; i++) { 566 ssize_t ret; 567 retry: 568 ret = recv(sioc->fd, 569 iov[i].iov_base, 570 iov[i].iov_len, 571 0); 572 if (ret < 0) { 573 if (errno == EAGAIN) { 574 if (done) { 575 return done; 576 } else { 577 return QIO_CHANNEL_ERR_BLOCK; 578 } 579 } else if (errno == EINTR) { 580 goto retry; 581 } else { 582 error_setg_errno(errp, errno, 583 "Unable to read from socket"); 584 return -1; 585 } 586 } 587 done += ret; 588 if (ret < iov[i].iov_len) { 589 return done; 590 } 591 } 592 593 return done; 594 } 595 596 static ssize_t qio_channel_socket_writev(QIOChannel *ioc, 597 const struct iovec *iov, 598 size_t niov, 599 int *fds, 600 size_t nfds, 601 Error **errp) 602 { 603 QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); 604 ssize_t done = 0; 605 ssize_t i; 606 607 for (i = 0; i < niov; i++) { 608 ssize_t ret; 609 retry: 610 ret = send(sioc->fd, 611 iov[i].iov_base, 612 iov[i].iov_len, 613 0); 614 if (ret < 0) { 615 if (errno == EAGAIN) { 616 if (done) { 617 return done; 618 } else { 619 return QIO_CHANNEL_ERR_BLOCK; 620 } 621 } else if (errno == EINTR) { 622 goto retry; 623 } else { 624 error_setg_errno(errp, errno, 625 "Unable to write to socket"); 626 return -1; 627 } 628 } 629 done += ret; 630 if (ret < iov[i].iov_len) { 631 return done; 632 } 633 } 634 635 return done; 636 } 637 #endif /* WIN32 */ 638 639 static int 640 qio_channel_socket_set_blocking(QIOChannel *ioc, 641 bool enabled, 642 Error **errp) 643 { 644 QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); 645 646 if (enabled) { 647 qemu_set_block(sioc->fd); 648 } else { 649 qemu_set_nonblock(sioc->fd); 650 #ifdef WIN32 651 WSAEventSelect(sioc->fd, ioc->event, 652 FD_READ | FD_ACCEPT | FD_CLOSE | 653 FD_CONNECT | FD_WRITE | FD_OOB); 654 #endif 655 } 656 return 0; 657 } 658 659 660 static void 661 qio_channel_socket_set_delay(QIOChannel *ioc, 662 bool enabled) 663 { 664 QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); 665 int v = enabled ? 0 : 1; 666 667 qemu_setsockopt(sioc->fd, 668 IPPROTO_TCP, TCP_NODELAY, 669 &v, sizeof(v)); 670 } 671 672 673 static void 674 qio_channel_socket_set_cork(QIOChannel *ioc, 675 bool enabled) 676 { 677 QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); 678 int v = enabled ? 1 : 0; 679 680 socket_set_cork(sioc->fd, v); 681 } 682 683 684 static int 685 qio_channel_socket_close(QIOChannel *ioc, 686 Error **errp) 687 { 688 QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); 689 690 if (sioc->fd != -1) { 691 #ifdef WIN32 692 WSAEventSelect(sioc->fd, NULL, 0); 693 #endif 694 if (closesocket(sioc->fd) < 0) { 695 sioc->fd = -1; 696 error_setg_errno(errp, errno, 697 "Unable to close socket"); 698 return -1; 699 } 700 sioc->fd = -1; 701 } 702 return 0; 703 } 704 705 static int 706 qio_channel_socket_shutdown(QIOChannel *ioc, 707 QIOChannelShutdown how, 708 Error **errp) 709 { 710 QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); 711 int sockhow; 712 713 switch (how) { 714 case QIO_CHANNEL_SHUTDOWN_READ: 715 sockhow = SHUT_RD; 716 break; 717 case QIO_CHANNEL_SHUTDOWN_WRITE: 718 sockhow = SHUT_WR; 719 break; 720 case QIO_CHANNEL_SHUTDOWN_BOTH: 721 default: 722 sockhow = SHUT_RDWR; 723 break; 724 } 725 726 if (shutdown(sioc->fd, sockhow) < 0) { 727 error_setg_errno(errp, errno, 728 "Unable to shutdown socket"); 729 return -1; 730 } 731 return 0; 732 } 733 734 static GSource *qio_channel_socket_create_watch(QIOChannel *ioc, 735 GIOCondition condition) 736 { 737 QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); 738 return qio_channel_create_socket_watch(ioc, 739 sioc->fd, 740 condition); 741 } 742 743 static void qio_channel_socket_class_init(ObjectClass *klass, 744 void *class_data G_GNUC_UNUSED) 745 { 746 QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); 747 748 ioc_klass->io_writev = qio_channel_socket_writev; 749 ioc_klass->io_readv = qio_channel_socket_readv; 750 ioc_klass->io_set_blocking = qio_channel_socket_set_blocking; 751 ioc_klass->io_close = qio_channel_socket_close; 752 ioc_klass->io_shutdown = qio_channel_socket_shutdown; 753 ioc_klass->io_set_cork = qio_channel_socket_set_cork; 754 ioc_klass->io_set_delay = qio_channel_socket_set_delay; 755 ioc_klass->io_create_watch = qio_channel_socket_create_watch; 756 } 757 758 static const TypeInfo qio_channel_socket_info = { 759 .parent = TYPE_QIO_CHANNEL, 760 .name = TYPE_QIO_CHANNEL_SOCKET, 761 .instance_size = sizeof(QIOChannelSocket), 762 .instance_init = qio_channel_socket_init, 763 .instance_finalize = qio_channel_socket_finalize, 764 .class_init = qio_channel_socket_class_init, 765 }; 766 767 static void qio_channel_socket_register_types(void) 768 { 769 type_register_static(&qio_channel_socket_info); 770 } 771 772 type_init(qio_channel_socket_register_types); 773