static void test_dup2(abts_case *tc, void *data) { apr_file_t *testfile = NULL; apr_file_t *errfile = NULL; apr_file_t *saveerr = NULL; apr_status_t rv; rv = apr_file_open(&testfile, FILEPATH "testdup2.file", APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_DELONCLOSE, APR_OS_DEFAULT, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, testfile); rv = apr_file_open_stderr(&errfile, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); /* Set aside the real errfile */ rv = apr_file_dup(&saveerr, errfile, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, saveerr); rv = apr_file_dup2(errfile, testfile, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, errfile); apr_file_close(testfile); rv = apr_file_dup2(errfile, saveerr, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, errfile); apr_file_close(saveerr); }
static void test_dup2_readwrite(abts_case *tc, void *data) { apr_file_t *errfile = NULL; apr_file_t *testfile = NULL; apr_file_t *saveerr = NULL; apr_status_t rv; apr_size_t txtlen = sizeof(TEST); char buff[50]; apr_off_t fpos; rv = apr_file_open(&testfile, FILEPATH "testdup2.readwrite.file", APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_DELONCLOSE, APR_OS_DEFAULT, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, testfile); rv = apr_file_open_stderr(&errfile, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); /* Set aside the real errfile */ rv = apr_file_dup(&saveerr, errfile, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, saveerr); rv = apr_file_dup2(errfile, testfile, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, errfile); txtlen = sizeof(TEST2); rv = apr_file_write(errfile, TEST2, &txtlen); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_SIZE_EQUAL(tc, sizeof(TEST2), txtlen); fpos = 0; rv = apr_file_seek(testfile, APR_SET, &fpos); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_ASSERT(tc, "File position mismatch, expected 0", fpos == 0); txtlen = 50; rv = apr_file_read(testfile, buff, &txtlen); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_STR_EQUAL(tc, TEST2, buff); apr_file_close(testfile); rv = apr_file_dup2(errfile, saveerr, p); ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); ABTS_PTR_NOTNULL(tc, errfile); apr_file_close(saveerr); }
AP_DECLARE(apr_status_t) ap_replace_stderr_log(apr_pool_t *p, const char *fname) { apr_file_t *stderr_file; apr_status_t rc; char *filename = ap_server_root_relative(p, fname); if (!filename) { ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, APR_EBADPATH, NULL, "Invalid -E error log file %s", fname); return APR_EBADPATH; } if ((rc = apr_file_open(&stderr_file, filename, APR_APPEND | APR_WRITE | APR_CREATE | APR_LARGEFILE, APR_OS_DEFAULT, p)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL, "%s: could not open error log file %s.", ap_server_argv0, fname); return rc; } if ((rc = apr_file_open_stderr(&stderr_log, p)) == APR_SUCCESS) { apr_file_flush(stderr_log); if ((rc = apr_file_dup2(stderr_log, stderr_file, p)) == APR_SUCCESS) { apr_file_close(stderr_file); } } if (rc != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rc, NULL, "unable to replace stderr with error_log"); } return rc; }
AP_DECLARE(void) ap_error_log2stderr(server_rec *s) { apr_file_t *errfile = NULL; apr_file_open_stderr(&errfile, s->process->pool); if (s->error_log != NULL) { apr_file_dup2(s->error_log, errfile, s->process->pool); } }
/* Redirect stdout to stderr. ARG is the pool. * * In tunnel or inetd mode, we don't want hook scripts corrupting the * data stream by sending data to stdout, so we need to redirect * stdout somewhere else. Sending it to stderr is acceptable; sending * it to /dev/null is another option, but apr doesn't provide a way to * do that without also detaching from the controlling terminal. */ static apr_status_t redirect_stdout(void *arg) { apr_pool_t *pool = arg; apr_file_t *out_file, *err_file; apr_status_t apr_err; if ((apr_err = apr_file_open_stdout(&out_file, pool))) return apr_err; if ((apr_err = apr_file_open_stderr(&err_file, pool))) return apr_err; return apr_file_dup2(out_file, err_file, pool); }
AP_DECLARE(apr_status_t) ap_replace_stderr_log(apr_pool_t *p, const char *fname) { apr_file_t *stderr_file; apr_status_t rc; char *filename = ap_server_root_relative(p, fname); if (!filename) { ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, APR_EBADPATH, NULL, "Invalid -E error log file %s", fname); return APR_EBADPATH; } if ((rc = apr_file_open(&stderr_file, filename, APR_APPEND | APR_WRITE | APR_CREATE | APR_LARGEFILE, APR_OS_DEFAULT, p)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL, "%s: could not open error log file %s.", ap_server_argv0, fname); return rc; } if (!stderr_pool) { /* This is safe provided we revert it when we are finished. * We don't manager the callers pool! */ stderr_pool = p; } if ((rc = apr_file_open_stderr(&stderr_log, stderr_pool)) == APR_SUCCESS) { apr_file_flush(stderr_log); if ((rc = apr_file_dup2(stderr_log, stderr_file, stderr_pool)) == APR_SUCCESS) { apr_file_close(stderr_file); /* * You might ponder why stderr_pool should survive? * The trouble is, stderr_pool may have s_main->error_log, * so we aren't in a position to destory stderr_pool until * the next recycle. There's also an apparent bug which * is not; if some folk decided to call this function before * the core open error logs hook, this pool won't survive. * Neither does the stderr logger, so this isn't a problem. */ } } /* Revert, see above */ if (stderr_pool == p) stderr_pool = NULL; if (rc != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rc, NULL, "unable to replace stderr with error log file"); } return rc; }
void mpm_nt_eventlog_stderr_open(char *argv0, apr_pool_t *p) { SECURITY_ATTRIBUTES sa; HANDLE hProc = GetCurrentProcess(); HANDLE hPipeRead = NULL; HANDLE hPipeWrite = NULL; HANDLE hDup = NULL; DWORD threadid; apr_file_t *eventlog_file; apr_file_t *stderr_file; display_name = argv0; /* Create a pipe to send stderr messages to the system error log. * * _dup2() duplicates the write handle inheritable for us. */ sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = FALSE; CreatePipe(&hPipeRead, &hPipeWrite, NULL, 0); ap_assert(hPipeRead && hPipeWrite); stderr_ready = CreateEvent(NULL, FALSE, FALSE, NULL); stderr_thread = CreateThread(NULL, 0, service_stderr_thread, (LPVOID) hPipeRead, 0, &threadid); ap_assert(stderr_ready && stderr_thread); WaitForSingleObject(stderr_ready, INFINITE); if ((apr_file_open_stderr(&stderr_file, p) == APR_SUCCESS) && (apr_os_file_put(&eventlog_file, &hPipeWrite, APR_WRITE, p) == APR_SUCCESS)) apr_file_dup2(stderr_file, eventlog_file, p); /* The code above _will_ corrupt the StdHandle... * and we must do so anyways. We set this up only * after we initialized the posix stderr API. */ ap_open_stderr_log(p); }
int ap_open_logs(apr_pool_t *pconf, apr_pool_t *p /* plog */, apr_pool_t *ptemp, server_rec *s_main) { apr_status_t rc = APR_SUCCESS; server_rec *virt, *q; int replace_stderr; apr_file_t *errfile = NULL; apr_pool_cleanup_register(p, NULL, clear_handle_list, apr_pool_cleanup_null); if (open_error_log(s_main, 1, p) != OK) { return DONE; } replace_stderr = 1; if (s_main->error_log) { /* replace stderr with this new log */ apr_file_flush(s_main->error_log); if ((rc = apr_file_open_stderr(&errfile, p)) == APR_SUCCESS) { rc = apr_file_dup2(errfile, s_main->error_log, p); } if (rc != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rc, s_main, "unable to replace stderr with error_log"); } else { replace_stderr = 0; } } /* note that stderr may still need to be replaced with something * because it points to the old error log, or back to the tty * of the submitter. * XXX: This is BS - /dev/null is non-portable */ if (replace_stderr && freopen("/dev/null", "w", stderr) == NULL) { ap_log_error(APLOG_MARK, APLOG_CRIT, errno, s_main, "unable to replace stderr with /dev/null"); } for (virt = s_main->next; virt; virt = virt->next) { if (virt->error_fname) { for (q=s_main; q != virt; q = q->next) { if (q->error_fname != NULL && strcmp(q->error_fname, virt->error_fname) == 0) { break; } } if (q == virt) { if (open_error_log(virt, 0, p) != OK) { return DONE; } } else { virt->error_log = q->error_log; } } else { virt->error_log = s_main->error_log; } } return OK; }
int main(int argc, const char * const argv[]) { apr_file_t *infd, *skwrapper; apr_sockaddr_t *skaddr; apr_getopt_t *gopt; apr_socket_t *skt; apr_pool_t *pool; apr_status_t rv; apr_proc_t proc; /* Command line arguments */ int num_to_start = 1, port = 0; const char *interface = NULL; const char *command = NULL; apr_app_initialize(&argc, &argv, NULL); atexit(apr_terminate); apr_pool_create(&pool, NULL); rv = apr_getopt_init(&gopt, pool, argc, argv); if (rv) { return EXIT_FAILURE; } for (;;) { const char *arg; char opt; rv = apr_getopt(gopt, "c:p:i:N:", &opt, &arg); if (APR_STATUS_IS_EOF(rv)) { break; } else if (rv) { usage(); } else { switch (opt) { case 'c': command = arg; break; case 'p': port = atoi(arg); if (! port) { usage(); } break; case 'i': interface = arg; break; case 'N': num_to_start = atoi(arg); if (! num_to_start) { usage(); } break; default: break; } } } if (! command || ! port) { usage(); } rv = apr_sockaddr_info_get(&skaddr, interface, APR_UNSPEC, port, 0, pool); if (rv) { exit_error(rv, "apr_sockaddr_info_get"); } rv = apr_socket_create(&skt, skaddr->family, SOCK_STREAM, APR_PROTO_TCP, pool); if (rv) { exit_error(rv, "apr_socket_create"); } rv = apr_socket_bind(skt, skaddr); if (rv) { exit_error(rv, "apr_socket_bind"); } rv = apr_socket_listen(skt, 1024); if (rv) { exit_error(rv, "apr_socket_listen"); } rv = apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); if (rv) { exit_error(rv, "apr_proc_detach"); } #if defined(WIN32) || defined(OS2) || defined(NETWARE) #error "Please implement me." #else while (--num_to_start >= 0) { rv = apr_proc_fork(&proc, pool); if (rv == APR_INCHILD) { apr_os_file_t oft = 0; apr_os_sock_t oskt; /* Ok, so we need a file that has file descriptor 0 (which * FastCGI wants), but points to our socket. This isn't really * possible in APR, so we cheat a bit. I have no idea how to * do this on a non-unix platform, so for now this is platform * specific. Ick. * * Note that this has to happen post-detach, otherwise fd 0 * gets closed during apr_proc_detach and it's all for nothing. * * Unfortunately, doing this post detach means we have no way * to let anyone know if there's a problem at this point :( */ rv = apr_os_file_put(&infd, &oft, APR_READ | APR_WRITE, pool); if (rv) { exit(EXIT_FAILURE); } rv = apr_os_sock_get(&oskt, skt); if (rv) { exit(EXIT_FAILURE); } rv = apr_os_file_put(&skwrapper, &oskt, APR_READ | APR_WRITE, pool); if (rv) { exit(EXIT_FAILURE); } rv = apr_file_dup2(infd, skwrapper, pool); if (rv) { exit(EXIT_FAILURE); } /* XXX Can't use apr_proc_create because there's no way to get * infd into the procattr without going through another dup2, * which means by the time it gets to the fastcgi process it * is no longer fd 0, so it doesn't work. Sigh. */ execl(command, command, NULL); } else if (rv == APR_INPARENT) { if (num_to_start == 0) { apr_socket_close(skt); } } else { exit_error(rv, "apr_proc_fork"); } } #endif return EXIT_SUCCESS; }
int ap_open_logs(apr_pool_t *pconf, apr_pool_t *p /* plog */, apr_pool_t *ptemp, server_rec *s_main) { apr_pool_t *stderr_p; server_rec *virt, *q; int replace_stderr; /* Register to throw away the read_handles list when we * cleanup plog. Upon fork() for the apache children, * this read_handles list is closed so only the parent * can relaunch a lost log child. These read handles * are always closed on exec. * We won't care what happens to our stderr log child * between log phases, so we don't mind losing stderr's * read_handle a little bit early. */ apr_pool_cleanup_register(p, NULL, clear_handle_list, apr_pool_cleanup_null); /* HERE we need a stdout log that outlives plog. * We *presume* the parent of plog is a process * or global pool which spans server restarts. * Create our stderr_pool as a child of the plog's * parent pool. */ apr_pool_create(&stderr_p, apr_pool_parent_get(p)); apr_pool_tag(stderr_p, "stderr_pool"); if (open_error_log(s_main, 1, stderr_p) != OK) { return DONE; } replace_stderr = 1; if (s_main->error_log) { apr_status_t rv; /* Replace existing stderr with new log. */ apr_file_flush(s_main->error_log); rv = apr_file_dup2(stderr_log, s_main->error_log, stderr_p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s_main, "unable to replace stderr with error_log"); } else { /* We are done with stderr_pool, close it, killing * the previous generation's stderr logger */ if (stderr_pool) apr_pool_destroy(stderr_pool); stderr_pool = stderr_p; replace_stderr = 0; /* * Now that we have dup'ed s_main->error_log to stderr_log * close it and set s_main->error_log to stderr_log. This avoids * this fd being inherited by the next piped logger who would * keep open the writing end of the pipe that this one uses * as stdin. This in turn would prevent the piped logger from * exiting. */ apr_file_close(s_main->error_log); s_main->error_log = stderr_log; } } /* note that stderr may still need to be replaced with something * because it points to the old error log, or back to the tty * of the submitter. * XXX: This is BS - /dev/null is non-portable */ if (replace_stderr && freopen("/dev/null", "w", stderr) == NULL) { ap_log_error(APLOG_MARK, APLOG_CRIT, errno, s_main, "unable to replace stderr with /dev/null"); } for (virt = s_main->next; virt; virt = virt->next) { if (virt->error_fname) { for (q=s_main; q != virt; q = q->next) { if (q->error_fname != NULL && strcmp(q->error_fname, virt->error_fname) == 0) { break; } } if (q == virt) { if (open_error_log(virt, 0, p) != OK) { return DONE; } } else { virt->error_log = q->error_log; } } else { virt->error_log = s_main->error_log; } } return OK; }