summaryrefslogtreecommitdiff
path: root/subversion/libsvn_repos/hooks.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_repos/hooks.c')
-rw-r--r--subversion/libsvn_repos/hooks.c221
1 files changed, 180 insertions, 41 deletions
diff --git a/subversion/libsvn_repos/hooks.c b/subversion/libsvn_repos/hooks.c
index 0a1b447..9727599 100644
--- a/subversion/libsvn_repos/hooks.c
+++ b/subversion/libsvn_repos/hooks.c
@@ -27,9 +27,12 @@
#include <apr_pools.h>
#include <apr_file_io.h>
+#include "svn_config.h"
+#include "svn_hash.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
+#include "svn_pools.h"
#include "svn_repos.h"
#include "svn_utf.h"
#include "repos.h"
@@ -159,6 +162,37 @@ check_hook_result(const char *name, const char *cmd, apr_proc_t *cmd_proc,
failure_message->data);
}
+/* Copy the environment given as key/value pairs of ENV_HASH into
+ * an array of C strings allocated in RESULT_POOL.
+ * If the hook environment is empty, return NULL.
+ * Use SCRATCH_POOL for temporary allocations. */
+static const char **
+env_from_env_hash(apr_hash_t *env_hash,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *hi;
+ const char **env;
+ const char **envp;
+
+ if (!env_hash)
+ return NULL;
+
+ env = apr_palloc(result_pool,
+ sizeof(const char *) * (apr_hash_count(env_hash) + 1));
+ envp = env;
+ for (hi = apr_hash_first(scratch_pool, env_hash); hi; hi = apr_hash_next(hi))
+ {
+ *envp = apr_psprintf(result_pool, "%s=%s",
+ (const char *)svn__apr_hash_index_key(hi),
+ (const char *)svn__apr_hash_index_val(hi));
+ envp++;
+ }
+ *envp = NULL;
+
+ return env;
+}
+
/* NAME, CMD and ARGS are the name, path to and arguments for the hook
program that is to be run. The hook's exit status will be checked,
and if an error occurred the hook's stderr output will be added to
@@ -174,13 +208,16 @@ run_hook_cmd(svn_string_t **result,
const char *name,
const char *cmd,
const char **args,
+ apr_hash_t *hooks_env,
apr_file_t *stdin_handle,
apr_pool_t *pool)
{
apr_file_t *null_handle;
apr_status_t apr_err;
svn_error_t *err;
- apr_proc_t cmd_proc;
+ apr_proc_t cmd_proc = {0};
+ apr_pool_t *cmd_pool;
+ apr_hash_t *hook_env = NULL;
if (result)
{
@@ -196,41 +233,49 @@ run_hook_cmd(svn_string_t **result,
(apr_err, _("Can't create null stdout for hook '%s'"), cmd);
}
- err = svn_io_start_cmd2(&cmd_proc, ".", cmd, args, FALSE,
- FALSE, stdin_handle, result != NULL, null_handle,
- TRUE, NULL, pool);
+ /* Tie resources allocated for the command to a special pool which we can
+ * destroy in order to clean up the stderr pipe opened for the process. */
+ cmd_pool = svn_pool_create(pool);
- if (err)
+ /* Check if a custom environment is defined for this hook, or else
+ * whether a default environment is defined. */
+ if (hooks_env)
{
- /* CMD_PROC is not safe to use. Bail. */
- return svn_error_createf
- (SVN_ERR_REPOS_HOOK_FAILURE, err, _("Failed to start '%s' hook"), cmd);
+ hook_env = svn_hash_gets(hooks_env, name);
+ if (hook_env == NULL)
+ hook_env = svn_hash_gets(hooks_env,
+ SVN_REPOS__HOOKS_ENV_DEFAULT_SECTION);
}
+
+ err = svn_io_start_cmd3(&cmd_proc, ".", cmd, args,
+ env_from_env_hash(hook_env, pool, pool),
+ FALSE, FALSE, stdin_handle, result != NULL,
+ null_handle, TRUE, NULL, cmd_pool);
+ if (!err)
+ err = check_hook_result(name, cmd, &cmd_proc, cmd_proc.err, pool);
else
{
- err = check_hook_result(name, cmd, &cmd_proc, cmd_proc.err, pool);
+ /* The command could not be started for some reason. */
+ err = svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, err,
+ _("Failed to start '%s' hook"), cmd);
}
/* Hooks are fallible, and so hook failure is "expected" to occur at
times. When such a failure happens we still want to close the pipe
and null file */
- apr_err = apr_file_close(cmd_proc.err);
- if (!err && apr_err)
- return svn_error_wrap_apr
- (apr_err, _("Error closing read end of stderr pipe"));
-
- if (result)
+ if (!err && result)
{
svn_stringbuf_t *native_stdout;
- SVN_ERR(svn_stringbuf_from_aprfile(&native_stdout, cmd_proc.out, pool));
- apr_err = apr_file_close(cmd_proc.out);
- if (!err && apr_err)
- return svn_error_wrap_apr
- (apr_err, _("Error closing read end of stderr pipe"));
-
- *result = svn_stringbuf__morph_into_string(native_stdout);
+ err = svn_stringbuf_from_aprfile(&native_stdout, cmd_proc.out, pool);
+ if (!err)
+ *result = svn_stringbuf__morph_into_string(native_stdout);
}
- else
+
+ /* Close resources allocated by svn_io_start_cmd3(), such as the pipe. */
+ svn_pool_destroy(cmd_pool);
+
+ /* Close the null handle. */
+ if (null_handle)
{
apr_err = apr_file_close(null_handle);
if (!err && apr_err)
@@ -311,6 +356,86 @@ check_hook_cmd(const char *hook, svn_boolean_t *broken_link, apr_pool_t *pool)
return NULL;
}
+/* Baton for parse_hooks_env_option. */
+struct parse_hooks_env_option_baton {
+ /* The name of the section being parsed. If not the default section,
+ * the section name should match the name of a hook to which the
+ * options apply. */
+ const char *section;
+ apr_hash_t *hooks_env;
+} parse_hooks_env_option_baton;
+
+/* An implementation of svn_config_enumerator2_t.
+ * Set environment variable NAME to value VALUE in the environment for
+ * all hooks (in case the current section is the default section),
+ * or the hook with the name corresponding to the current section's name. */
+static svn_boolean_t
+parse_hooks_env_option(const char *name, const char *value,
+ void *baton, apr_pool_t *pool)
+{
+ struct parse_hooks_env_option_baton *bo = baton;
+ apr_pool_t *result_pool = apr_hash_pool_get(bo->hooks_env);
+ apr_hash_t *hook_env;
+
+ hook_env = svn_hash_gets(bo->hooks_env, bo->section);
+ if (hook_env == NULL)
+ {
+ hook_env = apr_hash_make(result_pool);
+ svn_hash_sets(bo->hooks_env, apr_pstrdup(result_pool, bo->section),
+ hook_env);
+ }
+ svn_hash_sets(hook_env, apr_pstrdup(result_pool, name),
+ apr_pstrdup(result_pool, value));
+
+ return TRUE;
+}
+
+struct parse_hooks_env_section_baton {
+ svn_config_t *cfg;
+ apr_hash_t *hooks_env;
+} parse_hooks_env_section_baton;
+
+/* An implementation of svn_config_section_enumerator2_t. */
+static svn_boolean_t
+parse_hooks_env_section(const char *name, void *baton, apr_pool_t *pool)
+{
+ struct parse_hooks_env_section_baton *b = baton;
+ struct parse_hooks_env_option_baton bo;
+
+ bo.section = name;
+ bo.hooks_env = b->hooks_env;
+
+ (void)svn_config_enumerate2(b->cfg, name, parse_hooks_env_option, &bo, pool);
+
+ return TRUE;
+}
+
+svn_error_t *
+svn_repos__parse_hooks_env(apr_hash_t **hooks_env_p,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_config_t *cfg;
+ struct parse_hooks_env_section_baton b;
+
+ if (local_abspath)
+ {
+ SVN_ERR(svn_config_read3(&cfg, local_abspath, FALSE,
+ TRUE, TRUE, scratch_pool));
+ b.cfg = cfg;
+ b.hooks_env = apr_hash_make(result_pool);
+ (void)svn_config_enumerate_sections2(cfg, parse_hooks_env_section, &b,
+ scratch_pool);
+ *hooks_env_p = b.hooks_env;
+ }
+ else
+ {
+ *hooks_env_p = NULL;
+ }
+
+ return SVN_NO_ERROR;
+}
/* Return an error for the failure of HOOK due to a broken symlink. */
static svn_error_t *
@@ -323,8 +448,10 @@ hook_symlink_error(const char *hook)
svn_error_t *
svn_repos__hooks_start_commit(svn_repos_t *repos,
+ apr_hash_t *hooks_env,
const char *user,
const apr_array_header_t *capabilities,
+ const char *txn_name,
apr_pool_t *pool)
{
const char *hook = svn_repos_start_commit_hook(repos, pool);
@@ -336,7 +463,7 @@ svn_repos__hooks_start_commit(svn_repos_t *repos,
}
else if (hook)
{
- const char *args[5];
+ const char *args[6];
char *capabilities_string;
if (capabilities)
@@ -356,10 +483,11 @@ svn_repos__hooks_start_commit(svn_repos_t *repos,
args[1] = svn_dirent_local_style(svn_repos_path(repos, pool), pool);
args[2] = user ? user : "";
args[3] = capabilities_string;
- args[4] = NULL;
+ args[4] = txn_name;
+ args[5] = NULL;
- SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_START_COMMIT, hook, args, NULL,
- pool));
+ SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_START_COMMIT, hook, args,
+ hooks_env, NULL, pool));
}
return SVN_NO_ERROR;
@@ -403,6 +531,7 @@ lock_token_content(apr_file_t **handle, apr_hash_t *lock_tokens,
svn_error_t *
svn_repos__hooks_pre_commit(svn_repos_t *repos,
+ apr_hash_t *hooks_env,
const char *txn_name,
apr_pool_t *pool)
{
@@ -438,7 +567,7 @@ svn_repos__hooks_pre_commit(svn_repos_t *repos,
APR_READ, APR_OS_DEFAULT, pool));
SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_PRE_COMMIT, hook, args,
- stdin_handle, pool));
+ hooks_env, stdin_handle, pool));
}
return SVN_NO_ERROR;
@@ -447,7 +576,9 @@ svn_repos__hooks_pre_commit(svn_repos_t *repos,
svn_error_t *
svn_repos__hooks_post_commit(svn_repos_t *repos,
+ apr_hash_t *hooks_env,
svn_revnum_t rev,
+ const char *txn_name,
apr_pool_t *pool)
{
const char *hook = svn_repos_post_commit_hook(repos, pool);
@@ -459,15 +590,16 @@ svn_repos__hooks_post_commit(svn_repos_t *repos,
}
else if (hook)
{
- const char *args[4];
+ const char *args[5];
args[0] = hook;
args[1] = svn_dirent_local_style(svn_repos_path(repos, pool), pool);
args[2] = apr_psprintf(pool, "%ld", rev);
- args[3] = NULL;
+ args[3] = txn_name;
+ args[4] = NULL;
- SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_POST_COMMIT, hook, args, NULL,
- pool));
+ SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_POST_COMMIT, hook, args,
+ hooks_env, NULL, pool));
}
return SVN_NO_ERROR;
@@ -476,6 +608,7 @@ svn_repos__hooks_post_commit(svn_repos_t *repos,
svn_error_t *
svn_repos__hooks_pre_revprop_change(svn_repos_t *repos,
+ apr_hash_t *hooks_env,
svn_revnum_t rev,
const char *author,
const char *name,
@@ -514,8 +647,8 @@ svn_repos__hooks_pre_revprop_change(svn_repos_t *repos,
args[5] = action_string;
args[6] = NULL;
- SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_PRE_REVPROP_CHANGE, hook, args,
- stdin_handle, pool));
+ SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_PRE_REVPROP_CHANGE, hook,
+ args, hooks_env, stdin_handle, pool));
SVN_ERR(svn_io_file_close(stdin_handle, pool));
}
@@ -538,6 +671,7 @@ svn_repos__hooks_pre_revprop_change(svn_repos_t *repos,
svn_error_t *
svn_repos__hooks_post_revprop_change(svn_repos_t *repos,
+ apr_hash_t *hooks_env,
svn_revnum_t rev,
const char *author,
const char *name,
@@ -577,7 +711,7 @@ svn_repos__hooks_post_revprop_change(svn_repos_t *repos,
args[6] = NULL;
SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_POST_REVPROP_CHANGE, hook,
- args, stdin_handle, pool));
+ args, hooks_env, stdin_handle, pool));
SVN_ERR(svn_io_file_close(stdin_handle, pool));
}
@@ -588,6 +722,7 @@ svn_repos__hooks_post_revprop_change(svn_repos_t *repos,
svn_error_t *
svn_repos__hooks_pre_lock(svn_repos_t *repos,
+ apr_hash_t *hooks_env,
const char **token,
const char *path,
const char *username,
@@ -607,6 +742,7 @@ svn_repos__hooks_pre_lock(svn_repos_t *repos,
const char *args[7];
svn_string_t *buf;
+
args[0] = hook;
args[1] = svn_dirent_local_style(svn_repos_path(repos, pool), pool);
args[2] = path;
@@ -615,8 +751,8 @@ svn_repos__hooks_pre_lock(svn_repos_t *repos,
args[5] = steal_lock ? "1" : "0";
args[6] = NULL;
- SVN_ERR(run_hook_cmd(&buf, SVN_REPOS__HOOK_PRE_LOCK, hook, args, NULL,
- pool));
+ SVN_ERR(run_hook_cmd(&buf, SVN_REPOS__HOOK_PRE_LOCK, hook, args,
+ hooks_env, NULL, pool));
if (token)
/* No validation here; the FS will take care of that. */
@@ -632,6 +768,7 @@ svn_repos__hooks_pre_lock(svn_repos_t *repos,
svn_error_t *
svn_repos__hooks_post_lock(svn_repos_t *repos,
+ apr_hash_t *hooks_env,
const apr_array_header_t *paths,
const char *username,
apr_pool_t *pool)
@@ -660,7 +797,7 @@ svn_repos__hooks_post_lock(svn_repos_t *repos,
args[4] = NULL;
SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_POST_LOCK, hook, args,
- stdin_handle, pool));
+ hooks_env, stdin_handle, pool));
SVN_ERR(svn_io_file_close(stdin_handle, pool));
}
@@ -671,6 +808,7 @@ svn_repos__hooks_post_lock(svn_repos_t *repos,
svn_error_t *
svn_repos__hooks_pre_unlock(svn_repos_t *repos,
+ apr_hash_t *hooks_env,
const char *path,
const char *username,
const char *token,
@@ -696,8 +834,8 @@ svn_repos__hooks_pre_unlock(svn_repos_t *repos,
args[5] = break_lock ? "1" : "0";
args[6] = NULL;
- SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_PRE_UNLOCK, hook, args, NULL,
- pool));
+ SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_PRE_UNLOCK, hook, args,
+ hooks_env, NULL, pool));
}
return SVN_NO_ERROR;
@@ -706,6 +844,7 @@ svn_repos__hooks_pre_unlock(svn_repos_t *repos,
svn_error_t *
svn_repos__hooks_post_unlock(svn_repos_t *repos,
+ apr_hash_t *hooks_env,
const apr_array_header_t *paths,
const char *username,
apr_pool_t *pool)
@@ -734,7 +873,7 @@ svn_repos__hooks_post_unlock(svn_repos_t *repos,
args[4] = NULL;
SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_POST_UNLOCK, hook, args,
- stdin_handle, pool));
+ hooks_env, stdin_handle, pool));
SVN_ERR(svn_io_file_close(stdin_handle, pool));
}