APR_DECLARE(apr_status_t) apr_file_pipe_create_ex(apr_file_t **in, apr_file_t **out, apr_int32_t blocking, apr_pool_t *pool) { apr_status_t status; if ((status = apr_file_pipe_create(in, out, pool)) != APR_SUCCESS) return status; switch (blocking) { case APR_FULL_BLOCK: break; case APR_READ_BLOCK: apr_file_pipe_timeout_set(*out, 0); break; case APR_WRITE_BLOCK: apr_file_pipe_timeout_set(*in, 0); break; default: apr_file_pipe_timeout_set(*out, 0); apr_file_pipe_timeout_set(*in, 0); } return APR_SUCCESS; }
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); }
static apr_status_t pipe_bucket_read(apr_bucket *a, const char **str, apr_size_t *len, apr_read_type_e block) { apr_file_t *p = a->data; char *buf; apr_status_t rv; apr_interval_time_t timeout; if (block == APR_NONBLOCK_READ) { apr_file_pipe_timeout_get(p, &timeout); apr_file_pipe_timeout_set(p, 0); } *str = NULL; *len = APR_BUCKET_BUFF_SIZE; buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */ rv = apr_file_read(p, buf, len); if (block == APR_NONBLOCK_READ) { apr_file_pipe_timeout_set(p, timeout); } if (rv != APR_SUCCESS && rv != APR_EOF) { apr_bucket_free(buf); return rv; } /* * If there's more to read we have to keep the rest of the pipe * for later. Otherwise, we'll close the pipe. * XXX: Note that more complicated bucket types that * refer to data not in memory and must therefore have a read() * function similar to this one should be wary of copying this * code because if they have a destroy function they probably * want to migrate the bucket's subordinate structure from the * old bucket to a raw new one and adjust it as appropriate, * rather than destroying the old one and creating a completely * new bucket. */ if (*len > 0) { apr_bucket_heap *h; /* Change the current bucket to refer to what we read */ a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free); h = a->data; h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */ *str = buf; APR_BUCKET_INSERT_AFTER(a, apr_bucket_pipe_create(p, a->list)); } else { apr_bucket_free(buf); a = apr_bucket_immortal_make(a, "", 0); *str = a->data; if (rv == APR_EOF) { apr_file_close(p); } } return APR_SUCCESS; }
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); }
/* Implements ra_svn_timeout_fn_t */ static void file_timeout_cb(void *baton, apr_interval_time_t interval) { apr_file_t *f = baton; if (f) apr_file_pipe_timeout_set(f, interval); }
static int pipe_timeout_set(lua_State *L) { lua_apr_file *pipe; apr_status_t status; apr_interval_time_t timeout; pipe = file_check(L, 1, 1); if (lua_isnumber(L, 2)) timeout = luaL_checkinteger(L, 2); else timeout = lua_toboolean(L, 2) ? -1 : 0; status = apr_file_pipe_timeout_set(pipe->handle, timeout); return push_file_status(L, pipe, status); }
AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod) { apr_status_t rv; *pod = apr_palloc(p, sizeof(**pod)); rv = apr_file_pipe_create(&((*pod)->pod_in), &((*pod)->pod_out), p); if (rv != APR_SUCCESS) { return rv; } apr_file_pipe_timeout_set((*pod)->pod_in, 0); (*pod)->p = p; /* close these before exec. */ apr_file_inherit_unset((*pod)->pod_in); apr_file_inherit_unset((*pod)->pod_out); return APR_SUCCESS; }
static void set_timeout(abts_case *tc, void *data) { apr_status_t rv; apr_interval_time_t timeout; rv = apr_file_pipe_create(&readp, &writep, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, readp); ABTS_PTR_NOTNULL(tc, writep); rv = apr_file_pipe_timeout_get(readp, &timeout); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_ASSERT(tc, "Timeout mismatch, expected -1", timeout == -1); rv = apr_file_pipe_timeout_set(readp, apr_time_from_sec(1)); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); rv = apr_file_pipe_timeout_get(readp, &timeout); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_ASSERT(tc, "Timeout mismatch, expected 1 second", timeout == apr_time_from_sec(1)); }
static void read_write(abts_case *tc, void *data) { apr_status_t rv; char *buf; apr_size_t nbytes; nbytes = strlen("this is a test"); buf = (char *)apr_palloc(p, nbytes + 1); rv = apr_file_pipe_create(&readp, &writep, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, readp); ABTS_PTR_NOTNULL(tc, writep); rv = apr_file_pipe_timeout_set(readp, apr_time_from_sec(1)); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); if (!rv) { rv = apr_file_read(readp, buf, &nbytes); ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv)); ABTS_INT_EQUAL(tc, 0, nbytes); } }
static void read_write(CuTest *tc) { apr_status_t rv; char *buf; apr_size_t nbytes; nbytes = strlen("this is a test"); buf = (char *)apr_palloc(p, nbytes + 1); rv = apr_file_pipe_create(&readp, &writep, p); CuAssertIntEquals(tc, APR_SUCCESS, rv); CuAssertPtrNotNull(tc, readp); CuAssertPtrNotNull(tc, writep); rv = apr_file_pipe_timeout_set(readp, apr_time_from_sec(1)); CuAssertIntEquals(tc, APR_SUCCESS, rv); rv = apr_file_read(readp, buf, &nbytes); if (!rv) { CuAssertIntEquals(tc, 1, APR_STATUS_IS_TIMEUP(rv)); CuAssertIntEquals(tc, 0, nbytes); } }
static void set_timeout(CuTest *tc) { apr_status_t rv; apr_file_t *readp = NULL; apr_file_t *writep = NULL; apr_interval_time_t timeout; rv = apr_file_pipe_create(&readp, &writep, p); CuAssertIntEquals(tc, APR_SUCCESS, rv); CuAssertPtrNotNull(tc, readp); CuAssertPtrNotNull(tc, writep); rv = apr_file_pipe_timeout_get(readp, &timeout); CuAssertIntEquals(tc, APR_SUCCESS, rv); CuAssert(tc, "Timeout mismatch, expected -1", timeout == -1); rv = apr_file_pipe_timeout_set(readp, apr_time_from_sec(1)); CuAssertIntEquals(tc, APR_SUCCESS, rv); rv = apr_file_pipe_timeout_get(readp, &timeout); CuAssertIntEquals(tc, APR_SUCCESS, rv); CuAssert(tc, "Timeout mismatch, expected 1 second", timeout == apr_time_from_sec(1)); }
static int cgi_handler(request_rec *r) { int nph; apr_size_t dbpos = 0; const char *argv0; const char *command; const char **argv; char *dbuf = NULL; apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL; apr_bucket_brigade *bb; apr_bucket *b; int is_included; int seen_eos, child_stopped_reading; apr_pool_t *p; cgi_server_conf *conf; apr_status_t rv; cgi_exec_info_t e_info; conn_rec *c = r->connection; if(strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script")) return DECLINED; is_included = !strcmp(r->protocol, "INCLUDED"); p = r->main ? r->main->pool : r->pool; argv0 = apr_filepath_name_get(r->filename); nph = !(strncmp(argv0, "nph-", 4)); conf = ap_get_module_config(r->server->module_config, &cgi_module); if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r)) return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, "Options ExecCGI is off in this directory"); if (nph && is_included) return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, "attempt to include NPH CGI script"); if (r->finfo.filetype == 0) return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, "script not found or unable to stat"); if (r->finfo.filetype == APR_DIR) return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, "attempt to invoke directory as script"); if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) && r->path_info && *r->path_info) { /* default to accept */ return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, "AcceptPathInfo off disallows user's path"); } /* if (!ap_suexec_enabled) { if (!ap_can_exec(&r->finfo)) return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, "file permissions deny server execution"); } */ ap_add_common_vars(r); ap_add_cgi_vars(r); e_info.process_cgi = 1; e_info.cmd_type = APR_PROGRAM; e_info.detached = 0; e_info.in_pipe = APR_CHILD_BLOCK; e_info.out_pipe = APR_CHILD_BLOCK; e_info.err_pipe = APR_CHILD_BLOCK; e_info.prog_type = RUN_AS_CGI; e_info.bb = NULL; e_info.ctx = NULL; e_info.next = NULL; e_info.addrspace = 0; /* build the command line */ if ((rv = cgi_build_command(&command, &argv, r, p, &e_info)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "don't know how to spawn child process: %s", r->filename); return HTTP_INTERNAL_SERVER_ERROR; } /* run the script in its own process */ if ((rv = run_cgi_child(&script_out, &script_in, &script_err, command, argv, r, p, &e_info)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "couldn't spawn child process: %s", r->filename); return HTTP_INTERNAL_SERVER_ERROR; } /* Transfer any put/post args, CERN style... * Note that we already ignore SIGPIPE in the core server. */ bb = apr_brigade_create(r->pool, c->bucket_alloc); seen_eos = 0; child_stopped_reading = 0; if (conf->logname) { dbuf = apr_palloc(r->pool, conf->bufbytes + 1); dbpos = 0; } do { apr_bucket *bucket; rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "Error reading request entity data"); return ap_map_http_request_error(rv, HTTP_BAD_REQUEST); } for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb); bucket = APR_BUCKET_NEXT(bucket)) { const char *data; apr_size_t len; if (APR_BUCKET_IS_EOS(bucket)) { seen_eos = 1; break; } /* We can't do much with this. */ if (APR_BUCKET_IS_FLUSH(bucket)) { continue; } /* If the child stopped, we still must read to EOS. */ if (child_stopped_reading) { continue; } /* read */ apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); if (conf->logname && dbpos < conf->bufbytes) { int cursize; if ((dbpos + len) > conf->bufbytes) { cursize = conf->bufbytes - dbpos; } else { cursize = len; } memcpy(dbuf + dbpos, data, cursize); dbpos += cursize; } /* Keep writing data to the child until done or too much time * elapses with no progress or an error occurs. */ rv = apr_file_write_full(script_out, data, len, NULL); if (rv != APR_SUCCESS) { /* silly script stopped reading, soak up remaining message */ child_stopped_reading = 1; } } apr_brigade_cleanup(bb); } while (!seen_eos); if (conf->logname) { dbuf[dbpos] = '\0'; } /* Is this flush really needed? */ apr_file_flush(script_out); apr_file_close(script_out); AP_DEBUG_ASSERT(script_in != NULL); apr_brigade_cleanup(bb); #if APR_FILES_AS_SOCKETS apr_file_pipe_timeout_set(script_in, 0); apr_file_pipe_timeout_set(script_err, 0); b = cgi_bucket_create(r, script_in, script_err, c->bucket_alloc); #else b = apr_bucket_pipe_create(script_in, c->bucket_alloc); #endif APR_BRIGADE_INSERT_TAIL(bb, b); b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); /* Handle script return... */ if (!nph) { const char *location; char sbuf[MAX_STRING_LEN]; int ret; if ((ret = ap_scan_script_header_err_brigade(r, bb, sbuf))) { ret = log_script(r, conf, ret, dbuf, sbuf, bb, script_err); /* * ret could be HTTP_NOT_MODIFIED in the case that the CGI script * does not set an explicit status and ap_meets_conditions, which * is called by ap_scan_script_header_err_brigade, detects that * the conditions of the requests are met and the response is * not modified. * In this case set r->status and return OK in order to prevent * running through the error processing stack as this would * break with mod_cache, if the conditions had been set by * mod_cache itself to validate a stale entity. * BTW: We circumvent the error processing stack anyway if the * CGI script set an explicit status code (whatever it is) and * the only possible values for ret here are: * * HTTP_NOT_MODIFIED (set by ap_meets_conditions) * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the * processing of the response of the CGI script, e.g broken headers * or a crashed CGI process). */ if (ret == HTTP_NOT_MODIFIED) { r->status = ret; return OK; } return ret; } location = apr_table_get(r->headers_out, "Location"); if (location && r->status == 200) { /* For a redirect whether internal or not, discard any * remaining stdout from the script, and log any remaining * stderr output, as normal. */ discard_script_output(bb); apr_brigade_destroy(bb); apr_file_pipe_timeout_set(script_err, r->server->timeout); log_script_err(r, script_err); } if (location && location[0] == '/' && r->status == 200) { /* This redirect needs to be a GET no matter what the original * method was. */ r->method = apr_pstrdup(r->pool, "GET"); r->method_number = M_GET; /* We already read the message body (if any), so don't allow * the redirected request to think it has one. We can ignore * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. */ apr_table_unset(r->headers_in, "Content-Length"); ap_internal_redirect_handler(location, r); return OK; } else if (location && r->status == 200) { /* XX Note that if a script wants to produce its own Redirect * body, it now has to explicitly *say* "Status: 302" */ return HTTP_MOVED_TEMPORARILY; } rv = ap_pass_brigade(r->output_filters, bb); } else /* nph */ { struct ap_filter_t *cur; /* get rid of all filters up through protocol... since we * haven't parsed off the headers, there is no way they can * work */ cur = r->proto_output_filters; while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { cur = cur->next; } r->output_filters = r->proto_output_filters = cur; rv = ap_pass_brigade(r->output_filters, bb); } /* don't soak up script output if errors occurred writing it * out... otherwise, we prolong the life of the script when the * connection drops or we stopped sending output for some other * reason */ if (rv == APR_SUCCESS && !r->connection->aborted) { apr_file_pipe_timeout_set(script_err, r->server->timeout); log_script_err(r, script_err); } apr_file_close(script_err); return OK; /* NOT r->status, even if it has changed. */ }
/* copied from mod_cgi.c and slightly modified * to work with fcgi_record types */ static int fcgi_server_parse_headers(fcgi_request_t *fr, uint16_t request_id, char **data) { request_rec *r = fr->r; int ret, termarg; if ((ret = ap_scan_script_header_err_strs(r, NULL, (const char **)data, &termarg, *data, NULL))) { /* * ret could be HTTP_NOT_MODIFIED in the case that the CGI script * does not set an explicit status and ap_meets_conditions, which * is called by ap_scan_script_header_err_brigade, detects that * the conditions of the requests are met and the response is * not modified. * In this case set r->status and return OK in order to prevent * running through the error processing stack as this would * break with mod_cache, if the conditions had been set by * mod_cache itself to validate a stale entity. * BTW: We circumvent the error processing stack anyway if the * CGI script set an explicit status code (whatever it is) and * the only possible values for ret here are: * * HTTP_NOT_MODIFIED (set by ap_meets_conditions) * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the * processing of the response of the CGI script, e.g broken headers * or a crashed CGI process). */ if (ret == HTTP_NOT_MODIFIED) { r->status = ret; return OK; } return ret; } const char *location = apr_table_get(r->headers_out, "Location"); #if 0 if (location && r->status == 200) { /* For a redirect whether internal or not, discard any * remaining stdout from the script, and log any remaining * stderr output, as normal. */ discard_script_output(bb); apr_brigade_destroy(bb); apr_file_pipe_timeout_set(script_err, r->server->timeout); log_script_err(r, script_err); } #endif if (location && location[0] == '/' && r->status == 200) { /* This redirect needs to be a GET no matter what the original * method was. */ r->method = apr_pstrdup(r->pool, "GET"); r->method_number = M_GET; /* We already read the message body (if any), so don't allow * the redirected request to think it has one. We can ignore * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. */ apr_table_unset(r->headers_in, "Content-Length"); ap_internal_redirect_handler(location, r); return OK; } else if (location && r->status == 200) { /* XX Note that if a script wants to produce its own Redirect * body, it now has to explicitly *say* "Status: 302" */ return HTTP_MOVED_TEMPORARILY; } }
static apr_status_t run_cgi_child(apr_file_t **script_out, apr_file_t **script_in, apr_file_t **script_err, const char *command, const char * const argv[], request_rec *r, apr_pool_t *p, cgi_exec_info_t *e_info) { const char * const *env; apr_procattr_t *procattr; apr_proc_t *procnew; apr_status_t rc = APR_SUCCESS; #if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \ defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS) core_dir_config *conf = ap_get_module_config(r->per_dir_config, &core_module); #endif #ifdef DEBUG_CGI #ifdef OS2 /* Under OS/2 need to use device con. */ FILE *dbg = fopen("con", "w"); #else FILE *dbg = fopen("/dev/tty", "w"); #endif int i; #endif RAISE_SIGSTOP(CGI_CHILD); #ifdef DEBUG_CGI fprintf(dbg, "Attempting to exec %s as CGI child (argv0 = %s)\n", r->filename, argv[0]); #endif env = (const char * const *)ap_create_environment(p, r->subprocess_env); #ifdef DEBUG_CGI fprintf(dbg, "Environment: \n"); for (i = 0; env[i]; ++i) fprintf(dbg, "'%s'\n", env[i]); #endif /* Transmute ourselves into the script. * NB only ISINDEX scripts get decoded arguments. */ if (((rc = apr_procattr_create(&procattr, p)) != APR_SUCCESS) || ((rc = apr_procattr_io_set(procattr, e_info->in_pipe, e_info->out_pipe, e_info->err_pipe)) != APR_SUCCESS) || ((rc = apr_procattr_dir_set(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS) || #ifdef RLIMIT_CPU ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_CPU, conf->limit_cpu)) != APR_SUCCESS) || #endif #if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_MEM, conf->limit_mem)) != APR_SUCCESS) || #endif #ifdef RLIMIT_NPROC ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_NPROC, conf->limit_nproc)) != APR_SUCCESS) || #endif ((rc = apr_procattr_cmdtype_set(procattr, e_info->cmd_type)) != APR_SUCCESS) || ((rc = apr_procattr_detach_set(procattr, e_info->detached)) != APR_SUCCESS) || ((rc = apr_procattr_addrspace_set(procattr, e_info->addrspace)) != APR_SUCCESS) || ((rc = apr_procattr_child_errfn_set(procattr, cgi_child_errfn)) != APR_SUCCESS)) { /* Something bad happened, tell the world. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "couldn't set child process attributes: %s", r->filename); } else { procnew = apr_pcalloc(p, sizeof(*procnew)); rc = ap_os_create_privileged_process(r, procnew, command, argv, env, procattr, p); if (rc != APR_SUCCESS) { /* Bad things happened. Everyone should have cleaned up. */ ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, rc, r, "couldn't create child process: %d: %s", rc, apr_filepath_name_get(r->filename)); } else { apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT); *script_in = procnew->out; if (!*script_in) return APR_EBADF; apr_file_pipe_timeout_set(*script_in, r->server->timeout); if (e_info->prog_type == RUN_AS_CGI) { *script_out = procnew->in; if (!*script_out) return APR_EBADF; apr_file_pipe_timeout_set(*script_out, r->server->timeout); *script_err = procnew->err; if (!*script_err) return APR_EBADF; apr_file_pipe_timeout_set(*script_err, r->server->timeout); } } } #ifdef DEBUG_CGI fclose(dbg); #endif return (rc); }
int LLProcessLauncher::launch(void) { // If there was already a process associated with this object, kill it. kill(); orphan(); int result = 0; int current_wd = -1; // create an argv vector for the child process const char ** fake_argv = new const char *[mLaunchArguments.size() + 2]; // 1 for the executable path, 1 for the NULL terminator int i = 0; // add the executable path fake_argv[i++] = mExecutable.c_str(); // and any arguments for(int j=0; j < mLaunchArguments.size(); j++) fake_argv[i++] = mLaunchArguments[j].c_str(); // terminate with a null pointer fake_argv[i] = NULL; if(!mWorkingDir.empty()) { // save the current working directory current_wd = ::open(".", O_RDONLY); // and change to the one the child will be executed in if (::chdir(mWorkingDir.c_str())) { // chdir failed } } pid_t id; { #ifdef DEBUG_PIPE_CHILD_ERROR_REPORTING // Set up a pipe to the child process for error reporting. apr_file_t* in; apr_file_t* out; LLAPRPool pool; pool.create(); #if(APR_VERSION_MAJOR==1 && APR_VERSION_MINOR>=3 || APR_VERSION_MAJOR>1) apr_status_t status = apr_file_pipe_create_ex(&in, &out, APR_FULL_BLOCK, pool()); #else apr_status_t status = apr_file_pipe_create(&in, &out, pool()); #endif assert(status == APR_SUCCESS); bool success = (status == APR_SUCCESS); if (success) { apr_interval_time_t const timeout = 10000000; // 10 seconds. status = apr_file_pipe_timeout_set(in, timeout); assert(status == APR_SUCCESS); success = (status == APR_SUCCESS); } #endif // DEBUG_PIPE_CHILD_ERROR_REPORTING // flush all buffers before the child inherits them ::fflush(NULL); id = vfork(); if (id == 0) { // child process #ifdef DEBUG_PIPE_CHILD_ERROR_REPORTING // Tell parent process we're about to call execv. write_pipe(out, "CALLING EXECV"); #ifdef _DEBUG char const* display = getenv("DISPLAY"); std::cerr << "Calling ::execv(\"" << mExecutable << '"'; for(int j = 0; j < i; ++j) std::cerr << ", \"" << fake_argv[j] << '"'; std::cerr << ") with DISPLAY=\"" << (display ? display : "NULL") << '"' << std::endl; #endif #endif // DEBUG_PIPE_CHILD_ERROR_REPORTING ::execv(mExecutable.c_str(), (char * const *)fake_argv); #ifdef DEBUG_PIPE_CHILD_ERROR_REPORTING status = APR_FROM_OS_ERROR(apr_get_os_error()); char message[256]; char errbuf[128]; apr_strerror(status, errbuf, sizeof(errbuf)); snprintf(message, sizeof(message), "Child process: execv: %s: %s", mExecutable.c_str(), errbuf); write_pipe(out, message); #ifdef _DEBUG std::cerr << "::execv() failed." << std::endl; #endif #endif // DEBUG_PIPE_CHILD_ERROR_REPORTING // If we reach this point, the exec failed. // Use _exit() instead of exit() per the vfork man page. _exit(0); } // parent process #ifdef DEBUG_PIPE_CHILD_ERROR_REPORTING // Close unused pipe end. apr_file_close(out); if (success) { // Attempt to do error reporting. std::string message = read_pipe(in); success = (message == "CALLING EXECV"); assert(success); if (success) { status = apr_file_pipe_timeout_set(in, 2000000); // Only wait 2 seconds. message = read_pipe(in, true); if (message != "TIMEOUT" && message != "END OF FILE") { // Most likely execv failed. llwarns << message << llendl; assert(false); // Fail in debug mode. } } } // Clean up. apr_file_close(in); #endif // DEBUG_PIPE_CHILD_ERROR_REPORTING } if(current_wd >= 0) { // restore the previous working directory if (::fchdir(current_wd)) { // chdir failed } ::close(current_wd); } delete[] fake_argv; mProcessID = id; // At this point, the child process will have been created (since that's how vfork works -- the child borrowed our execution context until it forked) // If the process doesn't exist at this point, the exec failed. if(!isRunning()) { result = -1; } return result; }
static int client(apr_pool_t *p, client_socket_mode_t socket_mode, const char *host, int start_server) { apr_status_t rv, tmprv; apr_socket_t *sock; char buf[120]; apr_file_t *f = NULL; apr_size_t len; apr_size_t expected_len; apr_off_t current_file_offset; apr_hdtr_t hdtr; struct iovec headers[3]; struct iovec trailers[3]; apr_size_t bytes_read; apr_pollset_t *pset; apr_int32_t nsocks; int connect_tries = 1; int i; int family; apr_sockaddr_t *destsa; apr_proc_t server; apr_interval_time_t connect_retry_interval = apr_time_from_msec(50); if (start_server) { spawn_server(p, &server); connect_tries = 5; /* give it a chance to start up */ } create_testfile(p, TESTFILE); rv = apr_file_open(&f, TESTFILE, APR_FOPEN_READ, 0, p); if (rv != APR_SUCCESS) { aprerr("apr_file_open()", rv); } if (!host) { host = "127.0.0.1"; } family = APR_INET; rv = apr_sockaddr_info_get(&destsa, host, family, TESTSF_PORT, 0, p); if (rv != APR_SUCCESS) { aprerr("apr_sockaddr_info_get()", rv); } while (connect_tries--) { apr_setup(p, &sock, &family); rv = apr_socket_connect(sock, destsa); if (connect_tries && APR_STATUS_IS_ECONNREFUSED(rv)) { apr_status_t tmprv = apr_socket_close(sock); if (tmprv != APR_SUCCESS) { aprerr("apr_socket_close()", tmprv); } apr_sleep(connect_retry_interval); connect_retry_interval *= 2; } else { break; } } if (rv != APR_SUCCESS) { aprerr("apr_socket_connect()", rv); } switch(socket_mode) { case BLK: /* leave it blocking */ break; case NONBLK: /* set it non-blocking */ rv = apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1); if (rv != APR_SUCCESS) { aprerr("apr_socket_opt_set(APR_SO_NONBLOCK)", rv); } break; case TIMEOUT: /* set a timeout */ rv = apr_socket_timeout_set(sock, 100 * APR_USEC_PER_SEC); if (rv != APR_SUCCESS) { aprerr("apr_socket_opt_set(APR_SO_NONBLOCK)", rv); exit(1); } break; default: assert(1 != 1); } printf("Sending the file...\n"); hdtr.headers = headers; hdtr.numheaders = 3; hdtr.headers[0].iov_base = HDR1; hdtr.headers[0].iov_len = strlen(hdtr.headers[0].iov_base); hdtr.headers[1].iov_base = HDR2; hdtr.headers[1].iov_len = strlen(hdtr.headers[1].iov_base); hdtr.headers[2].iov_base = malloc(HDR3_LEN); assert(hdtr.headers[2].iov_base); memset(hdtr.headers[2].iov_base, HDR3_CHAR, HDR3_LEN); hdtr.headers[2].iov_len = HDR3_LEN; hdtr.trailers = trailers; hdtr.numtrailers = 3; hdtr.trailers[0].iov_base = TRL1; hdtr.trailers[0].iov_len = strlen(hdtr.trailers[0].iov_base); hdtr.trailers[1].iov_base = TRL2; hdtr.trailers[1].iov_len = strlen(hdtr.trailers[1].iov_base); hdtr.trailers[2].iov_base = malloc(TRL3_LEN); memset(hdtr.trailers[2].iov_base, TRL3_CHAR, TRL3_LEN); assert(hdtr.trailers[2].iov_base); hdtr.trailers[2].iov_len = TRL3_LEN; expected_len = strlen(HDR1) + strlen(HDR2) + HDR3_LEN + strlen(TRL1) + strlen(TRL2) + TRL3_LEN + FILE_LENGTH; if (socket_mode == BLK) { current_file_offset = 0; len = FILE_LENGTH; rv = apr_socket_sendfile(sock, f, &hdtr, ¤t_file_offset, &len, 0); if (rv != APR_SUCCESS) { aprerr("apr_socket_sendfile()", rv); } printf("apr_socket_sendfile() updated offset with %ld\n", (long int)current_file_offset); printf("apr_socket_sendfile() updated len with %ld\n", (long int)len); printf("bytes really sent: %" APR_SIZE_T_FMT "\n", expected_len); if (len != expected_len) { fprintf(stderr, "apr_socket_sendfile() didn't report the correct " "number of bytes sent!\n"); exit(1); } } else { /* non-blocking... wooooooo */ apr_size_t total_bytes_sent; apr_pollfd_t pfd; pset = NULL; rv = apr_pollset_create(&pset, 1, p, 0); assert(!rv); pfd.p = p; pfd.desc_type = APR_POLL_SOCKET; pfd.reqevents = APR_POLLOUT; pfd.rtnevents = 0; pfd.desc.s = sock; pfd.client_data = NULL; rv = apr_pollset_add(pset, &pfd); assert(!rv); total_bytes_sent = 0; current_file_offset = 0; len = FILE_LENGTH; do { apr_size_t tmplen; tmplen = len; /* bytes remaining to send from the file */ printf("Calling apr_socket_sendfile()...\n"); printf("Headers (%d):\n", hdtr.numheaders); for (i = 0; i < hdtr.numheaders; i++) { printf("\t%ld bytes (%c)\n", (long)hdtr.headers[i].iov_len, *(char *)hdtr.headers[i].iov_base); } printf("File: %ld bytes from offset %ld\n", (long)tmplen, (long)current_file_offset); printf("Trailers (%d):\n", hdtr.numtrailers); for (i = 0; i < hdtr.numtrailers; i++) { printf("\t%ld bytes\n", (long)hdtr.trailers[i].iov_len); } rv = apr_socket_sendfile(sock, f, &hdtr, ¤t_file_offset, &tmplen, 0); printf("apr_socket_sendfile()->%d, sent %ld bytes\n", rv, (long)tmplen); if (rv) { if (APR_STATUS_IS_EAGAIN(rv)) { assert(tmplen == 0); nsocks = 1; tmprv = apr_pollset_poll(pset, -1, &nsocks, NULL); assert(!tmprv); assert(nsocks == 1); /* continue; */ } } total_bytes_sent += tmplen; /* Adjust hdtr to compensate for partially-written * data. */ /* First, skip over any header data which might have * been written. */ while (tmplen && hdtr.numheaders) { if (tmplen >= hdtr.headers[0].iov_len) { tmplen -= hdtr.headers[0].iov_len; --hdtr.numheaders; ++hdtr.headers; } else { hdtr.headers[0].iov_len -= tmplen; hdtr.headers[0].iov_base = (char*) hdtr.headers[0].iov_base + tmplen; tmplen = 0; } } /* Now, skip over any file data which might have been * written. */ if (tmplen <= len) { current_file_offset += tmplen; len -= tmplen; tmplen = 0; } else { tmplen -= len; len = 0; current_file_offset = 0; } /* Last, skip over any trailer data which might have * been written. */ while (tmplen && hdtr.numtrailers) { if (tmplen >= hdtr.trailers[0].iov_len) { tmplen -= hdtr.trailers[0].iov_len; --hdtr.numtrailers; ++hdtr.trailers; } else { hdtr.trailers[0].iov_len -= tmplen; hdtr.trailers[0].iov_base = (char *)hdtr.trailers[0].iov_base + tmplen; tmplen = 0; } } } while (total_bytes_sent < expected_len && (rv == APR_SUCCESS || (APR_STATUS_IS_EAGAIN(rv) && socket_mode != TIMEOUT))); if (total_bytes_sent != expected_len) { fprintf(stderr, "client problem: sent %ld of %ld bytes\n", (long)total_bytes_sent, (long)expected_len); exit(1); } if (rv) { fprintf(stderr, "client problem: rv %d\n", rv); exit(1); } } current_file_offset = 0; rv = apr_file_seek(f, APR_CUR, ¤t_file_offset); if (rv != APR_SUCCESS) { aprerr("apr_file_seek()", rv); } printf("After apr_socket_sendfile(), the kernel file pointer is " "at offset %ld.\n", (long int)current_file_offset); rv = apr_socket_shutdown(sock, APR_SHUTDOWN_WRITE); if (rv != APR_SUCCESS) { aprerr("apr_socket_shutdown()", rv); } /* in case this is the non-blocking test, set socket timeout; * we're just waiting for EOF */ rv = apr_socket_timeout_set(sock, apr_time_from_sec(3)); if (rv != APR_SUCCESS) { aprerr("apr_socket_timeout_set()", rv); } bytes_read = 1; rv = apr_socket_recv(sock, buf, &bytes_read); if (rv != APR_EOF) { aprerr("apr_socket_recv() (expected APR_EOF)", rv); } if (bytes_read != 0) { fprintf(stderr, "We expected to get 0 bytes read with APR_EOF\n" "but instead we read %ld bytes.\n", (long int)bytes_read); exit(1); } printf("client: apr_socket_sendfile() worked as expected!\n"); rv = apr_file_remove(TESTFILE, p); if (rv != APR_SUCCESS) { aprerr("apr_file_remove()", rv); } if (start_server) { apr_exit_why_e exitwhy; apr_size_t nbytes; char responsebuf[1024]; int exitcode; rv = apr_file_pipe_timeout_set(server.out, apr_time_from_sec(2)); if (rv != APR_SUCCESS) { aprerr("apr_file_pipe_timeout_set()", rv); } nbytes = sizeof(responsebuf); rv = apr_file_read(server.out, responsebuf, &nbytes); if (rv != APR_SUCCESS) { aprerr("apr_file_read() messages from server", rv); } printf("%.*s", (int)nbytes, responsebuf); rv = apr_proc_wait(&server, &exitcode, &exitwhy, APR_WAIT); if (rv != APR_CHILD_DONE) { aprerr("apr_proc_wait() (expected APR_CHILD_DONE)", rv); } if (exitcode != 0) { fprintf(stderr, "sendfile server returned %d\n", exitcode); exit(1); } } return 0; }
static apr_status_t create_process_manager(server_rec * main_server, apr_pool_t * configpool) { apr_status_t rv; g_process_manager = (apr_proc_t *) apr_pcalloc(configpool, sizeof(*g_process_manager)); rv = apr_proc_fork(g_process_manager, configpool); if (rv == APR_INCHILD) { /* I am the child */ g_pm_pid = getpid(); ap_log_error(APLOG_MARK, APLOG_INFO, 0, main_server, "mod_fcgid: Process manager %" APR_PID_T_FMT " started", getpid()); if ((rv = init_signal(main_server)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: can't install signal handler, exiting now"); exit(DAEMON_STARTUP_ERROR); } /* If running as root, switch to configured user. * * When running children via suexec, only the effective uid is * switched, so that the PM can return to euid 0 to kill child * processes. * * When running children as the configured user, the real uid * is switched. */ if (ap_unixd_config.suexec_enabled) { if (getuid() != 0) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, main_server, "mod_fcgid: current user is not root while suexec is enabled, exiting now"); exit(DAEMON_STARTUP_ERROR); } suexec_setup_child(); } else ap_unixd_setup_child(); apr_file_pipe_timeout_set(g_pm_read_pipe, apr_time_from_sec(g_wakeup_timeout)); apr_file_close(g_ap_write_pipe); apr_file_close(g_ap_read_pipe); /* Initialize spawn controler */ spawn_control_init(main_server, configpool); pm_main(main_server, configpool); ap_log_error(APLOG_MARK, APLOG_INFO, 0, main_server, "mod_fcgid: Process manager %" APR_PID_T_FMT " stopped", getpid()); exit(0); } else if (rv != APR_INPARENT) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: Create process manager error"); exit(1); } /* I am the parent I will send the stop signal in procmgr_stop_procmgr() */ apr_pool_note_subprocess(configpool, g_process_manager, APR_KILL_ONLY_ONCE); apr_proc_other_child_register(g_process_manager, fcgid_maint, g_process_manager, NULL, configpool); return APR_SUCCESS; }
/* Implements ra_svn_timeout_fn_t */ static void file_timeout_cb(void *baton, apr_interval_time_t interval) { file_baton_t *b = baton; apr_file_pipe_timeout_set(b->out_file, interval); }
/** * Set the timeout value for a pipe or manipulate the blocking state. * @param thepipe The pipe we are setting a timeout on. * @param timeout The timeout value in microseconds. Values < 0 mean wait * forever, 0 means do not wait at all. */ SWITCH_DECLARE(switch_status_t) switch_file_pipe_timeout_set(switch_file_t *thepipe, switch_interval_time_t timeout) { return apr_file_pipe_timeout_set((apr_file_t *) thepipe, (apr_interval_time_t) timeout); }
/** * Execute system command. First line of the output will be returned in * the "output" parameter. */ int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char **output) { apr_procattr_t *procattr = NULL; apr_proc_t *procnew = NULL; apr_status_t rc = APR_SUCCESS; const char *const *env = NULL; apr_file_t *script_out = NULL; request_rec *r = msr->r; if (argv == NULL) { argv = apr_pcalloc(r->pool, 3 * sizeof(char *)); argv[0] = command; argv[1] = NULL; } ap_add_cgi_vars(r); ap_add_common_vars(r); /* PHP hack, getting around its silly security checks. */ apr_table_add(r->subprocess_env, "PATH_TRANSLATED", command); apr_table_add(r->subprocess_env, "REDIRECT_STATUS", "302"); env = (const char * const *)ap_create_environment(r->pool, r->subprocess_env); if (env == NULL) { msr_log(msr, 1, "Exec: Unable to create environment."); return -1; } procnew = apr_pcalloc(r->pool, sizeof(*procnew)); if (procnew == NULL) { msr_log(msr, 1, "Exec: Unable to allocate %lu bytes.", (unsigned long)sizeof(*procnew)); return -1; } apr_procattr_create(&procattr, r->pool); if (procattr == NULL) { msr_log(msr, 1, "Exec: Unable to create procattr."); return -1; } apr_procattr_io_set(procattr, APR_NO_PIPE, APR_FULL_BLOCK, APR_NO_PIPE); apr_procattr_cmdtype_set(procattr, APR_SHELLCMD); if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "Exec: %s", log_escape_nq(r->pool, command)); } rc = apr_proc_create(procnew, command, argv, env, procattr, r->pool); if (rc != APR_SUCCESS) { msr_log(msr, 1, "Exec: Execution failed: %s (%s)", log_escape_nq(r->pool, command), get_apr_error(r->pool, rc)); return -1; } apr_pool_note_subprocess(r->pool, procnew, APR_KILL_AFTER_TIMEOUT); script_out = procnew->out; if (!script_out) { msr_log(msr, 1, "Exec: Failed to get script output pipe."); return -1; } apr_file_pipe_timeout_set(script_out, r->server->timeout); /* Now read from the pipe. */ { char buf[260] = ""; char *p = buf; apr_size_t nbytes = 255; apr_status_t rc2; rc2 = apr_file_read(script_out, buf, &nbytes); if (rc2 == APR_SUCCESS) { buf[nbytes] = 0; /* if there is more than one line ignore them */ while(*p != 0) { if (*p == 0x0a) *p = 0; p++; } if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Exec: First line from script output: \"%s\"", log_escape(r->pool, buf)); } if (output != NULL) *output = apr_pstrdup(r->pool, buf); /* Soak up the remaining data. */ nbytes = 255; while(apr_file_read(script_out, buf, &nbytes) == APR_SUCCESS) nbytes = 255; } else { msr_log(msr, 1, "Exec: Execution failed while reading output: %s (%s)", log_escape_nq(r->pool, command), get_apr_error(r->pool, rc2)); return -1; } } apr_proc_wait(procnew, NULL, NULL, APR_WAIT); return 1; }
static int aikido_handler(request_rec *r) { apr_bucket_brigade *bb; apr_bucket *b; int seen_eos, child_stopped_reading; apr_pool_t *p; apr_status_t rv; conn_rec *c = r->connection; void *aikidoreq; int inpipes[2]; int outpipes[2]; apr_file_t *infile; apr_file_t *outfile; int e; pthread_t tid; pthread_attr_t attrs; ThreadData tdata; void *tresult; if (aikido == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "mod_aikido failed to initalize"); return HTTP_INTERNAL_SERVER_ERROR; } p = r->main ? r->main->pool : r->pool; if (r->method_number == M_OPTIONS) { r->allowed |= (AP_METHOD_BIT << M_GET); r->allowed |= (AP_METHOD_BIT << M_POST); return DECLINED; } // create the request object aikidoreq = aikido_new_request (r); if (!aikido_check_uri(aikido, aikidoreq)) { aikido_delete_request (aikidoreq); return DECLINED; } e = socketpair (AF_UNIX, SOCK_STREAM, 0, inpipes); e |= socketpair (AF_UNIX, SOCK_STREAM, 0, outpipes); if (e != 0) { perror ("socketpair"); aikido_delete_request (aikidoreq); ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "Failed to create pipes"); return HTTP_INTERNAL_SERVER_ERROR; } // redirect the output from the webapp to our outfile apr_os_file_put (&outfile, &outpipes[0], O_RDONLY, r->pool); // open for reading from outpipes pipe // redirect the input to the webapp from out infile apr_os_file_put (&infile, &inpipes[1], O_CREAT | O_WRONLY, r->pool); // open for writing to inpipes pipe ap_add_common_vars(r); ap_add_cgi_vars(r); bb = apr_brigade_create(r->pool, c->bucket_alloc); seen_eos = 0; child_stopped_reading = 0; do { apr_bucket *bucket; rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN); if (rv != APR_SUCCESS) { close (inpipes[0]); close (inpipes[1]); close (outpipes[0]); close (outpipes[1]); aikido_delete_request (aikidoreq); return rv; } bucket = APR_BRIGADE_FIRST(bb); while (bucket != APR_BRIGADE_SENTINEL(bb)) { const char *data; apr_size_t len; if (APR_BUCKET_IS_EOS(bucket)) { seen_eos = 1; break; } /* We can't do much with this. */ if (APR_BUCKET_IS_FLUSH(bucket)) { continue; } /* If the child stopped, we still must read to EOS. */ if (child_stopped_reading) { continue; } /* read */ apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); /* Keep writing data to the child until done or too much time * elapses with no progress or an error occurs. */ rv = apr_file_write_full(infile, data, len, NULL); if (rv != APR_SUCCESS) { /* silly script stopped reading, soak up remaining message */ child_stopped_reading = 1; } bucket = APR_BUCKET_NEXT(bucket); } apr_brigade_cleanup(bb); } while (!seen_eos); /* Is this flush really needed? */ apr_file_flush(infile); apr_file_close(infile); apr_brigade_cleanup(bb); /* start the reader thread */ e = pthread_attr_init (&attrs); if (e != 0) { close (inpipes[0]); close (inpipes[1]); close (outpipes[0]); close (outpipes[1]); aikido_delete_request (aikidoreq); ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "Failed to allocate script thread attributes"); return HTTP_INTERNAL_SERVER_ERROR; } e = pthread_attr_setdetachstate (&attrs, PTHREAD_CREATE_JOINABLE) ; if (e != 0) { close (inpipes[0]); close (inpipes[1]); close (outpipes[0]); close (outpipes[1]); aikido_delete_request (aikidoreq); ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "Failed to set script thread attributes"); return HTTP_INTERNAL_SERVER_ERROR; } tdata.req = aikidoreq; tdata.in = inpipes[0]; tdata.out = outpipes[1]; e = pthread_create (&tid, NULL, script_thread, &tdata) ; if (e != 0) { close (inpipes[0]); close (inpipes[1]); close (outpipes[0]); close (outpipes[1]); aikido_delete_request (aikidoreq); ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "Failed to run script thread"); return HTTP_INTERNAL_SERVER_ERROR; } /* script thread is now executing script with i/o redirected to the pipes */ /* read the script output from the pipe*/ apr_file_pipe_timeout_set(outfile, 0); b = aikido_bucket_create(r, outfile, c->bucket_alloc); if (b == NULL) { pthread_join (tid, &tresult); /* wait for script to complete */ close (inpipes[0]); close (inpipes[1]); close (outpipes[0]); close (outpipes[1]); aikido_delete_request (aikidoreq); return HTTP_INTERNAL_SERVER_ERROR; } APR_BRIGADE_INSERT_TAIL(bb, b); b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); ap_pass_brigade(r->output_filters, bb); /* wait for the script thread to complete */ pthread_join (tid, &tresult); /* terminate script output */ close (inpipes[0]); close (inpipes[1]); close (outpipes[0]); close (outpipes[1]); /* delete the aikido request objects */ aikido_delete_request (aikidoreq); return OK; }
static int ef_unified_filter(ap_filter_t *f, apr_bucket_brigade *bb) { request_rec *r = f->r; conn_rec *c = r->connection; ef_ctx_t *ctx = f->ctx; apr_bucket *b; ef_dir_t *dc; apr_size_t len; const char *data; apr_status_t rv; char buf[4096]; apr_bucket *eos = NULL; apr_bucket_brigade *bb_tmp; dc = ctx->dc; bb_tmp = apr_brigade_create(r->pool, c->bucket_alloc); for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) { if (APR_BUCKET_IS_EOS(b)) { eos = b; break; } rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_bucket_read()"); return rv; } /* Good cast, we just tested len isn't negative */ if (len > 0 && (rv = pass_data_to_filter(f, data, (apr_size_t)len, bb_tmp)) != APR_SUCCESS) { return rv; } } apr_brigade_cleanup(bb); APR_BRIGADE_CONCAT(bb, bb_tmp); apr_brigade_destroy(bb_tmp); if (eos) { /* close the child's stdin to signal that no more data is coming; * that will cause the child to finish generating output */ if ((rv = apr_file_close(ctx->proc->in)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_file_close(child input)"); return rv; } /* since we've seen eos and closed the child's stdin, set the proper pipe * timeout; we don't care if we don't return from apr_file_read() for a while... */ rv = apr_file_pipe_timeout_set(ctx->proc->out, r->server->timeout); if (rv) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_file_pipe_timeout_set(child output)"); return rv; } } do { len = sizeof(buf); rv = apr_file_read(ctx->proc->out, buf, &len); if ((rv && !APR_STATUS_IS_EOF(rv) && !APR_STATUS_IS_EAGAIN(rv)) || dc->debug >= DBGLVL_GORY) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, "apr_file_read(child output), len %" APR_SIZE_T_FMT, !rv ? len : -1); } if (APR_STATUS_IS_EAGAIN(rv)) { if (eos) { /* should not occur, because we have an APR timeout in place */ AP_DEBUG_ASSERT(1 != 1); } return APR_SUCCESS; } if (rv == APR_SUCCESS) { b = apr_bucket_heap_create(buf, len, NULL, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); } } while (rv == APR_SUCCESS); if (!APR_STATUS_IS_EOF(rv)) { return rv; } if (eos) { b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); ctx->hit_eos = 1; } return APR_SUCCESS; }
static guint svn_cat_command_run (AnjutaCommand *command) { SvnCatCommand *self; SvnCommand *svn_command; svn_opt_revision_t revision; svn_opt_revision_t peg_revision; svn_stream_t *cat_stream; apr_file_t *cat_input; apr_file_t *cat_output; apr_size_t read_size; gchar *line; svn_error_t *error; apr_status_t apr_error; self = SVN_CAT_COMMAND (command); svn_command = SVN_COMMAND (command); apr_file_pipe_create (&cat_output, &cat_input, svn_command_get_pool (svn_command)); apr_file_pipe_timeout_set (cat_output, 0); apr_file_pipe_timeout_set (cat_input, 0); cat_stream = svn_stream_from_aprfile2 (cat_input, FALSE, svn_command_get_pool (svn_command)); revision.kind = svn_opt_revision_number; revision.value.number = self->priv->revision; peg_revision.kind = svn_opt_revision_unspecified; error = svn_client_cat2 (cat_stream, self->priv->path, &peg_revision, &revision, svn_command_get_client_context (svn_command), svn_command_get_pool (svn_command)); if (error) { svn_command_set_error (svn_command, error); return 1; } while (apr_file_eof (cat_output) != APR_EOF) { read_size = 80; line = g_new0 (gchar, (read_size + 1)); apr_error = apr_file_read (cat_output, line, &read_size); if (apr_error) break; if (strlen (line)) { anjuta_async_command_lock (ANJUTA_ASYNC_COMMAND (command)); g_queue_push_tail (self->priv->output, g_strdup (line)); anjuta_async_command_unlock (ANJUTA_ASYNC_COMMAND (command)); g_free (line); anjuta_command_notify_data_arrived (command); } } return 0; }
static apr_status_t ef_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) { request_rec *r = f->r; conn_rec *c = r->connection; ef_ctx_t *ctx = f->ctx; apr_bucket *b; ef_dir_t *dc; apr_size_t len; const char *data; apr_status_t rv; char buf[4096]; apr_bucket *eos = NULL; if (!ctx) { if ((rv = init_filter_instance(f)) != APR_SUCCESS) { return rv; } ctx = f->ctx; } if (ctx->noop) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } dc = ctx->dc; APR_BRIGADE_FOREACH(b, bb) { if (APR_BUCKET_IS_EOS(b)) { eos = b; break; } rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_bucket_read()"); return rv; } /* Good cast, we just tested len isn't negative */ if (len > 0 && (rv = pass_data_to_filter(f, data, (apr_size_t)len)) != APR_SUCCESS) { return rv; } } apr_brigade_destroy(bb); /* XXX What we *really* need to do once we've hit eos is create a pipe bucket * from the child output pipe and pass down the pipe bucket + eos. */ if (eos) { /* close the child's stdin to signal that no more data is coming; * that will cause the child to finish generating output */ if ((rv = apr_file_close(ctx->proc->in)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_file_close(child input)"); return rv; } /* since we've seen eos and closed the child's stdin, set the proper pipe * timeout; we don't care if we don't return from apr_file_read() for a while... */ rv = apr_file_pipe_timeout_set(ctx->proc->out, r->server->timeout); if (rv) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_file_pipe_timeout_set(child output)"); return rv; } } do { len = sizeof(buf); rv = apr_file_read(ctx->proc->out, buf, &len); if ((rv && !APR_STATUS_IS_EOF(rv) && !APR_STATUS_IS_EAGAIN(rv)) || dc->debug >= DBGLVL_GORY) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, "apr_file_read(child output), len %" APR_SIZE_T_FMT, !rv ? len : -1); } if (APR_STATUS_IS_EAGAIN(rv)) { if (eos) { /* should not occur, because we have an APR timeout in place */ AP_DEBUG_ASSERT(1 != 1); } return APR_SUCCESS; } if (rv == APR_SUCCESS) { bb = apr_brigade_create(r->pool, c->bucket_alloc); b = apr_bucket_transient_create(buf, len, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); if ((rv = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "ap_pass_brigade(filtered buffer) failed"); return rv; } } } while (rv == APR_SUCCESS); if (!APR_STATUS_IS_EOF(rv)) { return rv; } if (eos) { /* pass down eos */ bb = apr_brigade_create(r->pool, c->bucket_alloc); b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); if ((rv = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "ap_pass_brigade(eos) failed"); return rv; } } return APR_SUCCESS; }