Change the format of the revprops block sent in svnserve for
[svn.git] / subversion / libsvn_repos / hooks.c
blob13fa0cd384c819a8d110169e442331368088a0d3
1 /* hooks.c : running repository hooks
3 * ====================================================================
4 * Copyright (c) 2000-2006 CollabNet. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://subversion.tigris.org/.
15 * ====================================================================
18 #include <stdio.h>
19 #include <string.h>
20 #include <ctype.h>
22 #include <apr_pools.h>
23 #include <apr_file_io.h>
25 #ifdef AS400
26 #include <apr_portable.h>
27 #include <spawn.h>
28 #include <fcntl.h>
29 #endif
31 #include "svn_error.h"
32 #include "svn_path.h"
33 #include "svn_repos.h"
34 #include "svn_utf.h"
35 #include "repos.h"
36 #include "svn_private_config.h"
40 /*** Hook drivers. ***/
42 #ifndef AS400
43 /* Helper function for run_hook_cmd(). Wait for a hook to finish
44 executing and return either SVN_NO_ERROR if the hook script completed
45 without error, or an error describing the reason for failure.
47 NAME and CMD are the name and path of the hook program, CMD_PROC
48 is a pointer to the structure representing the running process,
49 and READ_ERRHANDLE is an open handle to the hook's stderr.
51 Hooks are considered to have failed if we are unable to wait for the
52 process, if we are unable to read from the hook's stderr, if the
53 process has failed to exit cleanly (due to a coredump, for example),
54 or if the process returned a non-zero return code.
56 Any error output returned by the hook's stderr will be included in an
57 error message, though the presence of output on stderr is not itself
58 a reason to fail a hook. */
59 static svn_error_t *
60 check_hook_result(const char *name, const char *cmd, apr_proc_t *cmd_proc,
61 apr_file_t *read_errhandle, apr_pool_t *pool)
63 svn_error_t *err, *err2;
64 svn_stringbuf_t *native_stderr, *failure_message;
65 const char *utf8_stderr;
66 int exitcode;
67 apr_exit_why_e exitwhy;
69 err2 = svn_stringbuf_from_aprfile(&native_stderr, read_errhandle, pool);
71 err = svn_io_wait_for_cmd(cmd_proc, cmd, &exitcode, &exitwhy, pool);
72 if (err)
74 svn_error_clear(err2);
75 return err;
78 if (APR_PROC_CHECK_EXIT(exitwhy) && exitcode == 0)
80 /* The hook exited cleanly. However, if we got an error reading
81 the hook's stderr, fail the hook anyway, because this might be
82 symptomatic of a more important problem. */
83 if (err2)
85 return svn_error_createf
86 (SVN_ERR_REPOS_HOOK_FAILURE, err2,
87 _("'%s' hook succeeded, but error output could not be read"),
88 name);
91 return SVN_NO_ERROR;
94 /* The hook script failed. */
96 /* If we got the stderr output okay, try to translate it into UTF-8.
97 Ensure there is something sensible in the UTF-8 string regardless. */
98 if (!err2)
100 err2 = svn_utf_cstring_to_utf8(&utf8_stderr, native_stderr->data, pool);
101 if (err2)
102 utf8_stderr = _("[Error output could not be translated from the "
103 "native locale to UTF-8.]");
105 else
107 utf8_stderr = _("[Error output could not be read.]");
109 /*### It would be nice to include the text of any translation or read
110 error in the messages above before we clear it here. */
111 svn_error_clear(err2);
113 if (!APR_PROC_CHECK_EXIT(exitwhy))
115 failure_message = svn_stringbuf_createf(pool,
116 _("'%s' hook failed (did not exit cleanly: "
117 "apr_exit_why_e was %d, exitcode was %d). "),
118 name, exitwhy, exitcode);
120 else
122 const char *action;
123 if (strcmp(name, "start-commit") == 0
124 || strcmp(name, "pre-commit") == 0)
125 action = _("Commit");
126 else if (strcmp(name, "pre-revprop-change") == 0)
127 action = _("Revprop change");
128 else if (strcmp(name, "pre-lock") == 0)
129 action = _("Lock");
130 else if (strcmp(name, "pre-unlock") == 0)
131 action = _("Unlock");
132 else
133 action = NULL;
134 if (action == NULL)
135 failure_message = svn_stringbuf_createf(
136 pool, _("%s hook failed (exit code %d)"),
137 name, exitcode);
138 else
139 failure_message = svn_stringbuf_createf(
140 pool, _("%s blocked by %s hook (exit code %d)"),
141 action, name, exitcode);
144 if (utf8_stderr[0])
146 svn_stringbuf_appendcstr(failure_message,
147 _(" with output:\n"));
148 svn_stringbuf_appendcstr(failure_message, utf8_stderr);
150 else
152 svn_stringbuf_appendcstr(failure_message,
153 _(" with no output."));
156 return svn_error_create(SVN_ERR_REPOS_HOOK_FAILURE, err,
157 failure_message->data);
159 #endif /* AS400 */
161 /* NAME, CMD and ARGS are the name, path to and arguments for the hook
162 program that is to be run. The hook's exit status will be checked,
163 and if an error occurred the hook's stderr output will be added to
164 the returned error.
166 If STDIN_HANDLE is non-null, pass it as the hook's stdin, else pass
167 no stdin to the hook. */
168 static svn_error_t *
169 run_hook_cmd(const char *name,
170 const char *cmd,
171 const char **args,
172 apr_file_t *stdin_handle,
173 apr_pool_t *pool)
174 #ifndef AS400
176 apr_file_t *read_errhandle, *write_errhandle, *null_handle;
177 apr_status_t apr_err;
178 svn_error_t *err;
179 apr_proc_t cmd_proc;
181 /* Create a pipe to access stderr of the child. */
182 apr_err = apr_file_pipe_create(&read_errhandle, &write_errhandle, pool);
183 if (apr_err)
184 return svn_error_wrap_apr
185 (apr_err, _("Can't create pipe for hook '%s'"), cmd);
187 /* Pipes are inherited by default, but we don't want that, since
188 APR will duplicate the write end of the pipe for the child process.
189 Not closing the read end is harmless, but if the write end is inherited,
190 it will be inherited by grandchildren as well. This causes problems
191 if a hook script puts long-running jobs in the background. Even if
192 they redirect stderr to something else, the write end of our pipe will
193 still be open, causing us to block. */
194 apr_err = apr_file_inherit_unset(read_errhandle);
195 if (apr_err)
196 return svn_error_wrap_apr
197 (apr_err, _("Can't make pipe read handle non-inherited for hook '%s'"),
198 cmd);
200 apr_err = apr_file_inherit_unset(write_errhandle);
201 if (apr_err)
202 return svn_error_wrap_apr
203 (apr_err, _("Can't make pipe write handle non-inherited for hook '%s'"),
204 cmd);
207 /* Redirect stdout to the null device */
208 apr_err = apr_file_open(&null_handle, SVN_NULL_DEVICE_NAME, APR_WRITE,
209 APR_OS_DEFAULT, pool);
210 if (apr_err)
211 return svn_error_wrap_apr
212 (apr_err, _("Can't create null stdout for hook '%s'"), cmd);
214 err = svn_io_start_cmd(&cmd_proc, ".", cmd, args, FALSE,
215 stdin_handle, null_handle, write_errhandle, pool);
217 /* This seems to be done automatically if we pass the third parameter of
218 apr_procattr_child_in/out_set(), but svn_io_run_cmd()'s interface does
219 not support those parameters. We need to close the write end of the
220 pipe so we don't hang on the read end later, if we need to read it. */
221 apr_err = apr_file_close(write_errhandle);
222 if (!err && apr_err)
223 return svn_error_wrap_apr
224 (apr_err, _("Error closing write end of stderr pipe"));
226 if (err)
228 err = svn_error_createf
229 (SVN_ERR_REPOS_HOOK_FAILURE, err, _("Failed to start '%s' hook"), cmd);
231 else
233 err = check_hook_result(name, cmd, &cmd_proc, read_errhandle, pool);
236 /* Hooks are fallible, and so hook failure is "expected" to occur at
237 times. When such a failure happens we still want to close the pipe
238 and null file */
239 apr_err = apr_file_close(read_errhandle);
240 if (!err && apr_err)
241 return svn_error_wrap_apr
242 (apr_err, _("Error closing read end of stderr pipe"));
244 apr_err = apr_file_close(null_handle);
245 if (!err && apr_err)
246 return svn_error_wrap_apr(apr_err, _("Error closing null file"));
248 return err;
250 #else /* Run hooks with spawn() on OS400. */
251 #define AS400_BUFFER_SIZE 256
253 const char **native_args;
254 int fd_map[3], stderr_pipe[2], exitcode;
255 svn_stringbuf_t *script_output;
256 pid_t child_pid, wait_rv;
257 apr_size_t args_arr_size = 0, i;
258 struct inheritance xmp_inherit = {0};
259 #pragma convert(0)
260 /* Despite the UTF support in V5R4 a few functions still require
261 * EBCDIC args. */
262 char *xmp_envp[2] = {"QIBM_USE_DESCRIPTOR_STDIO=Y", NULL};
263 const char *dev_null_ebcdic = SVN_NULL_DEVICE_NAME;
264 #pragma convert(1208)
266 /* Find number of elements in args array. */
267 while (args[args_arr_size] != NULL)
268 args_arr_size++;
270 /* Allocate memory for the native_args string array plus one for
271 * the ending null element. */
272 native_args = apr_palloc(pool, sizeof(char *) * args_arr_size + 1);
274 /* Convert UTF-8 args to EBCDIC for use by spawn(). */
275 for (i = 0; args[i] != NULL; i++)
277 SVN_ERR(svn_utf_cstring_from_utf8_ex2((const char**)(&(native_args[i])),
278 args[i], (const char *)0,
279 pool));
282 /* Make the last element in the array a NULL pointer as required
283 * by spawn. */
284 native_args[args_arr_size] = NULL;
286 /* Map stdin. */
287 if (stdin_handle)
289 /* Get OS400 file descriptor of APR stdin file and map it. */
290 if (apr_os_file_get(&fd_map[0], stdin_handle))
292 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
293 "Error converting APR file to OS400 "
294 "type for hook script '%s'", cmd);
297 else
299 fd_map[0] = open(dev_null_ebcdic, O_RDONLY);
300 if (fd_map[0] == -1)
302 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
303 "Error opening /dev/null for hook "
304 "script '%s'", cmd);
308 /* Map stdout. */
309 fd_map[1] = open(dev_null_ebcdic, O_WRONLY);
310 if (fd_map[1] == -1)
311 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
312 "Error opening /dev/null for hook script '%s'",
313 cmd);
315 /* Map stderr. */
316 /* Get pipe for hook's stderr. */
317 if (pipe(stderr_pipe) != 0)
319 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
320 "Can't create stderr pipe for "
321 "hook '%s'", cmd);
323 fd_map[2] = stderr_pipe[1];
325 /* Spawn the hook command. */
326 child_pid = spawn(native_args[0], 3, fd_map, &xmp_inherit, native_args,
327 xmp_envp);
328 if (child_pid == -1)
330 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
331 "Error spawning process for hook script '%s'",
332 cmd);
335 /* Close the stdout file descriptor. */
336 if (close(fd_map[1]) == -1)
337 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
338 "Error closing write end of stdout pipe to "
339 "hook script '%s'", cmd);
341 /* Close the write end of the stderr pipe so any subsequent reads
342 * don't hang. */
343 if (close(fd_map[2]) == -1)
344 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
345 "Error closing write end of stderr pipe to "
346 "hook script '%s'", cmd);
348 script_output = svn_stringbuf_create("", pool);
350 while (1)
352 int rc;
354 svn_stringbuf_ensure(script_output,
355 script_output->len + AS400_BUFFER_SIZE + 1);
357 rc = read(stderr_pipe[0],
358 &(script_output->data[script_output->len]),
359 AS400_BUFFER_SIZE);
361 if (rc == -1)
363 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
364 "Error reading stderr of hook "
365 "script '%s'", cmd);
368 script_output->len += rc;
370 /* If read() returned 0 then EOF was found and we are done reading
371 * stderr. */
372 if (rc == 0)
374 script_output->data[script_output->len] = '\0';
375 break;
379 /* Close the read end of the stderr pipe. */
380 if (close(stderr_pipe[0]) == -1)
381 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
382 "Error closing read end of stderr "
383 "pipe to hook script '%s'", cmd);
385 /* Wait for the child process to complete. */
386 wait_rv = waitpid(child_pid, &exitcode, 0);
387 if (wait_rv == -1)
389 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
390 "Error waiting for process completion of "
391 "hook script '%s'", cmd);
395 if (WIFEXITED(exitcode))
397 if (WEXITSTATUS(exitcode))
399 svn_error_t *err;
400 const char *utf8_stderr = NULL;
401 svn_stringbuf_t *failure_message = svn_stringbuf_createf(
402 pool, "'%s' hook failed (exited with a non-zero exitcode "
403 "of %d). ", name, exitcode);
405 if (!svn_stringbuf_isempty(script_output))
407 /* OS400 scripts produce EBCDIC stderr, so convert it. */
408 err = svn_utf_cstring_to_utf8_ex2(&utf8_stderr,
409 script_output->data,
410 (const char*)0, pool);
411 if (err)
413 utf8_stderr = "[Error output could not be translated from "
414 "the native locale to UTF-8.]";
415 svn_error_clear(err);
420 if (utf8_stderr)
422 svn_stringbuf_appendcstr(failure_message,
423 "The following error output was "
424 "produced by the hook:\n");
425 svn_stringbuf_appendcstr(failure_message, utf8_stderr);
427 else
429 svn_stringbuf_appendcstr(failure_message,
430 "No error output was produced by "
431 "the hook.");
433 return svn_error_create(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
434 failure_message->data);
436 else
437 /* Success! */
438 return SVN_NO_ERROR;
440 else if (WIFSIGNALED(exitcode))
442 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
443 "Process '%s' failed because of an "
444 "uncaught terminating signal", cmd);
446 else if (WIFEXCEPTION(exitcode))
448 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
449 "Process '%s' failed unexpectedly with "
450 "OS400 exception %d", cmd,
451 WEXCEPTNUMBER(exitcode));
453 else if (WIFSTOPPED(exitcode))
455 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
456 "Process '%s' stopped unexpectedly by "
457 "signal %d", cmd, WSTOPSIG(exitcode));
459 else
461 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
462 "Process '%s' failed unexpectedly", cmd);
465 #endif /* AS400 */
468 /* Create a temporary file F that will automatically be deleted when it is
469 closed. Fill it with VALUE, and leave it open and rewound, ready to be
470 read from. */
471 static svn_error_t *
472 create_temp_file(apr_file_t **f, const svn_string_t *value, apr_pool_t *pool)
474 const char *dir;
475 apr_off_t offset = 0;
477 SVN_ERR(svn_io_temp_dir(&dir, pool));
478 SVN_ERR(svn_io_open_unique_file2(f, NULL,
479 svn_path_join(dir, "hook-input", pool),
480 "", svn_io_file_del_on_close, pool));
481 SVN_ERR(svn_io_file_write_full(*f, value->data, value->len, NULL, pool));
482 SVN_ERR(svn_io_file_seek(*f, APR_SET, &offset, pool));
483 return SVN_NO_ERROR;
487 /* Check if the HOOK program exists and is a file or a symbolic link, using
488 POOL for temporary allocations.
490 If the hook exists but is a broken symbolic link, set *BROKEN_LINK
491 to TRUE, else if the hook program exists set *BROKEN_LINK to FALSE.
493 Return the hook program if found, else return NULL and don't touch
494 *BROKEN_LINK.
496 static const char*
497 check_hook_cmd(const char *hook, svn_boolean_t *broken_link, apr_pool_t *pool)
499 static const char* const check_extns[] = {
500 #ifdef WIN32
501 /* For WIN32, we need to check with file name extension(s) added.
503 As Windows Scripting Host (.wsf) files can accomodate (at least)
504 JavaScript (.js) and VB Script (.vbs) code, extensions for the
505 corresponding file types need not be enumerated explicitly. */
506 ".exe", ".cmd", ".bat", ".wsf", /* ### Any other extensions? */
507 #else
509 #endif
510 NULL
513 const char *const *extn;
514 svn_error_t *err = NULL;
515 svn_boolean_t is_special;
516 for (extn = check_extns; *extn; ++extn)
518 const char *const hook_path =
519 (**extn ? apr_pstrcat(pool, hook, *extn, 0) : hook);
521 svn_node_kind_t kind;
522 if (!(err = svn_io_check_resolved_path(hook_path, &kind, pool))
523 && kind == svn_node_file)
525 *broken_link = FALSE;
526 return hook_path;
528 svn_error_clear(err);
529 if (!(err = svn_io_check_special_path(hook_path, &kind, &is_special,
530 pool))
531 && is_special == TRUE)
533 *broken_link = TRUE;
534 return hook_path;
536 svn_error_clear(err);
538 return NULL;
542 /* Return an error for the failure of HOOK due to a broken symlink. */
543 static svn_error_t *
544 hook_symlink_error(const char *hook)
546 return svn_error_createf
547 (SVN_ERR_REPOS_HOOK_FAILURE, NULL,
548 _("Failed to run '%s' hook; broken symlink"), hook);
551 svn_error_t *
552 svn_repos__hooks_start_commit(svn_repos_t *repos,
553 const char *user,
554 apr_array_header_t *capabilities,
555 apr_pool_t *pool)
557 const char *hook = svn_repos_start_commit_hook(repos, pool);
558 svn_boolean_t broken_link;
560 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
562 return hook_symlink_error(hook);
564 else if (hook)
566 const char *args[5];
567 char *capabilities_string = svn_cstring_join(capabilities, ":", pool);
569 /* Get rid of that annoying final colon. */
570 if (capabilities_string[0])
571 capabilities_string[strlen(capabilities_string) - 1] = '\0';
573 args[0] = hook;
574 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
575 args[2] = user ? user : "";
576 args[3] = capabilities_string;
577 args[4] = NULL;
579 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_START_COMMIT, hook, args, NULL,
580 pool));
583 return SVN_NO_ERROR;
587 svn_error_t *
588 svn_repos__hooks_pre_commit(svn_repos_t *repos,
589 const char *txn_name,
590 apr_pool_t *pool)
592 const char *hook = svn_repos_pre_commit_hook(repos, pool);
593 svn_boolean_t broken_link;
595 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
597 return hook_symlink_error(hook);
599 else if (hook)
601 const char *args[4];
603 args[0] = hook;
604 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
605 args[2] = txn_name;
606 args[3] = NULL;
608 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_PRE_COMMIT, hook, args, NULL,
609 pool));
612 return SVN_NO_ERROR;
616 svn_error_t *
617 svn_repos__hooks_post_commit(svn_repos_t *repos,
618 svn_revnum_t rev,
619 apr_pool_t *pool)
621 const char *hook = svn_repos_post_commit_hook(repos, pool);
622 svn_boolean_t broken_link;
624 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
626 return hook_symlink_error(hook);
628 else if (hook)
630 const char *args[4];
632 args[0] = hook;
633 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
634 args[2] = apr_psprintf(pool, "%ld", rev);
635 args[3] = NULL;
637 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_POST_COMMIT, hook, args, NULL,
638 pool));
641 return SVN_NO_ERROR;
645 svn_error_t *
646 svn_repos__hooks_pre_revprop_change(svn_repos_t *repos,
647 svn_revnum_t rev,
648 const char *author,
649 const char *name,
650 const svn_string_t *new_value,
651 char action,
652 apr_pool_t *pool)
654 const char *hook = svn_repos_pre_revprop_change_hook(repos, pool);
655 svn_boolean_t broken_link;
657 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
659 return hook_symlink_error(hook);
661 else if (hook)
663 const char *args[7];
664 apr_file_t *stdin_handle = NULL;
665 char action_string[2];
667 /* Pass the new value as stdin to hook */
668 if (new_value)
669 SVN_ERR(create_temp_file(&stdin_handle, new_value, pool));
670 else
671 SVN_ERR(svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME,
672 APR_READ, APR_OS_DEFAULT, pool));
674 action_string[0] = action;
675 action_string[1] = '\0';
677 args[0] = hook;
678 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
679 args[2] = apr_psprintf(pool, "%ld", rev);
680 args[3] = author ? author : "";
681 args[4] = name;
682 args[5] = action_string;
683 args[6] = NULL;
685 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_PRE_REVPROP_CHANGE, hook, args,
686 stdin_handle, pool));
688 SVN_ERR(svn_io_file_close(stdin_handle, pool));
690 else
692 /* If the pre- hook doesn't exist at all, then default to
693 MASSIVE PARANOIA. Changing revision properties is a lossy
694 operation; so unless the repository admininstrator has
695 *deliberately* created the pre-hook, disallow all changes. */
696 return
697 svn_error_create
698 (SVN_ERR_REPOS_DISABLED_FEATURE, NULL,
699 _("Repository has not been enabled to accept revision propchanges;\n"
700 "ask the administrator to create a pre-revprop-change hook"));
703 return SVN_NO_ERROR;
707 svn_error_t *
708 svn_repos__hooks_post_revprop_change(svn_repos_t *repos,
709 svn_revnum_t rev,
710 const char *author,
711 const char *name,
712 svn_string_t *old_value,
713 char action,
714 apr_pool_t *pool)
716 const char *hook = svn_repos_post_revprop_change_hook(repos, pool);
717 svn_boolean_t broken_link;
719 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
721 return hook_symlink_error(hook);
723 else if (hook)
725 const char *args[7];
726 apr_file_t *stdin_handle = NULL;
727 char action_string[2];
729 /* Pass the old value as stdin to hook */
730 if (old_value)
731 SVN_ERR(create_temp_file(&stdin_handle, old_value, pool));
732 else
733 SVN_ERR(svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME,
734 APR_READ, APR_OS_DEFAULT, pool));
736 action_string[0] = action;
737 action_string[1] = '\0';
739 args[0] = hook;
740 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
741 args[2] = apr_psprintf(pool, "%ld", rev);
742 args[3] = author ? author : "";
743 args[4] = name;
744 args[5] = action_string;
745 args[6] = NULL;
747 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_POST_REVPROP_CHANGE, hook, args,
748 stdin_handle, pool));
750 SVN_ERR(svn_io_file_close(stdin_handle, pool));
753 return SVN_NO_ERROR;
758 svn_error_t *
759 svn_repos__hooks_pre_lock(svn_repos_t *repos,
760 const char *path,
761 const char *username,
762 apr_pool_t *pool)
764 const char *hook = svn_repos_pre_lock_hook(repos, pool);
765 svn_boolean_t broken_link;
767 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
769 return hook_symlink_error(hook);
771 else if (hook)
773 const char *args[5];
775 args[0] = hook;
776 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
777 args[2] = path;
778 args[3] = username;
779 args[4] = NULL;
781 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_PRE_LOCK, hook, args, NULL, pool));
784 return SVN_NO_ERROR;
788 svn_error_t *
789 svn_repos__hooks_post_lock(svn_repos_t *repos,
790 apr_array_header_t *paths,
791 const char *username,
792 apr_pool_t *pool)
794 const char *hook = svn_repos_post_lock_hook(repos, pool);
795 svn_boolean_t broken_link;
797 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
799 return hook_symlink_error(hook);
801 else if (hook)
803 const char *args[5];
804 apr_file_t *stdin_handle = NULL;
805 svn_string_t *paths_str = svn_string_create(svn_cstring_join
806 (paths, "\n", pool),
807 pool);
809 SVN_ERR(create_temp_file(&stdin_handle, paths_str, pool));
811 args[0] = hook;
812 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
813 args[2] = username;
814 args[3] = NULL;
815 args[4] = NULL;
817 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_POST_LOCK, hook, args, stdin_handle,
818 pool));
820 SVN_ERR(svn_io_file_close(stdin_handle, pool));
823 return SVN_NO_ERROR;
827 svn_error_t *
828 svn_repos__hooks_pre_unlock(svn_repos_t *repos,
829 const char *path,
830 const char *username,
831 apr_pool_t *pool)
833 const char *hook = svn_repos_pre_unlock_hook(repos, pool);
834 svn_boolean_t broken_link;
836 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
838 return hook_symlink_error(hook);
840 else if (hook)
842 const char *args[5];
844 args[0] = hook;
845 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
846 args[2] = path;
847 args[3] = username ? username : "";
848 args[4] = NULL;
850 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_PRE_UNLOCK, hook, args, NULL,
851 pool));
854 return SVN_NO_ERROR;
858 svn_error_t *
859 svn_repos__hooks_post_unlock(svn_repos_t *repos,
860 apr_array_header_t *paths,
861 const char *username,
862 apr_pool_t *pool)
864 const char *hook = svn_repos_post_unlock_hook(repos, pool);
865 svn_boolean_t broken_link;
867 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
869 return hook_symlink_error(hook);
871 else if (hook)
873 const char *args[5];
874 apr_file_t *stdin_handle = NULL;
875 svn_string_t *paths_str = svn_string_create(svn_cstring_join
876 (paths, "\n", pool),
877 pool);
879 SVN_ERR(create_temp_file(&stdin_handle, paths_str, pool));
881 args[0] = hook;
882 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
883 args[2] = username ? username : "";
884 args[3] = NULL;
885 args[4] = NULL;
887 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_POST_UNLOCK, hook, args,
888 stdin_handle, pool));
890 SVN_ERR(svn_io_file_close(stdin_handle, pool));
893 return SVN_NO_ERROR;
899 * vim:ts=4:sw=4:expandtab:tw=80:fo=tcroq
900 * vim:isk=a-z,A-Z,48-57,_,.,-,>
901 * vim:cino=>1s,e0,n0,f0,{.5s,}0,^-.5s,=.5s,t0,+1s,c3,(0,u0,\:0