/* 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; }
/* 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, apr_cmdtype_e cmdtype, 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 *outfile, *errfile; if (((rc = apr_procattr_create(&procattr, p)) == APR_SUCCESS) && ((rc = apr_procattr_cmdtype_set(procattr, cmdtype)) == 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)) { 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)); if ((rc = apr_file_open_stdout(&outfile, p)) == APR_SUCCESS) { rc = apr_procattr_child_out_set(procattr, outfile, NULL); if (dummy_stderr) rc = apr_procattr_child_err_set(procattr, outfile, NULL); else if ((rc = apr_file_open_stderr(&errfile, p)) == APR_SUCCESS) rc = apr_procattr_child_err_set(procattr, errfile, NULL); } 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() */ } } return rc; }
static void test_file_redir(abts_case *tc, void *data) { apr_file_t *testout = NULL; apr_file_t *testerr = NULL; apr_off_t offset; apr_status_t rv; const char *args[2]; apr_procattr_t *attr; apr_file_t *testfile = NULL; apr_size_t length; char *buf; testfile = NULL; rv = apr_file_open(&testfile, "data/stdin", APR_READ | APR_WRITE | APR_CREATE | APR_EXCL, APR_OS_DEFAULT, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); rv = apr_file_open(&testout, "data/stdout", APR_READ | APR_WRITE | APR_CREATE | APR_EXCL, APR_OS_DEFAULT, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); rv = apr_file_open(&testerr, "data/stderr", APR_READ | APR_WRITE | APR_CREATE | APR_EXCL, APR_OS_DEFAULT, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); length = strlen(TESTSTR); apr_file_write(testfile, TESTSTR, &length); offset = 0; rv = apr_file_seek(testfile, APR_SET, &offset); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_ASSERT(tc, "File position mismatch, expected 0", offset == 0); rv = apr_procattr_create(&attr, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); rv = apr_procattr_child_in_set(attr, testfile, NULL); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); rv = apr_procattr_child_out_set(attr, testout, NULL); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); rv = apr_procattr_child_err_set(attr, testerr, NULL); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); rv = apr_procattr_dir_set(attr, "data"); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); rv = apr_procattr_cmdtype_set(attr, APR_PROGRAM); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); args[0] = "proc_child"; args[1] = NULL; rv = apr_proc_create(&newproc, "../" TESTBINPATH "proc_child" EXTENSION, args, NULL, attr, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); rv = apr_proc_wait(&newproc, NULL, NULL, APR_WAIT); ABTS_INT_EQUAL(tc, APR_CHILD_DONE, rv); offset = 0; rv = apr_file_seek(testout, APR_SET, &offset); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); length = 256; buf = apr_pcalloc(p, length); rv = apr_file_read(testout, buf, &length); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_STR_EQUAL(tc, TESTSTR, buf); apr_file_close(testfile); apr_file_close(testout); apr_file_close(testerr); rv = apr_file_remove("data/stdin", p);; ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); rv = apr_file_remove("data/stdout", p);; ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); rv = apr_file_remove("data/stderr", p);; ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); }
/** Try to start a process running the master job. */ static int trell_start_master( trell_sconf_t* svr_conf, request_rec* r ) { apr_status_t rv; // Open file into which master job stdout is piped. apr_file_t* master_stdout = NULL; rv = apr_file_open( &master_stdout, apr_psprintf( r->pool, "/tmp/%s.stdout", svr_conf->m_master_id ), APR_FOPEN_CREATE | APR_WRITE | APR_TRUNCATE | APR_XTHREAD, APR_OS_DEFAULT, r->pool ); if( rv != APR_SUCCESS ) { ap_log_rerror( APLOG_MARK, APLOG_CRIT, rv, r, "mod_trell: Failed to open master job stdout" ); return rv; } // Open file into which master job stderr is piped. apr_file_t* master_stderr = NULL; rv = apr_file_open( &master_stderr, apr_psprintf( r->pool, "/tmp/%s.stderr", svr_conf->m_master_id ), APR_FOPEN_CREATE | APR_WRITE | APR_TRUNCATE | APR_XTHREAD, APR_OS_DEFAULT, r->pool ); if( rv != APR_SUCCESS ) { ap_log_rerror( APLOG_MARK, APLOG_CRIT, rv, r, "mod_trell: Failed to open master job stderr" ); return rv; } // Create process attribute and set file handles for pipes apr_procattr_t* procattr; rv = apr_procattr_create( &procattr, r->pool ); if( rv != APR_SUCCESS ) { ap_log_rerror( APLOG_MARK, APLOG_CRIT, rv, r, "mod_trell: apr_procattr_create failed" ); return rv; } rv = apr_procattr_child_out_set( procattr, master_stdout, NULL ); if( rv != APR_SUCCESS ) { ap_log_rerror( APLOG_MARK, APLOG_CRIT, rv, r, "mod_trell: apr_procattr_child_out_set failed" ); return rv; } rv = apr_procattr_child_err_set( procattr, master_stderr, NULL ); if( rv != APR_SUCCESS ) { ap_log_rerror( APLOG_MARK, APLOG_CRIT, rv, r, "mod_trell: apr_procattr_child_err_set failed" ); return rv; } rv = apr_procattr_cmdtype_set( procattr, APR_PROGRAM ); if( rv != APR_SUCCESS ) { ap_log_rerror( APLOG_MARK, APLOG_CRIT, rv, r, "mod_trell: apr_procattr_cmdtype_set failed" ); return rv; } const char* env[7] = { apr_psprintf( r->pool, "TINIA_JOB_ID=%s", svr_conf->m_master_id ), apr_psprintf( r->pool, "TINIA_MASTER_ID=%s", svr_conf->m_master_id ), apr_psprintf( r->pool, "TINIA_APP_ROOT=%s", svr_conf->m_app_root_dir ), NULL, // PATH NULL, // LD_LIBRARY_PATH NULL, // DISPLAY NULL }; // Copy PATH and LD_LIBRARY_PATH from the current environement, if set. It // might be an idea to pass these variables through the apache-config, // allowing more detailded control on jobs. int p = 3; char* PATH = NULL; if( (apr_env_get( &PATH, "PATH", r->pool ) == APR_SUCCESS ) && (PATH != NULL) ) { env[p++] = apr_psprintf( r->pool, "PATH=%s", PATH ); } char* LD_LIBRARY_PATH = NULL; if( (apr_env_get( &LD_LIBRARY_PATH, "LD_LIBRARY_PATH", r->pool ) == APR_SUCCESS ) && (LD_LIBRARY_PATH != NULL) ) { env[p++] = apr_psprintf( r->pool, "LD_LIBRARY_PATH=%s", LD_LIBRARY_PATH ); } char* DISPLAY = NULL; if( (apr_env_get( &DISPLAY, "DISPLAY", r->pool ) == APR_SUCCESS ) && (DISPLAY != NULL) ) { env[p++] = apr_psprintf( r->pool, "DISPLAY=%s", DISPLAY ); } // Set up arguments const char* args[2] = { svr_conf->m_master_exe, NULL }; apr_proc_t newproc; rv = apr_proc_create( &newproc, svr_conf->m_master_exe, args, env, procattr, r->pool ); if( rv != APR_SUCCESS ) { ap_log_rerror( APLOG_MARK, APLOG_CRIT, rv, r, "mod_trell: Failed to execute '%s'", svr_conf->m_master_exe ); return rv; } rv = trell_set_master_pid( svr_conf, r, newproc.pid ); if( rv != APR_SUCCESS ) { return rv; } ap_log_rerror( APLOG_MARK, APLOG_NOTICE, OK, r, "mod_trell: Master running at pid %d", newproc.pid ); return APR_SUCCESS; }