summaryrefslogtreecommitdiff
path: root/src/socket-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/socket-client.c')
-rw-r--r--src/socket-client.c241
1 files changed, 241 insertions, 0 deletions
diff --git a/src/socket-client.c b/src/socket-client.c
new file mode 100644
index 0000000..159cacf
--- /dev/null
+++ b/src/socket-client.c
@@ -0,0 +1,241 @@
+/* CVS socket client stuff.
+
+ 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 2, 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. */
+
+/***
+ *** THIS FILE SHOULD NEVER BE COMPILED UNLESS NO_SOCKET_TO_FD IS DEFINED.
+ ***/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef CLIENT_SUPPORT
+
+#include "cvs.h"
+#include "buffer.h"
+
+#include "socket-client.h"
+
+
+/* Under certain circumstances, we must communicate with the server
+ via a socket using send() and recv(). This is because under some
+ operating systems (OS/2 and Windows 95 come to mind), a socket
+ cannot be converted to a file descriptor -- it must be treated as a
+ socket and nothing else.
+
+ We may also need to deal with socket routine error codes differently
+ in these cases. This is handled through the SOCK_ERRNO and
+ SOCK_STRERROR macros. */
+
+/* These routines implement a buffer structure which uses send and
+ recv. The buffer is always in blocking mode so we don't implement
+ the block routine. */
+
+/* Note that it is important that these routines always handle errors
+ internally and never return a positive errno code, since it would in
+ general be impossible for the caller to know in general whether any
+ error code came from a socket routine (to decide whether to use
+ SOCK_STRERROR or simply strerror to print an error message). */
+
+/* We use an instance of this structure as the closure field. */
+
+struct socket_buffer
+{
+ /* The socket number. */
+ int socket;
+};
+
+
+
+/* The buffer input function for a buffer built on a socket. */
+
+static int
+socket_buffer_input (void *closure, char *data, size_t need, size_t size,
+ size_t *got)
+{
+ struct socket_buffer *sb = closure;
+ int nbytes;
+
+ /* I believe that the recv function gives us exactly the semantics
+ we want. If there is a message, it returns immediately with
+ whatever it could get. If there is no message, it waits until
+ one comes in. In other words, it is not like read, which in
+ blocking mode normally waits until all the requested data is
+ available. */
+
+ assert (size >= need);
+
+ *got = 0;
+
+ do
+ {
+
+ /* Note that for certain (broken?) networking stacks, like
+ VMS's UCX (not sure what version, problem reported with
+ recv() in 1997), and (according to windows-NT/config.h)
+ Windows NT 3.51, we must call recv or send with a
+ moderately sized buffer (say, less than 200K or something),
+ or else there may be network errors (somewhat hard to
+ produce, e.g. WAN not LAN or some such). buf_read_data
+ makes sure that we only recv() BUFFER_DATA_SIZE bytes at
+ a time. */
+
+ nbytes = recv (sb->socket, data + *got, size - *got, 0);
+ if (nbytes < 0)
+ error (1, 0, "reading from server: %s",
+ SOCK_STRERROR (SOCK_ERRNO));
+ if (nbytes == 0)
+ {
+ /* End of file (for example, the server has closed
+ the connection). If we've already read something, we
+ just tell the caller about the data, not about the end of
+ file. If we've read nothing, we return end of file. */
+ if (*got == 0)
+ return -1;
+ else
+ return 0;
+ }
+ *got += nbytes;
+ }
+ while (*got < need);
+
+ return 0;
+}
+
+
+
+/* The buffer output function for a buffer built on a socket. */
+
+static int
+socket_buffer_output (void *closure, const char *data, size_t have,
+ size_t *wrote)
+{
+ struct socket_buffer *sb = closure;
+
+ *wrote = have;
+
+ /* See comment in socket_buffer_input regarding buffer size we pass
+ to send and recv. */
+
+# ifdef SEND_NEVER_PARTIAL
+ /* If send() never will produce a partial write, then just do it. This
+ is needed for systems where its return value is something other than
+ the number of bytes written. */
+ if (send (sb->socket, data, have, 0) < 0)
+ error (1, 0, "writing to server socket: %s",
+ SOCK_STRERROR (SOCK_ERRNO));
+# else
+ while (have > 0)
+ {
+ int nbytes;
+
+ nbytes = send (sb->socket, data, have, 0);
+ if (nbytes < 0)
+ error (1, 0, "writing to server socket: %s",
+ SOCK_STRERROR (SOCK_ERRNO));
+
+ have -= nbytes;
+ data += nbytes;
+ }
+# endif
+
+ return 0;
+}
+
+
+
+/* The buffer flush function for a buffer built on a socket. */
+
+/*ARGSUSED*/
+static int
+socket_buffer_flush (void *closure)
+{
+ /* Nothing to do. Sockets are always flushed. */
+ return 0;
+}
+
+
+
+static int
+socket_buffer_shutdown (struct buffer *buf)
+{
+ struct socket_buffer *n = buf->closure;
+ char tmp;
+
+ /* no need to flush children of an endpoint buffer here */
+
+ if (buf->input)
+ {
+ int err = 0;
+ if (! buf_empty_p (buf)
+ || (err = recv (n->socket, &tmp, 1, 0)) > 0)
+ error (0, 0, "dying gasps from %s unexpected",
+ current_parsed_root->hostname);
+ else if (err == -1)
+ error (0, 0, "reading from %s: %s", current_parsed_root->hostname,
+ SOCK_STRERROR (SOCK_ERRNO));
+
+ /* shutdown() socket */
+# ifdef SHUTDOWN_SERVER
+ if (current_parsed_root->method != server_method)
+# endif
+ if (shutdown (n->socket, 0) < 0)
+ {
+ error (1, 0, "shutting down server socket: %s",
+ SOCK_STRERROR (SOCK_ERRNO));
+ }
+
+ buf->input = NULL;
+ }
+ else if (buf->output)
+ {
+ /* shutdown() socket */
+# ifdef SHUTDOWN_SERVER
+ /* FIXME: Should have a SHUTDOWN_SERVER_INPUT &
+ * SHUTDOWN_SERVER_OUTPUT
+ */
+ if (current_parsed_root->method == server_method)
+ SHUTDOWN_SERVER (n->socket);
+ else
+# endif
+ if (shutdown (n->socket, 1) < 0)
+ {
+ error (1, 0, "shutting down server socket: %s",
+ SOCK_STRERROR (SOCK_ERRNO));
+ }
+
+ buf->output = NULL;
+ }
+
+ return 0;
+}
+
+
+
+/* Create a buffer based on a socket. */
+
+struct buffer *
+socket_buffer_initialize (int socket, int input,
+ void (*memory) (struct buffer *))
+{
+ struct socket_buffer *sbuf = xmalloc (sizeof *sbuf);
+ sbuf->socket = socket;
+ return buf_initialize (input ? socket_buffer_input : NULL,
+ input ? NULL : socket_buffer_output,
+ input ? NULL : socket_buffer_flush,
+ NULL, NULL,
+ socket_buffer_shutdown,
+ memory,
+ sbuf);
+}
+
+#endif /* CLIENT_SUPPORT */