Пример #1
0
/* Spawn the piped logger process pl->program. */
static apr_status_t piped_log_spawn(piped_log *pl)
{
    apr_procattr_t *procattr;
    apr_proc_t *procnew = NULL;
    apr_status_t status;

    if (((status = apr_procattr_create(&procattr, pl->p)) != APR_SUCCESS) ||
        ((status = apr_procattr_cmdtype_set(procattr, pl->cmdtype))
         != APR_SUCCESS) ||
        ((status = apr_procattr_child_in_set(procattr,
                                             ap_piped_log_read_fd(pl),
                                             ap_piped_log_write_fd(pl)))
         != APR_SUCCESS) ||
        ((status = apr_procattr_child_errfn_set(procattr, log_child_errfn))
         != APR_SUCCESS) ||
        ((status = apr_procattr_error_check_set(procattr, 1)) != APR_SUCCESS)) {
        char buf[120];
        /* Something bad happened, give up and go away. */
        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
                     "piped_log_spawn: unable to setup child process '%s': %s",
                     pl->program, apr_strerror(status, buf, sizeof(buf)));
    }
    else {
        char **args;
        const char *pname;
        apr_file_t *outfile, *errfile;

        if ((status = apr_file_open_stdout(&outfile, pl->p)) == APR_SUCCESS)
            status = apr_procattr_child_out_set(procattr, outfile, NULL);
        if ((status = apr_file_open_stderr(&errfile, pl->p)) == APR_SUCCESS)
            status = apr_procattr_child_err_set(procattr, errfile, NULL);

        apr_tokenize_to_argv(pl->program, &args, pl->p);
        pname = apr_pstrdup(pl->p, args[0]);
        procnew = apr_pcalloc(pl->p, sizeof(apr_proc_t));
        status = apr_proc_create(procnew, pname, (const char * const *) args,
                                 NULL, procattr, pl->p);

        if (status == APR_SUCCESS) {
            pl->pid = procnew;
            /* procnew->in was dup2'd from ap_piped_log_write_fd(pl);
             * since the original fd is still valid, close the copy to
             * avoid a leak. */
            apr_file_close(procnew->in);
            procnew->in = NULL;
            apr_proc_other_child_register(procnew, piped_log_maintenance, pl,
                                          ap_piped_log_write_fd(pl), pl->p);
            close_handle_in_child(pl->p, ap_piped_log_read_fd(pl));
        }
        else {
            char buf[120];
            /* Something bad happened, give up and go away. */
            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
                         "unable to start piped log program '%s': %s",
                         pl->program, apr_strerror(status, buf, sizeof(buf)));
        }
    }

    return status;
}
Пример #2
0
static void launch_child(abts_case *tc, apr_proc_t *proc, const char *arg1, apr_pool_t *p)
{
    apr_procattr_t *procattr;
    const char *args[3];
    apr_status_t rv;

    rv = apr_procattr_create(&procattr, p);
    APR_ASSERT_SUCCESS(tc, "Couldn't create procattr", rv);

    rv = apr_procattr_io_set(procattr, APR_NO_PIPE, APR_NO_PIPE,
            APR_NO_PIPE);
    APR_ASSERT_SUCCESS(tc, "Couldn't set io in procattr", rv);

    rv = apr_procattr_error_check_set(procattr, 1);
    APR_ASSERT_SUCCESS(tc, "Couldn't set error check in procattr", rv);

    rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM_ENV);
    APR_ASSERT_SUCCESS(tc, "Couldn't set copy environment", rv);

    args[0] = "sockchild" EXTENSION;
    args[1] = arg1;
    args[2] = NULL;
    rv = apr_proc_create(proc, TESTBINPATH "sockchild" EXTENSION, args, NULL,
                         procattr, p);
    APR_ASSERT_SUCCESS(tc, "Couldn't launch program", rv);
}
Пример #3
0
static int launch_reader(abts_case *tc)
{
    apr_proc_t proc = {0};
    apr_procattr_t *procattr;
    const char *args[2];
    apr_status_t rv;
    apr_exit_why_e why;
    int exitcode;

    rv = apr_procattr_create(&procattr, p);
    APR_ASSERT_SUCCESS(tc, "Couldn't create procattr", rv);

    rv = apr_procattr_io_set(procattr, APR_NO_PIPE, APR_NO_PIPE,
            APR_NO_PIPE);
    APR_ASSERT_SUCCESS(tc, "Couldn't set io in procattr", rv);

    rv = apr_procattr_error_check_set(procattr, 1);
    APR_ASSERT_SUCCESS(tc, "Couldn't set error check in procattr", rv);

    args[0] = "tryread" EXTENSION;
    args[1] = NULL;
    rv = apr_proc_create(&proc, "./tryread" EXTENSION, args, NULL, procattr, p);
    APR_ASSERT_SUCCESS(tc, "Couldn't launch program", rv);

    ABTS_ASSERT(tc, "wait for child process",
            apr_proc_wait(&proc, &exitcode, &why, APR_WAIT) == APR_CHILD_DONE);

    ABTS_ASSERT(tc, "child terminated normally", why == APR_PROC_EXIT);
    return exitcode;
}
Пример #4
0
static void test_pipe_writefull(abts_case *tc, void *data)
{
    int iterations = 1000;
    int i;
    int bytes_per_iteration = 8000;
    char *buf = (char *)malloc(bytes_per_iteration);
    char responsebuf[128];
    apr_size_t nbytes;
    int bytes_processed;
    apr_proc_t proc = {0};
    apr_procattr_t *procattr;
    const char *args[2];
    apr_status_t rv;
    apr_exit_why_e why;
    
    rv = apr_procattr_create(&procattr, p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    rv = apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK,
                             APR_CHILD_BLOCK);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    rv = apr_procattr_error_check_set(procattr, 1);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    args[0] = "readchild" EXTENSION;
    args[1] = NULL;
    rv = apr_proc_create(&proc, "./readchild" EXTENSION, args, NULL, procattr, p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    rv = apr_file_pipe_timeout_set(proc.in, apr_time_from_sec(10));
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    rv = apr_file_pipe_timeout_set(proc.out, apr_time_from_sec(10));
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    i = iterations;
    do {
        rv = apr_file_write_full(proc.in, buf, bytes_per_iteration, NULL);
        ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    } while (--i);

    free(buf);

    rv = apr_file_close(proc.in);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    
    nbytes = sizeof(responsebuf);
    rv = apr_file_read(proc.out, responsebuf, &nbytes);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    bytes_processed = (int)apr_strtoi64(responsebuf, NULL, 10);
    ABTS_INT_EQUAL(tc, iterations * bytes_per_iteration, bytes_processed);

    ABTS_ASSERT(tc, "wait for child process",
             apr_proc_wait(&proc, NULL, &why, APR_WAIT) == APR_CHILD_DONE);
    
    ABTS_ASSERT(tc, "child terminated normally", why == APR_PROC_EXIT);
}
Пример #5
0
/* Create a child process running PROGNAME with a pipe connected to
 * the childs stdin.  The write-end of the pipe will be placed in
 * *FPIN on successful return.  If dummy_stderr is non-zero, the
 * stderr for the child will be the same as the stdout of the parent.
 * Otherwise the child will inherit the stderr from the parent. */
static int log_child(apr_pool_t *p, const char *progname,
                     apr_file_t **fpin, int dummy_stderr)
{
    /* Child process code for 'ErrorLog "|..."';
     * may want a common framework for this, since I expect it will
     * be common for other foo-loggers to want this sort of thing...
     */
    apr_status_t rc;
    apr_procattr_t *procattr;
    apr_proc_t *procnew;
    apr_file_t *errfile;

    if (((rc = apr_procattr_create(&procattr, p)) == APR_SUCCESS)
        && ((rc = apr_procattr_cmdtype_set(procattr,
                                           APR_SHELLCMD_ENV)) == APR_SUCCESS)
        && ((rc = apr_procattr_io_set(procattr,
                                      APR_FULL_BLOCK,
                                      APR_NO_PIPE,
                                      APR_NO_PIPE)) == APR_SUCCESS)
        && ((rc = apr_procattr_error_check_set(procattr, 1)) == APR_SUCCESS)
        && ((rc = apr_procattr_child_errfn_set(procattr, log_child_errfn)) == APR_SUCCESS)
        && (!dummy_stderr 
            || ((rc = apr_file_open_stdout(&errfile, p)) == APR_SUCCESS
                && (rc = apr_procattr_child_err_set(procattr, 
                                                    errfile, 
                                                    errfile)) == APR_SUCCESS))) {
        char **args;
        const char *pname;

        apr_tokenize_to_argv(progname, &args, p);
        pname = apr_pstrdup(p, args[0]);
        procnew = (apr_proc_t *)apr_pcalloc(p, sizeof(*procnew));
        rc = apr_proc_create(procnew, pname, (const char * const *)args,
                             NULL, procattr, p);

        if (rc == APR_SUCCESS) {
            apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
            (*fpin) = procnew->in;
            /* read handle to pipe not kept open, so no need to call
             * close_handle_in_child()
             */
        }

        /* apr_procattr_child_err_set dups errfile twice: close the
         * remaining "parent-side" copy (apr_proc_create closes the
         * other). */
        if (dummy_stderr) {
            apr_file_close(procnew->err);
        }
    }

    return rc;
}
Пример #6
0
static int proc_error_check_set(lua_State *L)
{
  apr_status_t status;
  apr_int32_t error_check;
  lua_apr_proc *process;

  process = proc_check(L, 1);
  error_check = lua_toboolean(L, 2);
  status = apr_procattr_error_check_set(process->attr, error_check);

  return push_status(L, status);
}
Пример #7
0
static void test_pipe_writefull(CuTest *tc)
{
    int iterations = 1000;
    int i;
    int bytes_per_iteration = 8000;
    char *buf = (char *)malloc(bytes_per_iteration);
    char responsebuf[128];
    apr_size_t nbytes;
    int bytes_processed;
    apr_proc_t proc = {0};
    apr_procattr_t *procattr;
    const char *args[2];
    apr_status_t rv;
    
    rv = apr_procattr_create(&procattr, p);
    CuAssertIntEquals(tc, APR_SUCCESS, rv);

    rv = apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK,
                             APR_CHILD_BLOCK);
    CuAssertIntEquals(tc, APR_SUCCESS, rv);

    rv = apr_procattr_error_check_set(procattr, 1);
    CuAssertIntEquals(tc, APR_SUCCESS, rv);

    args[0] = "readchild" EXTENSION;
    args[1] = NULL;
    rv = apr_proc_create(&proc, "./readchild" EXTENSION, args, NULL, procattr, p);
    CuAssertIntEquals(tc, APR_SUCCESS, rv);

    rv = apr_file_pipe_timeout_set(proc.in, apr_time_from_sec(10));
    CuAssertIntEquals(tc, APR_SUCCESS, rv);

    rv = apr_file_pipe_timeout_set(proc.out, apr_time_from_sec(10));
    CuAssertIntEquals(tc, APR_SUCCESS, rv);

    i = iterations;
    do {
        rv = apr_file_write_full(proc.in, buf, bytes_per_iteration, NULL);
        CuAssertIntEquals(tc, APR_SUCCESS, rv);
    } while (--i);

    free(buf);

    rv = apr_file_close(proc.in);
    CuAssertIntEquals(tc, APR_SUCCESS, rv);
    
    nbytes = sizeof(responsebuf);
    rv = apr_file_read(proc.out, responsebuf, &nbytes);
    CuAssertIntEquals(tc, APR_SUCCESS, rv);
    bytes_processed = (int)apr_strtoi64(responsebuf, NULL, 10);
    CuAssertIntEquals(tc, iterations * bytes_per_iteration, bytes_processed);
}
Пример #8
0
static void spawn_server(apr_pool_t *p, apr_proc_t *out_proc)
{
    apr_proc_t proc = {0};
    apr_procattr_t *procattr;
    apr_status_t rv;
    const char *args[3];

    rv = apr_procattr_create(&procattr, p);
    if (rv != APR_SUCCESS) {
        aprerr("apr_procattr_create()", rv);
    }

    rv = apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK,
                             APR_CHILD_BLOCK);
    if (rv != APR_SUCCESS) {
        aprerr("apr_procattr_io_set()", rv);
    }

    rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM_ENV);
    if (rv != APR_SUCCESS) {
        aprerr("apr_procattr_cmdtype_set()", rv);
    }

    rv = apr_procattr_error_check_set(procattr, 1);
    if (rv != APR_SUCCESS) {
        aprerr("apr_procattr_error_check_set()", rv);
    }

    args[0] = "sendfile" EXTENSION;
    args[1] = "server";
    args[2] = NULL;
    rv = apr_proc_create(&proc, TESTBINPATH "sendfile" EXTENSION, args, NULL, procattr, p);
    if (rv != APR_SUCCESS) {
        aprerr("apr_proc_create()", rv);
    }

    *out_proc = proc;
}
/*
 * Handle post-rotate processing.
 */
