📄 client.c
字号:
return SVN_NO_ERROR;}static svn_error_t *ra_svn_delete_path(void *baton, const char *path, apr_pool_t *pool){ ra_svn_reporter_baton_t *b = baton; SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "delete-path", "c", path)); return SVN_NO_ERROR;}static svn_error_t *ra_svn_link_path(void *baton, const char *path, const char *url, svn_revnum_t rev, svn_boolean_t start_empty, const char *lock_token, apr_pool_t *pool){ ra_svn_reporter_baton_t *b = baton; SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "link-path", "ccrb(?c)", path, url, rev, start_empty, lock_token)); return SVN_NO_ERROR;}static svn_error_t *ra_svn_finish_report(void *baton, apr_pool_t *pool){ ra_svn_reporter_baton_t *b = baton; SVN_ERR(svn_ra_svn_write_cmd(b->conn, b->pool, "finish-report", "")); SVN_ERR(handle_auth_request(b->sess_baton, b->pool)); SVN_ERR(svn_ra_svn_drive_editor(b->conn, b->pool, b->editor, b->edit_baton, NULL)); SVN_ERR(svn_ra_svn_read_cmd_response(b->conn, b->pool, "")); return SVN_NO_ERROR;}static svn_error_t *ra_svn_abort_report(void *baton, apr_pool_t *pool){ ra_svn_reporter_baton_t *b = baton; SVN_ERR(svn_ra_svn_write_cmd(b->conn, b->pool, "abort-report", "")); return SVN_NO_ERROR;}static svn_ra_reporter2_t ra_svn_reporter = { ra_svn_set_path, ra_svn_delete_path, ra_svn_link_path, ra_svn_finish_report, ra_svn_abort_report};static void ra_svn_get_reporter(ra_svn_session_baton_t *sess_baton, apr_pool_t *pool, const svn_delta_editor_t *editor, void *edit_baton, const svn_ra_reporter2_t **reporter, void **report_baton){ ra_svn_reporter_baton_t *b; b = apr_palloc(pool, sizeof(*b)); b->sess_baton = sess_baton; b->conn = sess_baton->conn; b->pool = pool; b->editor = editor; b->edit_baton = edit_baton; *reporter = &ra_svn_reporter; *report_baton = b;}/* --- RA LAYER IMPLEMENTATION --- *//* (Note: *ARGV is an output parameter.) */static svn_error_t *find_tunnel_agent(const char *tunnel, const char *hostinfo, const char ***argv, apr_hash_t *config, apr_pool_t *pool){ svn_config_t *cfg; const char *val, *var, *cmd; char **cmd_argv; apr_size_t len; apr_status_t status; int n; /* Look up the tunnel specification in config. */ cfg = config ? apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING) : NULL; svn_config_get(cfg, &val, SVN_CONFIG_SECTION_TUNNELS, tunnel, NULL); /* We have one predefined tunnel scheme, if it isn't overridden by config. */ if (!val && strcmp(tunnel, "ssh") == 0) val = "$SVN_SSH ssh"; if (!val || !*val) return svn_error_createf(SVN_ERR_BAD_URL, NULL, _("Undefined tunnel scheme '%s'"), tunnel); /* If the scheme definition begins with "$varname", it means there * is an environment variable which can override the command. */ if (*val == '$') { val++; len = strcspn(val, " "); var = apr_pstrmemdup(pool, val, len); cmd = getenv(var); if (!cmd) { cmd = val + len; while (*cmd == ' ') cmd++; if (!*cmd) return svn_error_createf(SVN_ERR_BAD_URL, NULL, _("Tunnel scheme %s requires environment " "variable %s to be defined"), tunnel, var); } } else cmd = val; /* Tokenize the command into a list of arguments. */ status = apr_tokenize_to_argv(cmd, &cmd_argv, pool); if (status != APR_SUCCESS) return svn_error_wrap_apr(status, _("Can't tokenize command '%s'"), cmd); /* Append the fixed arguments to the result. */ for (n = 0; cmd_argv[n] != NULL; n++) ; *argv = apr_palloc(pool, (n + 4) * sizeof(char *)); memcpy(*argv, cmd_argv, n * sizeof(char *)); (*argv)[n++] = svn_path_uri_decode(hostinfo, pool); (*argv)[n++] = "svnserve"; (*argv)[n++] = "-t"; (*argv)[n] = NULL; return SVN_NO_ERROR;}/* This function handles any errors which occur in the child process * created for a tunnel agent. We write the error out as a command * failure; the code in ra_svn_open() to read the server's greeting * will see the error and return it to the caller. */static void handle_child_process_error(apr_pool_t *pool, apr_status_t status, const char *desc){ svn_ra_svn_conn_t *conn; apr_file_t *in_file, *out_file; svn_error_t *err; if (apr_file_open_stdin(&in_file, pool) || apr_file_open_stdout(&out_file, pool)) return; conn = svn_ra_svn_create_conn(NULL, in_file, out_file, pool); err = svn_error_wrap_apr(status, _("Error in child process: %s"), desc); svn_error_clear(svn_ra_svn_write_cmd_failure(conn, pool, err)); svn_error_clear(svn_ra_svn_flush(conn, pool));}/* (Note: *CONN is an output parameter.) */static svn_error_t *make_tunnel(const char **args, svn_ra_svn_conn_t **conn, apr_pool_t *pool){ apr_status_t status; apr_proc_t *proc; apr_procattr_t *attr; 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_PATH); if (status == APR_SUCCESS) status = apr_procattr_child_errfn_set(attr, handle_child_process_error); proc = apr_palloc(pool, sizeof(*proc)); if (status == APR_SUCCESS) status = apr_proc_create(proc, *args, args, NULL, attr, pool); if (status != APR_SUCCESS) return svn_error_wrap_apr(status, _("Can't create tunnel")); /* Arrange for the tunnel agent to get a SIGKILL on pool * cleanup. This is a little extreme, but the alternatives * weren't working out: * - Closing the pipes and waiting for the process to die * was prone to mysterious hangs which are difficult to * diagnose (e.g. svnserve dumps core due to unrelated bug; * sshd goes into zombie state; ssh connection is never * closed; ssh never terminates). * - Killing the tunnel agent with SIGTERM leads to unsightly * stderr output from ssh. */ apr_pool_note_subprocess(pool, proc, APR_KILL_ALWAYS); /* 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); /* Guard against dotfile output to stdout on the server. */ *conn = svn_ra_svn_create_conn(NULL, proc->out, proc->in, pool); (*conn)->proc = proc; SVN_ERR(svn_ra_svn_skip_leading_garbage(*conn, pool)); return SVN_NO_ERROR;}/* Parse URL inot URI, validating it and setting the default port if none was given. Allocate the URI fileds out of POOL. */static svn_error_t *parse_url(const char *url, apr_uri_t *uri, apr_pool_t *pool){ apr_status_t apr_err; apr_err = apr_uri_parse(pool, url, uri); if (apr_err != 0) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Illegal svn repository URL '%s'"), url); if (! uri->port) uri->port = SVN_RA_SVN_PORT; return SVN_NO_ERROR;}/* Open a session to URL, returning it in *SESS_P, allocating it in POOL. URI is a parsed version of URL. AUTH_BATON is 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. */static svn_error_t *open_session(ra_svn_session_baton_t **sess_p, const char *url, const apr_uri_t *uri, svn_auth_baton_t *auth_baton, const char **tunnel_argv, apr_pool_t *pool){ ra_svn_session_baton_t *sess; svn_ra_svn_conn_t *conn; apr_socket_t *sock; apr_uint64_t minver, maxver; apr_array_header_t *mechlist, *caplist; if (tunnel_argv) SVN_ERR(make_tunnel(tunnel_argv, &conn, pool)); else { SVN_ERR(make_connection(uri->hostname, uri->port, &sock, pool)); conn = svn_ra_svn_create_conn(sock, NULL, NULL, pool); } /* Read server's greeting. */ SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "nnll", &minver, &maxver, &mechlist, &caplist)); /* We support protocol versions 1 and 2. */ if (minver > 2) return svn_error_createf(SVN_ERR_RA_SVN_BAD_VERSION, NULL, _("Server requires minimum version %d"), (int) minver); SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist)); sess = apr_palloc(pool, sizeof(*sess)); sess->pool = pool; sess->conn = conn; sess->protocol_version = (maxver > 2) ? 2 : maxver; sess->is_tunneled = (tunnel_argv != NULL); sess->auth_baton = auth_baton; sess->user = uri->user; sess->realm_prefix = apr_psprintf(pool, "<svn://%s:%d>", uri->hostname, uri->port); sess->tunnel_argv = tunnel_argv; /* In protocol version 2, we send back our protocol version, our * capability list, and the URL, and subsequently there is an auth * request. In version 1, we send back the protocol version, auth * mechanism, mechanism initial response, and capability list, and; * then send the URL after authentication. do_auth temporarily has * support for the mixed-style response. */ /* When we punt support for protocol version 1, we should: * - Eliminate this conditional and the similar one below * - Remove v1 support from auth_response and inline it into do_auth * - Remove the (realm == NULL) support from do_auth * - Inline do_auth into handle_auth_request * - Remove the protocol version check from handle_auth_request */ if (sess->protocol_version == 1) { SVN_ERR(do_auth(sess, mechlist, NULL, pool)); SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "c", url)); } else { SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "n(www)c", (apr_uint64_t) 2, SVN_RA_SVN_CAP_EDIT_PIPELINE, SVN_RA_SVN_CAP_SVNDIFF1, SVN_RA_SVN_CAP_ABSENT_ENTRIES, url)); SVN_ERR(handle_auth_request(sess, pool)); } /* This is where the security layer would go into effect if we * supported security layers, which is a ways off. */ /* Read the repository's uuid and root URL. */ SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "c?c", &conn->uuid, &conn->repos_root)); if (conn->repos_root) { conn->repos_root = svn_path_canonicalize(conn->repos_root, pool); /* We should check that the returned string is a prefix of url, since that's the API guarantee, but this isn't true for 1.0 servers. Checking the length prevents client crashes. */ if (strlen(conn->repos_root) > strlen(url)) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Impossibly long repository root from " "server")); } *sess_p = sess; return SVN_NO_ERROR;}#define RA_SVN_DESCRIPTION \ N_("Module for accessing a repository using the svn network protocol.")static const char *ra_svn_get_description(void){ return _(RA_SVN_DESCRIPTION);}static const char * const *ra_svn_get_schemes(apr_pool_t *pool){ static const char *schemes[] = { "svn", NULL }; return schemes;}static svn_error_t *ra_svn_open(svn_ra_session_t *session, const char *url, const svn_ra_callbacks2_t *callbacks, void *callback_baton, apr_hash_t *config, apr_pool_t *pool){ apr_pool_t *sess_pool = svn_pool_create(pool); ra_svn_session_baton_t *sess; const char *tunnel, **tunnel_argv; apr_uri_t uri; SVN_ERR(parse_url(url, &uri, sess_pool)); parse_tunnel(url, &tunnel, pool); if (tunnel) SVN_ERR(find_tunnel_agent(tunnel, uri.hostinfo, &tunnel_argv, config,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -