/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* The purpose of this file is to store the code that MOST mpm's will need * this does not mean a function only goes into this file if every MPM needs * it. It means that if a function is needed by more than one MPM, and * future maintenance would be served by making the code common, then the * function belongs here. * * This is going in src/main because it is not platform specific, it is * specific to multi-process servers, but NOT to Unix. Which is why it * does not belong in src/os/unix
*/
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); /* FALLTHROUGH */ case SEND_SIGTERM_NOLOG:
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;
}
AP_DECLARE(void) ap_reclaim_child_processes(int terminate,
ap_reclaim_callback_fn_t *mpm_callback)
{
apr_time_t waittime = 1024 * 16; int i;
extra_process_t *cur_extra; int not_dead_yet; int max_daemons;
apr_time_t starttime = apr_time_now(); /* this table of actions and elapsed times tells what action is taken * at which elapsed time from starting the reclaim
*/ struct {
action_t action;
apr_time_t action_time;
} action_table[] = {
{DO_NOTHING, 0}, /* dummy entry for iterations where we reap * children but take no action against * stragglers
*/
{SEND_SIGTERM_NOLOG, 0}, /* skipped if terminate == 0 */
{SEND_SIGTERM, apr_time_from_sec(3)},
{SEND_SIGTERM, apr_time_from_sec(5)},
{SEND_SIGTERM, apr_time_from_sec(7)},
{SEND_SIGKILL, apr_time_from_sec(9)},
{GIVEUP, apr_time_from_sec(10)}
}; int cur_action; /* index of action we decided to take this * iteration
*/ int next_action = terminate ? 1 : 2; /* index of first real action */
do { if (action_table[next_action].action_time > 0) {
apr_sleep(waittime); /* don't let waittime get longer than 1 second; otherwise, we don't * react quickly to the last child exiting, and taking action can * be delayed
*/
waittime = waittime * 4; if (waittime > apr_time_from_sec(1)) {
waittime = apr_time_from_sec(1);
}
}
/* see what action to take, if any */ if (action_table[next_action].action_time <= apr_time_now() - starttime) {
cur_action = next_action;
++next_action;
} else {
cur_action = 0; /* nothing to do */
}
/* now see who is done */
not_dead_yet = 0; for (i = 0; i < max_daemons; ++i) {
process_score *ps = ap_get_scoreboard_process(i);
pid_t pid = ps->pid;
if (pid == 0) { continue; /* not every scoreboard entry is in use */
}
/* 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. */
AP_DECLARE(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;
proc.pid = pid;
rv = apr_proc_wait(&proc, &status, &why, APR_NOWAIT); if (rv == APR_CHILD_DONE) { /* The child already died - log the termination status if
* necessary: */
ap_process_child_status(&proc, why, status); return APR_EINVAL;
} elseif (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, APLOGNO(00048) "cannot send signal %d to pid %ld (non-child or " "already dead)", sig, (long)pid); return APR_EINVAL;
} #else
pid_t pg;
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, APLOGNO(00049) "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;
}
AP_DECLARE(int) ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, int status)
{ int signum = status; constchar *sigdesc;
/* Child died... if it died due to a fatal error, * we should simply bail out. The caller needs to * check for bad rc from us and exit, running any * appropriate cleanups. * * If the child died due to a resource shortage, * the parent should limit the rate of forking
*/ if (APR_PROC_CHECK_EXIT(why)) { if (status == APEXIT_CHILDSICK) { return status;
}
if (status == APEXIT_CHILDFATAL) {
ap_log_error(APLOG_MARK, APLOG_ALERT,
0, ap_server_conf, APLOGNO(00050) "Child %" APR_PID_T_FMT " returned a Fatal error... Apache is exiting!",
pid->pid); return APEXIT_CHILDFATAL;
}
return 0;
}
if (APR_PROC_CHECK_SIGNALED(why)) {
sigdesc = apr_signal_description_get(signum);
switch (signum) { case SIGTERM: case SIGHUP: case AP_SIG_GRACEFUL: case SIGKILL: break;
/* close these before exec. */
apr_file_inherit_unset((*pod)->pod_in);
apr_file_inherit_unset((*pod)->pod_out);
return APR_SUCCESS;
}
AP_DECLARE(int) ap_mpm_podx_check(ap_pod_t *pod)
{ char c;
apr_os_file_t fd; int rc;
/* we need to surface EINTR so we'll have to grab the * native file descriptor and do the OS read() ourselves
*/
apr_os_file_get(&fd, pod->pod_in);
rc = read(fd, &c, 1); if (rc == 1) { switch (c) { case AP_MPM_PODX_RESTART_CHAR: return AP_MPM_PODX_RESTART; case AP_MPM_PODX_GRACEFUL_CHAR: return AP_MPM_PODX_GRACEFUL;
}
} return AP_MPM_PODX_NORESTART;
}
AP_DECLARE(void) ap_mpm_podx_killpg(ap_pod_t * pod, int num,
ap_podx_restart_t graceful)
{ int i;
apr_status_t rv = APR_SUCCESS;
for (i = 0; i < num && rv == APR_SUCCESS; i++) {
rv = podx_signal_internal(pod, graceful);
}
}
/* This function connects to the server and sends enough data to * ensure the child wakes up and processes a new connection. This * permits the MPM to skip the poll when there is only one listening * socket, because it provides a alternate way to unblock an accept()
* when the pod is used. */ static apr_status_t dummy_connection(ap_pod_t *pod)
{ constchar *data;
apr_status_t rv;
apr_socket_t *sock;
apr_pool_t *p;
apr_size_t len;
ap_listen_rec *lp;
/* create a temporary pool for the socket. pconf stays around too long */
rv = apr_pool_create(&p, pod->p); if (rv != APR_SUCCESS) { return rv;
}
apr_pool_tag(p, "dummy_connection");
/* If possible, find a listener which is configured for * plain-HTTP, not SSL; using an SSL port would either be * expensive to do correctly (performing a complete SSL handshake)
* or cause log spam by doing incorrectly (simply sending EOF). */
lp = ap_listeners; while (lp && lp->protocol && ap_cstr_casecmp(lp->protocol, "http") != 0) {
lp = lp->next;
} if (!lp) {
lp = ap_listeners;
}
rv = apr_socket_create(&sock, lp->bind_addr->family, SOCK_STREAM, 0, p); if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00054) "get socket to connect to listener");
apr_pool_destroy(p); return rv;
}
/* on some platforms (e.g., FreeBSD), the kernel won't accept many * queued connections before it starts blocking local connects... * we need to keep from blocking too long and instead return an error, * because the MPM won't want to hold up a graceful restart for a * long time
*/
rv = apr_socket_timeout_set(sock, apr_time_from_sec(3)); if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00055) "set timeout on socket to connect to listener");
apr_socket_close(sock);
apr_pool_destroy(p); return rv;
}
rv = apr_socket_connect(sock, lp->bind_addr); if (rv != APR_SUCCESS) { int log_level = APLOG_WARNING;
if (APR_STATUS_IS_TIMEUP(rv)) { /* probably some server processes bailed out already and there * is nobody around to call accept and clear out the kernel * connection queue; usually this is not worth logging
*/
log_level = APLOG_DEBUG;
}
ap_log_error(APLOG_MARK, log_level, rv, ap_server_conf, APLOGNO(00056) "connect to listener on %pI", lp->bind_addr);
apr_pool_destroy(p); return rv;
}
if (lp->protocol && ap_cstr_casecmp(lp->protocol, "https") == 0) { /* Send a TLS 1.0 close_notify alert. This is perhaps the * "least wrong" way to open and cleanly terminate an SSL * connection. It should "work" without noisy error logs if * the server actually expects SSLv3/TLSv1. With * SSLv23_server_method() OpenSSL's SSL_accept() fails * ungracefully on receipt of this message, since it requires
* an 11-byte ClientHello message and this is too short. */ staticconstunsignedchar tls10_close_notify[7] = { '\x15', /* TLSPlainText.type = Alert (21) */ '\x03', '\x01', /* TLSPlainText.version = {3, 1} */ '\x00', '\x02', /* TLSPlainText.length = 2 */ '\x01', /* Alert.level = warning (1) */ '\x00'/* Alert.description = close_notify (0) */
};
data = (constchar *)tls10_close_notify;
len = sizeof(tls10_close_notify);
} else/* ... XXX other request types here? */ { /* Create an HTTP request string. We include a User-Agent so * that administrators can track down the cause of the * odd-looking requests in their logs. A complete request is * used since kernel-level filtering may require that much
* data before returning from accept(). */
data = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ",
ap_get_server_description(), " (internal dummy connection)\r\n\r\n", NULL);
len = strlen(data);
}
void ap_mpm_pod_killpg(ap_pod_t *pod, int num)
{ int i;
apr_status_t rv = APR_SUCCESS;
/* we don't write anything to the pod here... we assume * that the would-be reader of the pod has another way to * see that it is time to die once we wake it up * * writing lots of things to the pod at once is very * problematic... we can fill the kernel pipe buffer and * be blocked until somebody consumes some bytes or * we hit a timeout... if we hit a timeout we can't just * keep trying because maybe we'll never successfully * write again... but then maybe we'll leave would-be * readers stranded (a number of them could be tied up for * a while serving time-consuming requests)
*/ /* Recall: we only worry about IDLE child processes here */ for (i = 0; i < num && rv == APR_SUCCESS; i++) { if (ap_scoreboard_image->servers[i][0].status != SERVER_READY ||
ap_scoreboard_image->servers[i][0].pid == 0) { continue;
}
rv = dummy_connection(pod);
}
}
staticint send_signal(pid_t pid, int sig)
{ if (kill(pid, sig) < 0) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, errno, NULL, APLOGNO(00057) "sending signal to server"); return 1;
} return 0;
}
int ap_signal_server(int *exit_status, apr_pool_t *pconf)
{
apr_status_t rv;
pid_t otherpid; int running = 0; constchar *status;
*exit_status = 0;
rv = ap_read_pid(pconf, ap_pid_fname, &otherpid); if (rv != APR_SUCCESS) { if (!APR_STATUS_IS_ENOENT(rv)) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, NULL, APLOGNO(00058) "Error retrieving pid file %s", ap_pid_fname);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00059) "Remove it before continuing if it is corrupted.");
*exit_status = 1; return 1;
}
status = "httpd (no pid file) not running";
} else { /* With containerization, httpd may get the same PID at each startup, * handle it as if it were not running (it obviously can't).
*/ if (otherpid != getpid() && kill(otherpid, 0) == 0) {
running = 1;
status = apr_psprintf(pconf, "httpd (pid %" APR_PID_T_FMT ") already " "running", otherpid);
} else {
status = apr_psprintf(pconf, "httpd (pid %" APR_PID_T_FMT "?) not running",
otherpid);
}
}
if (!strcmp(dash_k_arg, "start") || dash_k_arg == dash_k_arg_noarg) { if (running) {
printf("%s\n", status); return 1;
}
}
if (!strcmp(dash_k_arg, "stop")) { if (!running) {
printf("%s\n", status);
} else {
send_signal(otherpid, SIGTERM);
} return 1;
}
if (!strcmp(dash_k_arg, "restart")) { if (!running) {
printf("httpd not running, trying to start\n");
} else {
*exit_status = send_signal(otherpid, SIGHUP); return 1;
}
}
if (!strcmp(dash_k_arg, "graceful")) { if (!running) {
printf("httpd not running, trying to start\n");
} else {
*exit_status = send_signal(otherpid, AP_SIG_GRACEFUL); return 1;
}
}
if (!strcmp(dash_k_arg, "graceful-stop")) { if (!running) {
printf("%s\n", status);
} else {
*exit_status = send_signal(otherpid, AP_SIG_GRACEFUL_STOP);
} return 1;
}
/* handle all varieties of core dumping signals */ staticvoid sig_coredump(int sig)
{
apr_filepath_set(ap_coredump_dir, pconf);
apr_signal(sig, SIG_DFL); #if AP_ENABLE_EXCEPTION_HOOK
run_fatal_exception_hook(sig); #endif /* linuxthreads issue calling getpid() here: * This comparison won't match if the crashing thread is * some module's thread that runs in the parent process. * The fallout, which is limited to linuxthreads: * The special log message won't be written when such a * thread in the parent causes the parent to crash.
*/ if (getpid() == parent_pid) {
ap_log_error(APLOG_MARK, APLOG_NOTICE,
0, ap_server_conf, APLOGNO(00060) "seg fault or similar nasty error detected " "in the parent process"); /* XXX we can probably add some rudimentary cleanup code here, * like getting rid of the pid file. If any additional bad stuff * happens, we are protected from recursive errors taking down the * system since this function is no longer the signal handler GLA
*/
}
kill(getpid(), sig); /* At this point we've got sig blocked, because we're still inside * the signal handler. When we leave the signal handler it will * be unblocked, and we'll take the signal... and coredump or whatever * is appropriate for this particular Unix. In addition the parent * will see the real signal we received -- whereas if we called * abort() here, the parent would only see SIGABRT.
*/
}
/* We can't call sig_coredump (ap_log_error) once pconf is destroyed, so * avoid double faults by restoring each default signal handler on cleanup.
*/ static apr_status_t fatal_signal_cleanup(void *unused)
{
(void)unused;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.