static void post_rotate(apr_pool_t *pool, struct logfile *newlog,
                        rotate_config_t *config, rotate_status_t *status)
{
    apr_status_t rv;
    char error[120];
    apr_procattr_t *pattr;
    const char *argv[4];
    apr_proc_t proc;

    /* Handle link file, if configured. */
    if (config->linkfile) {
        apr_file_remove(config->linkfile, newlog->pool);
        if (config->verbose) {
            fprintf(stderr,"Linking %s to %s\n", newlog->name, config->linkfile);
        }
        rv = apr_file_link(newlog->name, config->linkfile);
        if (rv != APR_SUCCESS) {
            apr_strerror(rv, error, sizeof error);
            fprintf(stderr, "Error linking file %s to %s (%s)\n",
                    newlog->name, config->linkfile, error);
            exit(2);
        }
    }

    if (!config->postrotate_prog) {
        /* Nothing more to do. */
        return;
    }

    /* Collect any zombies from a previous run, but don't wait. */
    while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT, pool) == APR_CHILD_DONE)
        /* noop */;

    if ((rv = apr_procattr_create(&pattr, pool)) != APR_SUCCESS) {
        fprintf(stderr,
                "post_rotate: apr_procattr_create failed for '%s': %s\n",
                config->postrotate_prog,
                apr_strerror(rv, error, sizeof(error)));
        return;
    }

    rv = apr_procattr_error_check_set(pattr, 1);
    if (rv == APR_SUCCESS)
        rv = apr_procattr_cmdtype_set(pattr, APR_PROGRAM_ENV);

    if (rv != APR_SUCCESS) {
        fprintf(stderr,
                "post_rotate: could not set up process attributes for '%s': %s\n",
                config->postrotate_prog,
                apr_strerror(rv, error, sizeof(error)));
        return;
    }

    argv[0] = config->postrotate_prog;
    argv[1] = newlog->name;
    if (status->current.fd) {
        argv[2] = status->current.name;
        argv[3] = NULL;
    }
    else {
        argv[2] = NULL;
    }

    if (config->verbose)
        fprintf(stderr, "Calling post-rotate program: %s\n", argv[0]);

    rv = apr_proc_create(&proc, argv[0], argv, NULL, pattr, pool);
    if (rv != APR_SUCCESS) {
        fprintf(stderr, "Could not spawn post-rotate process '%s': %s\n",
                config->postrotate_prog,
                apr_strerror(rv, error, sizeof(error)));
        return;
    }
}
Пример #10
0
static void im_exec_start(nx_module_t *module)
{
    nx_im_exec_conf_t *imconf;
    apr_status_t rv;
    apr_procattr_t *pattr;
    apr_exit_why_e exitwhy;
    int exitval;

    ASSERT(module->config != NULL);

    imconf = (nx_im_exec_conf_t *) module->config;

    if ( module->input.desc.f == NULL )
    {
	CHECKERR_MSG(apr_procattr_create(&pattr, module->input.pool),
		     "apr_procattr_create() failed");
	CHECKERR_MSG(apr_procattr_error_check_set(pattr, 1),
		     "apr_procattr_error_check_set() failed");
#ifdef WIN32
	CHECKERR_MSG(apr_procattr_io_set(pattr, APR_NO_PIPE, APR_FULL_NONBLOCK,
					 APR_NO_PIPE),
		     "apr_procattr_io_set() failed");
#else
	CHECKERR_MSG(apr_procattr_io_set(pattr, APR_NO_PIPE, APR_FULL_BLOCK,
					 APR_NO_PIPE),
		     "apr_procattr_io_set() failed");
#endif
	CHECKERR_MSG(apr_procattr_cmdtype_set(pattr, APR_PROGRAM_ENV),
		     "apr_procattr_cmdtype_set() failed");
	CHECKERR_MSG(apr_proc_create(&(imconf->proc), imconf->cmd, (const char* const*)imconf->argv,
				     NULL, (apr_procattr_t*)pattr, module->input.pool),
		     "couldn't execute process %s", imconf->cmd);
	if ( (rv = apr_proc_wait(&(imconf->proc), &exitval, &exitwhy, APR_NOWAIT)) != APR_SUCCESS )
	{
	    if ( APR_STATUS_IS_CHILD_DONE(rv) )
	    {
		throw(rv, "im_exec process %s exited %s with exitval: %d",
		      imconf->cmd, exitwhy == APR_PROC_EXIT ? "normally" : "due to a signal",
		      exitval);
	    }
	    else
	    {
		// still running OK
	    }
	}
	log_debug("im_exec successfully spawned process");
	imconf->running = TRUE;

	module->input.desc_type = APR_POLL_FILE;
	module->input.desc.f = imconf->proc.out;
	module->input.module = module;
	module->input.inputfunc = imconf->inputfunc;
#ifdef WIN32
	im_exec_add_read_event(module, 0);
#else
	nx_module_pollset_add_file(module, module->input.desc.f, APR_POLLIN | APR_POLLHUP);
#endif
    }
    else
    {
	log_debug("%s already executed", imconf->cmd);
    }
#ifndef WIN32
    nx_module_add_poll_event(module);
#endif
}
/* init_ext_filter_process: get the external filter process going
 * This is per-filter-instance (i.e., per-request) initialization.
 */
static apr_status_t init_ext_filter_process(ap_filter_t *f)
{
    ef_ctx_t *ctx = f->ctx;
    apr_status_t rc;
    ef_dir_t *dc = ctx->dc;
    const char * const *env;

    ctx->proc = apr_pcalloc(ctx->p, sizeof(*ctx->proc));

    rc = apr_procattr_create(&ctx->procattr, ctx->p);
    ap_assert(rc == APR_SUCCESS);

    rc = apr_procattr_io_set(ctx->procattr,
                            APR_CHILD_BLOCK,
                            APR_CHILD_BLOCK,
                            APR_CHILD_BLOCK);
    ap_assert(rc == APR_SUCCESS);

    rc = set_resource_limits(f->r, ctx->procattr);
    ap_assert(rc == APR_SUCCESS);

    if (dc->log_stderr > 0) {
        rc = apr_procattr_child_err_set(ctx->procattr,
                                      f->r->server->error_log, /* stderr in child */
                                      NULL);
        ap_assert(rc == APR_SUCCESS);
    }

    rc = apr_procattr_child_errfn_set(ctx->procattr, child_errfn);
    ap_assert(rc == APR_SUCCESS);
    apr_pool_userdata_set(f->r, ERRFN_USERDATA_KEY, apr_pool_cleanup_null, ctx->p);

    rc = apr_procattr_error_check_set(ctx->procattr, 1);
    if (rc != APR_SUCCESS) {
        return rc;
    }

    /* add standard CGI variables as well as DOCUMENT_URI, DOCUMENT_PATH_INFO,
     * and QUERY_STRING_UNESCAPED
     */
    ap_add_cgi_vars(f->r);
    ap_add_common_vars(f->r);
    apr_table_setn(f->r->subprocess_env, "DOCUMENT_URI", f->r->uri);
    apr_table_setn(f->r->subprocess_env, "DOCUMENT_PATH_INFO", f->r->path_info);
    if (f->r->args) {
            /* QUERY_STRING is added by ap_add_cgi_vars */
        char *arg_copy = apr_pstrdup(f->r->pool, f->r->args);
        ap_unescape_url(arg_copy);
        apr_table_setn(f->r->subprocess_env, "QUERY_STRING_UNESCAPED",
                       ap_escape_shell_cmd(f->r->pool, arg_copy));
    }

    env = (const char * const *) ap_create_environment(ctx->p,
                                                       f->r->subprocess_env);

    rc = apr_proc_create(ctx->proc,
                            ctx->filter->command,
                            (const char * const *)ctx->filter->args,
                            env, /* environment */
                            ctx->procattr,
                            ctx->p);
    if (rc != APR_SUCCESS) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, f->r,
                      "couldn't create child process to run `%s'",
                      ctx->filter->command);
        return rc;
    }

    apr_pool_note_subprocess(ctx->p, ctx->proc, APR_KILL_AFTER_TIMEOUT);

    /* We don't want the handle to the child's stdin inherited by any
     * other processes created by httpd.  Otherwise, when we close our
     * handle, the child won't see EOF because another handle will still
     * be open.
     */

    apr_pool_cleanup_register(ctx->p, ctx->proc->in,
                         apr_pool_cleanup_null, /* other mechanism */
                         ef_close_file);

