2 * QEMU I/O channels sockets driver
4 * Copyright (c) 2015 Red Hat, Inc.
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.1 of the License, or (at your option) any later version.
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.
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/>.
20 #include "qemu/osdep.h"
21 #include "qapi/error.h"
22 #include "qapi/qapi-visit-sockets.h"
23 #include "qemu/module.h"
24 #include "io/channel-socket.h"
25 #include "io/channel-watch.h"
27 #include "qapi/clone-visitor.h"
29 #include <linux/errqueue.h>
30 #include <sys/socket.h>
32 #if (defined(MSG_ZEROCOPY) && defined(SO_ZEROCOPY))
33 #define QEMU_MSG_ZEROCOPY
37 #define SOCKET_MAX_FDS 16
40 qio_channel_socket_get_local_address(QIOChannelSocket
*ioc
,
43 return socket_sockaddr_to_address(&ioc
->localAddr
,
49 qio_channel_socket_get_remote_address(QIOChannelSocket
*ioc
,
52 return socket_sockaddr_to_address(&ioc
->remoteAddr
,
58 qio_channel_socket_new(void)
60 QIOChannelSocket
*sioc
;
63 sioc
= QIO_CHANNEL_SOCKET(object_new(TYPE_QIO_CHANNEL_SOCKET
));
65 sioc
->zero_copy_queued
= 0;
66 sioc
->zero_copy_sent
= 0;
68 ioc
= QIO_CHANNEL(sioc
);
69 qio_channel_set_feature(ioc
, QIO_CHANNEL_FEATURE_SHUTDOWN
);
72 ioc
->event
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
75 trace_qio_channel_socket_new(sioc
);
82 qio_channel_socket_set_fd(QIOChannelSocket
*sioc
,
87 error_setg(errp
, "Socket is already open");
92 sioc
->remoteAddrLen
= sizeof(sioc
->remoteAddr
);
93 sioc
->localAddrLen
= sizeof(sioc
->localAddr
);
96 if (getpeername(fd
, (struct sockaddr
*)&sioc
->remoteAddr
,
97 &sioc
->remoteAddrLen
) < 0) {
98 if (errno
== ENOTCONN
) {
99 memset(&sioc
->remoteAddr
, 0, sizeof(sioc
->remoteAddr
));
100 sioc
->remoteAddrLen
= sizeof(sioc
->remoteAddr
);
102 error_setg_errno(errp
, errno
,
103 "Unable to query remote socket address");
108 if (getsockname(fd
, (struct sockaddr
*)&sioc
->localAddr
,
109 &sioc
->localAddrLen
) < 0) {
110 error_setg_errno(errp
, errno
,
111 "Unable to query local socket address");
116 if (sioc
->localAddr
.ss_family
== AF_UNIX
) {
117 QIOChannel
*ioc
= QIO_CHANNEL(sioc
);
118 qio_channel_set_feature(ioc
, QIO_CHANNEL_FEATURE_FD_PASS
);
125 sioc
->fd
= -1; /* Let the caller close FD on failure */
130 qio_channel_socket_new_fd(int fd
,
133 QIOChannelSocket
*ioc
;
135 ioc
= qio_channel_socket_new();
136 if (qio_channel_socket_set_fd(ioc
, fd
, errp
) < 0) {
137 object_unref(OBJECT(ioc
));
141 trace_qio_channel_socket_new_fd(ioc
, fd
);
147 int qio_channel_socket_connect_sync(QIOChannelSocket
*ioc
,
153 trace_qio_channel_socket_connect_sync(ioc
, addr
);
154 fd
= socket_connect(addr
, errp
);
156 trace_qio_channel_socket_connect_fail(ioc
);
160 trace_qio_channel_socket_connect_complete(ioc
, fd
);
161 if (qio_channel_socket_set_fd(ioc
, fd
, errp
) < 0) {
166 #ifdef QEMU_MSG_ZEROCOPY
168 ret
= setsockopt(fd
, SOL_SOCKET
, SO_ZEROCOPY
, &v
, sizeof(v
));
170 /* Zero copy available on host */
171 qio_channel_set_feature(QIO_CHANNEL(ioc
),
172 QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY
);
176 qio_channel_set_feature(QIO_CHANNEL(ioc
),
177 QIO_CHANNEL_FEATURE_READ_MSG_PEEK
);
183 static void qio_channel_socket_connect_worker(QIOTask
*task
,
186 QIOChannelSocket
*ioc
= QIO_CHANNEL_SOCKET(qio_task_get_source(task
));
187 SocketAddress
*addr
= opaque
;
190 qio_channel_socket_connect_sync(ioc
, addr
, &err
);
192 qio_task_set_error(task
, err
);
196 void qio_channel_socket_connect_async(QIOChannelSocket
*ioc
,
198 QIOTaskFunc callback
,
200 GDestroyNotify destroy
,
201 GMainContext
*context
)
203 QIOTask
*task
= qio_task_new(
204 OBJECT(ioc
), callback
, opaque
, destroy
);
205 SocketAddress
*addrCopy
;
207 addrCopy
= QAPI_CLONE(SocketAddress
, addr
);
209 /* socket_connect() does a non-blocking connect(), but it
210 * still blocks in DNS lookups, so we must use a thread */
211 trace_qio_channel_socket_connect_async(ioc
, addr
);
212 qio_task_run_in_thread(task
,
213 qio_channel_socket_connect_worker
,
215 (GDestroyNotify
)qapi_free_SocketAddress
,
220 int qio_channel_socket_listen_sync(QIOChannelSocket
*ioc
,
227 trace_qio_channel_socket_listen_sync(ioc
, addr
, num
);
228 fd
= socket_listen(addr
, num
, errp
);
230 trace_qio_channel_socket_listen_fail(ioc
);
234 trace_qio_channel_socket_listen_complete(ioc
, fd
);
235 if (qio_channel_socket_set_fd(ioc
, fd
, errp
) < 0) {
239 qio_channel_set_feature(QIO_CHANNEL(ioc
), QIO_CHANNEL_FEATURE_LISTEN
);
245 struct QIOChannelListenWorkerData
{
247 int num
; /* amount of expected connections */
250 static void qio_channel_listen_worker_free(gpointer opaque
)
252 struct QIOChannelListenWorkerData
*data
= opaque
;
254 qapi_free_SocketAddress(data
->addr
);
258 static void qio_channel_socket_listen_worker(QIOTask
*task
,
261 QIOChannelSocket
*ioc
= QIO_CHANNEL_SOCKET(qio_task_get_source(task
));
262 struct QIOChannelListenWorkerData
*data
= opaque
;
265 qio_channel_socket_listen_sync(ioc
, data
->addr
, data
->num
, &err
);
267 qio_task_set_error(task
, err
);
271 void qio_channel_socket_listen_async(QIOChannelSocket
*ioc
,
274 QIOTaskFunc callback
,
276 GDestroyNotify destroy
,
277 GMainContext
*context
)
279 QIOTask
*task
= qio_task_new(
280 OBJECT(ioc
), callback
, opaque
, destroy
);
281 struct QIOChannelListenWorkerData
*data
;
283 data
= g_new0(struct QIOChannelListenWorkerData
, 1);
284 data
->addr
= QAPI_CLONE(SocketAddress
, addr
);
287 /* socket_listen() blocks in DNS lookups, so we must use a thread */
288 trace_qio_channel_socket_listen_async(ioc
, addr
, num
);
289 qio_task_run_in_thread(task
,
290 qio_channel_socket_listen_worker
,
292 qio_channel_listen_worker_free
,
297 int qio_channel_socket_dgram_sync(QIOChannelSocket
*ioc
,
298 SocketAddress
*localAddr
,
299 SocketAddress
*remoteAddr
,
304 trace_qio_channel_socket_dgram_sync(ioc
, localAddr
, remoteAddr
);
305 fd
= socket_dgram(remoteAddr
, localAddr
, errp
);
307 trace_qio_channel_socket_dgram_fail(ioc
);
311 trace_qio_channel_socket_dgram_complete(ioc
, fd
);
312 if (qio_channel_socket_set_fd(ioc
, fd
, errp
) < 0) {
321 struct QIOChannelSocketDGramWorkerData
{
322 SocketAddress
*localAddr
;
323 SocketAddress
*remoteAddr
;
327 static void qio_channel_socket_dgram_worker_free(gpointer opaque
)
329 struct QIOChannelSocketDGramWorkerData
*data
= opaque
;
330 qapi_free_SocketAddress(data
->localAddr
);
331 qapi_free_SocketAddress(data
->remoteAddr
);
335 static void qio_channel_socket_dgram_worker(QIOTask
*task
,
338 QIOChannelSocket
*ioc
= QIO_CHANNEL_SOCKET(qio_task_get_source(task
));
339 struct QIOChannelSocketDGramWorkerData
*data
= opaque
;
342 /* socket_dgram() blocks in DNS lookups, so we must use a thread */
343 qio_channel_socket_dgram_sync(ioc
, data
->localAddr
,
344 data
->remoteAddr
, &err
);
346 qio_task_set_error(task
, err
);
350 void qio_channel_socket_dgram_async(QIOChannelSocket
*ioc
,
351 SocketAddress
*localAddr
,
352 SocketAddress
*remoteAddr
,
353 QIOTaskFunc callback
,
355 GDestroyNotify destroy
,
356 GMainContext
*context
)
358 QIOTask
*task
= qio_task_new(
359 OBJECT(ioc
), callback
, opaque
, destroy
);
360 struct QIOChannelSocketDGramWorkerData
*data
= g_new0(
361 struct QIOChannelSocketDGramWorkerData
, 1);
363 data
->localAddr
= QAPI_CLONE(SocketAddress
, localAddr
);
364 data
->remoteAddr
= QAPI_CLONE(SocketAddress
, remoteAddr
);
366 trace_qio_channel_socket_dgram_async(ioc
, localAddr
, remoteAddr
);
367 qio_task_run_in_thread(task
,
368 qio_channel_socket_dgram_worker
,
370 qio_channel_socket_dgram_worker_free
,
376 qio_channel_socket_accept(QIOChannelSocket
*ioc
,
379 QIOChannelSocket
*cioc
;
381 cioc
= qio_channel_socket_new();
382 cioc
->remoteAddrLen
= sizeof(ioc
->remoteAddr
);
383 cioc
->localAddrLen
= sizeof(ioc
->localAddr
);
386 trace_qio_channel_socket_accept(ioc
);
387 cioc
->fd
= qemu_accept(ioc
->fd
, (struct sockaddr
*)&cioc
->remoteAddr
,
388 &cioc
->remoteAddrLen
);
390 if (errno
== EINTR
) {
393 error_setg_errno(errp
, errno
, "Unable to accept connection");
394 trace_qio_channel_socket_accept_fail(ioc
);
398 if (getsockname(cioc
->fd
, (struct sockaddr
*)&cioc
->localAddr
,
399 &cioc
->localAddrLen
) < 0) {
400 error_setg_errno(errp
, errno
,
401 "Unable to query local socket address");
406 if (cioc
->localAddr
.ss_family
== AF_UNIX
) {
407 QIOChannel
*ioc_local
= QIO_CHANNEL(cioc
);
408 qio_channel_set_feature(ioc_local
, QIO_CHANNEL_FEATURE_FD_PASS
);
412 qio_channel_set_feature(QIO_CHANNEL(cioc
),
413 QIO_CHANNEL_FEATURE_READ_MSG_PEEK
);
415 trace_qio_channel_socket_accept_complete(ioc
, cioc
, cioc
->fd
);
419 object_unref(OBJECT(cioc
));
423 static void qio_channel_socket_init(Object
*obj
)
425 QIOChannelSocket
*ioc
= QIO_CHANNEL_SOCKET(obj
);
429 static void qio_channel_socket_finalize(Object
*obj
)
431 QIOChannelSocket
*ioc
= QIO_CHANNEL_SOCKET(obj
);
434 QIOChannel
*ioc_local
= QIO_CHANNEL(ioc
);
435 if (qio_channel_has_feature(ioc_local
, QIO_CHANNEL_FEATURE_LISTEN
)) {
438 socket_listen_cleanup(ioc
->fd
, &err
);
440 error_report_err(err
);
445 qemu_socket_unselect(ioc
->fd
, NULL
);
454 static void qio_channel_socket_copy_fds(struct msghdr
*msg
,
455 int **fds
, size_t *nfds
)
457 struct cmsghdr
*cmsg
;
462 for (cmsg
= CMSG_FIRSTHDR(msg
); cmsg
; cmsg
= CMSG_NXTHDR(msg
, cmsg
)) {
466 if (cmsg
->cmsg_len
< CMSG_LEN(sizeof(int)) ||
467 cmsg
->cmsg_level
!= SOL_SOCKET
||
468 cmsg
->cmsg_type
!= SCM_RIGHTS
) {
472 fd_size
= cmsg
->cmsg_len
- CMSG_LEN(0);
478 gotfds
= fd_size
/ sizeof(int);
479 *fds
= g_renew(int, *fds
, *nfds
+ gotfds
);
480 memcpy(*fds
+ *nfds
, CMSG_DATA(cmsg
), fd_size
);
482 for (i
= 0; i
< gotfds
; i
++) {
483 int fd
= (*fds
)[*nfds
+ i
];
488 /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */
489 qemu_socket_set_block(fd
);
491 #ifndef MSG_CMSG_CLOEXEC
492 qemu_set_cloexec(fd
);
500 static ssize_t
qio_channel_socket_readv(QIOChannel
*ioc
,
501 const struct iovec
*iov
,
508 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
510 struct msghdr msg
= { NULL
, };
511 char control
[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS
)];
514 memset(control
, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS
));
516 msg
.msg_iov
= (struct iovec
*)iov
;
517 msg
.msg_iovlen
= niov
;
519 msg
.msg_control
= control
;
520 msg
.msg_controllen
= sizeof(control
);
521 #ifdef MSG_CMSG_CLOEXEC
522 sflags
|= MSG_CMSG_CLOEXEC
;
527 if (flags
& QIO_CHANNEL_READ_FLAG_MSG_PEEK
) {
532 ret
= recvmsg(sioc
->fd
, &msg
, sflags
);
534 if (errno
== EAGAIN
) {
535 return QIO_CHANNEL_ERR_BLOCK
;
537 if (errno
== EINTR
) {
541 error_setg_errno(errp
, errno
,
542 "Unable to read from socket");
547 qio_channel_socket_copy_fds(&msg
, fds
, nfds
);
553 static ssize_t
qio_channel_socket_writev(QIOChannel
*ioc
,
554 const struct iovec
*iov
,
561 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
563 struct msghdr msg
= { NULL
, };
564 char control
[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS
)];
565 size_t fdsize
= sizeof(int) * nfds
;
566 struct cmsghdr
*cmsg
;
569 memset(control
, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS
));
571 msg
.msg_iov
= (struct iovec
*)iov
;
572 msg
.msg_iovlen
= niov
;
575 if (nfds
> SOCKET_MAX_FDS
) {
576 error_setg_errno(errp
, EINVAL
,
577 "Only %d FDs can be sent, got %zu",
578 SOCKET_MAX_FDS
, nfds
);
582 msg
.msg_control
= control
;
583 msg
.msg_controllen
= CMSG_SPACE(sizeof(int) * nfds
);
585 cmsg
= CMSG_FIRSTHDR(&msg
);
586 cmsg
->cmsg_len
= CMSG_LEN(fdsize
);
587 cmsg
->cmsg_level
= SOL_SOCKET
;
588 cmsg
->cmsg_type
= SCM_RIGHTS
;
589 memcpy(CMSG_DATA(cmsg
), fds
, fdsize
);
592 if (flags
& QIO_CHANNEL_WRITE_FLAG_ZERO_COPY
) {
593 #ifdef QEMU_MSG_ZEROCOPY
594 sflags
= MSG_ZEROCOPY
;
597 * We expect QIOChannel class entry point to have
598 * blocked this code path already
600 g_assert_not_reached();
605 ret
= sendmsg(sioc
->fd
, &msg
, sflags
);
609 return QIO_CHANNEL_ERR_BLOCK
;
613 if (flags
& QIO_CHANNEL_WRITE_FLAG_ZERO_COPY
) {
614 error_setg_errno(errp
, errno
,
615 "Process can't lock enough memory for using MSG_ZEROCOPY");
621 error_setg_errno(errp
, errno
,
622 "Unable to write to socket");
626 if (flags
& QIO_CHANNEL_WRITE_FLAG_ZERO_COPY
) {
627 sioc
->zero_copy_queued
++;
633 static ssize_t
qio_channel_socket_readv(QIOChannel
*ioc
,
634 const struct iovec
*iov
,
641 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
646 if (flags
& QIO_CHANNEL_READ_FLAG_MSG_PEEK
) {
650 for (i
= 0; i
< niov
; i
++) {
658 if (errno
== EAGAIN
) {
662 return QIO_CHANNEL_ERR_BLOCK
;
664 } else if (errno
== EINTR
) {
667 error_setg_errno(errp
, errno
,
668 "Unable to read from socket");
673 if (ret
< iov
[i
].iov_len
) {
681 static ssize_t
qio_channel_socket_writev(QIOChannel
*ioc
,
682 const struct iovec
*iov
,
689 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
693 for (i
= 0; i
< niov
; i
++) {
701 if (errno
== EAGAIN
) {
705 return QIO_CHANNEL_ERR_BLOCK
;
707 } else if (errno
== EINTR
) {
710 error_setg_errno(errp
, errno
,
711 "Unable to write to socket");
716 if (ret
< iov
[i
].iov_len
) {
726 #ifdef QEMU_MSG_ZEROCOPY
727 static int qio_channel_socket_flush(QIOChannel
*ioc
,
730 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
731 struct msghdr msg
= {};
732 struct sock_extended_err
*serr
;
734 char control
[CMSG_SPACE(sizeof(*serr
))];
738 if (sioc
->zero_copy_queued
== sioc
->zero_copy_sent
) {
742 msg
.msg_control
= control
;
743 msg
.msg_controllen
= sizeof(control
);
744 memset(control
, 0, sizeof(control
));
748 while (sioc
->zero_copy_sent
< sioc
->zero_copy_queued
) {
749 received
= recvmsg(sioc
->fd
, &msg
, MSG_ERRQUEUE
);
753 /* Nothing on errqueue, wait until something is available */
754 qio_channel_wait(ioc
, G_IO_ERR
);
759 error_setg_errno(errp
, errno
,
760 "Unable to read errqueue");
765 cm
= CMSG_FIRSTHDR(&msg
);
766 if (cm
->cmsg_level
!= SOL_IP
&& cm
->cmsg_type
!= IP_RECVERR
&&
767 cm
->cmsg_level
!= SOL_IPV6
&& cm
->cmsg_type
!= IPV6_RECVERR
) {
768 error_setg_errno(errp
, EPROTOTYPE
,
769 "Wrong cmsg in errqueue");
773 serr
= (void *) CMSG_DATA(cm
);
774 if (serr
->ee_errno
!= SO_EE_ORIGIN_NONE
) {
775 error_setg_errno(errp
, serr
->ee_errno
,
779 if (serr
->ee_origin
!= SO_EE_ORIGIN_ZEROCOPY
) {
780 error_setg_errno(errp
, serr
->ee_origin
,
781 "Error not from zero copy");
785 /* No errors, count successfully finished sendmsg()*/
786 sioc
->zero_copy_sent
+= serr
->ee_data
- serr
->ee_info
+ 1;
788 /* If any sendmsg() succeeded using zero copy, return 0 at the end */
789 if (serr
->ee_code
!= SO_EE_CODE_ZEROCOPY_COPIED
) {
797 #endif /* QEMU_MSG_ZEROCOPY */
800 qio_channel_socket_set_blocking(QIOChannel
*ioc
,
804 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
807 qemu_socket_set_block(sioc
->fd
);
809 qemu_socket_set_nonblock(sioc
->fd
);
816 qio_channel_socket_set_delay(QIOChannel
*ioc
,
819 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
820 int v
= enabled
? 0 : 1;
823 IPPROTO_TCP
, TCP_NODELAY
,
829 qio_channel_socket_set_cork(QIOChannel
*ioc
,
832 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
833 int v
= enabled
? 1 : 0;
835 socket_set_cork(sioc
->fd
, v
);
840 qio_channel_socket_close(QIOChannel
*ioc
,
843 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
847 if (sioc
->fd
!= -1) {
849 qemu_socket_unselect(sioc
->fd
, NULL
);
851 if (qio_channel_has_feature(ioc
, QIO_CHANNEL_FEATURE_LISTEN
)) {
852 socket_listen_cleanup(sioc
->fd
, errp
);
855 if (close(sioc
->fd
) < 0) {
857 error_setg_errno(&err
, errno
, "Unable to close socket");
858 error_propagate(errp
, err
);
867 qio_channel_socket_shutdown(QIOChannel
*ioc
,
868 QIOChannelShutdown how
,
871 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
875 case QIO_CHANNEL_SHUTDOWN_READ
:
878 case QIO_CHANNEL_SHUTDOWN_WRITE
:
881 case QIO_CHANNEL_SHUTDOWN_BOTH
:
887 if (shutdown(sioc
->fd
, sockhow
) < 0) {
888 error_setg_errno(errp
, errno
,
889 "Unable to shutdown socket");
895 static void qio_channel_socket_set_aio_fd_handler(QIOChannel
*ioc
,
901 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
902 aio_set_fd_handler(ctx
, sioc
->fd
, false,
903 io_read
, io_write
, NULL
, NULL
, opaque
);
906 static GSource
*qio_channel_socket_create_watch(QIOChannel
*ioc
,
907 GIOCondition condition
)
909 QIOChannelSocket
*sioc
= QIO_CHANNEL_SOCKET(ioc
);
910 return qio_channel_create_socket_watch(ioc
,
915 static void qio_channel_socket_class_init(ObjectClass
*klass
,
916 void *class_data G_GNUC_UNUSED
)
918 QIOChannelClass
*ioc_klass
= QIO_CHANNEL_CLASS(klass
);
920 ioc_klass
->io_writev
= qio_channel_socket_writev
;
921 ioc_klass
->io_readv
= qio_channel_socket_readv
;
922 ioc_klass
->io_set_blocking
= qio_channel_socket_set_blocking
;
923 ioc_klass
->io_close
= qio_channel_socket_close
;
924 ioc_klass
->io_shutdown
= qio_channel_socket_shutdown
;
925 ioc_klass
->io_set_cork
= qio_channel_socket_set_cork
;
926 ioc_klass
->io_set_delay
= qio_channel_socket_set_delay
;
927 ioc_klass
->io_create_watch
= qio_channel_socket_create_watch
;
928 ioc_klass
->io_set_aio_fd_handler
= qio_channel_socket_set_aio_fd_handler
;
929 #ifdef QEMU_MSG_ZEROCOPY
930 ioc_klass
->io_flush
= qio_channel_socket_flush
;
934 static const TypeInfo qio_channel_socket_info
= {
935 .parent
= TYPE_QIO_CHANNEL
,
936 .name
= TYPE_QIO_CHANNEL_SOCKET
,
937 .instance_size
= sizeof(QIOChannelSocket
),
938 .instance_init
= qio_channel_socket_init
,
939 .instance_finalize
= qio_channel_socket_finalize
,
940 .class_init
= qio_channel_socket_class_init
,
943 static void qio_channel_socket_register_types(void)
945 type_register_static(&qio_channel_socket_info
);
948 type_init(qio_channel_socket_register_types
);