The branch, v4-16-test has been updated via 618395a7eaf s3: libsmbclient: Fix smbc_stat() to return ENOENT on a non-existent file. via efa48817d3c s4: torture: libsmbclient: Add a torture test to ensure smbc_stat() returns ENOENT on a non-existent file. via f7a84cffe9d s4:ldap_server: let ldapsrv_call_writev_start use conn_idle_time to limit the time via bc16a8abe3f lib/tsocket: avoid endless cpu-spinning in tstream_bsd_fde_handler() via aeb7dd2ca89 lib/tsocket: remember the first error as tstream_bsd->error via d8d5146d167 lib/tsocket: check for errors indicated by poll() before getsockopt(fd, SOL_SOCKET, SO_ERROR) via 119bf609985 lib/tsocket: split out tsocket_bsd_error() from tsocket_bsd_pending() via c805ccba339 lib/tsocket: Add tests for loop on EAGAIN from c2095819c31 VERSION: Bump version up to Samba 4.16.7...
https://git.samba.org/?p=samba.git;a=shortlog;h=v4-16-test - Log ----------------------------------------------------------------- commit 618395a7eafbb6224047610659c2d343318a1d33 Author: Jeremy Allison <j...@samba.org> Date: Mon Oct 17 13:24:27 2022 -0700 s3: libsmbclient: Fix smbc_stat() to return ENOENT on a non-existent file. Remove knownfail. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15195 Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Pavel Filipenský <pfilipen...@samba.org> (backported from commit fd0c01da1c744ae6fd9d8675616d8b6d3531e469) j...@samba.org: Older SMBC_getatr returns bool not NTSTATUS. Autobuild-User(v4-16-test): Jule Anger <jan...@samba.org> Autobuild-Date(v4-16-test): Mon Oct 31 15:31:53 UTC 2022 on sn-devel-184 commit efa48817d3c6fd3c64051bdf29648dff1702cf5d Author: Jeremy Allison <j...@samba.org> Date: Mon Oct 17 13:14:41 2022 -0700 s4: torture: libsmbclient: Add a torture test to ensure smbc_stat() returns ENOENT on a non-existent file. Add knownfail. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15195 Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Pavel Filipenský <pfilipen...@samba.org> (cherry picked from commit 9eda432836bfff3d3d4a365a08a5ecb54f0f2e34) commit f7a84cffe9d9c61df7a7c5dd94e9caf3d18d9b3c Author: Stefan Metzmacher <me...@samba.org> Date: Thu Oct 13 10:17:25 2022 +0200 s4:ldap_server: let ldapsrv_call_writev_start use conn_idle_time to limit the time If the client is not able to receive the results within connections idle time, then we should treat it as dead. It's value is 15 minutes (900 s) by default. In order to limit that further an admin can use 'socket options' and set TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL and/or TCP_USER_TIMEOUT to useful values. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15202 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> Autobuild-User(master): Stefan Metzmacher <me...@samba.org> Autobuild-Date(master): Wed Oct 19 17:13:39 UTC 2022 on sn-devel-184 (cherry picked from commit eb2f3526032803f34c88ef1619a832a741f71910) commit bc16a8abe3f1446a0da7e672cdba469fcc8ef96a Author: Stefan Metzmacher <me...@samba.org> Date: Wed Oct 12 17:26:16 2022 +0200 lib/tsocket: avoid endless cpu-spinning in tstream_bsd_fde_handler() There were some reports that strace output an LDAP server socket is in CLOSE_WAIT state, returning EAGAIN for writev over and over (after a call to epoll() each time). In the tstream_bsd code the problem happens when we have a pending writev_send, while there's no readv_send pending. In that case we still ask for TEVENT_FD_READ in order to notice connection errors early, so we try to call writev even if the socket doesn't report TEVENT_FD_WRITE. And there are situations where we do that over and over again. It happens like this with a Linux kernel: tcp_fin() has this: struct tcp_sock *tp = tcp_sk(sk); inet_csk_schedule_ack(sk); sk->sk_shutdown |= RCV_SHUTDOWN; sock_set_flag(sk, SOCK_DONE); switch (sk->sk_state) { case TCP_SYN_RECV: case TCP_ESTABLISHED: /* Move to CLOSE_WAIT */ tcp_set_state(sk, TCP_CLOSE_WAIT); inet_csk_enter_pingpong_mode(sk); break; It means RCV_SHUTDOWN gets set as well as TCP_CLOSE_WAIT, but sk->sk_err is not changed to indicate an error. tcp_sendmsg_locked has this: ... err = -EPIPE; if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) goto do_error; while (msg_data_left(msg)) { int copy = 0; skb = tcp_write_queue_tail(sk); if (skb) copy = size_goal - skb->len; if (copy <= 0 || !tcp_skb_can_collapse_to(skb)) { bool first_skb; new_segment: if (!sk_stream_memory_free(sk)) goto wait_for_space; ... wait_for_space: set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); if (copied) tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH, size_goal); err = sk_stream_wait_memory(sk, &timeo); if (err != 0) goto do_error; It means if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) doesn't hit as we only have RCV_SHUTDOWN and sk_stream_wait_memory returns -EAGAIN. tcp_poll has this: if (sk->sk_shutdown & RCV_SHUTDOWN) mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; So we'll get EPOLLIN | EPOLLRDNORM | EPOLLRDHUP triggering TEVENT_FD_READ and writev/sendmsg keeps getting EAGAIN. So we need to always clear TEVENT_FD_READ if we don't have readable handler in order to avoid burning cpu. But we turn it on again after a timeout of 1 second in order to monitor the error state of the connection. And now that our tsocket_bsd_error() helper checks for POLLRDHUP, we can check if the socket is in an error state before calling the writable handler when TEVENT_FD_READ was reported. Only on error we'll call the writable handler, which will pick the error without calling writev(). BUG: https://bugzilla.samba.org/show_bug.cgi?id=15202 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> (cherry picked from commit e232ba946f00aac39d67197d9939bc923814479c) commit aeb7dd2ca89e7a010baaf3a5da17eaa466ace06e Author: Stefan Metzmacher <me...@samba.org> Date: Thu Oct 13 16:23:03 2022 +0200 lib/tsocket: remember the first error as tstream_bsd->error If we found that the connection is broken, there's no point in trying to use it anymore, so just return the first error we detected. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15202 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> (cherry picked from commit 4c7e2b9b60de5d02bb3f69effe7eddbf466a6155) commit d8d5146d1679383747ad0533759f97020b78221e Author: Stefan Metzmacher <me...@samba.org> Date: Thu Oct 13 14:46:14 2022 +0200 lib/tsocket: check for errors indicated by poll() before getsockopt(fd, SOL_SOCKET, SO_ERROR) This also returns an error if we got TCP_FIN from the peer, which is only reported by an explicit POLLRDHUP check. Also on FreeBSD getsockopt(fd, SOL_SOCKET, SO_ERROR) fetches and resets the error, so a 2nd call no longer returns an error. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15202 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> (cherry picked from commit 29a65da63d730ecead1e7d4a81a76dd1c8c179ea) commit 119bf609985a873346891c5ca55e69178d712eb0 Author: Stefan Metzmacher <me...@samba.org> Date: Thu Oct 13 10:39:59 2022 +0200 lib/tsocket: split out tsocket_bsd_error() from tsocket_bsd_pending() This will be used on its own soon. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15202 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> (cherry picked from commit 9950efd83e1a4b5e711f1d36fefa8a5d5e8b2410) commit c805ccba33985ca07da63b4be3affafb495e13a1 Author: Andrew Bartlett <abart...@samba.org> Date: Mon Oct 17 16:08:42 2022 +1300 lib/tsocket: Add tests for loop on EAGAIN BUG: https://bugzilla.samba.org/show_bug.cgi?id=15202 Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> Signed-off-by: Andrew Bartlett <abart...@samba.org> Signed-off-by: Stefan Metzmacher <me...@samba.org> (cherry picked from commit f0fb8b9508346aed50528216fd959a9b1a941409) ----------------------------------------------------------------------- Summary of changes: lib/tsocket/tests/socketpair_tcp.c | 89 ++++ .../tsocket/tests/socketpair_tcp.h | 30 +- lib/tsocket/tests/test_tstream.c | 517 +++++++++++++++++++++ lib/tsocket/tsocket_bsd.c | 274 ++++++++++- lib/tsocket/wscript_build | 6 + selftest/tests.py | 3 + source3/libsmb/libsmb_file.c | 39 +- source4/ldap_server/ldap_server.c | 5 + source4/torture/libsmbclient/libsmbclient.c | 63 +++ 9 files changed, 976 insertions(+), 50 deletions(-) create mode 100644 lib/tsocket/tests/socketpair_tcp.c copy source3/lib/namearray.c => lib/tsocket/tests/socketpair_tcp.h (61%) create mode 100644 lib/tsocket/tests/test_tstream.c Changeset truncated at 500 lines: diff --git a/lib/tsocket/tests/socketpair_tcp.c b/lib/tsocket/tests/socketpair_tcp.c new file mode 100644 index 00000000000..251b8bc0212 --- /dev/null +++ b/lib/tsocket/tests/socketpair_tcp.c @@ -0,0 +1,89 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Tim Potter 2000-2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/network.h" +#include "socketpair_tcp.h" + +/******************************************************************* +this is like socketpair but uses tcp. It is used by the Samba +regression test code +The function guarantees that nobody else can attach to the socket, +or if they do that this function fails and the socket gets closed +returns 0 on success, -1 on failure +the resulting file descriptors are symmetrical + ******************************************************************/ +int socketpair_tcp(int fd[2]) +{ + int listener; + struct sockaddr_in sock; + struct sockaddr_in sock2; + socklen_t socklen = sizeof(sock); + int connect_done = 0; + + fd[0] = fd[1] = listener = -1; + + memset(&sock, 0, sizeof(sock)); + + if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed; + + memset(&sock2, 0, sizeof(sock2)); +#ifdef HAVE_SOCK_SIN_LEN + sock2.sin_len = sizeof(sock2); +#endif + sock2.sin_family = PF_INET; + + if (bind(listener, (struct sockaddr *)&sock2, sizeof(sock2)) != 0) goto failed; + + if (listen(listener, 1) != 0) goto failed; + + if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) goto failed; + + if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed; + + set_blocking(fd[1], 0); + + sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (connect(fd[1], (struct sockaddr *)&sock, socklen) == -1) { + if (errno != EINPROGRESS) goto failed; + } else { + connect_done = 1; + } + + if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) goto failed; + + if (connect_done == 0) { + if (connect(fd[1], (struct sockaddr *)&sock, socklen) != 0 + && errno != EISCONN) goto failed; + } + close(listener); + + set_blocking(fd[1], 1); + + /* all OK! */ + return 0; + + failed: + if (fd[0] != -1) close(fd[0]); + if (fd[1] != -1) close(fd[1]); + if (listener != -1) close(listener); + return -1; +} diff --git a/source3/lib/namearray.c b/lib/tsocket/tests/socketpair_tcp.h similarity index 61% copy from source3/lib/namearray.c copy to lib/tsocket/tests/socketpair_tcp.h index e5c3bd983b5..dbee4efec70 100644 --- a/source3/lib/namearray.c +++ b/lib/tsocket/tests/socketpair_tcp.h @@ -2,10 +2,7 @@ Unix SMB/CIFS implementation. Samba utility functions Copyright (C) Andrew Tridgell 1992-1998 - Copyright (C) Jeremy Allison 2001-2007 - Copyright (C) Simo Sorce 2001 - Copyright (C) Jim McDonough <j...@us.ibm.com> 2003 - Copyright (C) James Peach 2006 + Copyright (C) Tim Potter 2000-2001 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,19 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "includes.h" -/**************************************************************************** - Routine to free a namearray. -****************************************************************************/ - -void free_namearray(name_compare_entry *name_array) -{ - int i; - - if(name_array == NULL) - return; - - for(i=0; name_array[i].name!=NULL; i++) - SAFE_FREE(name_array[i].name); - SAFE_FREE(name_array); -} +/******************************************************************* +this is like socketpair but uses tcp. It is used by the Samba +regression test code +The function guarantees that nobody else can attach to the socket, +or if they do that this function fails and the socket gets closed +returns 0 on success, -1 on failure +the resulting file descriptors are symmetrical + ******************************************************************/ +int socketpair_tcp(int fd[2]); diff --git a/lib/tsocket/tests/test_tstream.c b/lib/tsocket/tests/test_tstream.c new file mode 100644 index 00000000000..a920e671cda --- /dev/null +++ b/lib/tsocket/tests/test_tstream.c @@ -0,0 +1,517 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) 2022 Andrew Bartlett <abart...@samba.org> + * Copyright (C) 2021 Andreas Schneider <a...@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> +#include "includes.h" +#include "system/network.h" +#include "socketpair_tcp.h" +#include "tsocket.h" + +enum socket_pair_selector { + SOCKET_SERVER = 0, + SOCKET_CLIENT = 1, +}; + +struct socket_pair { + struct tevent_context *ev; + int socket_server; + int socket_client; + + /* for tstream tests */ + int rc; + int sys_errno; + int expected_errno; + struct timeval endtime; + size_t max_loops; + size_t num_loops; +}; + +/* If this is too large, we get EPIPE rather than EAGAIN */ +static const uint8_t TEST_STRING[128] = { 0 }; + +static int sigpipe_setup(void **state) +{ + BlockSignals(true, SIGPIPE); + return 0; +} + +static int setup_socketpair_tcp_context(void **state) +{ + int fd[2]; + struct socket_pair *sp = talloc_zero(NULL, struct socket_pair); + assert_non_null(sp); + + /* Set up a socketpair over TCP to test with */ + assert_return_code(socketpair_tcp(fd), errno); + + sp->socket_server = fd[SOCKET_SERVER]; + sp->socket_client = fd[SOCKET_CLIENT]; + + sp->ev = tevent_context_init(sp); + assert_non_null(sp->ev); + + *state = sp; + return 0; +} + +static int setup_socketpair_context(void **state) +{ + int fd[2]; + struct socket_pair *sp = talloc_zero(NULL, struct socket_pair); + assert_non_null(sp); + + /* Set up a socketpair over TCP to test with */ + assert_return_code(socketpair(AF_UNIX, SOCK_STREAM, 0, fd), errno); + + sp->socket_server = fd[SOCKET_SERVER]; + sp->socket_client = fd[SOCKET_CLIENT]; + + sp->ev = tevent_context_init(sp); + assert_non_null(sp->ev); + + *state = sp; + return 0; +} + +static int teardown_socketpair_context(void **state) +{ + struct socket_pair *sp = *state; + struct socket_pair sp_save = *sp; + + TALLOC_FREE(sp); + + /* + * Close these after the TALLOC_FREE() to allow clean shutdown + * of epoll() in tstream + */ + if (sp_save.socket_client != -1) { + close(sp_save.socket_client); + } + if (sp_save.socket_server != -1) { + close(sp_save.socket_server); + } + return 0; +} + + +/* Test socket behaviour */ +static void test_simple_socketpair(void **state) { + + struct socket_pair *sp = *state; + + char buf[sizeof(TEST_STRING)]; + + assert_int_equal(write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)), + sizeof(TEST_STRING)); + assert_int_equal(read(sp->socket_client, buf, sizeof(buf)), + sizeof(buf)); + + +} + +/* Test socket behaviour */ +static void test_read_client_after_close_server_socket(void **state) { + + struct socket_pair *sp = *state; + int rc; + char buf[sizeof(TEST_STRING)]; + + rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)); + assert_return_code(rc, errno); + assert_int_equal(rc, sizeof(TEST_STRING)); + + assert_return_code(close(sp->socket_server), 0); + + rc = read(sp->socket_client, buf, sizeof(buf)); + + assert_return_code(rc, errno); + assert_int_equal(rc, sizeof(buf)); +} + +static void test_write_server_after_close_client_socket(void **state) { + + struct socket_pair *sp = *state; + int rc; + + assert_return_code(close(sp->socket_client), 0); + sp->socket_client = -1; + + rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)); + assert_return_code(rc, errno); + assert_int_equal(rc, sizeof(TEST_STRING)); +} + +static void test_fill_socket(int sock) +{ + size_t num_busy = 0; + int rc; + + while (true) { + rc = write(sock, TEST_STRING, sizeof(TEST_STRING)); + if (rc == -1 && errno == EAGAIN) { + /* + * This makes sure we write until we get a whole second + * only with EAGAIN every 50 ms (20 times) + * + * Otherwise the tests are not reliable... + */ + num_busy++; + if (num_busy > 20) { + break; + } + smb_msleep(50); + continue; + } + /* try again next time */ + num_busy = 0; + } + + assert_int_equal(rc, -1); + assert_int_equal(errno, EAGAIN); +} + +static void test_big_write_server(void **state) { + + struct socket_pair *sp = *state; + int rc; + + rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)); + assert_return_code(rc, errno); + assert_int_equal(rc, sizeof(TEST_STRING)); + + rc = set_blocking(sp->socket_server, 0); + assert_return_code(rc, errno); + + test_fill_socket(sp->socket_server); +} + +static void test_big_write_server_close_write(void **state) { + + struct socket_pair *sp = *state; + int rc; + + rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)); + assert_return_code(rc, errno); + assert_int_equal(rc, sizeof(TEST_STRING)); + + rc = set_blocking(sp->socket_server, 0); + assert_return_code(rc, errno); + + test_fill_socket(sp->socket_server); + + assert_return_code(close(sp->socket_client), 0); + sp->socket_client = -1; + + rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)); + assert_int_equal(errno, ECONNRESET); + +} + +static void test_big_write_server_shutdown_wr_write(void **state) { + + struct socket_pair *sp = *state; + int rc; + + rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)); + assert_return_code(rc, errno); + assert_int_equal(rc, sizeof(TEST_STRING)); + + rc = set_blocking(sp->socket_server, 0); + assert_return_code(rc, errno); + + test_fill_socket(sp->socket_server); + + assert_return_code(shutdown(sp->socket_client, SHUT_WR), 0); + sp->socket_client = -1; + + rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)); + assert_int_equal(rc, -1); + assert_int_equal(errno, EAGAIN); +} + +static void test_big_write_server_shutdown_rd_write(void **state) { + + struct socket_pair *sp = *state; + int rc; + + rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)); + assert_return_code(rc, errno); + assert_int_equal(rc, sizeof(TEST_STRING)); + + rc = set_blocking(sp->socket_server, 0); + assert_return_code(rc, errno); + + test_fill_socket(sp->socket_server); + + assert_return_code(shutdown(sp->socket_client, SHUT_RD), 0); + sp->socket_client = -1; + + rc = write(sp->socket_server, TEST_STRING, sizeof(TEST_STRING)); + assert_int_equal(rc, -1); + assert_int_equal(errno, EAGAIN); +} + +static void test_call_writev_done(struct tevent_req *subreq) +{ + struct socket_pair *sp = + tevent_req_callback_data(subreq, + struct socket_pair); + int rc; + + rc = tstream_writev_recv(subreq, &sp->sys_errno); + TALLOC_FREE(subreq); + + sp->rc = rc; +} + +static void test_tstream_server_spin_client_shutdown(struct socket_pair *sp) +{ + int rc; + + rc = shutdown(sp->socket_client, SHUT_WR); + assert_return_code(rc, errno); + /* + * It should only take a few additional loop to realise that this socket is + * in CLOSE_WAIT + */ + sp->max_loops = sp->num_loops + 2; + sp->expected_errno = ECONNRESET; +} + +static void test_tstream_server_spin_client_write(struct socket_pair *sp) +{ + int rc; + int timeout = 5000; + + sp->endtime = timeval_current_ofs_msec(timeout); + + rc = write(sp->socket_client, TEST_STRING, sizeof(TEST_STRING)); + assert_return_code(rc, errno); + sp->expected_errno = ETIMEDOUT; +} + +static void test_tstream_server_spin_client_tcp_user_timeout(struct socket_pair *sp) +{ + int rc; + int timeout = 5000; + + rc = setsockopt(sp->socket_server, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout)); + assert_return_code(rc, errno); + + rc = write(sp->socket_client, TEST_STRING, sizeof(TEST_STRING)); + assert_return_code(rc, errno); + sp->expected_errno = ETIMEDOUT; + sp->max_loops = 15; +} + +static void test_tstream_server_spin_client_both_timer(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct socket_pair *sp = + talloc_get_type_abort(private_data, + struct socket_pair); + + test_tstream_server_spin_client_shutdown(sp); +} + +static void test_tstream_server_spin_client_both(struct socket_pair *sp) +{ + struct tevent_timer *te = NULL; + struct timeval endtime; + + test_tstream_server_spin_client_write(sp); + + endtime = timeval_current_ofs_msec(2500); + + te = tevent_add_timer(sp->ev, + sp, + endtime, -- Samba Shared Repository