Index: subversion/include/svn_io.h =================================================================== --- subversion/include/svn_io.h (revision 18637) +++ subversion/include/svn_io.h (working copy) @@ -860,6 +860,41 @@ apr_file_t *errfile, apr_pool_t *pool); +#ifdef AS400 +/** Alternative to svn_io_start_cmd() and svn_io_wait_for_cmd() for running + * unix-type qsh(ell) scripts on OS400. Overcomes limitation of + * apr_proc_wait() on OS400 where child process always appears to exit + * successfully. + * + * Invoke @a cmd with @a args. @a args is a list of utf8-encoded + * (const char *)'s, terminated by @c NULL. @c ARGS[0] is the full + * path to the program, though it need not be the same as @a cmd. + * + * If @a exitcode is not @c NULL, @a *exitcode will contain the exit code + * of the process upon return, and if @a exitwhy is not @c NULL, @a + * *exitwhy will indicate why the process terminated. If @a exitwhy is + * @c NULL, and the exit reason is not @c APR_PROC_CHECK_EXIT(), or if + * @a exitcode is @c NULL and the exit code is non-zero, then an + * @c SVN_ERR_EXTERNAL_PROGRAM error will be returned. + * + * If @a read_stdout is @c TRUE, any stdout from the command is appended + * to @a *err_stream. + * + * If @a read_stderr is @c TRUE, any stderr from the command is appended + * to @a *err_stream. + * + * All allocations are done in @a pool. + */ +svn_error_t *svn_io_run_os400_cmd(const char *cmd, + const char *const *args, + int *exitcode, + apr_exit_why_e *exitwhy, + svn_boolean_t read_stdout, + svn_boolean_t read_stderr, + svn_stringbuf_t **err_stream, + apr_pool_t *pool); +#endif /* AS400 */ + /** Invoke @c the configured diff program, with @a user_args (an array * of utf8-encoded @a num_user_args arguments), if they are specified, * or "-u" if they are not. Index: subversion/libsvn_repos/hooks.c =================================================================== --- subversion/libsvn_repos/hooks.c (revision 18637) +++ subversion/libsvn_repos/hooks.c (working copy) @@ -60,6 +60,7 @@ apr_exit_why_e exitwhy; apr_proc_t cmd_proc; +#ifndef AS400 /* Create a pipe to access stderr of the child. */ apr_err = apr_file_pipe_create(&read_errhandle, &write_errhandle, pool); if (apr_err) @@ -95,7 +96,13 @@ err = svn_io_start_cmd(&cmd_proc, ".", cmd, args, FALSE, stdin_handle, null_handle, write_errhandle, pool); +#else + /* The functionality of svn_io_start_cmd() and svn_io_wait_for_cmd() is + * handled in svn_io_run_as400_cmd() on OS400. */ + err = NULL; +#endif /* AS400 */ +#ifndef AS400 /* This seems to be done automatically if we pass the third parameter of apr_procattr_child_in/out_set(), but svn_io_run_cmd()'s interface does not support those parameters. We need to close the write end of the @@ -104,6 +111,7 @@ if (!err && apr_err) return svn_error_wrap_apr (apr_err, _("Error closing write end of stderr pipe")); +#endif /* AS400 */ if (err) { @@ -116,9 +124,17 @@ const char *error; svn_error_t *err2; +#ifndef AS400 err2 = svn_stringbuf_from_aprfile(&native_error, read_errhandle, pool); err = svn_io_wait_for_cmd(&cmd_proc, cmd, &exitcode, &exitwhy, pool); +#else + err2 = SVN_NO_ERROR; + + err = svn_io_run_os400_cmd(cmd, args, &exitcode, &exitwhy, FALSE, + TRUE, &native_error, pool); +#endif /* AS400 */ + if (! err) { if (! APR_PROC_CHECK_EXIT(exitwhy) || exitcode != 0) @@ -150,6 +166,7 @@ } } +#ifndef AS400 /* 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 */ @@ -161,6 +178,7 @@ apr_err = apr_file_close(null_handle); if (!err && apr_err) return svn_error_wrap_apr(apr_err, _("Error closing null file")); +#endif return err; } Index: subversion/libsvn_subr/io.c =================================================================== --- subversion/libsvn_subr/io.c (revision 18637) +++ subversion/libsvn_subr/io.c (working copy) @@ -42,6 +42,10 @@ #include #include +#if AS400 +#include /* For spawn() and struct inheritance. */ +#endif + #include "svn_types.h" #include "svn_path.h" #include "svn_string.h" @@ -54,6 +58,7 @@ #ifdef AS400 #define SVN_UTF_UTOE_XLATE_HANDLE "svn-utf-utoe-xlate-handle" +#define SVN_UTF_ETOU_XLATE_HANDLE "svn-utf-etou-xlate-handle" #endif /* @@ -2058,7 +2063,164 @@ } +#if AS400 svn_error_t * +svn_io_run_os400_cmd(const char *cmd, + const char *const *args, + int *exitcode, + apr_exit_why_e *exitwhy, + svn_boolean_t read_stdout, + svn_boolean_t read_stderr, + svn_stringbuf_t **err_stream, + apr_pool_t *pool) +{ + int rc, fd_map[3], ignoreFds[2], useFds[2], exitcode_val; + char buffer[20]; + const char **native_args = NULL; + struct inheritance xmp_inherit = {0}; + pid_t child_pid, wait_rv; + svn_stringbuf_t *script_output = svn_stringbuf_create("", pool); + apr_size_t args_arr_size = 0, i = 0; + apr_exit_why_e exitwhy_val; +#pragma convert(0) + /* Even with the UTF support in V5R4 a few functions don't support utf-8 + * args, including spawn(), which takes this char array. */ + char *xmp_envp[2] = {"QIBM_USE_DESCRIPTOR_STDIO=Y", NULL}; +#pragma convert(1208) + + *err_stream = svn_stringbuf_create("", pool); + + /* Find number of elements in args array. */ + while (args[args_arr_size] != NULL) + args_arr_size++; + + /* Allocate memory for the native_args string array plus one for + * the ending null element. */ + native_args = apr_palloc(pool, sizeof(char *) * args_arr_size + 1); + + /* Convert utf-8 args to ebcdic for use by spawn(). */ + while(args[i] != NULL) + { + SVN_ERR(svn_utf_cstring_from_utf8_ex((const char**)(&(native_args[i])), + args[i++], (const char *)0, + SVN_UTF_UTOE_XLATE_HANDLE, + pool)); + } + + /* Make the last element in the array a NULL pointer as required + * by spawn. */ + native_args[args_arr_size] = NULL; + + /* Get two data pipes, allowing stdout and stderr to be separate. */ + if (pipe(ignoreFds) != 0 || pipe(useFds) != 0) + { + return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, + "Error piping hook script %s.", cmd); + } + + /* Map stdin, stdout, stderr. */ + fd_map[0] = ignoreFds[1]; + fd_map[1] = read_stdout ? useFds[1] : ignoreFds[1]; + fd_map[2] = read_stderr ? useFds[1] : ignoreFds[1]; + + /* Spawn the command... */ + if ((child_pid = spawn(native_args[0], 3, fd_map, &xmp_inherit, + native_args, xmp_envp)) == -1) + { + return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, + "Error spawning process for hook script %s.", + cmd); + } + + /* ...and wait for it to finish. */ + if ((wait_rv = waitpid(child_pid, &exitcode_val, 0)) == -1) + { + return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, + "Error waiting for process completion of " \ + "hook script %s.", cmd); + } + + close(ignoreFds[1]); + close(useFds[1]); + + /* Create svn_stringbuf containing any messages the script sent to + * stderr and/or stdout. */ + while ((rc = read(useFds[0], buffer, sizeof(buffer))) > 0) + { + buffer[rc] = '\0'; + svn_stringbuf_appendcstr(script_output, buffer); + } + + if (script_output->len > 1) + { + const char* script_out_utf8; + SVN_ERR(svn_utf_cstring_to_utf8_ex(&script_out_utf8, + script_output->data, + (const char*)0, + SVN_UTF_ETOU_XLATE_HANDLE, + pool)); + svn_stringbuf_appendcstr (*err_stream, script_out_utf8); + } + + close(ignoreFds[0]); + close(useFds[0]); + + if (exitwhy) + { + /* waitpid() doesn't explicity give us apr_exit_why_e *exitwhy so we do + * our best to determine it's value. */ + if (WIFEXITED(*exitcode)) + { + exitwhy_val = APR_PROC_EXIT; + } + else if (WIFSIGNALED(*exitcode)) + { + exitwhy_val = APR_PROC_SIGNAL; + } + else if (WIFEXCEPTION(*exitcode)) + { + return svn_error_createf + (SVN_ERR_EXTERNAL_PROGRAM, NULL, + _("Process '%s' failed unexpectedly with OS400 exception %d"), + cmd, WEXCEPTNUMBER(*exitcode)); + } + else if (WIFSTOPPED(*exitcode)) + { + return svn_error_createf + (SVN_ERR_EXTERNAL_PROGRAM, NULL, + _("Process '%s' stopped unexpectedly by signal %d"), + cmd, WSTOPSIG(*exitcode)); + } + else + { + return svn_error_createf + (SVN_ERR_EXTERNAL_PROGRAM, NULL, + _("Process '%s' failed unexpectedly with %d"), + cmd, APR_EGENERAL); + } + } + + if (exitwhy) + *exitwhy = exitwhy_val; + else if (! APR_PROC_CHECK_EXIT(exitwhy_val)) + return svn_error_createf + (SVN_ERR_EXTERNAL_PROGRAM, NULL, + _("Process '%s' failed (exitwhy %d)"), cmd, exitwhy_val); + + if (exitcode) + *exitcode = exitcode_val; + else if (exitcode_val != 0) + { + return svn_error_createf + (SVN_ERR_EXTERNAL_PROGRAM, NULL, + _("Process '%s' returned error exitcode %d"), cmd, exitcode_val); + } + return SVN_NO_ERROR; +} +#endif + + +svn_error_t * svn_io_run_diff(const char *dir, const char *const *user_args, int num_user_args,