Index: include/svn_io.h =================================================================== --- include/svn_io.h +++ include/svn_io.h 2002-09-27 12:25:37.000000000 -0700 @@ -401,6 +401,41 @@ Connect CMD's stdin, stdout, and stderr to INFILE, OUTFILE, and ERRFILE, except where they are null. + ARGS is a list of utf8-encoded (const char *)'s, terminated by + NULL. ARGS[0] is the name of the program, though it need not be + the same as CMD. + + *PCMD_PROC is returned as a handle to the started process, to be used in + later calls to svn_io_wait_for_cmd. + + INHERIT sets whether the invoked program shall inherit its environment or + run "clean". */ +svn_error_t * svn_io_start_cmd(apr_proc_t *pcmd_proc, + const char *path, + const char *cmd, + const char *const *args, + svn_boolean_t inherit, + apr_file_t *infile, + apr_file_t *outfile, + apr_file_t *errfile, + apr_pool_t *pool); + +/* Wait for the command indicated by CMD_PROC to exit. + + If set, EXITCODE will contain the exit code of the process upon return, + and EXITWHY will indicate why the process terminated. If EXITWHY is not + set and the exit reason is not APR_PROC_CHECK_EXIT(), or if EXITCODE is + not set and the exit code is non-zero, then an SVN_ERR_EXTERNAL_PROGRAM + error will be returned. */ +svn_error_t * svn_io_wait_for_cmd(apr_proc_t *cmd_proc, + int *exitcode, + apr_exit_why_e *exitwhy, + apr_pool_t *pool); + +/* Invoke CMD with ARGS, using utf8-encoded PATH as working directory. + Connect CMD's stdin, stdout, and stderr to INFILE, OUTFILE, and + ERRFILE, except where they are null. + If set, EXITCODE will contain the exit code of the process upon return, and EXITWHY will indicate why the process terminated. If EXITWHY is not set and the exit reason is not APR_PROC_CHECK_EXIT(), or if EXITCODE is Index: libsvn_subr/io.c =================================================================== --- libsvn_subr/io.c +++ libsvn_subr/io.c 2002-09-27 16:51:48.000000000 -0700 @@ -1189,24 +1189,20 @@ return SVN_NO_ERROR; } - svn_error_t * -svn_io_run_cmd (const char *path, - const char *cmd, - const char *const *args, - int *exitcode, - apr_exit_why_e *exitwhy, - svn_boolean_t inherit, - apr_file_t *infile, - apr_file_t *outfile, - apr_file_t *errfile, - apr_pool_t *pool) +svn_io_start_cmd(apr_proc_t *pcmd_proc, + const char *path, + const char *cmd, + const char *const *args, + svn_boolean_t inherit, + apr_file_t *infile, + apr_file_t *outfile, + apr_file_t *errfile, + apr_pool_t *pool) { apr_status_t apr_err; - apr_proc_t cmd_proc; apr_procattr_t *cmdproc_attr; - apr_exit_why_e exitwhy_val; - int exitcode_val, num_args; + unsigned int num_args; const char **args_native; const char *cmd_native; @@ -1215,7 +1211,7 @@ if (apr_err) return svn_error_createf (apr_err, 0, NULL, pool, - "svn_io_run_cmd: error creating %s process attributes", + "svn_io_start_cmd: error creating %s process attributes", cmd); /* Make sure we invoke cmd directly, not through a shell. */ @@ -1225,7 +1221,7 @@ if (apr_err) return svn_error_createf (apr_err, 0, NULL, pool, - "svn_io_run_cmd: error setting %s process cmdtype", + "svn_io_start_cmd: error setting %s process cmdtype", cmd); /* Set the process's working directory. */ @@ -1240,7 +1236,7 @@ if (apr_err) return svn_error_createf (apr_err, 0, NULL, pool, - "svn_io_run_cmd: error setting %s process directory", + "svn_io_start_cmd: error setting %s process directory", cmd); } @@ -1255,7 +1251,7 @@ if (apr_err) return svn_error_createf (apr_err, 0, NULL, pool, - "svn_io_run_cmd: error setting %s process child input", + "svn_io_start_cmd: error setting %s process child input", cmd); } if (outfile) @@ -1264,7 +1260,7 @@ if (apr_err) return svn_error_createf (apr_err, 0, NULL, pool, - "svn_io_run_cmd: error setting %s process child outfile", + "svn_io_start_cmd: error setting %s process child outfile", cmd); } if (errfile) @@ -1273,7 +1269,7 @@ if (apr_err) return svn_error_createf (apr_err, 0, NULL, pool, - "svn_io_run_cmd: error setting %s process child errfile", + "svn_io_start_cmd: error setting %s process child errfile", cmd); } @@ -1290,42 +1286,74 @@ pool)); } - /* Start the cmd command. */ - apr_err = apr_proc_create (&cmd_proc, cmd_native, args_native, NULL, + apr_err = apr_proc_create (pcmd_proc, cmd_native, args_native, NULL, cmdproc_attr, pool); if (apr_err) return svn_error_createf (apr_err, 0, NULL, pool, - "svn_io_run_cmd: error starting %s process", + "svn_io_start_cmd: error starting %s process", cmd); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_io_wait_for_cmd(apr_proc_t *cmd_proc, + int *exitcode, + apr_exit_why_e *exitwhy, + apr_pool_t *pool) +{ + apr_status_t apr_err; + apr_exit_why_e exitwhy_val; + int exitcode_val; + /* The Win32 apr_proc_wait doesn't set this... */ exitwhy_val = APR_PROC_EXIT; /* Wait for the cmd command to finish. */ - apr_err = apr_proc_wait (&cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT); + apr_err = apr_proc_wait (cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT); if (APR_STATUS_IS_CHILD_NOTDONE (apr_err)) - return svn_error_createf + return svn_error_create (apr_err, 0, NULL, pool, - "svn_io_run_cmd: error waiting for %s process", - cmd); + "svn_io_wait_for_cmd: error waiting for process"); if (exitwhy) *exitwhy = exitwhy_val; else if (! APR_PROC_CHECK_EXIT(exitwhy_val)) return svn_error_createf (SVN_ERR_EXTERNAL_PROGRAM, 0, NULL, pool, - "svn_io_run_cmd: error exitwhy %d for process %s", - exitwhy_val, cmd); + "svn_io_wait_for_cmd: error exitwhy %d for process", + exitwhy_val); if (exitcode) *exitcode = exitcode_val; else if (exitcode_val != 0) return svn_error_createf (SVN_ERR_EXTERNAL_PROGRAM, 0, NULL, pool, - "svn_io_run_cmd: error exitcode %d for process %s", - exitcode_val, cmd); + "svn_io_wait_for_cmd: error exitcode %d for process", + exitcode_val); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_io_run_cmd (const char *path, + const char *cmd, + const char *const *args, + int *exitcode, + apr_exit_why_e *exitwhy, + svn_boolean_t inherit, + apr_file_t *infile, + apr_file_t *outfile, + apr_file_t *errfile, + apr_pool_t *pool) +{ + apr_proc_t cmd_proc; + SVN_ERR (svn_io_start_cmd (&cmd_proc, path, cmd, args, inherit, infile, + outfile, errfile, pool)); + SVN_ERR (svn_io_wait_for_cmd (&cmd_proc, exitcode, exitwhy, pool)); return SVN_NO_ERROR; } Index: libsvn_repos/hooks.c =================================================================== --- libsvn_repos/hooks.c +++ libsvn_repos/hooks.c 2002-09-27 16:54:25.000000000 -0700 @@ -105,7 +105,7 @@ if (!err && apr_err) return svn_error_create (apr_err, 0, NULL, pool, - "can't close read end of stdout pipe"); + "can't close read end of stderr pipe"); return err; } @@ -209,16 +209,103 @@ && (kind == svn_node_file)) { const char *args[5]; - - /* ### somehow pass VALUE as stdin to hook?! */ + apr_status_t apr_err; + svn_error_t *err; + apr_proc_t cmd_proc; + apr_file_t *read_errhandle, *write_errhandle; + apr_file_t *read_inhandle, *write_inhandle; + apr_exit_why_e exitwhy; + int exitcode; + + /* Create a pipe to access stderr of the child. */ + apr_err = apr_file_pipe_create(&read_errhandle, &write_errhandle, pool); + if (apr_err) + return svn_error_create + (apr_err, 0, NULL, pool, + "can't create pipe for pre-revprop-change hook"); + + /* Create a pipe to access stdin of the child. */ + apr_err = apr_file_pipe_create(&read_inhandle, &write_inhandle, pool); + if (apr_err) + return svn_error_create + (apr_err, 0, NULL, pool, + "can't create pipe for pre-revprop-change hook"); args[0] = hook; args[1] = svn_repos_path (repos, pool); args[2] = apr_psprintf (pool, "%" SVN_REVNUM_T_FMT, rev); args[3] = name; args[4] = NULL; + err = svn_io_start_cmd (&cmd_proc, ".", "pre-rev-prop-change", + args, FALSE, read_inhandle, NULL, + write_errhandle, pool); + + /* Now the process is running, waiting for the new property value as its + stdin. */ + apr_err = apr_file_write_full (write_inhandle, value, value->len, NULL); + if (!err && apr_err) + { + return svn_error_create + (apr_err, 0, NULL, pool, + "can't write new value to pre-rev-prop-change hook"); + } + apr_err = apr_file_flush (write_inhandle); + if (!err && apr_err) + return svn_error_create (apr_err, 0, NULL, pool, + "can't flush data to the stdin pipe"); + + /* Close the write handle so the script gets an eof. */ + apr_err = apr_file_close (write_inhandle); + if (!err && apr_err) + return svn_error_create (apr_err, 0, NULL, pool, + "can't close write end of stdin pipe"); + + if (!err) + err = svn_io_wait_for_cmd (&cmd_proc, &exitcode, &exitwhy, pool); + + /* Clean up all the pipes, except the one we read stderr from. */ + apr_err = apr_file_close (read_inhandle); + if (!err && apr_err) + return svn_error_create (apr_err, 0, NULL, pool, + "can't close read end of stdin pipe"); + apr_err = apr_file_close (write_errhandle); + if (!err && apr_err) + return svn_error_create (apr_err, 0, NULL, pool, + "can't close write end of stderr pipe"); + /* Function failed. */ + if (err) + { + err = svn_error_create + (SVN_ERR_REPOS_HOOK_FAILURE, 0, err, pool, + "failed to run pre-rev-prop-change hook"); + } + + if (!err) + { + /* Command failed. */ + if (! APR_PROC_CHECK_EXIT (exitwhy) || exitcode != 0) + { + svn_stringbuf_t *error; + + /* Read the file's contents into a stringbuf, allocated in POOL.*/ + SVN_ERR (svn_string_from_aprfile (&error, read_errhandle, pool)); + + err = svn_error_createf + (SVN_ERR_REPOS_HOOK_FAILURE, 0, err, pool, + "%s hook failed with error output:\n%s", + name, error->data); + } + } + + /* Hooks are falible, and so hook failure is "expected" to occur at + times. When such a failure happens we still want to close the pipe */ + apr_err = apr_file_close(read_errhandle); + if (!err && apr_err) + return svn_error_create + (apr_err, 0, NULL, pool, + "can't close read end of stderr pipe"); - SVN_ERR (run_hook_cmd ("pre-revprop-change", hook, args, TRUE, pool)); + return err; } else {