You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2013/10/12 20:43:20 UTC

svn commit: r1531574 - in /subversion/trunk: CHANGES subversion/include/svn_ra.h subversion/libsvn_ra_svn/client.c subversion/libsvn_ra_svn/ra_svn.h subversion/tests/libsvn_ra/ra-test.c

Author: brane
Date: Sat Oct 12 18:43:20 2013
New Revision: 1531574

URL: http://svn.apache.org/r1531574
Log:
Added two new callbacks to the svn_ra_callbacks2_t table for opening
and closing ra_svn tunnels. When they're used, they override all
tunnel definitions in the config file, includint the built-in svn+ssh.

* subversion/include/svn_ra.h (struct svn_ra_svn_conn_st): Forward-declare
   this struct that is otherwise typedef'd in svn_ra_svn.h.
  (svn_ra_open_tunnel_func_t, svn_ra_close_tunnel_func_t):
   New callback function typedefs.
  (svn_ra_callbacks2_t.open_tunnel, svn_ra_callbacks2_t.close_tunnel):
   New RA callbacks. Used only by ra_svn, and only if a tunnel is needed.

* subversion/libsvn_ra_svn/ra_svn.h (svn_ra_svn__session_baton_t):
   New member tunnel_name.
* subversion/libsvn_ra_svn/client.c (tunnel_data_t): New private struct,
   used as a pool-cleanup baton.
  (close_tunnel_cleanup): New; pool cleanup for closing tunnels.
  (open_session): New argument tunnel_name. Use the new callbacks
   if they're defined.
  (ra_svn_open): Do not call find_tunnel_agent if the open-tunnel
   callback is defined. Update call to open_sesssion.
  (ra_svn_reparent): Update call to open_sesssion.

* subversion/tests/libsvn_ra/ra-test.c
  (make_and_open_local_repos): Use svn_ra_open4 instead of the
   deprecated svn_ra_open3.
  (tunnel_open_count, open_tunnel, close_tunnel): New.
  (tunel_callback_test): New test for tunnel callbacks.
  (test_funcs): Add tunel_callback_test.

Modified:
    subversion/trunk/CHANGES
    subversion/trunk/subversion/include/svn_ra.h
    subversion/trunk/subversion/libsvn_ra_svn/client.c
    subversion/trunk/subversion/libsvn_ra_svn/ra_svn.h
    subversion/trunk/subversion/tests/libsvn_ra/ra-test.c

Modified: subversion/trunk/CHANGES
URL: http://svn.apache.org/viewvc/subversion/trunk/CHANGES?rev=1531574&r1=1531573&r2=1531574&view=diff
==============================================================================
--- subversion/trunk/CHANGES (original)
+++ subversion/trunk/CHANGES Sat Oct 12 18:43:20 2013
@@ -20,6 +20,8 @@ http://svn.apache.org/repos/asf/subversi
   - General:
 
   - API changes:
+    * New RA callbacks for managing ra_svn tunnels:
+      svn_ra_callbacks2_t::open_tunnel and svn_ra_callbacks2_t::close_tunnel.
 
   - Bindings:
 

Modified: subversion/trunk/subversion/include/svn_ra.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_ra.h?rev=1531574&r1=1531573&r2=1531574&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_ra.h (original)
+++ subversion/trunk/subversion/include/svn_ra.h Sat Oct 12 18:43:20 2013
@@ -270,6 +270,52 @@ typedef svn_error_t *(*svn_ra_replay_rev
   apr_hash_t *rev_props,
   apr_pool_t *pool);
 
