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