[SCM] Samba Shared Repository - branch master updated

Stefan Metzmacher metze at samba.org
Wed Oct 19 17:14:02 UTC 2022


The branch, master has been updated
       via  eb2f3526032 s4:ldap_server: let ldapsrv_call_writev_start use conn_idle_time to limit the time
       via  e232ba946f0 lib/tsocket: avoid endless cpu-spinning in tstream_bsd_fde_handler()
       via  4c7e2b9b60d lib/tsocket: remember the first error as tstream_bsd->error
       via  29a65da63d7 lib/tsocket: check for errors indicated by poll() before getsockopt(fd, SOL_SOCKET, SO_ERROR)
       via  9950efd83e1 lib/tsocket: split out tsocket_bsd_error() from tsocket_bsd_pending()
       via  f0fb8b95083 lib/tsocket: Add tests for loop on EAGAIN
      from  fd0c01da1c7 s3: libsmbclient: Fix smbc_stat() to return ENOENT on a non-existent file.

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit eb2f3526032803f34c88ef1619a832a741f71910
Author: Stefan Metzmacher <metze at 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 <metze at samba.org>
    Reviewed-by: Ralph Boehme <slow at samba.org>
    
    Autobuild-User(master): Stefan Metzmacher <metze at samba.org>
    Autobuild-Date(master): Wed Oct 19 17:13:39 UTC 2022 on sn-devel-184

commit e232ba946f00aac39d67197d9939bc923814479c
Author: Stefan Metzmacher <metze at 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 <metze at samba.org>
    Reviewed-by: Ralph Boehme <slow at samba.org>

commit 4c7e2b9b60de5d02bb3f69effe7eddbf466a6155
Author: Stefan Metzmacher <metze at 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 <metze at samba.org>
    Reviewed-by: Ralph Boehme <slow at samba.org>

commit 29a65da63d730ecead1e7d4a81a76dd1c8c179ea
Author: Stefan Metzmacher <metze at 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 <metze at samba.org>
    Reviewed-by: Ralph Boehme <slow at samba.org>

commit 9950efd83e1a4b5e711f1d36fefa8a5d5e8b2410
Author: Stefan Metzmacher <metze at 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 <metze at samba.org>
    Reviewed-by: Ralph Boehme <slow at samba.org>

commit f0fb8b9508346aed50528216fd959a9b1a941409
Author: Andrew Bartlett <abartlet at 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 <metze at samba.org>
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Signed-off-by: Stefan Metzmacher <metze at samba.org>

-----------------------------------------------------------------------

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 +
 source4/ldap_server/ldap_server.c                  |   5 +
 7 files changed, 883 insertions(+), 41 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 <jmcd at 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 <abartlet at samba.org>
+ * Copyright (C) 2021      Andreas Schneider <asn at 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



More information about the samba-cvs mailing list