blob: 7b7025c2a7e0871a496ac2d11f229e8c74d68e19 [file] [log] [blame]
/* hooks.c : running repository hooks and sentinels
*
* ====================================================================
* Copyright (c) 2000-2002 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "apr_pools.h"
#include "apr_file_io.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_delta.h"
#include "svn_fs.h"
#include "svn_repos.h"
#include "repos.h"
/* In the code below, "hook" is sometimes used indiscriminately to
mean either hook or sentinel. */
/*** Hook drivers. ***/
static svn_error_t *
run_cmd_with_output (const char *cmd,
const char **args,
int *exitcode,
apr_exit_why_e *exitwhy,
apr_pool_t *pool)
{
apr_file_t *outhandle, *errhandle;
apr_status_t apr_err;
/* Get an apr_file_t representing stdout and stderr. */
apr_err = apr_file_open_stdout (&outhandle, pool);
if (apr_err)
return svn_error_create
(apr_err, 0, NULL, pool,
"run_cmd_with_output: can't open handle to stdout");
apr_err = apr_file_open_stderr (&errhandle, pool);
if (apr_err)
return svn_error_create
(apr_err, 0, NULL, pool,
"run_cmd_with_output: can't open handle to stderr");
return svn_io_run_cmd (".", cmd, args, exitcode, exitwhy, FALSE,
NULL, outhandle, errhandle, pool);
}
/* Run the start-commit hook for REPOS. Use POOL for any temporary
allocations. If the hook fails, return SVN_ERR_REPOS_HOOK_FAILURE. */
static svn_error_t *
run_start_commit_hook (svn_repos_t *repos,
const char *user,
apr_pool_t *pool)
{
enum svn_node_kind kind;
const char *hook = svn_repos_start_commit_hook (repos, pool);
if ((! svn_io_check_path (hook, &kind, pool))
&& (kind == svn_node_file))
{
svn_error_t *err;
const char *args[4];
args[0] = hook;
args[1] = svn_repos_path (repos, pool);
args[2] = user;
args[3] = NULL;
if ((err = run_cmd_with_output (hook, args, NULL, NULL, pool)))
{
return svn_error_createf
(SVN_ERR_REPOS_HOOK_FAILURE, 0, err, pool,
"run_start_commit_hook: error running cmd `%s'", hook);
}
}
return SVN_NO_ERROR;
}
/* Run the pre-commit hook for REPOS. Use POOL for any temporary
allocations. If the hook fails, return SVN_ERR_REPOS_HOOK_FAILURE. */
static svn_error_t *
run_pre_commit_hook (svn_repos_t *repos,
const char *txn_name,
apr_pool_t *pool)
{
enum svn_node_kind kind;
const char *hook = svn_repos_pre_commit_hook (repos, pool);
if ((! svn_io_check_path (hook, &kind, pool))
&& (kind == svn_node_file))
{
svn_error_t *err;
int exitcode;
apr_exit_why_e exitwhy;
const char *args[4];
args[0] = hook;
args[1] = svn_repos_path (repos, pool);
args[2] = txn_name;
args[3] = NULL;
if ((err = run_cmd_with_output (hook, args, &exitcode, &exitwhy, pool)))
{
return svn_error_createf
(SVN_ERR_REPOS_HOOK_FAILURE, 0, err, pool,
"run_pre_commit_hook: error running cmd `%s'", hook);
}
if (! APR_PROC_CHECK_EXIT (exitwhy) || exitcode != 0)
{
return svn_error_create
(SVN_ERR_REPOS_HOOK_FAILURE, 0, err, pool,
"pre-commit hook return non-zero status. Aborting txn.");
}
}
return SVN_NO_ERROR;
}
/* Run the post-commit hook for REPOS. Use POOL for any temporary
allocations. If the hook fails, run SVN_ERR_REPOS_HOOK_FAILURE. */
static svn_error_t *
run_post_commit_hook (svn_repos_t *repos,
svn_revnum_t rev,
apr_pool_t *pool)
{
enum svn_node_kind kind;
const char *hook = svn_repos_post_commit_hook (repos, pool);
if ((! svn_io_check_path (hook, &kind, pool))
&& (kind == svn_node_file))
{
svn_error_t *err;
const char *args[4];
args[0] = hook;
args[1] = svn_repos_path (repos, pool);
args[2] = apr_psprintf (pool, "%lu", rev);
args[3] = NULL;
if ((err = run_cmd_with_output (hook, args, NULL, NULL, pool)))
{
return svn_error_createf
(SVN_ERR_REPOS_HOOK_FAILURE, 0, err, pool,
"run_post_commit_hook: error running cmd `%s'", hook);
}
}
return SVN_NO_ERROR;
}
/*** Public interface. ***/
svn_error_t *
svn_repos_fs_commit_txn (const char **conflict_p,
svn_repos_t *repos,
svn_revnum_t *new_rev,
svn_fs_txn_t *txn)
{
svn_fs_t *fs = repos->fs;
apr_pool_t *pool = svn_fs_txn_pool (txn);
if (fs != svn_fs_txn_fs (txn))
return svn_error_createf
(SVN_ERR_FS_GENERAL, 0, NULL, pool,
"Transaction does not belong to given repository's filesystem");
/* Run pre-commit hooks. */
{
const char *txn_name;
SVN_ERR (svn_fs_txn_name (&txn_name, txn, pool));
SVN_ERR (run_pre_commit_hook (repos, txn_name, pool));
}
/* Commit. */
SVN_ERR (svn_fs_commit_txn (conflict_p, new_rev, txn));
/* Run post-commit hooks. */
SVN_ERR (run_post_commit_hook (repos, *new_rev, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_fs_begin_txn_for_commit (svn_fs_txn_t **txn_p,
svn_repos_t *repos,
svn_revnum_t rev,
const char *author,
svn_string_t *log_msg,
apr_pool_t *pool)
{
/* Run start-commit hooks. */
SVN_ERR (run_start_commit_hook (repos, author, pool));
/* Begin the transaction. */
SVN_ERR (svn_fs_begin_txn (txn_p, repos->fs, rev, pool));
/* We pass the author and log message to the filesystem by adding
them as properties on the txn. Later, when we commit the txn,
these properties will be copied into the newly created revision. */
{
/* User (author). */
{
svn_string_t val;
val.data = author;
val.len = strlen (author);
SVN_ERR (svn_fs_change_txn_prop (*txn_p, SVN_PROP_REVISION_AUTHOR,
&val, pool));
}
/* Log message. */
if (log_msg != NULL)
SVN_ERR (svn_fs_change_txn_prop (*txn_p, SVN_PROP_REVISION_LOG,
log_msg, pool));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_fs_begin_txn_for_update (svn_fs_txn_t **txn_p,
svn_repos_t *repos,
svn_revnum_t rev,
const char *author,
apr_pool_t *pool)
{
/* ### someday, we might run a read-hook here. */
/* Begin the transaction. */
SVN_ERR (svn_fs_begin_txn (txn_p, repos->fs, rev, pool));
/* We pass the author to the filesystem by adding it as a property
on the txn. */
{
/* User (author). */
{
svn_string_t val;
val.data = author;
val.len = strlen (author);
SVN_ERR (svn_fs_change_txn_prop (*txn_p, SVN_PROP_REVISION_AUTHOR,
&val, pool));
}
}
return SVN_NO_ERROR;
}
/*
* local variables:
* eval: (load-file "../../tools/dev/svn-dev.el")
* end:
* vim:ts=4:sw=4:expandtab:tw=80:fo=tcroq
* vim:isk=a-z,A-Z,48-57,_,.,-,>
* vim:cino=>1s,e0,n0,f0,{.5s,}0,^-.5s,=.5s,t0,+1s,c3,(0,u0,\:0
*/