In the command-line client, forbid
[svn.git] / subversion / libsvn_repos / hooks.c
blob251281c7e275c79913f8fcfd3f0c21bc53ed4944
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 failure_message = svn_stringbuf_createf(pool,
123 _("'%s' hook failed (exited with a non-zero exitcode of %d). "),
124 name, exitcode);
127 if (utf8_stderr[0])
129 svn_stringbuf_appendcstr(failure_message,
130 _("The following error output was produced "
131 "by the hook:\n"));
132 svn_stringbuf_appendcstr(failure_message, utf8_stderr);
134 else
136 svn_stringbuf_appendcstr(failure_message,
137 _("No error output was produced by the "
138 "hook."));
141 return svn_error_create(SVN_ERR_REPOS_HOOK_FAILURE, err,
142 failure_message->data);
144 #endif /* AS400 */
146 /* NAME, CMD and ARGS are the name, path to and arguments for the hook
147 program that is to be run. The hook's exit status will be checked,
148 and if an error occurred the hook's stderr output will be added to
149 the returned error.
151 If STDIN_HANDLE is non-null, pass it as the hook's stdin, else pass
152 no stdin to the hook. */
153 static svn_error_t *
154 run_hook_cmd(const char *name,
155 const char *cmd,
156 const char **args,
157 apr_file_t *stdin_handle,
158 apr_pool_t *pool)
159 #ifndef AS400
161 apr_file_t *read_errhandle, *write_errhandle, *null_handle;
162 apr_status_t apr_err;
163 svn_error_t *err;
164 apr_proc_t cmd_proc;
166 /* Create a pipe to access stderr of the child. */
167 apr_err = apr_file_pipe_create(&read_errhandle, &write_errhandle, pool);
168 if (apr_err)
169 return svn_error_wrap_apr
170 (apr_err, _("Can't create pipe for hook '%s'"), cmd);
172 /* Pipes are inherited by default, but we don't want that, since
173 APR will duplicate the write end of the pipe for the child process.
174 Not closing the read end is harmless, but if the write end is inherited,
175 it will be inherited by grandchildren as well. This causes problems
176 if a hook script puts long-running jobs in the background. Even if
177 they redirect stderr to something else, the write end of our pipe will
178 still be open, causing us to block. */
179 apr_err = apr_file_inherit_unset(read_errhandle);
180 if (apr_err)
181 return svn_error_wrap_apr
182 (apr_err, _("Can't make pipe read handle non-inherited for hook '%s'"),
183 cmd);
185 apr_err = apr_file_inherit_unset(write_errhandle);
186 if (apr_err)
187 return svn_error_wrap_apr
188 (apr_err, _("Can't make pipe write handle non-inherited for hook '%s'"),
189 cmd);
192 /* Redirect stdout to the null device */
193 apr_err = apr_file_open(&null_handle, SVN_NULL_DEVICE_NAME, APR_WRITE,
194 APR_OS_DEFAULT, pool);
195 if (apr_err)
196 return svn_error_wrap_apr
197 (apr_err, _("Can't create null stdout for hook '%s'"), cmd);
199 err = svn_io_start_cmd(&cmd_proc, ".", cmd, args, FALSE,
200 stdin_handle, null_handle, write_errhandle, pool);
202 /* This seems to be done automatically if we pass the third parameter of
203 apr_procattr_child_in/out_set(), but svn_io_run_cmd()'s interface does
204 not support those parameters. We need to close the write end of the
205 pipe so we don't hang on the read end later, if we need to read it. */
206 apr_err = apr_file_close(write_errhandle);
207 if (!err && apr_err)
208 return svn_error_wrap_apr
209 (apr_err, _("Error closing write end of stderr pipe"));
211 if (err)
213 err = svn_error_createf
214 (SVN_ERR_REPOS_HOOK_FAILURE, err, _("Failed to start '%s' hook"), cmd);
216 else
218 err = check_hook_result(name, cmd, &cmd_proc, read_errhandle, pool);
221 /* Hooks are fallible, and so hook failure is "expected" to occur at
222 times. When such a failure happens we still want to close the pipe
223 and null file */
224 apr_err = apr_file_close(read_errhandle);
225 if (!err && apr_err)
226 return svn_error_wrap_apr
227 (apr_err, _("Error closing read end of stderr pipe"));
229 apr_err = apr_file_close(null_handle);
230 if (!err && apr_err)
231 return svn_error_wrap_apr(apr_err, _("Error closing null file"));
233 return err;
235 #else /* Run hooks with spawn() on OS400. */
236 #define AS400_BUFFER_SIZE 256
238 const char **native_args;
239 int fd_map[3], stderr_pipe[2], exitcode;
240 svn_stringbuf_t *script_output;
241 pid_t child_pid, wait_rv;
242 apr_size_t args_arr_size = 0, i;
243 struct inheritance xmp_inherit = {0};
244 #pragma convert(0)
245 /* Despite the UTF support in V5R4 a few functions still require
246 * EBCDIC args. */
247 char *xmp_envp[2] = {"QIBM_USE_DESCRIPTOR_STDIO=Y", NULL};
248 const char *dev_null_ebcdic = SVN_NULL_DEVICE_NAME;
249 #pragma convert(1208)
251 /* Find number of elements in args array. */
252 while (args[args_arr_size] != NULL)
253 args_arr_size++;
255 /* Allocate memory for the native_args string array plus one for
256 * the ending null element. */
257 native_args = apr_palloc(pool, sizeof(char *) * args_arr_size + 1);
259 /* Convert UTF-8 args to EBCDIC for use by spawn(). */
260 for (i = 0; args[i] != NULL; i++)
262 SVN_ERR(svn_utf_cstring_from_utf8_ex2((const char**)(&(native_args[i])),
263 args[i], (const char *)0,
264 pool));
267 /* Make the last element in the array a NULL pointer as required
268 * by spawn. */
269 native_args[args_arr_size] = NULL;
271 /* Map stdin. */
272 if (stdin_handle)
274 /* Get OS400 file descriptor of APR stdin file and map it. */
275 if (apr_os_file_get(&fd_map[0], stdin_handle))
277 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
278 "Error converting APR file to OS400 "
279 "type for hook script '%s'", cmd);
282 else
284 fd_map[0] = open(dev_null_ebcdic, O_RDONLY);
285 if (fd_map[0] == -1)
287 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
288 "Error opening /dev/null for hook "
289 "script '%s'", cmd);
293 /* Map stdout. */
294 fd_map[1] = open(dev_null_ebcdic, O_WRONLY);
295 if (fd_map[1] == -1)
296 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
297 "Error opening /dev/null for hook script '%s'",
298 cmd);
300 /* Map stderr. */
301 /* Get pipe for hook's stderr. */
302 if (pipe(stderr_pipe) != 0)
304 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
305 "Can't create stderr pipe for "
306 "hook '%s'", cmd);
308 fd_map[2] = stderr_pipe[1];
310 /* Spawn the hook command. */
311 child_pid = spawn(native_args[0], 3, fd_map, &xmp_inherit, native_args,
312 xmp_envp);
313 if (child_pid == -1)
315 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
316 "Error spawning process for hook script '%s'",
317 cmd);
320 /* Close the stdout file descriptor. */
321 if (close(fd_map[1]) == -1)
322 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
323 "Error closing write end of stdout pipe to "
324 "hook script '%s'", cmd);
326 /* Close the write end of the stderr pipe so any subsequent reads
327 * don't hang. */
328 if (close(fd_map[2]) == -1)
329 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
330 "Error closing write end of stderr pipe to "
331 "hook script '%s'", cmd);
333 script_output = svn_stringbuf_create("", pool);
335 while (1)
337 int rc;
339 svn_stringbuf_ensure(script_output,
340 script_output->len + AS400_BUFFER_SIZE + 1);
342 rc = read(stderr_pipe[0],
343 &(script_output->data[script_output->len]),
344 AS400_BUFFER_SIZE);
346 if (rc == -1)
348 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
349 "Error reading stderr of hook "
350 "script '%s'", cmd);
353 script_output->len += rc;
355 /* If read() returned 0 then EOF was found and we are done reading
356 * stderr. */
357 if (rc == 0)
359 script_output->data[script_output->len] = '\0';
360 break;
364 /* Close the read end of the stderr pipe. */
365 if (close(stderr_pipe[0]) == -1)
366 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
367 "Error closing read end of stderr "
368 "pipe to hook script '%s'", cmd);
370 /* Wait for the child process to complete. */
371 wait_rv = waitpid(child_pid, &exitcode, 0);
372 if (wait_rv == -1)
374 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
375 "Error waiting for process completion of "
376 "hook script '%s'", cmd);
380 if (WIFEXITED(exitcode))
382 if (WEXITSTATUS(exitcode))
384 svn_error_t *err;
385 const char *utf8_stderr = NULL;
386 svn_stringbuf_t *failure_message = svn_stringbuf_createf(
387 pool, "'%s' hook failed (exited with a non-zero exitcode "
388 "of %d). ", name, exitcode);
390 if (!svn_stringbuf_isempty(script_output))
392 /* OS400 scripts produce EBCDIC stderr, so convert it. */
393 err = svn_utf_cstring_to_utf8_ex2(&utf8_stderr,
394 script_output->data,
395 (const char*)0, pool);
396 if (err)
398 utf8_stderr = "[Error output could not be translated from "
399 "the native locale to UTF-8.]";
400 svn_error_clear(err);
405 if (utf8_stderr)
407 svn_stringbuf_appendcstr(failure_message,
408 "The following error output was "
409 "produced by the hook:\n");
410 svn_stringbuf_appendcstr(failure_message, utf8_stderr);
412 else
414 svn_stringbuf_appendcstr(failure_message,
415 "No error output was produced by "
416 "the hook.");
418 return svn_error_create(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
419 failure_message->data);
421 else
422 /* Success! */
423 return SVN_NO_ERROR;
425 else if (WIFSIGNALED(exitcode))
427 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
428 "Process '%s' failed because of an "
429 "uncaught terminating signal", cmd);
431 else if (WIFEXCEPTION(exitcode))
433 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
434 "Process '%s' failed unexpectedly with "
435 "OS400 exception %d", cmd,
436 WEXCEPTNUMBER(exitcode));
438 else if (WIFSTOPPED(exitcode))
440 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
441 "Process '%s' stopped unexpectedly by "
442 "signal %d", cmd, WSTOPSIG(exitcode));
444 else
446 return svn_error_createf(SVN_ERR_REPOS_HOOK_FAILURE, NULL,
447 "Process '%s' failed unexpectedly", cmd);
450 #endif /* AS400 */
453 /* Create a temporary file F that will automatically be deleted when it is
454 closed. Fill it with VALUE, and leave it open and rewound, ready to be
455 read from. */
456 static svn_error_t *
457 create_temp_file(apr_file_t **f, const svn_string_t *value, apr_pool_t *pool)
459 const char *dir;
460 apr_off_t offset = 0;
462 SVN_ERR(svn_io_temp_dir(&dir, pool));
463 SVN_ERR(svn_io_open_unique_file2(f, NULL,
464 svn_path_join(dir, "hook-input", pool),
465 "", svn_io_file_del_on_close, pool));
466 SVN_ERR(svn_io_file_write_full(*f, value->data, value->len, NULL, pool));
467 SVN_ERR(svn_io_file_seek(*f, APR_SET, &offset, pool));
468 return SVN_NO_ERROR;
472 /* Check if the HOOK program exists and is a file or a symbolic link, using
473 POOL for temporary allocations.
475 If the hook exists but is a broken symbolic link, set *BROKEN_LINK
476 to TRUE, else if the hook program exists set *BROKEN_LINK to FALSE.
478 Return the hook program if found, else return NULL and don't touch
479 *BROKEN_LINK.
481 static const char*
482 check_hook_cmd(const char *hook, svn_boolean_t *broken_link, apr_pool_t *pool)
484 static const char* const check_extns[] = {
485 #ifdef WIN32
486 /* For WIN32, we need to check with file name extension(s) added.
488 As Windows Scripting Host (.wsf) files can accomodate (at least)
489 JavaScript (.js) and VB Script (.vbs) code, extensions for the
490 corresponding file types need not be enumerated explicitly. */
491 ".exe", ".cmd", ".bat", ".wsf", /* ### Any other extensions? */
492 #else
494 #endif
495 NULL
498 const char *const *extn;
499 svn_error_t *err = NULL;
500 svn_boolean_t is_special;
501 for (extn = check_extns; *extn; ++extn)
503 const char *const hook_path =
504 (**extn ? apr_pstrcat(pool, hook, *extn, 0) : hook);
506 svn_node_kind_t kind;
507 if (!(err = svn_io_check_resolved_path(hook_path, &kind, pool))
508 && kind == svn_node_file)
510 *broken_link = FALSE;
511 return hook_path;
513 svn_error_clear(err);
514 if (!(err = svn_io_check_special_path(hook_path, &kind, &is_special,
515 pool))
516 && is_special == TRUE)
518 *broken_link = TRUE;
519 return hook_path;
521 svn_error_clear(err);
523 return NULL;
527 /* Return an error for the failure of HOOK due to a broken symlink. */
528 static svn_error_t *
529 hook_symlink_error(const char *hook)
531 return svn_error_createf
532 (SVN_ERR_REPOS_HOOK_FAILURE, NULL,
533 _("Failed to run '%s' hook; broken symlink"), hook);
536 svn_error_t *
537 svn_repos__hooks_start_commit(svn_repos_t *repos,
538 const char *user,
539 apr_array_header_t *capabilities,
540 apr_pool_t *pool)
542 const char *hook = svn_repos_start_commit_hook(repos, pool);
543 svn_boolean_t broken_link;
545 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
547 return hook_symlink_error(hook);
549 else if (hook)
551 const char *args[5];
552 char *capabilities_string = svn_cstring_join(capabilities, ":", pool);
554 /* Get rid of that annoying final colon. */
555 if (capabilities_string[0])
556 capabilities_string[strlen(capabilities_string) - 1] = '\0';
558 args[0] = hook;
559 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
560 args[2] = user ? user : "";
561 args[3] = capabilities_string;
562 args[4] = NULL;
564 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_START_COMMIT, hook, args, NULL,
565 pool));
568 return SVN_NO_ERROR;
572 svn_error_t *
573 svn_repos__hooks_pre_commit(svn_repos_t *repos,
574 const char *txn_name,
575 apr_pool_t *pool)
577 const char *hook = svn_repos_pre_commit_hook(repos, pool);
578 svn_boolean_t broken_link;
580 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
582 return hook_symlink_error(hook);
584 else if (hook)
586 const char *args[4];
588 args[0] = hook;
589 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
590 args[2] = txn_name;
591 args[3] = NULL;
593 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_PRE_COMMIT, hook, args, NULL,
594 pool));
597 return SVN_NO_ERROR;
601 svn_error_t *
602 svn_repos__hooks_post_commit(svn_repos_t *repos,
603 svn_revnum_t rev,
604 apr_pool_t *pool)
606 const char *hook = svn_repos_post_commit_hook(repos, pool);
607 svn_boolean_t broken_link;
609 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
611 return hook_symlink_error(hook);
613 else if (hook)
615 const char *args[4];
617 args[0] = hook;
618 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
619 args[2] = apr_psprintf(pool, "%ld", rev);
620 args[3] = NULL;
622 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_POST_COMMIT, hook, args, NULL,
623 pool));
626 return SVN_NO_ERROR;
630 svn_error_t *
631 svn_repos__hooks_pre_revprop_change(svn_repos_t *repos,
632 svn_revnum_t rev,
633 const char *author,
634 const char *name,
635 const svn_string_t *new_value,
636 char action,
637 apr_pool_t *pool)
639 const char *hook = svn_repos_pre_revprop_change_hook(repos, pool);
640 svn_boolean_t broken_link;
642 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
644 return hook_symlink_error(hook);
646 else if (hook)
648 const char *args[7];
649 apr_file_t *stdin_handle = NULL;
650 char action_string[2];
652 /* Pass the new value as stdin to hook */
653 if (new_value)
654 SVN_ERR(create_temp_file(&stdin_handle, new_value, pool));
655 else
656 SVN_ERR(svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME,
657 APR_READ, APR_OS_DEFAULT, pool));
659 action_string[0] = action;
660 action_string[1] = '\0';
662 args[0] = hook;
663 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
664 args[2] = apr_psprintf(pool, "%ld", rev);
665 args[3] = author ? author : "";
666 args[4] = name;
667 args[5] = action_string;
668 args[6] = NULL;
670 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_PRE_REVPROP_CHANGE, hook, args,
671 stdin_handle, pool));
673 SVN_ERR(svn_io_file_close(stdin_handle, pool));
675 else
677 /* If the pre- hook doesn't exist at all, then default to
678 MASSIVE PARANOIA. Changing revision properties is a lossy
679 operation; so unless the repository admininstrator has
680 *deliberately* created the pre-hook, disallow all changes. */
681 return
682 svn_error_create
683 (SVN_ERR_REPOS_DISABLED_FEATURE, NULL,
684 _("Repository has not been enabled to accept revision propchanges;\n"
685 "ask the administrator to create a pre-revprop-change hook"));
688 return SVN_NO_ERROR;
692 svn_error_t *
693 svn_repos__hooks_post_revprop_change(svn_repos_t *repos,
694 svn_revnum_t rev,
695 const char *author,
696 const char *name,
697 svn_string_t *old_value,
698 char action,
699 apr_pool_t *pool)
701 const char *hook = svn_repos_post_revprop_change_hook(repos, pool);
702 svn_boolean_t broken_link;
704 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
706 return hook_symlink_error(hook);
708 else if (hook)
710 const char *args[7];
711 apr_file_t *stdin_handle = NULL;
712 char action_string[2];
714 /* Pass the old value as stdin to hook */
715 if (old_value)
716 SVN_ERR(create_temp_file(&stdin_handle, old_value, pool));
717 else
718 SVN_ERR(svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME,
719 APR_READ, APR_OS_DEFAULT, pool));
721 action_string[0] = action;
722 action_string[1] = '\0';
724 args[0] = hook;
725 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
726 args[2] = apr_psprintf(pool, "%ld", rev);
727 args[3] = author ? author : "";
728 args[4] = name;
729 args[5] = action_string;
730 args[6] = NULL;
732 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_POST_REVPROP_CHANGE, hook, args,
733 stdin_handle, pool));
735 SVN_ERR(svn_io_file_close(stdin_handle, pool));
738 return SVN_NO_ERROR;
743 svn_error_t *
744 svn_repos__hooks_pre_lock(svn_repos_t *repos,
745 const char *path,
746 const char *username,
747 apr_pool_t *pool)
749 const char *hook = svn_repos_pre_lock_hook(repos, pool);
750 svn_boolean_t broken_link;
752 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
754 return hook_symlink_error(hook);
756 else if (hook)
758 const char *args[5];
760 args[0] = hook;
761 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
762 args[2] = path;
763 args[3] = username;
764 args[4] = NULL;
766 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_PRE_LOCK, hook, args, NULL, pool));
769 return SVN_NO_ERROR;
773 svn_error_t *
774 svn_repos__hooks_post_lock(svn_repos_t *repos,
775 apr_array_header_t *paths,
776 const char *username,
777 apr_pool_t *pool)
779 const char *hook = svn_repos_post_lock_hook(repos, pool);
780 svn_boolean_t broken_link;
782 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
784 return hook_symlink_error(hook);
786 else if (hook)
788 const char *args[5];
789 apr_file_t *stdin_handle = NULL;
790 svn_string_t *paths_str = svn_string_create(svn_cstring_join
791 (paths, "\n", pool),
792 pool);
794 SVN_ERR(create_temp_file(&stdin_handle, paths_str, pool));
796 args[0] = hook;
797 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
798 args[2] = username;
799 args[3] = NULL;
800 args[4] = NULL;
802 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_POST_LOCK, hook, args, stdin_handle,
803 pool));
805 SVN_ERR(svn_io_file_close(stdin_handle, pool));
808 return SVN_NO_ERROR;
812 svn_error_t *
813 svn_repos__hooks_pre_unlock(svn_repos_t *repos,
814 const char *path,
815 const char *username,
816 apr_pool_t *pool)
818 const char *hook = svn_repos_pre_unlock_hook(repos, pool);
819 svn_boolean_t broken_link;
821 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
823 return hook_symlink_error(hook);
825 else if (hook)
827 const char *args[5];
829 args[0] = hook;
830 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
831 args[2] = path;
832 args[3] = username ? username : "";
833 args[4] = NULL;
835 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_PRE_UNLOCK, hook, args, NULL,
836 pool));
839 return SVN_NO_ERROR;
843 svn_error_t *
844 svn_repos__hooks_post_unlock(svn_repos_t *repos,
845 apr_array_header_t *paths,
846 const char *username,
847 apr_pool_t *pool)
849 const char *hook = svn_repos_post_unlock_hook(repos, pool);
850 svn_boolean_t broken_link;
852 if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
854 return hook_symlink_error(hook);
856 else if (hook)
858 const char *args[5];
859 apr_file_t *stdin_handle = NULL;
860 svn_string_t *paths_str = svn_string_create(svn_cstring_join
861 (paths, "\n", pool),
862 pool);
864 SVN_ERR(create_temp_file(&stdin_handle, paths_str, pool));
866 args[0] = hook;
867 args[1] = svn_path_local_style(svn_repos_path(repos, pool), pool);
868 args[2] = username ? username : "";
869 args[3] = NULL;
870 args[4] = NULL;
872 SVN_ERR(run_hook_cmd(SVN_REPOS__HOOK_POST_UNLOCK, hook, args,
873 stdin_handle, pool));
875 SVN_ERR(svn_io_file_close(stdin_handle, pool));
878 return SVN_NO_ERROR;
884 * vim:ts=4:sw=4:expandtab:tw=80:fo=tcroq
885 * vim:isk=a-z,A-Z,48-57,_,.,-,>
886 * vim:cino=>1s,e0,n0,f0,{.5s,}0,^-.5s,=.5s,t0,+1s,c3,(0,u0,\:0