static int proc_wait(lua_State *L) { apr_status_t status; apr_exit_why_e why; apr_wait_how_e how; lua_apr_proc *process; int code; process = proc_check(L, 1); how = lua_toboolean(L, 2) ? APR_WAIT : APR_NOWAIT; status = apr_proc_wait(&process->handle, &code, &why, how); if (APR_STATUS_IS_CHILD_NOTDONE(status)) return (lua_pushboolean(L, 0), 1); else if (!APR_STATUS_IS_CHILD_DONE(status)) return push_error_status(L, status); else lua_pushboolean(L, 1); switch (why) { default: case APR_PROC_EXIT: lua_pushliteral(L, "exit"); break; case APR_PROC_SIGNAL: lua_pushliteral(L, "signal"); break; case APR_PROC_SIGNAL_CORE: lua_pushliteral(L, "signal/core"); break; } lua_pushinteger(L, code); return 3; }
static void test_proc_wait(CuTest *tc) { apr_status_t rv; rv = apr_proc_wait(&newproc, NULL, NULL, APR_WAIT); CuAssertIntEquals(tc, APR_CHILD_DONE, rv); }
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; }
static void test_proc_wait(abts_case *tc, void *data) { apr_status_t rv; rv = apr_proc_wait(&newproc, NULL, NULL, APR_WAIT); ABTS_INT_EQUAL(tc, APR_CHILD_DONE, rv); }
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); }
/* Wait for a child process and check it terminated with success. */ static void await_child(CuTest *tc, apr_proc_t *proc) { int code; apr_exit_why_e why; apr_status_t rv; rv = apr_proc_wait(proc, &code, &why, APR_WAIT); CuAssert(tc, "child did not terminate with success", rv == APR_CHILD_DONE && why == APR_PROC_EXIT && code == 0); }
/* Before sending the signal to the pid this function verifies that * the pid is a member of the current process group; either using * apr_proc_wait(), where waitpid() guarantees to fail for non-child * processes; or by using getpgid() directly, if available. */ apr_status_t ap_mpm_safe_kill(pid_t pid, int sig) { #ifndef HAVE_GETPGID apr_proc_t proc; apr_status_t rv; apr_exit_why_e why; int status; /* Ensure pid sanity */ if (pid < 1) { return APR_EINVAL; } proc.pid = pid; rv = apr_proc_wait(&proc, &status, &why, APR_NOWAIT); if (rv == APR_CHILD_DONE) { #ifdef AP_MPM_WANT_PROCESS_CHILD_STATUS /* The child already died - log the termination status if * necessary: */ ap_process_child_status(&proc, why, status); #endif return APR_EINVAL; } else if (rv != APR_CHILD_NOTDONE) { /* The child is already dead and reaped, or was a bogus pid - * log this either way. */ ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, "cannot send signal %d to pid %ld (non-child or " "already dead)", sig, (long)pid); return APR_EINVAL; } #else pid_t pg; /* Ensure pid sanity. */ if (pid < 1) { return APR_EINVAL; } pg = getpgid(pid); if (pg == -1) { /* Process already dead... */ return errno; } if (pg != getpgrp()) { ap_log_error(APLOG_MARK, APLOG_ALERT, 0, ap_server_conf, "refusing to send signal %d to pid %ld outside " "process group", sig, (long)pid); return APR_EINVAL; } #endif return kill(pid, sig) ? errno : APR_SUCCESS; }
static apr_status_t fortune_process(conn_rec *c, apr_procattr_t *pattr, apr_bucket_brigade *bb) { apr_status_t rv; int argc = 0; const char *argv[APP_MAX_ARGC]; apr_proc_t proc; apr_bucket *b; apr_pool_t *p = c->pool; fortune_conf_t *fconf = ap_get_module_config(c->base_server->module_config, &fortune_module); argv[argc++] = fconf->progname; argv[argc++] = NULL; /* @argvs should be null-terminated */ if ((rv = apr_proc_create(&proc, fconf->progname, (const char *const *) argv, NULL, (apr_procattr_t *) pattr, p)) != APR_SUCCESS) { return rv; } while (TRUE) { char buf[BUFSIZE] = { 0, }; /* read the command's output through the pipe */ rv = apr_file_gets(buf, sizeof(buf), proc.out); if (APR_STATUS_IS_EOF(rv)) { break; } b = apr_bucket_pool_create(apr_pstrdup(p, buf), strlen(buf), p, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); } apr_file_close(proc.out); { int st; apr_exit_why_e why; rv = apr_proc_wait(&proc, &st, &why, APR_WAIT); if (APR_STATUS_IS_CHILD_DONE(rv)) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "child done: why = %d, exit status = %d", why, st); } else { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "child notdone"); return APR_EGENERAL; } } return APR_SUCCESS; }
static int wait_child(abts_case *tc, apr_proc_t *proc) { int exitcode; apr_exit_why_e why; ABTS_ASSERT(tc, "Error waiting 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; }
static void test_anon(abts_case *tc, void *data) { apr_proc_t proc; apr_status_t rv; apr_shm_t *shm; apr_size_t retsize; int cnt, i; int recvd; rv = apr_shm_create(&shm, SHARED_SIZE, NULL, p); APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv); ABTS_PTR_NOTNULL(tc, shm); retsize = apr_shm_size_get(shm); ABTS_INT_EQUAL(tc, SHARED_SIZE, retsize); boxes = apr_shm_baseaddr_get(shm); ABTS_PTR_NOTNULL(tc, boxes); rv = apr_proc_fork(&proc, p); if (rv == APR_INCHILD) { /* child */ int num = msgwait(5, 0, N_BOXES); /* exit with the number of messages received so that the parent * can check that all messages were received. */ exit(num); } else if (rv == APR_INPARENT) { /* parent */ i = N_BOXES; cnt = 0; while (cnt++ < N_MESSAGES) { if ((i-=3) < 0) { i += N_BOXES; /* start over at the top */ } msgput(i, MSG); apr_sleep(apr_time_make(0, 10000)); } } else { ABTS_FAIL(tc, "apr_proc_fork failed"); } /* wait for the child */ rv = apr_proc_wait(&proc, &recvd, NULL, APR_WAIT); ABTS_INT_EQUAL(tc, N_MESSAGES, recvd); rv = apr_shm_destroy(shm); APR_ASSERT_SUCCESS(tc, "Error destroying shared memory block", rv); }
apr_status_t proc_wait_process(server_rec *main_server, fcgid_procnode *procnode) { apr_status_t rv; int exitcode; apr_exit_why_e exitwhy; if ((rv = apr_proc_wait(&(procnode->proc_id), &exitcode, &exitwhy, APR_NOWAIT)) == APR_CHILD_DONE) { /* Log why and how it die */ proc_print_exit_info(procnode, exitcode, exitwhy, main_server); /* Register the death */ register_termination(main_server, procnode); /* Destroy pool */ apr_pool_destroy(procnode->proc_pool); procnode->proc_pool = NULL; } return rv; }
static void close_tunnel(void *tunnel_context, void *tunnel_baton) { close_baton_t *b = tunnel_context; if (b->magic != CLOSE_MAGIC) abort(); if (--b->tb->open_count == 0) { apr_status_t child_exit_status; int child_exit_code; apr_exit_why_e child_exit_why; SVN_TEST_ASSERT_NO_RETURN(0 == apr_file_close(b->proc->in)); SVN_TEST_ASSERT_NO_RETURN(0 == apr_file_close(b->proc->out)); child_exit_status = apr_proc_wait(b->proc, &child_exit_code, &child_exit_why, APR_WAIT); SVN_TEST_ASSERT_NO_RETURN(child_exit_status == APR_CHILD_DONE); SVN_TEST_ASSERT_NO_RETURN(child_exit_code == 0); SVN_TEST_ASSERT_NO_RETURN(child_exit_why == APR_PROC_EXIT); } }
static void rand_fork(abts_case *tc, void *data) { apr_proc_t proc; apr_status_t rv; apr_size_t nbytes = RANDOM_BUF_SZ; apr_size_t cmd_size = 1; char cmd = 'X'; unsigned char expected[RANDOM_BUF_SZ] = { 0xac, 0x93, 0xd2, 0x5c, 0xc7, 0xf5, 0x8d, 0xc2, 0xd8, 0x8d, 0xb6, 0x7a, 0x94, 0xe1, 0x83, 0x4c, 0x26, 0xe2, 0x38, 0x6d, 0xf5, 0xbd, 0x9d, 0x6e, 0x91, 0x77, 0x3a, 0x4b, 0x9b, 0xef, 0x9b, 0xa3, 0x9f, 0xf6, 0x6d, 0x0c, 0xdc, 0x4b, 0x02, 0xe9, 0x5d, 0x3d, 0xfc, 0x92, 0x6b, 0xdf, 0xc9, 0xef, 0xb9, 0xa8, 0x74, 0x09, 0xa3, 0xff, 0x64, 0x8d, 0x19, 0xc1, 0x31, 0x31, 0x17, 0xe1, 0xb7, 0x7a, 0xe7, 0x55, 0x14, 0x92, 0x05, 0xe3, 0x1e, 0xb8, 0x9b, 0x1b, 0xdc, 0xac, 0x0e, 0x15, 0x08, 0xa2, 0x93, 0x13, 0xf6, 0x04, 0xc6, 0x9d, 0xf8, 0x7f, 0x26, 0x32, 0x68, 0x43, 0x2e, 0x5a, 0x4f, 0x47, 0xe8, 0xf8, 0x59, 0xb7, 0xfb, 0xbe, 0x30, 0x04, 0xb6, 0x63, 0x6f, 0x19, 0xf3, 0x2c, 0xd4, 0xeb, 0x32, 0x8a, 0x54, 0x01, 0xd0, 0xaf, 0x3f, 0x13, 0xc1, 0x7f, 0x10, 0x2e, 0x08, 0x1c, 0x28, 0x4b, }; apr_file_t *readdatap = NULL; apr_file_t *writedatap = NULL; apr_file_t *readcmdp = NULL; apr_file_t *writecmdp = NULL; apr_pool_t *p; int i; apr_pool_create(&p, NULL); /* Set up data pipe for children */ rv = apr_file_pipe_create(&readdatap, &writedatap, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, readdatap); ABTS_PTR_NOTNULL(tc, writedatap); /* Set up cmd pipe for children */ rv = apr_file_pipe_create(&readcmdp, &writecmdp, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, readcmdp); ABTS_PTR_NOTNULL(tc, writecmdp); rand_run_kat(tc, apr_random_secure_bytes, r, expected); for (i = 0; i< 10; i++) { rv = apr_proc_fork(&proc, p); if (rv == APR_INCHILD) { int n = rand_check_kat(apr_random_secure_bytes, r, expected, readcmdp, writedatap); exit(n); } else if (rv == APR_INPARENT) { int exitcode; apr_exit_why_e why; /* Read the random data generated by child */ rv = apr_file_read(readdatap, expected, &nbytes); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); /* Tell child to finish */ rv = apr_file_write(writecmdp, &cmd, &cmd_size); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); apr_proc_wait(&proc, &exitcode, &why, APR_WAIT); if (why != APR_PROC_EXIT) { ABTS_FAIL(tc, "Child terminated abnormally"); } else if (exitcode == 0) { if (i == 0) { ABTS_FAIL(tc, "Child produced our randomness"); } else { ABTS_FAIL(tc, "Child produced randomness of previous child"); } } else if (exitcode == 2) { ABTS_FAIL(tc, "Child randomness failed"); } else if (exitcode != 1) { ABTS_FAIL(tc, "Unknown child error"); } } else { ABTS_FAIL(tc, "Fork failed"); } } }
static int mod_mapcache_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { apr_status_t rc; mapcache_server_cfg* cfg = ap_get_module_config(s->module_config, &mapcache_module); apr_lockmech_e lock_type = APR_LOCK_DEFAULT; server_rec *sconf; if(!cfg) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, "configuration not found in server context"); return 1; } #if APR_HAS_PROC_PTHREAD_SERIALIZE lock_type = APR_LOCK_PROC_PTHREAD; #endif rc = apr_global_mutex_create(&cfg->mutex,cfg->mutex_name,lock_type,p); if(rc != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rc, s, "Could not create global parent mutex %s", cfg->mutex_name); return rc; } #ifdef AP_NEED_SET_MUTEX_PERMS rc = unixd_set_global_mutex_perms(cfg->mutex); if(rc != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rc, s, "Could not set permissions on global parent mutex %s", cfg->mutex_name); return rc; } #endif apr_pool_cleanup_register(p,cfg->mutex, (void*)apr_global_mutex_destroy, apr_pool_cleanup_null); #ifndef DISABLE_VERSION_STRING ap_add_version_component(p, MAPCACHE_USERAGENT); #endif for (sconf = s->next; sconf; sconf = sconf->next) { mapcache_server_cfg* config = ap_get_module_config(sconf->module_config, &mapcache_module); config->mutex = cfg->mutex; } #if APR_HAS_FORK /* fork a child process to let it accomplish post-configuration tasks with the uid of the runtime user */ apr_proc_t proc; apr_status_t rv; rv = apr_proc_fork(&proc, ptemp); if (rv == APR_INCHILD) { #define ap_unixd_setup_child unixd_setup_child ap_unixd_setup_child(); mapcache_context *ctx = (mapcache_context*)apache_server_context_create(s,p); for (sconf = s; sconf; sconf = sconf->next) { mapcache_server_cfg* config = ap_get_module_config(sconf->module_config, &mapcache_module); if(config->aliases) { apr_hash_index_t *entry = apr_hash_first(ptemp,config->aliases); /* loop through the configured configurations */ while (entry) { const char *alias; apr_ssize_t aliaslen; mapcache_cfg *c; apr_hash_this(entry,(const void**)&alias,&aliaslen,(void**)&c); mapcache_configuration_post_config(ctx, c); if(GC_HAS_ERROR(ctx)) { ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EGENERAL, s, "post config for %s failed: %s", alias, ctx->get_error_message(ctx)); exit(APR_EGENERAL); } entry = apr_hash_next(entry); } } } exit(0); } else if (rv == APR_INPARENT) { apr_exit_why_e exitwhy; int exitcode; apr_proc_wait(&proc,&exitcode,&exitwhy,APR_WAIT); if(exitwhy != APR_PROC_EXIT) { ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EGENERAL, s, "mapcache post-config child terminated abnormally"); return APR_EGENERAL; } else { if(exitcode != 0) { return APR_EGENERAL; } } return OK; } else { ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EGENERAL, s, "failed to fork mapcache post-config child"); return APR_EGENERAL; } #else /* APR_HAS_FORK */ mapcache_context *ctx = (mapcache_context*)apache_server_context_create(s,p); for (sconf = s; sconf; sconf = sconf->next) { mapcache_server_cfg* config = ap_get_module_config(sconf->module_config, &mapcache_module); if(config->aliases) { apr_hash_index_t *entry = apr_hash_first(ptemp,config->aliases); /* loop through the configured configurations */ while (entry) { const char *alias; apr_ssize_t aliaslen; mapcache_cfg *c; apr_hash_this(entry,(const void**)&alias,&aliaslen,(void**)&c); mapcache_configuration_post_config(ctx, c); if(GC_HAS_ERROR(ctx)) { ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EGENERAL, s, "post config for %s failed: %s", alias, ctx->get_error_message(ctx)); return APR_EGENERAL; } entry = apr_hash_next(entry); } } } return OK; #endif }
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); }
/* Run an external authentication program using the given method for passing * in the data. The login name is always passed in. Dataname is "GROUP" or * "PASS" and data is the group list or password being checked. To launch * a detached daemon, run this with extmethod=NULL. * * If the authenticator was run, we return the numeric code from the * authenticator, normally 0 if the login was valid, some small positive * number if not. If we were not able to run the authenticator, we log * an error message and return a numeric error code: * * -1 Could not execute authenticator, usually a path or permission problem * -2 The external authenticator crashed or was killed. * -3 Could not create process attribute structure * -4 apr_proc_wait() did not return a status code. Should never happen. * -5 apr_proc_wait() returned before child finished. Should never happen. */ static int exec_external(const char *extpath, const char *extmethod, const request_rec *r, const char *dataname, const char *data) { conn_rec *c= r->connection; apr_pool_t *p= r->pool; int isdaemon, usecheck= 0, usepipeout= 0, usepipein= 0; apr_procattr_t *procattr; apr_proc_t proc; apr_status_t rc= APR_SUCCESS; char *child_env[12]; char *child_arg[MAX_ARG+2]; const char *t; int i, status= -4; apr_exit_why_e why= APR_PROC_EXIT; apr_sigfunc_t *sigchld; /* Set various flags based on the execution method */ isdaemon= (extmethod == NULL); if (!isdaemon) { usecheck= extmethod && !strcasecmp(extmethod, "checkpassword"); usepipeout= usecheck || (extmethod && !strcasecmp(extmethod, "pipes")); usepipein= usepipeout || (extmethod && !strcasecmp(extmethod, "pipe")); } /* Create the environment for the child. Daemons don't get these, they * just inherit apache's environment variables. */ if (!isdaemon) { const char *cookie, *host, *remote_host; authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *) ap_get_module_config(r->per_dir_config, &authnz_external_module); i= 0; if (!usepipein) { /* Put user name and password/group into environment */ child_env[i++]= apr_pstrcat(p, ENV_USER"=", r->user, NULL); child_env[i++]= apr_pstrcat(p, dataname, "=", data, NULL); } child_env[i++]= apr_pstrcat(p, "PATH=", getenv("PATH"), NULL); child_env[i++]= apr_pstrcat(p, "AUTHTYPE=", dataname, NULL); remote_host= ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST,NULL); if (remote_host != NULL) child_env[i++]= apr_pstrcat(p, ENV_HOST"=", remote_host,NULL); if (r->useragent_ip) child_env[i++]= apr_pstrcat(p, ENV_IP"=", r->useragent_ip, NULL); if (r->uri) child_env[i++]= apr_pstrcat(p, ENV_URI"=", r->uri, NULL); if ((host= apr_table_get(r->headers_in, "Host")) != NULL) child_env[i++]= apr_pstrcat(p, ENV_HTTP_HOST"=", host, NULL); if (dir->context) child_env[i++]= apr_pstrcat(r->pool, ENV_CONTEXT"=", dir->context, NULL); #ifdef ENV_COOKIE if ((cookie= apr_table_get(r->headers_in, "Cookie")) != NULL) child_env[i++]= apr_pstrcat(p, ENV_COOKIE"=", cookie, NULL); #endif /* NOTE: If you add environment variables, * remember to increase the size of the child_env[] array */ /* End of environment */ child_env[i]= NULL; } /* Construct argument array */ for (t= extpath, i=0; *t != '\0' && (i <= MAX_ARG + 1); child_arg[i++]= ap_getword_white(p, &t)) {} child_arg[i]= NULL; /* Create the process attribute structure describing the script we * want to run using the Thread/Process functions from the Apache * portable runtime library. */ if (((rc= apr_procattr_create(&procattr, p)) != APR_SUCCESS) || /* should we create pipes to stdin, stdout and stderr? */ ((rc= apr_procattr_io_set(procattr, (usepipein && !usecheck) ? APR_FULL_BLOCK : APR_NO_PIPE, usepipeout ? APR_FULL_BLOCK : APR_NO_PIPE, (usepipein && usecheck) ? APR_FULL_BLOCK : APR_NO_PIPE)) != APR_SUCCESS ) || /* will give full path of program and make a new environment */ ((rc= apr_procattr_cmdtype_set(procattr, isdaemon ? APR_PROGRAM_ENV : APR_PROGRAM)) != APR_SUCCESS) || /* detach the child only if it is a daemon */ ((rc= apr_procattr_detach_set(procattr, isdaemon)) != APR_SUCCESS) || /* function to call if child has error after fork, before exec */ ((rc= apr_procattr_child_errfn_set(procattr, extchilderr) != APR_SUCCESS))) { /* Failed. Probably never happens. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "could not set child process attributes"); return -3; } /* Sometimes other modules wil mess up sigchild. Need to fix it for * the wait call to work correctly. */ sigchld= apr_signal(SIGCHLD,SIG_DFL); /* Start the child process */ rc= apr_proc_create(&proc, child_arg[0], (const char * const *)child_arg, (const char * const *)child_env, procattr, p); if (rc != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Could not run external authenticator: %d: %s", rc, child_arg[0]); return -1; } if (isdaemon) return 0; apr_pool_note_subprocess(p, &proc, APR_KILL_AFTER_TIMEOUT); if (usepipein) { /* Select appropriate pipe to write to */ apr_file_t *pipe= (usecheck ? proc.err : proc.in); /* Send the user */ apr_file_write_full(pipe, r->user, strlen(r->user), NULL); apr_file_putc(usecheck ? '\0' : '\n', pipe); /* Send the password */ apr_file_write_full(pipe, data, strlen(data), NULL); apr_file_putc(usecheck ? '\0' : '\n', pipe); /* Send the uri/path */ apr_file_write_full(pipe, r->uri, strlen(r->uri), NULL); apr_file_putc(usecheck ? '\0' : '\n', pipe); /* Send dummy timestamp for checkpassword */ if (usecheck) apr_file_write_full(pipe, "0", 2, NULL); /* Close the file */ apr_file_close(pipe); } /* Wait for the child process to terminate, and get status */ rc= apr_proc_wait(&proc,&status,&why,APR_WAIT); /* Restore sigchild to whatever it was before we reset it */ apr_signal(SIGCHLD,sigchld); if (!APR_STATUS_IS_CHILD_DONE(rc)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "Could not get status from child process"); return -5; } if (!APR_PROC_CHECK_EXIT(why)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "External authenticator died on signal %d",status); return -2; } return status; }
static int reclaim_one_pid(pid_t pid, action_t action) { apr_proc_t proc; apr_status_t waitret; /* Ensure pid sanity. */ if (pid < 1) { return 1; } proc.pid = pid; waitret = apr_proc_wait(&proc, NULL, NULL, APR_NOWAIT); if (waitret != APR_CHILD_NOTDONE) { return 1; } switch(action) { case DO_NOTHING: break; case SEND_SIGTERM: /* ok, now it's being annoying */ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf, "child process %" APR_PID_T_FMT " still did not exit, " "sending a SIGTERM", pid); kill(pid, SIGTERM); break; case SEND_SIGKILL: ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "child process %" APR_PID_T_FMT " still did not exit, " "sending a SIGKILL", pid); #ifndef BEOS kill(pid, SIGKILL); #else /* sending a SIGKILL kills the entire team on BeOS, and as * httpd thread is part of that team it removes any chance * of ever doing a restart. To counter this I'm changing to * use a kinder, gentler way of killing a specific thread * that is just as effective. */ kill_thread(pid); #endif break; case GIVEUP: /* gave it our best shot, but alas... If this really * is a child we are trying to kill and it really hasn't * exited, we will likely fail to bind to the port * after the restart. */ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "could not make child process %" APR_PID_T_FMT " exit, " "attempting to continue anyway", pid); break; } return 0; }
static void im_exec_stop(nx_module_t *module) { nx_im_exec_conf_t *imconf; int i; boolean sigterm_sent = FALSE; apr_exit_why_e exitwhy; int exitval; int sig_num; apr_status_t rv; apr_status_t stopped = APR_SUCCESS; ASSERT(module != NULL); imconf = (nx_im_exec_conf_t *) module->config; ASSERT(imconf != NULL); log_debug("im_exec stopped"); if ( module->input.desc.f != NULL ) { apr_file_close(module->input.desc.f); apr_pool_clear(module->input.pool); module->input.desc.f = NULL; } if ( imconf->running == TRUE ) { for ( i = 0; i < 50; i++ ) { if ( (rv = apr_proc_wait(&(imconf->proc), &exitval, &exitwhy, APR_NOWAIT)) != APR_SUCCESS ) { if ( APR_STATUS_IS_CHILD_DONE(rv) ) { rv = APR_SUCCESS; break; } else if ( i >= 30 ) { // still running, kill it after 3 sec if ( sigterm_sent == FALSE ) { sigterm_sent = TRUE; #ifdef WIN32 sig_num = 1; #else sig_num = SIGTERM; #endif } else { if ( i <= 31 ) { log_warn("process %s did not exit, killing it.", imconf->cmd); } #ifdef WIN32 sig_num = 1; #else sig_num = SIGKILL; #endif } if ( (rv = apr_proc_kill(&(imconf->proc), sig_num)) != APR_SUCCESS ) { stopped = rv; } } } apr_sleep(APR_USEC_PER_SEC / 10); } if ( !((stopped == APR_SUCCESS) || APR_STATUS_IS_ENOPROC(stopped)) ) { log_aprerror(stopped, "im_exec couldn't stop process"); } } imconf->event = NULL; }
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 }
static void test_named(abts_case *tc, void *data) { apr_status_t rv; apr_shm_t *shm = NULL; apr_size_t retsize; apr_proc_t pidproducer, pidconsumer; apr_procattr_t *attr1 = NULL, *attr2 = NULL; int sent, received; apr_exit_why_e why; const char *args[4]; apr_shm_remove(SHARED_FILENAME, p); rv = apr_shm_create(&shm, SHARED_SIZE, SHARED_FILENAME, p); APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv); if (rv != APR_SUCCESS) { return; } ABTS_PTR_NOTNULL(tc, shm); retsize = apr_shm_size_get(shm); ABTS_SIZE_EQUAL(tc, SHARED_SIZE, retsize); boxes = apr_shm_baseaddr_get(shm); ABTS_PTR_NOTNULL(tc, boxes); rv = apr_procattr_create(&attr1, p); ABTS_PTR_NOTNULL(tc, attr1); APR_ASSERT_SUCCESS(tc, "Couldn't create attr1", rv); rv = apr_procattr_cmdtype_set(attr1, APR_PROGRAM_ENV); APR_ASSERT_SUCCESS(tc, "Couldn't set copy environment", rv); args[0] = apr_pstrdup(p, "testshmproducer" EXTENSION); args[1] = NULL; rv = apr_proc_create(&pidproducer, TESTBINPATH "testshmproducer" EXTENSION, args, NULL, attr1, p); APR_ASSERT_SUCCESS(tc, "Couldn't launch producer", rv); rv = apr_procattr_create(&attr2, p); ABTS_PTR_NOTNULL(tc, attr2); APR_ASSERT_SUCCESS(tc, "Couldn't create attr2", rv); rv = apr_procattr_cmdtype_set(attr2, APR_PROGRAM_ENV); APR_ASSERT_SUCCESS(tc, "Couldn't set copy environment", rv); args[0] = apr_pstrdup(p, "testshmconsumer" EXTENSION); rv = apr_proc_create(&pidconsumer, TESTBINPATH "testshmconsumer" EXTENSION, args, NULL, attr2, p); APR_ASSERT_SUCCESS(tc, "Couldn't launch consumer", rv); rv = apr_proc_wait(&pidconsumer, &received, &why, APR_WAIT); ABTS_INT_EQUAL(tc, APR_CHILD_DONE, rv); ABTS_INT_EQUAL(tc, APR_PROC_EXIT, why); rv = apr_proc_wait(&pidproducer, &sent, &why, APR_WAIT); ABTS_INT_EQUAL(tc, APR_CHILD_DONE, rv); ABTS_INT_EQUAL(tc, APR_PROC_EXIT, why); /* Cleanup before testing that producer and consumer worked correctly. * This way, if they didn't succeed, we can just run this test again * without having to cleanup manually. */ APR_ASSERT_SUCCESS(tc, "Error destroying shared memory", apr_shm_destroy(shm)); ABTS_INT_EQUAL(tc, sent, received); }
/** Kill a process with a given pid. * * Used to from the apache module thread to kill the trell master job, and * we try to wait and determine if the process is dead. */ static int trell_kill_process( trell_sconf_t* svr_conf, request_rec* r, pid_t pid ) { int i; apr_status_t rv; // Shaky..? apr_proc_t proc; proc.pid = pid; proc.in = NULL; proc.out = NULL; proc.err = NULL; // This is slightly shaky as well, since we're not necessarily the parent // process, and if the child isn't waited upon, we have a defunct/zombie. rv = apr_proc_kill( &proc, SIGTERM ); if( rv != APR_SUCCESS ) { ap_log_rerror( APLOG_MARK, APLOG_NOTICE, rv, r, "mod_trell: %s@%d", __FILE__, __LINE__ ); return rv; } ap_log_rerror( APLOG_MARK, APLOG_NOTICE, OK, r, "mod_trell: Sent pid=%d SIGTERM", proc.pid ); apr_sleep( 1000 ); // Slight wait to let process hopefully terminate. for(i=0; i<3; i++ ) { // Check if child is dead. However, it is not safe to assume that the // master is child of current process. int exitcode; apr_exit_why_e why=0; rv = apr_proc_wait( &proc, &exitcode, &why, APR_NOWAIT ); if( rv == APR_CHILD_DONE ) { ap_log_rerror( APLOG_MARK, APLOG_NOTICE, OK, r, "mod_trell: SIGTERM why=%d", why ); return APR_SUCCESS; } // Wait failed, check to see if the messenger still lives... If not, // we assume it's dead. struct messenger msgr; messenger_status_t status = messenger_init( &msgr, svr_conf->m_master_id ); if( status != MESSENGER_OK ) { ap_log_rerror( APLOG_MARK, APLOG_NOTICE, OK, r, "mod_trell: failed to get master messenger, assuming master is dead." ); return APR_SUCCESS; } messenger_free( &msgr ); ap_log_rerror( APLOG_MARK, APLOG_NOTICE, OK, r, "mod_trell: Child not done, sleeping it=%d", i ); apr_sleep( 1000000 ); } rv = apr_proc_kill( &proc, SIGKILL ); apr_sleep( 1000 ); // Slight wait to let process hopefully terminate. if( rv != APR_SUCCESS ) { ap_log_rerror( APLOG_MARK, APLOG_NOTICE, rv, r, "mod_trell: %s@%d", __FILE__, __LINE__ ); return rv; } ap_log_rerror( APLOG_MARK, APLOG_NOTICE, OK, r, "mod_trell: Sent pid=%d SIGKILL", proc.pid ); for(i=0; i<3; i++ ) { int exitcode; apr_exit_why_e why=0; rv = apr_proc_wait( &proc, &exitcode, &why, APR_NOWAIT ); if( rv == APR_CHILD_DONE ) { ap_log_rerror( APLOG_MARK, APLOG_NOTICE, OK, r, "mod_trell: SIGKILL why=%d", why ); return APR_SUCCESS; } struct messenger msgr; messenger_status_t status = messenger_init( &msgr, svr_conf->m_master_id ); if( status != MESSENGER_OK ) { ap_log_rerror( APLOG_MARK, APLOG_NOTICE, OK, r, "mod_trell: failed to get master messenger, assuming master is dead." ); return APR_SUCCESS; } messenger_free( &msgr ); ap_log_rerror( APLOG_MARK, APLOG_NOTICE, OK, r, "mod_trell: Child not done, sleeping it=%d", i ); apr_sleep( 1000000 ); } ap_log_rerror( APLOG_MARK, APLOG_CRIT, OK, r, "mod_trell: Failed to kill master job." ); }
static int reclaim_one_pid(pid_t pid, action_t action) { apr_proc_t proc; apr_status_t waitret; apr_exit_why_e why; int status; /* Ensure pid sanity. */ if (pid < 1) { return 1; } proc.pid = pid; waitret = apr_proc_wait(&proc, &status, &why, APR_NOWAIT); if (waitret != APR_CHILD_NOTDONE) { if (waitret == APR_CHILD_DONE) ap_process_child_status(&proc, why, status); return 1; } switch(action) { case DO_NOTHING: break; case SEND_SIGTERM: /* ok, now it's being annoying */ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf, APLOGNO(00045) "child process %" APR_PID_T_FMT " still did not exit, " "sending a SIGTERM", pid); kill(pid, SIGTERM); break; case SEND_SIGKILL: ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00046) "child process %" APR_PID_T_FMT " still did not exit, " "sending a SIGKILL", pid); kill(pid, SIGKILL); break; case GIVEUP: /* gave it our best shot, but alas... If this really * is a child we are trying to kill and it really hasn't * exited, we will likely fail to bind to the port * after the restart. */ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00047) "could not make child process %" APR_PID_T_FMT " exit, " "attempting to continue anyway", pid); break; } return 0; }
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; }
/** * 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 privileges_req(request_rec *r) { /* secure mode: fork a process to handle the request */ apr_proc_t proc; apr_status_t rv; int exitcode; apr_exit_why_e exitwhy; int fork_req; priv_cfg *cfg = ap_get_module_config(r->server->module_config, &privileges_module); void *breadcrumb = ap_get_module_config(r->request_config, &privileges_module); if (!breadcrumb) { /* first call: this is the vhost */ fork_req = (cfg->mode == PRIV_SECURE); /* set breadcrumb */ ap_set_module_config(r->request_config, &privileges_module, &cfg->mode); /* If we have per-dir config, defer doing anything */ if ((cfg->mode == PRIV_SELECTIVE)) { /* Defer dropping privileges 'til we have a directory * context that'll tell us whether to fork. */ return DECLINED; } } else { /* second call is for per-directory. */ priv_dir_cfg *dcfg; if ((cfg->mode != PRIV_SELECTIVE)) { /* Our fate was already determined for the vhost - * nothing to do per-directory */ return DECLINED; } dcfg = ap_get_module_config(r->per_dir_config, &privileges_module); fork_req = (dcfg->mode == PRIV_SECURE); } if (fork_req) { rv = apr_proc_fork(&proc, r->pool); switch (rv) { case APR_INPARENT: ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02140) "parent waiting for child"); /* FIXME - does the child need to run synchronously? * esp. if we enable mod_privileges with threaded MPMs? * We do need at least to ensure r outlives the child. */ rv = apr_proc_wait(&proc, &exitcode, &exitwhy, APR_WAIT); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02141) "parent: child %s", (rv == APR_CHILD_DONE) ? "done" : "notdone"); /* The child has taken responsibility for reading all input * and sending all output. So we need to bow right out, * and even abandon "normal" housekeeping. */ r->eos_sent = 1; apr_table_unset(r->headers_in, "Content-Type"); apr_table_unset(r->headers_in, "Content-Length"); /* Testing with ab and 100k requests reveals no nasties * so I infer we're not leaking anything like memory * or file descriptors. That's nice! */ return DONE; case APR_INCHILD: ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02142) "In child!"); break; /* now we'll drop privileges in the child */ default: ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02143) "Failed to fork secure child process!"); return HTTP_INTERNAL_SERVER_ERROR; } } /* OK, now drop privileges. */ /* cleanup should happen even if something fails part-way through here */ apr_pool_cleanup_register(r->pool, r, privileges_end_req, apr_pool_cleanup_null); /* set user and group if configured */ if (cfg->uid || cfg->gid) { if (setppriv(PRIV_ON, PRIV_EFFECTIVE, priv_setid) == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02144) "No privilege to set user/group"); } /* if we should be able to set these but can't, it could be * a serious security issue. Bail out rather than risk it! */ if (cfg->uid && (setuid(cfg->uid) == -1)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02145) "Error setting userid"); return HTTP_INTERNAL_SERVER_ERROR; } if (cfg->gid && (setgid(cfg->gid) == -1)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02146) "Error setting group"); return HTTP_INTERNAL_SERVER_ERROR; } } /* set vhost's privileges */ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, cfg->priv) == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02147) "Error setting effective privileges"); return HTTP_INTERNAL_SERVER_ERROR; } /* ... including those of any subprocesses */ if (setppriv(PRIV_SET, PRIV_INHERITABLE, cfg->child_priv) == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02148) "Error setting inheritable privileges"); return HTTP_INTERNAL_SERVER_ERROR; } if (setppriv(PRIV_SET, PRIV_LIMIT, cfg->child_priv) == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02149) "Error setting limit privileges"); return HTTP_INTERNAL_SERVER_ERROR; } /* If we're in a child process, drop down PPERM too */ if (fork_req) { if (setppriv(PRIV_SET, PRIV_PERMITTED, cfg->priv) == -1) { ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, APLOGNO(02150) "Error setting permitted privileges"); return HTTP_INTERNAL_SERVER_ERROR; } } return OK; }
int main(void) { printf("hallo!\n"); apr_status_t rv; apr_pool_t *p = NULL; apr_proc_t newproc; rv = apr_initialize(); assert(rv == APR_SUCCESS); apr_pool_create(&p, NULL); // -- char cwd[1024]; assert(getcwd(cwd, 1024) != NULL); apr_procattr_t *attr = NULL; rv = apr_procattr_create(&attr, p); assert(rv == APR_SUCCESS); rv = apr_procattr_io_set(attr, APR_NO_PIPE, APR_NO_PIPE, APR_NO_PIPE); assert(rv == APR_SUCCESS); rv = apr_procattr_dir_set(attr, cwd); assert(rv == APR_SUCCESS); printf("cwd: %s\n", cwd); rv = apr_procattr_cmdtype_set(attr, APR_PROGRAM_ENV ); assert(rv == APR_SUCCESS); /* pseudo code of args to apr_proc_create() */ int argc = 0; const char* argv[32]; /* 32 is a magic number. enough size for the number of arguments list */ argv[argc++] = "test.sh"; /* program path of the command to run */ /*argv[argc++] = "-i"; argv[argc++] = "foo"; argv[argc++] = "--longopt"; argv[argc++] = "bar";*/ argv[argc++] = NULL; rv = apr_proc_create(&newproc, "test.sh", argv, /* env */ NULL, attr, p); assert(rv == APR_SUCCESS); // int exitCode = 0; apr_exit_why_e exitWhy = 0; rv = apr_proc_wait(&newproc, &exitCode, &exitWhy, APR_WAIT); if (APR_STATUS_IS_CHILD_DONE(rv)) { printf("child done: why = %d, exit status = %d\n", exitWhy, exitCode); } else { printf("child notdone\n"); } /*assert(rv == APR_CHILD_DONE); assert(exitCode == 0); assert(exitWhy == APR_PROC_EXIT);*/ // -- apr_pool_destroy(p); apr_terminate(); return 0; }