#if APR_FILES_AS_SOCKETS
    {
        apr_pollfd_t pfd = { 0 };

        rc = apr_pollset_create(&ctx->pollset, 2, ctx->p, 0);
        ap_assert(rc == APR_SUCCESS);

        pfd.p         = ctx->p;
        pfd.desc_type = APR_POLL_FILE;
        pfd.reqevents = APR_POLLOUT;
        pfd.desc.f    = ctx->proc->in;
        rc = apr_pollset_add(ctx->pollset, &pfd);
        ap_assert(rc == APR_SUCCESS);

        pfd.reqevents = APR_POLLIN;
        pfd.desc.f    = ctx->proc->out;
        rc = apr_pollset_add(ctx->pollset, &pfd);
        ap_assert(rc == APR_SUCCESS);
    }
#endif

    return APR_SUCCESS;
}
Пример #12
0
LIB_INTERNAL void run_bg_cmd(LOGMANAGER *mp, const char *cmd
	, const char *file_path, TIMESTAMP t)
{
char buf[32];
DECLARE_TPOOL;
apr_procattr_t *attr;
apr_proc_t proc;
const char *args[2];
apr_status_t status;
char errbuf[1024];

NORMALIZE_TIMESTAMP(t);
if (!cmd) return; /* Security */

DEBUG1(mp,1,"Running background command : %s",cmd);

/* Set environment variables */

(void)apr_env_set("LOGMANAGER_FILE_PATH",(file_path ? file_path : "")
	,CHECK_TPOOL());
(void)apr_env_set("LOGMANAGER_BASE_PATH",mp->base_path,CHECK_TPOOL());
(void)apr_env_set("LOGMANAGER_BASE_DIR",mp->base_dir,CHECK_TPOOL());
(void)apr_env_set("LOGMANAGER_LOG_PATH",mp->log_path,CHECK_TPOOL());
(void)apr_env_set("LOGMANAGER_LOG_DIR",mp->log_dir,CHECK_TPOOL());
(void)apr_env_set("LOGMANAGER_COMPRESSION"
	,mp->compress.handler->suffix,CHECK_TPOOL());
(void)apr_env_set("LOGMANAGER_VERSION",PACKAGE_VERSION,CHECK_TPOOL());

(void)apr_snprintf(buf,sizeof(buf),"%" TIMESTAMP_FMT,t);
(void)apr_env_set("LOGMANAGER_TIME",buf,CHECK_TPOOL());

/* Run background process */

(void)apr_procattr_create(&attr,CHECK_TPOOL());

/* APR_NO_FILE is defined in APR 1.3.0 and higher */

#ifdef APR_NO_FILE
#define LMGR_IO_ATTR APR_NO_FILE
#else
#define LMGR_IO_ATTR 0
#endif

/*(void)apr_procattr_io_set(attr,LMGR_IO_ATTR,LMGR_IO_ATTR,LMGR_IO_ATTR);*/
(void)apr_procattr_cmdtype_set(attr,APR_PROGRAM_ENV);
(void)apr_procattr_detach_set(attr,1);
(void)apr_procattr_error_check_set(attr,1);
(void)apr_procattr_child_errfn_set(attr,_proc_create_error_callback);

args[0]=cmd;
args[1]=NULL;

status=apr_proc_create(&proc,cmd,args,NULL, attr,CHECK_TPOOL());
if (status != APR_SUCCESS)
	{
	_proc_create_error(mp,CHECK_TPOOL(),status
		,apr_strerror(status,errbuf,sizeof(errbuf)));
	}

/* The background process lives its own life. Don't care about it anymore */

FREE_TPOOL();
return;
}