+
+/**
+ * Forward-declared ra_svn connection type.
+ * @see svn_ra_svn.h
+ */
+struct svn_ra_svn_conn_st;
+
+/**
+ * Callback function for opening a tunnel in ra_svn.
+ *
+ * Given the @a tunnel_name, tunnel @a user and server @a hostname and
+ * @a port, return a new ra_svn connection in @a conn. The returned
+ * connection must be allocated from @a pool.
+ *
+ * @a tunnel_baton will be passed on to the close-unnel callback.
+ *
+ * @a open_baton is the baton as originally passed to ra_open.
+ *
+ * @since New in 1.9.
+ */
+typedef svn_error_t *(*svn_ra_open_tunnel_func_t)(
+    struct svn_ra_svn_conn_st **conn,
+    void **tunnel_baton, void *open_baton,
+    const char *tunnel_name, const char *user,
+    const char *hostname, int port,
+    apr_pool_t *pool);
+
+/**
+ * Callback function for closing a tunnel in ra_svn.
+ *
+ * This function will be called when the pool that owns the tunnel
+ * connection is cleared or destroyed. It receives the @a baton that
+ * was created by the open-tunnel callback, and the same
+ * @a tunnel_name, @a user, @a hostname and @a port parameters.
+ *
+ * @a tunel_baton was returned by the open-tunnel callback.
+ *
+ * @a open_baton is the baton as originally passed to ra_open.
+ *
+ * @since New in 1.9.
+ */
+typedef svn_error_t *(*svn_ra_close_tunnel_func_t)(
+    void *tunnel_baton, void *open_baton,
+    const char *tunnel_name, const char *user,
+    const char *hostname, int port);
+
 
 /**
  * The update Reporter.
@@ -538,6 +584,24 @@ typedef struct svn_ra_callbacks2_t
    */
   svn_ra_get_wc_contents_func_t get_wc_contents;
 
+  /** Open-tunnel callback
+   * If not @c null, this callback will be invoked to create an ra_svn
+   * connection that needs a tunnel, overriding any tunnel definitions
+   * in the client config file. This callback is used only for ra_svn
+   * and ignored by the other RA modules.
+   * @since New in 1.9.
+   */
+  svn_ra_open_tunnel_func_t open_tunnel;
+
+  /** Close-tunnel callback
+   * If not @c null, this callback will be invoked when the pool that
+   * owns the connection created by the open_tunnel callback is
+   * cleared or destroyed. This callback is used only for ra_svn and
+   * ignored by the other RA modules.
+   * @since New in 1.9.
+   */
+  svn_ra_close_tunnel_func_t close_tunnel;
+
 } svn_ra_callbacks2_t;
 
 /** Similar to svn_ra_callbacks2_t, except that the progress

Modified: subversion/trunk/subversion/libsvn_ra_svn/client.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_svn/client.c?rev=1531574&r1=1531573&r2=1531574&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_svn/client.c (original)
+++ subversion/trunk/subversion/libsvn_ra_svn/client.c Sat Oct 12 18:43:20 2013
@@ -561,14 +561,42 @@ static svn_error_t *parse_url(const char
   return SVN_NO_ERROR;
 }
 
+/* This structure is used as a baton for the pool cleanup function to
+   store tunnel parameters used by the close-tunnel callback. */
+struct tunnel_data_t {
+  void *tunnel_baton;
+  void *open_baton;
+  const char *tunnel_name;
+  const char *user;
+  const char *hostname;
+  int port;
+  svn_ra_close_tunnel_func_t close_tunnel;
+};
+
+/* Pool cleanup function that invokes the close-tunel callback. */
+static apr_status_t close_tunnel_cleanup(void *baton)
+{
+  const struct tunnel_data_t *const td = baton;
+  svn_error_t *const err =
+    svn_error_root_cause(td->close_tunnel(td->tunnel_baton, td->open_baton,
+                                          td->tunnel_name, td->user,
+                                          td->hostname, td->port));
+  const apr_status_t ret = (err ? err->apr_err : 0);
+  svn_error_clear(err);
+  return ret;
+}
+
 /* Open a session to URL, returning it in *SESS_P, allocating it in POOL.
    URI is a parsed version of URL.  CALLBACKS and CALLBACKS_BATON
-   are provided by the caller of ra_svn_open. If tunnel_argv is non-null,
-   it points to a program argument list to use when invoking the tunnel agent.
+   are provided by the caller of ra_svn_open. If TUNNEL_NAME is not NULL,
+   it is the name of the tunnel type parsed from the URL scheme.
+   If TUNNEL_ARGV is not NULL, it points to a program argument list to use
+   when invoking the tunnel agent.
 */
 static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p,
                                  const char *url,
                                  const apr_uri_t *uri,
