static void char_pty_open(Chardev *chr, ChardevBackend *backend, bool *be_opened, Error **errp) { PtyChardev *s; int master_fd, slave_fd; char pty_name[PATH_MAX]; char *name; master_fd = qemu_openpty_raw(&slave_fd, pty_name); if (master_fd < 0) { error_setg_errno(errp, errno, "Failed to create PTY"); return; } close(slave_fd); qemu_set_nonblock(master_fd); chr->filename = g_strdup_printf("pty:%s", pty_name); error_report("char device redirected to %s (label %s)", pty_name, chr->label); s = PTY_CHARDEV(chr); s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd)); name = g_strdup_printf("chardev-pty-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(s->ioc), name); g_free(name); s->timer_src = NULL; *be_opened = false; }
static gboolean ga_channel_listen_accept(GIOChannel *channel, GIOCondition condition, gpointer data) { GAChannel *c = data; int ret, client_fd; bool accepted = false; struct sockaddr_un addr; socklen_t addrlen = sizeof(addr); g_assert(channel != NULL); client_fd = qemu_accept(g_io_channel_unix_get_fd(channel), (struct sockaddr *)&addr, &addrlen); if (client_fd == -1) { g_warning("error converting fd to gsocket: %s", strerror(errno)); goto out; } qemu_set_nonblock(client_fd); ret = ga_channel_client_add(c, client_fd); if (ret) { g_warning("error setting up connection"); close(client_fd); goto out; } accepted = true; out: /* only accept 1 connection at a time */ return !accepted; }
static int qio_channel_command_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) { QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); if (enabled) { qemu_set_block(cioc->writefd); qemu_set_block(cioc->readfd); } else { qemu_set_nonblock(cioc->writefd); qemu_set_nonblock(cioc->readfd); } return 0; }
static int inet_connect_addr(struct addrinfo *addr, bool *in_progress, ConnectState *connect_state, Error **errp) { int sock, rc; *in_progress = false; sock = qemu_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (sock < 0) { error_setg_errno(errp, errno, "Failed to create socket"); return -1; } socket_set_fast_reuse(sock); if (connect_state != NULL) { qemu_set_nonblock(sock); } /* connect to peer */ do { rc = 0; if (connect(sock, addr->ai_addr, addr->ai_addrlen) < 0) { rc = -errno; } } while (rc == -EINTR); if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { connect_state->fd = sock; qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state); *in_progress = true; } else if (rc < 0) { error_setg_errno(errp, errno, "Failed to connect socket"); closesocket(sock); return -1; } return sock; }
int socket_connect(SocketAddress *addr, Error **errp, NonBlockingConnectHandler *callback, void *opaque) { QemuOpts *opts; int fd; opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); switch (addr->kind) { case SOCKET_ADDRESS_KIND_INET: inet_addr_to_opts(opts, addr->inet); fd = inet_connect_opts(opts, errp, callback, opaque); break; case SOCKET_ADDRESS_KIND_UNIX: qemu_opt_set(opts, "path", addr->q_unix->path, &error_abort); fd = unix_connect_opts(opts, errp, callback, opaque); break; case SOCKET_ADDRESS_KIND_FD: fd = monitor_get_fd(cur_mon, addr->fd->str, errp); if (fd >= 0 && callback) { qemu_set_nonblock(fd); callback(fd, NULL, opaque); } break; default: abort(); } qemu_opts_del(opts); return fd; }
int socket_connect(SocketAddress *addr, Error **errp, NonBlockingConnectHandler *callback, void *opaque) { int fd; switch (addr->type) { case SOCKET_ADDRESS_KIND_INET: fd = inet_connect_saddr(addr->u.inet.data, errp, callback, opaque); break; case SOCKET_ADDRESS_KIND_UNIX: fd = unix_connect_saddr(addr->u.q_unix.data, errp, callback, opaque); break; case SOCKET_ADDRESS_KIND_FD: fd = monitor_get_fd(cur_mon, addr->u.fd.data->str, errp); if (fd >= 0 && callback) { qemu_set_nonblock(fd); callback(fd, NULL, opaque); } break; case SOCKET_ADDRESS_KIND_VSOCK: fd = vsock_connect_saddr(addr->u.vsock.data, errp, callback, opaque); break; default: abort(); } return fd; }
static int vsock_connect_addr(const struct sockaddr_vm *svm, bool *in_progress, ConnectState *connect_state, Error **errp) { int sock, rc; *in_progress = false; sock = qemu_socket(AF_VSOCK, SOCK_STREAM, 0); if (sock < 0) { error_setg_errno(errp, errno, "Failed to create socket"); return -1; } if (connect_state != NULL) { qemu_set_nonblock(sock); } /* connect to peer */ do { rc = 0; if (connect(sock, (const struct sockaddr *)svm, sizeof(*svm)) < 0) { rc = -errno; } } while (rc == -EINTR); if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { connect_state->fd = sock; qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state); *in_progress = true; } else if (rc < 0) { error_setg_errno(errp, errno, "Failed to connect socket"); closesocket(sock); return -1; } return sock; }
static void nbd_teardown_connection(NbdClientSession *client) { struct nbd_request request = { .type = NBD_CMD_DISC, .from = 0, .len = 0 }; nbd_send_request(client->sock, &request); /* finish any pending coroutines */ shutdown(client->sock, 2); nbd_recv_coroutines_enter_all(client); qemu_aio_set_fd_handler(client->sock, NULL, NULL, NULL); closesocket(client->sock); client->sock = -1; } void nbd_client_session_close(NbdClientSession *client) { if (!client->bs) { return; } nbd_teardown_connection(client); client->bs = NULL; } int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs, int sock, const char *export) { int ret; /* NBD handshake */ logout("session init %s\n", export); qemu_set_block(sock); ret = nbd_receive_negotiate(sock, export, &client->nbdflags, &client->size, &client->blocksize); if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); closesocket(sock); return ret; } qemu_co_mutex_init(&client->send_mutex); qemu_co_mutex_init(&client->free_sema); client->bs = bs; client->sock = sock; /* Now that we're connected, set the socket to be non-blocking and * kick the reply mechanism. */ qemu_set_nonblock(sock); qemu_aio_set_fd_handler(sock, nbd_reply_ready, NULL, client); logout("Established connection with NBD server\n"); return 0; }
int unix_connect_opts(QemuOpts *opts, Error **errp, NonBlockingConnectHandler *callback, void *opaque) { struct sockaddr_un un; const char *path = qemu_opt_get(opts, "path"); ConnectState *connect_state = NULL; int sock, rc; if (path == NULL) { error_setg(errp, "unix connect: no path specified"); return -1; } sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { error_setg_errno(errp, errno, "Failed to create socket"); return -1; } if (callback != NULL) { connect_state = g_malloc0(sizeof(*connect_state)); connect_state->callback = callback; connect_state->opaque = opaque; qemu_set_nonblock(sock); } memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; snprintf(un.sun_path, sizeof(un.sun_path), "%s", path); /* connect to peer */ do { rc = 0; if (connect(sock, (struct sockaddr *) &un, sizeof(un)) < 0) { rc = -socket_error(); } } while (rc == -EINTR); if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { connect_state->fd = sock; qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect, connect_state); return sock; } else if (rc >= 0) { /* non blocking socket immediate success, call callback */ if (callback != NULL) { callback(sock, NULL, opaque); } } if (rc < 0) { error_setg_errno(errp, -rc, "Failed to connect socket"); close(sock); sock = -1; } g_free(connect_state); return sock; }
void process_incoming_migration(QEMUFile *f) { Coroutine *co = qemu_coroutine_create(process_incoming_migration_co); int fd = qemu_get_fd(f); assert(fd != -1); qemu_set_nonblock(fd); qemu_coroutine_enter(co, f); }
static int qio_channel_file_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) { QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); if (enabled) { qemu_set_block(fioc->fd); } else { qemu_set_nonblock(fioc->fd); } return 0; }
static int qio_channel_socket_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); if (enabled) { qemu_set_block(sioc->fd); } else { qemu_set_nonblock(sioc->fd); } return 0; }
void nbd_client_close(BlockDriverState *bs) { NbdClientSession *client = nbd_get_client_session(bs); struct nbd_request request = { .type = NBD_CMD_DISC, .from = 0, .len = 0 }; if (client->sock == -1) { return; } nbd_send_request(client->sock, &request); nbd_teardown_connection(bs); } int nbd_client_init(BlockDriverState *bs, int sock, const char *export, Error **errp) { NbdClientSession *client = nbd_get_client_session(bs); int ret; /* NBD handshake */ logout("session init %s\n", export); qemu_set_block(sock); ret = nbd_receive_negotiate(sock, export, &client->nbdflags, &client->size, &client->blocksize, errp); if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); closesocket(sock); return ret; } qemu_co_mutex_init(&client->send_mutex); qemu_co_mutex_init(&client->free_sema); client->sock = sock; /* Now that we're connected, set the socket to be non-blocking and * kick the reply mechanism. */ qemu_set_nonblock(sock); nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); logout("Established connection with NBD server\n"); return 0; }
/* open a character device to a unix fd */ void qemu_chr_open_fd(Chardev *chr, int fd_in, int fd_out) { FDChardev *s = FD_CHARDEV(chr); char *name; s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in)); name = g_strdup_printf("chardev-file-in-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name); g_free(name); s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out)); name = g_strdup_printf("chardev-file-out-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name); g_free(name); qemu_set_nonblock(fd_out); s->chr = chr; }
static int qio_channel_socket_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); if (enabled) { qemu_set_block(sioc->fd); } else { qemu_set_nonblock(sioc->fd); #ifdef WIN32 WSAEventSelect(sioc->fd, ioc->event, FD_READ | FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_WRITE | FD_OOB); #endif } return 0; }
/* * Give a QEMUFile* off the same socket but data in the opposite * direction. */ static QEMUFile *socket_dup_return_path(void *opaque) { QEMUFileSocket *qfs = opaque; int revfd; bool this_is_read; QEMUFile *result; if (qemu_file_get_error(qfs->file)) { /* If the forward file is in error, don't try and open a return */ return NULL; } /* I don't think there's a better way to tell which direction 'this' is */ this_is_read = qfs->file->ops->get_buffer != NULL; revfd = dup(qfs->fd); if (revfd == -1) { error_report("Error duplicating fd for return path: %s", strerror(errno)); return NULL; } result = qemu_fopen_socket(revfd, this_is_read ? "wb" : "rb"); if (!result) { close(revfd); } if (this_is_read) { /* The qemu_fopen_socket "wb" will mark the socket blocking, * which would be OK for the return path, but the semantics * of non-blocking is that it follows the underlying connection * not the fd number, and thus setting the return path non-blocking * ends up setting the forward path blocking, which we don't want */ qemu_set_nonblock(revfd); } return result; }
static int inet_connect_addr(struct addrinfo *addr, bool *in_progress, ConnectState *connect_state, Error **errp) { int sock, rc; *in_progress = false; sock = qemu_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (sock < 0) { error_set_errno(errp, errno, QERR_SOCKET_CREATE_FAILED); return -1; } qemu_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (connect_state != NULL) { qemu_set_nonblock(sock); } /* connect to peer */ do { rc = 0; if (connect(sock, addr->ai_addr, addr->ai_addrlen) < 0) { rc = -socket_error(); } } while (rc == -EINTR); if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { connect_state->fd = sock; qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect, connect_state); *in_progress = true; } else if (rc < 0) { error_set_errno(errp, errno, QERR_SOCKET_CONNECT_FAILED); closesocket(sock); return -1; } return sock; }
static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, const char *model, const char *name, const char *ifname, const char *script, const char *downscript, const char *vhostfdname, int vnet_hdr, int fd, Error **errp) { Error *err = NULL; TAPState *s = net_tap_fd_init(peer, model, name, fd, vnet_hdr); int vhostfd; tap_set_sndbuf(s->fd, tap, &err); if (err) { error_propagate(errp, err); return; } if (tap->has_fd || tap->has_fds) { snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd); } else if (tap->has_helper) { snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s", tap->helper); } else { snprintf(s->nc.info_str, sizeof(s->nc.info_str), "ifname=%s,script=%s,downscript=%s", ifname, script, downscript); if (strcmp(downscript, "no") != 0) { snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname); } } if (tap->has_vhost ? tap->vhost : vhostfdname || (tap->has_vhostforce && tap->vhostforce)) { VhostNetOptions options; options.backend_type = VHOST_BACKEND_TYPE_KERNEL; options.net_backend = &s->nc; if (tap->has_poll_us) { options.busyloop_timeout = tap->poll_us; } else { options.busyloop_timeout = 0; } if (vhostfdname) { vhostfd = monitor_fd_param(cur_mon, vhostfdname, &err); if (vhostfd == -1) { if (tap->has_vhostforce && tap->vhostforce) { error_propagate(errp, err); } else { warn_report_err(err); } return; } qemu_set_nonblock(vhostfd); } else { vhostfd = open("/dev/vhost-net", O_RDWR); if (vhostfd < 0) { if (tap->has_vhostforce && tap->vhostforce) { error_setg_errno(errp, errno, "tap: open vhost char device failed"); } else { warn_report("tap: open vhost char device failed: %s", strerror(errno)); } return; } fcntl(vhostfd, F_SETFL, O_NONBLOCK); } options.opaque = (void *)(uintptr_t)vhostfd; s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { if (tap->has_vhostforce && tap->vhostforce) { error_setg(errp, VHOST_NET_INIT_FAILED); } else { warn_report(VHOST_NET_INIT_FAILED); } return; } } else if (vhostfdname) { error_setg(errp, "vhostfd(s)= is not valid without vhost"); } }
static int unix_connect_saddr(UnixSocketAddress *saddr, NonBlockingConnectHandler *callback, void *opaque, Error **errp) { struct sockaddr_un un; ConnectState *connect_state = NULL; int sock, rc; if (saddr->path == NULL) { error_setg(errp, "unix connect: no path specified"); return -1; } sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { error_setg_errno(errp, errno, "Failed to create socket"); return -1; } if (callback != NULL) { connect_state = g_malloc0(sizeof(*connect_state)); connect_state->callback = callback; connect_state->opaque = opaque; qemu_set_nonblock(sock); } if (strlen(saddr->path) > sizeof(un.sun_path)) { error_setg(errp, "UNIX socket path '%s' is too long", saddr->path); error_append_hint(errp, "Path must be less than %zu bytes\n", sizeof(un.sun_path)); goto err; } memset(&un, 0, sizeof(un)); un.sun_family = AF_UNIX; strncpy(un.sun_path, saddr->path, sizeof(un.sun_path)); /* connect to peer */ do { rc = 0; if (connect(sock, (struct sockaddr *) &un, sizeof(un)) < 0) { rc = -errno; } } while (rc == -EINTR); if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { connect_state->fd = sock; qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state); return sock; } else if (rc >= 0) { /* non blocking socket immediate success, call callback */ if (callback != NULL) { callback(sock, NULL, opaque); } } if (rc < 0) { error_setg_errno(errp, -rc, "Failed to connect socket %s", saddr->path); goto err; } g_free(connect_state); return sock; err: close(sock); g_free(connect_state); return -1; }
static void chr_read(void *opaque, const uint8_t *buf, int size) { CharDriverState *chr = opaque; VhostUserMsg msg; uint8_t *p = (uint8_t *) &msg; int fd; if (size != VHOST_USER_HDR_SIZE) { g_test_message("Wrong message size received %d\n", size); return; } g_mutex_lock(data_mutex); memcpy(p, buf, VHOST_USER_HDR_SIZE); if (msg.size) { p += VHOST_USER_HDR_SIZE; qemu_chr_fe_read_all(chr, p, msg.size); } switch (msg.request) { case VHOST_USER_GET_FEATURES: /* send back features to qemu */ msg.flags |= VHOST_USER_REPLY_MASK; msg.size = sizeof(m.u64); msg.u64 = 0; p = (uint8_t *) &msg; qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); break; case VHOST_USER_GET_VRING_BASE: /* send back vring base to qemu */ msg.flags |= VHOST_USER_REPLY_MASK; msg.size = sizeof(m.state); msg.state.num = 0; p = (uint8_t *) &msg; qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); break; case VHOST_USER_SET_MEM_TABLE: /* received the mem table */ memcpy(&memory, &msg.memory, sizeof(msg.memory)); fds_num = qemu_chr_fe_get_msgfds(chr, fds, sizeof(fds) / sizeof(int)); /* signal the test that it can continue */ g_cond_signal(data_cond); break; case VHOST_USER_SET_VRING_KICK: case VHOST_USER_SET_VRING_CALL: /* consume the fd */ qemu_chr_fe_get_msgfds(chr, &fd, 1); /* * This is a non-blocking eventfd. * The receive function forces it to be blocking, * so revert it back to non-blocking. */ qemu_set_nonblock(fd); break; default: break; } g_mutex_unlock(data_mutex); }
static void chr_read(void *opaque, const uint8_t *buf, int size) { TestServer *s = opaque; CharBackend *chr = &s->chr; VhostUserMsg msg; uint8_t *p = (uint8_t *) &msg; int fd = -1; if (s->test_fail) { qemu_chr_fe_disconnect(chr); /* now switch to non-failure */ s->test_fail = false; } if (size != VHOST_USER_HDR_SIZE) { g_test_message("Wrong message size received %d", size); return; } g_mutex_lock(&s->data_mutex); memcpy(p, buf, VHOST_USER_HDR_SIZE); if (msg.size) { p += VHOST_USER_HDR_SIZE; size = qemu_chr_fe_read_all(chr, p, msg.size); if (size != msg.size) { g_test_message("Wrong message size received %d != %d", size, msg.size); return; } } switch (msg.request) { case VHOST_USER_GET_FEATURES: /* send back features to qemu */ msg.flags |= VHOST_USER_REPLY_MASK; msg.size = sizeof(m.payload.u64); msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL | 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; if (s->queues > 1) { msg.payload.u64 |= 0x1ULL << VIRTIO_NET_F_MQ; } if (s->test_flags >= TEST_FLAGS_BAD) { msg.payload.u64 = 0; s->test_flags = TEST_FLAGS_END; } p = (uint8_t *) &msg; qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); break; case VHOST_USER_SET_FEATURES: g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES), !=, 0ULL); if (s->test_flags == TEST_FLAGS_DISCONNECT) { qemu_chr_fe_disconnect(chr); s->test_flags = TEST_FLAGS_BAD; } break; case VHOST_USER_GET_PROTOCOL_FEATURES: /* send back features to qemu */ msg.flags |= VHOST_USER_REPLY_MASK; msg.size = sizeof(m.payload.u64); msg.payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD; msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_CROSS_ENDIAN; if (s->queues > 1) { msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ; } p = (uint8_t *) &msg; qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); break; case VHOST_USER_GET_VRING_BASE: /* send back vring base to qemu */ msg.flags |= VHOST_USER_REPLY_MASK; msg.size = sizeof(m.payload.state); msg.payload.state.num = 0; p = (uint8_t *) &msg; qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); assert(msg.payload.state.index < s->queues * 2); s->rings &= ~(0x1ULL << msg.payload.state.index); g_cond_broadcast(&s->data_cond); break; case VHOST_USER_SET_MEM_TABLE: /* received the mem table */ memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory)); s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds, G_N_ELEMENTS(s->fds)); /* signal the test that it can continue */ g_cond_broadcast(&s->data_cond); break; case VHOST_USER_SET_VRING_KICK: case VHOST_USER_SET_VRING_CALL: /* consume the fd */ qemu_chr_fe_get_msgfds(chr, &fd, 1); /* * This is a non-blocking eventfd. * The receive function forces it to be blocking, * so revert it back to non-blocking. */ qemu_set_nonblock(fd); break; case VHOST_USER_SET_LOG_BASE: if (s->log_fd != -1) { close(s->log_fd); s->log_fd = -1; } qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1); msg.flags |= VHOST_USER_REPLY_MASK; msg.size = 0; p = (uint8_t *) &msg; qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE); g_cond_broadcast(&s->data_cond); break; case VHOST_USER_SET_VRING_BASE: assert(msg.payload.state.index < s->queues * 2); s->rings |= 0x1ULL << msg.payload.state.index; g_cond_broadcast(&s->data_cond); break; case VHOST_USER_GET_QUEUE_NUM: msg.flags |= VHOST_USER_REPLY_MASK; msg.size = sizeof(m.payload.u64); msg.payload.u64 = s->queues; p = (uint8_t *) &msg; qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size); break; default: break; } g_mutex_unlock(&s->data_mutex); }