+                                 const char *tunnel_name,
                                  const char **tunnel_argv,
                                  const svn_ra_callbacks2_t *callbacks,
                                  void *callbacks_baton,
@@ -583,19 +611,45 @@ static svn_error_t *open_session(svn_ra_
 
   sess = apr_palloc(pool, sizeof(*sess));
   sess->pool = pool;
-  sess->is_tunneled = (tunnel_argv != NULL);
+  sess->is_tunneled = (tunnel_name != NULL);
   sess->url = apr_pstrdup(pool, url);
   sess->user = uri->user;
   sess->hostname = uri->hostname;
   sess->realm_prefix = apr_psprintf(pool, "<svn://%s:%d>", uri->hostname,
                                     uri->port);
+  sess->tunnel_name = tunnel_name;
   sess->tunnel_argv = tunnel_argv;
   sess->callbacks = callbacks;
   sess->callbacks_baton = callbacks_baton;
   sess->bytes_read = sess->bytes_written = 0;
 
-  if (tunnel_argv)
-    SVN_ERR(make_tunnel(tunnel_argv, &conn, pool));
+  if (tunnel_name)
+    {
+      if (!callbacks->open_tunnel)
+        SVN_ERR(make_tunnel(tunnel_argv, &conn, pool));
+      else
+        {
+          void *tunnel_baton = NULL;
+          SVN_ERR(callbacks->open_tunnel(&conn, &tunnel_baton,
+                                         callbacks_baton,
+                                         tunnel_name, uri->user,
+                                         uri->hostname, uri->port,
+                                         pool));
+          if (callbacks->close_tunnel)
+            {
+              struct tunnel_data_t *const td = apr_palloc(pool, sizeof(*td));
+              td->tunnel_baton = tunnel_baton;
+              td->open_baton = callbacks_baton;
+              td->tunnel_name = apr_pstrdup(pool, tunnel_name);
+              td->user = apr_pstrdup(pool, uri->user);
+              td->hostname = apr_pstrdup(pool, uri->hostname);
+              td->port = uri->port;
+              td->close_tunnel = callbacks->close_tunnel;
+              apr_pool_cleanup_register(pool, td, close_tunnel_cleanup,
+                                        apr_pool_cleanup_null);
+            }
+        }
+    }
   else
     {
       SVN_ERR(make_connection(uri->hostname, uri->port, &sock, pool));
@@ -738,7 +792,7 @@ static svn_error_t *ra_svn_open(svn_ra_s
 
   parse_tunnel(url, &tunnel, pool);
 
-  if (tunnel)
+  if (tunnel && !callbacks->open_tunnel)
     SVN_ERR(find_tunnel_agent(tunnel, uri.hostinfo, &tunnel_argv, config,
                               pool));
   else
@@ -755,7 +809,7 @@ static svn_error_t *ra_svn_open(svn_ra_s
 
   /* We open the session in a subpool so we can get rid of it if we
      reparent with a server that doesn't support reparenting. */
-  SVN_ERR(open_session(&sess, url, &uri, tunnel_argv,
+  SVN_ERR(open_session(&sess, url, &uri, tunnel, tunnel_argv,
                        callbacks, callback_baton, sess_pool));
   session->priv = sess;
 
@@ -791,7 +845,7 @@ static svn_error_t *ra_svn_reparent(svn_
   sess_pool = svn_pool_create(ra_session->pool);
   err = parse_url(url, &uri, sess_pool);
   if (! err)
-    err = open_session(&new_sess, url, &uri, sess->tunnel_argv,
+    err = open_session(&new_sess, url, &uri, sess->tunnel_name, sess->tunnel_argv,
                        sess->callbacks, sess->callbacks_baton, sess_pool);
   /* We destroy the new session pool on error, since it is allocated in
      the main session pool. */

Modified: subversion/trunk/subversion/libsvn_ra_svn/ra_svn.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_svn/ra_svn.h?rev=1531574&r1=1531573&r2=1531574&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_svn/ra_svn.h (original)
+++ subversion/trunk/subversion/libsvn_ra_svn/ra_svn.h Sat Oct 12 18:43:20 2013
@@ -127,6 +127,7 @@ struct svn_ra_svn__session_baton_t {
   const char *user;
   const char *hostname; /* The remote hostname. */
   const char *realm_prefix;
+  const char *tunnel_name;
   const char **tunnel_argv;
   const svn_ra_callbacks2_t *callbacks;
   void *callbacks_baton;

Modified: subversion/trunk/subversion/tests/libsvn_ra/ra-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_ra/ra-test.c?rev=1531574&r1=1531573&r2=1531574&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_ra/ra-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_ra/ra-test.c Sat Oct 12 18:43:20 2013
@@ -31,11 +31,17 @@
 #include "svn_error.h"
 #include "svn_delta.h"
 #include "svn_ra.h"
+#include "svn_ra_svn.h"
+#include "svn_pools.h"
+#include "svn_cmdline.h"
+#include "svn_dirent_uri.h"
 
 #include "../svn_test.h"
 #include "../svn_test_fs.h"
 #include "../../libsvn_ra_local/ra_local.h"
 
+static const char tunnel_repos_name[] = "test-repo-tunnel";
+
 /*-------------------------------------------------------------------*/
 
 /** Helper routines. **/
@@ -58,7 +64,7 @@ make_and_open_local_repos(svn_ra_session
 
   SVN_ERR(svn_uri_get_file_url_from_dirent(&url, repos_name, pool));
 
-  SVN_ERR(svn_ra_open3(session, url, NULL, cbtable, NULL, NULL, pool));
+  SVN_ERR(svn_ra_open4(session, NULL, url, NULL, cbtable, NULL, NULL, pool));
 
   return SVN_NO_ERROR;
 }
@@ -88,6 +94,77 @@ commit_changes(svn_ra_session_t *session
   return SVN_NO_ERROR;
 }
 
+static int tunnel_open_count;
+
+static svn_error_t *
+open_tunnel(svn_ra_svn_conn_t **conn, void **tunnel_baton,
+            void *callbacks_baton,
+            const char *tunnel_name, const char *user,
+            const char *hostname, int port,
+            apr_pool_t *pool)
+{
+  svn_node_kind_t kind;
+  apr_proc_t *proc;
+  apr_procattr_t *attr;
+  apr_status_t status;
+  const char *args[] = { "svnserve", "-t", "-r", ".", NULL };
+  const char *svnserve;
+
+  SVN_ERR(svn_dirent_get_absolute(&svnserve, "../../svnserve/svnserve", pool));
+#ifdef WIN32
+  svnserve = apr_pstrcat(pool, svnserve, ".exe", NULL);
+#endif
+  SVN_ERR(svn_io_check_path(svnserve, &kind, pool));
+  if (kind != svn_node_file)
+    return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+                             "Could not find svnserve at %s",
+                             svn_dirent_local_style(svnserve, pool));
+
+  status = apr_procattr_create(&attr, pool);
+  if (status == APR_SUCCESS)
+    status = apr_procattr_io_set(attr, 1, 1, 0);
+  if (status == APR_SUCCESS)
+    status = apr_procattr_cmdtype_set(attr, APR_PROGRAM);
+  proc = apr_palloc(pool, sizeof(*proc));
+  if (status == APR_SUCCESS)
+    status = apr_proc_create(proc,
+                             svn_dirent_local_style(svnserve, pool),
+                             args, NULL, attr, pool);
+  if (status != APR_SUCCESS)
+    return svn_error_wrap_apr(status, "Could not run svnserve");
+#ifdef WIN32
+  apr_pool_note_subprocess(pool, proc, APR_KILL_NEVER);
+#else
+  apr_pool_note_subprocess(pool, proc, APR_KILL_ONLY_ONCE);
+#endif
+
+  /* APR pipe objects inherit by default.  But we don't want the
+   * tunnel agent's pipes held open by future child processes
+   * (such as other ra_svn sessions), so turn that off. */
+  apr_file_inherit_unset(proc->in);
+  apr_file_inherit_unset(proc->out);
+
+  *conn = svn_ra_svn_create_conn3(NULL, proc->out, proc->in,
+                                  SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
+                                  0, 0, pool);
+
+  *tunnel_baton = NULL;
+  ++tunnel_open_count;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_tunnel(void *tunnel_baton, void *callbacks_baton,
+             const char *tunnel_name, const char *user,
+             const char *hostname, int port)
+{
+  --tunnel_open_count;
+  return SVN_NO_ERROR;
+}
+
+
+
+
 /*-------------------------------------------------------------------*/
 
 /** The tests **/
@@ -151,6 +228,50 @@ location_segments_test(const svn_test_op
 }
 
 
+/* Test ra_svn tunnel callbacks. */
+static svn_error_t *
+tunel_callback_test(const svn_test_opts_t *opts,
+                    apr_pool_t *pool)
+{
+  apr_pool_t *connection_pool;
+  svn_repos_t *repos;
+  const char *url;
+  svn_ra_callbacks2_t *cbtable;
+  svn_ra_session_t *session;
+  svn_error_t *err;
+
+  SVN_ERR(svn_test__create_repos(&repos, tunnel_repos_name, opts, pool));
+
+  url = apr_pstrcat(pool, "svn+test://localhost/", tunnel_repos_name, NULL);
+  SVN_ERR(svn_ra_create_callbacks(&cbtable, pool));
+  cbtable->open_tunnel = open_tunnel;
+  cbtable->close_tunnel = close_tunnel;
+  SVN_ERR(svn_cmdline_create_auth_baton(&cbtable->auth_baton,
+                                        TRUE  /* non_interactive */,
+                                        "jrandom", "rayjandom",
+                                        NULL,
+                                        TRUE  /* no_auth_cache */,
+                                        FALSE /* trust_server_cert */,
+                                        NULL, NULL, NULL, pool));
+
+  tunnel_open_count = 0;
+  connection_pool = svn_pool_create(pool);
+  err = svn_ra_open4(&session, NULL, url, NULL, cbtable, NULL, NULL,
+                     connection_pool);
+  if (err && err->apr_err == SVN_ERR_TEST_FAILED)
+    {
+      svn_handle_error2(err, stderr, FALSE, "svn_tests: ");
+      svn_error_clear(err);
+      return SVN_NO_ERROR;
+    }
+  SVN_ERR(err);
+  SVN_TEST_ASSERT(tunnel_open_count > 0);
+  svn_pool_destroy(connection_pool);
+  SVN_TEST_ASSERT(tunnel_open_count == 0);
+  return SVN_NO_ERROR;
+}
+
+
 
 /* The test table.  */
 struct svn_test_descriptor_t test_funcs[] =
@@ -158,5 +279,7 @@ struct svn_test_descriptor_t test_funcs[
     SVN_TEST_NULL,
     SVN_TEST_OPTS_PASS(location_segments_test,
                        "test svn_ra_get_location_segments"),
+    SVN_TEST_OPTS_PASS(tunel_callback_test,
+                       "test ra_svn tunnel creation callbacks"),
     SVN_TEST_NULL
   };