#include "squid.h"
#include "globals.h"
#include "ipc/Kid.h"
+#include "SquidConfig.h"
#include <ctime>
#if HAVE_SYS_WAIT_H
assert(cpid > 0);
isRunning = true;
+ stopTime = 0;
pid = cpid;
- time(&startTime);
+ startTime = squid_curtime;
}
/// called when kid terminates, sets exiting status
assert(startTime != 0);
isRunning = false;
+ stopTime = squid_curtime;
+ status = theExitStatus;
- time_t stop_time;
- time(&stop_time);
- if ((stop_time - startTime) < fastFailureTimeLimit)
+ if ((stopTime - startTime) < fastFailureTimeLimit)
++badFailures;
else
badFailures = 0; // the failures are not "frequent" [any more]
- status = theExitStatus;
+ reportStopped(); // after all state changes
+}
+
+/// describes a recently stopped kid
+void
+Kid::reportStopped() const
+{
+ if (calledExit()) {
+ syslog(LOG_NOTICE,
+ "Squid Parent: %s process %d exited with status %d",
+ theName.termedBuf(), pid, exitStatus());
+ } else if (signaled()) {
+ syslog(LOG_NOTICE,
+ "Squid Parent: %s process %d exited due to signal %d with status %d",
+ theName.termedBuf(), pid, termSignal(), exitStatus());
+ } else {
+ syslog(LOG_NOTICE, "Squid Parent: %s process %d exited",
+ theName.termedBuf(), pid);
+ }
+
+ if (hopeless() && Config.hopelessKidRevivalDelay) {
+ syslog(LOG_NOTICE, "Squid Parent: %s process %d will not be restarted for %ld "
+ "seconds due to repeated, frequent failures",
+ theName.termedBuf(), pid, Config.hopelessKidRevivalDelay);
+ }
}
/// returns true if tracking of kid is stopped
return theName;
}
+time_t
+Kid::deathDuration() const
+{
+ return squid_curtime > stopTime ? squid_curtime - stopTime : 0;
+}
+
static volatile int do_reconfigure = 0;
static volatile int do_rotate = 0;
static volatile int do_shutdown = 0;
+static volatile int do_revive_kids = 0;
static volatile int shutdown_status = EXIT_SUCCESS;
static volatile int do_handle_stopped_child = 0;
static int RotateSignal = -1;
static int ReconfigureSignal = -1;
static int ShutdownSignal = -1;
+static int ReviveKidsSignal = -1;
static void mainRotate(void);
static void mainReconfigureStart(void);
#endif
}
+void
+master_revive_kids(int sig)
+{
+ ReviveKidsSignal = sig;
+ do_revive_kids = true;
+
+#if !_SQUID_WINDOWS_
+#if !HAVE_SIGACTION
+ signal(sig, master_revive_kids);
+#endif
+#endif
+}
+
/// Shutdown signal handler for master process
void
master_shutdown(int sig)
}
}
-#endif /* _SQUID_WINDOWS_ */
+/// Initiates shutdown sequence. Shutdown ends when the last running kids stops.
+static void
+masterShutdownStart()
+{
+ if (AvoidSignalAction("shutdown", do_shutdown))
+ return;
+ debugs(1, 2, "received shutdown command");
+ shutting_down = 1;
+}
-#if !_SQUID_WINDOWS_
+/// Initiates reconfiguration sequence. See also: masterReconfigureFinish().
static void
-masterCheckAndBroadcastSignals()
+masterReconfigureStart()
{
- // if (do_reconfigure)
- // TODO: hot-reconfiguration of the number of kids and PID file location
+ if (AvoidSignalAction("reconfiguration", do_reconfigure))
+ return;
+ debugs(1, 2, "received reconfiguration command");
+ reconfiguring = 1;
+ TheKids.forgetAllFailures();
+ // TODO: hot-reconfiguration of the number of kids, kids revival delay,
+ // PID file location, etc.
+}
+/// Ends reconfiguration sequence started by masterReconfigureStart().
+static void
+masterReconfigureFinish()
+{
+ reconfiguring = 0;
+}
+
+/// Reacts to the kid revival alarm.
+static void
+masterReviveKids()
+{
+ if (AvoidSignalAction("kids revival", do_revive_kids))
+ return;
+ debugs(1, 2, "woke up after ~" << Config.hopelessKidRevivalDelay << "s");
+ // nothing to do here -- actual revival happens elsewhere in the main loop
+ // the alarm was needed just to wake us up so that we do a loop iteration
+}
+
+static void
+masterCheckAndBroadcastSignals()
+{
if (do_shutdown)
- shutting_down = 1;
+ masterShutdownStart();
+ if (do_reconfigure)
+ masterReconfigureStart();
+ if (do_revive_kids)
+ masterReviveKids();
+
+ // emulate multi-step reconfiguration assumed by AvoidSignalAction()
+ if (reconfiguring)
+ masterReconfigureFinish();
BroadcastSignalIfAny(DebugSignal);
BroadcastSignalIfAny(RotateSignal);
BroadcastSignalIfAny(ReconfigureSignal);
BroadcastSignalIfAny(ShutdownSignal);
+ ReviveKidsSignal = -1; // alarms are not broadcasted
+}
+
+/// Maintains the following invariant: An alarm should be scheduled when and
+/// only when there are hopeless kid(s) that cannot be immediately revived.
+static void
+masterMaintainKidRevivalSchedule()
+{
+ const auto nextCheckDelay = TheKids.forgetOldFailures();
+ assert(nextCheckDelay >= 0);
+ (void)alarm(static_cast<unsigned int>(nextCheckDelay)); // resets or cancels
+ if (nextCheckDelay)
+ debugs(1, 2, "will recheck hopeless kids in " << nextCheckDelay << " seconds");
}
-#endif
static inline bool
masterSignaled()
{
- return (DebugSignal > 0 || RotateSignal > 0 || ReconfigureSignal > 0 || ShutdownSignal > 0);
+ return (DebugSignal > 0 || RotateSignal > 0 || ReconfigureSignal > 0 ||
+ ShutdownSignal > 0 || ReviveKidsSignal > 0);
}
-#if !_SQUID_WINDOWS_
/// makes the caller a daemon process running in the background
static void
GoIntoBackground()
}
// child, running as a background daemon (or a failed-to-fork parent)
}
+
+static void
+masterExit()
+{
+ if (TheKids.someSignaled(SIGINT) || TheKids.someSignaled(SIGTERM)) {
+ syslog(LOG_ALERT, "Exiting due to unexpected forced shutdown");
+ exit(EXIT_FAILURE);
+ }
+
+ if (TheKids.allHopeless()) {
+ syslog(LOG_ALERT, "Exiting due to repeated, frequent failures");
+ exit(EXIT_FAILURE);
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
#endif /* !_SQUID_WINDOWS_ */
static void
int nullfd;
+ // TODO: zero values are not supported because they result in
+ // misconfigured SMP Squid instances running forever, endlessly
+ // restarting each dying kid.
+ if (Config.hopelessKidRevivalDelay <= 0)
+ throw TexcHere("hopeless_kid_revival_delay must be positive");
+
enter_suid();
openlog(APP_SHORTNAME, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
squid_signal(SIGHUP, reconfigure, 0);
squid_signal(SIGTERM, master_shutdown, 0);
+ squid_signal(SIGALRM, master_revive_kids, 0);
squid_signal(SIGINT, master_shutdown, 0);
#ifdef SIGTTIN
squid_signal(SIGTTIN, master_shutdown, 0);
}
TheKids.init();
+ configured_once = 1;
+
syslog(LOG_NOTICE, "Squid Parent: will start %d kids", (int)TheKids.count());
// keep [re]starting kids until it is time to quit
waitFlag = WNOHANG;
PidStatus status;
pid = WaitForAnyPid(status, waitFlag);
+ getCurrentTime();
// check for a stopped kid
- Kid* kid = pid > 0 ? TheKids.find(pid) : NULL;
- if (kid) {
+ if (Kid *kid = pid > 0 ? TheKids.find(pid) : nullptr)
kid->stop(status);
- if (kid->calledExit()) {
- syslog(LOG_NOTICE,
- "Squid Parent: %s process %d exited with status %d",
- kid->name().termedBuf(),
- kid->getPid(), kid->exitStatus());
- } else if (kid->signaled()) {
- syslog(LOG_NOTICE,
- "Squid Parent: %s process %d exited due to signal %d with status %d",
- kid->name().termedBuf(),
- kid->getPid(), kid->termSignal(), kid->exitStatus());
- } else {
- syslog(LOG_NOTICE, "Squid Parent: %s process %d exited",
- kid->name().termedBuf(), kid->getPid());
- }
- if (kid->hopeless()) {
- syslog(LOG_NOTICE, "Squid Parent: %s process %d will not"
- " be restarted due to repeated, frequent failures",
- kid->name().termedBuf(), kid->getPid());
- }
- } else if (pid > 0) {
+ else if (pid > 0)
syslog(LOG_NOTICE, "Squid Parent: unknown child process %d exited", pid);
- }
+
+ masterCheckAndBroadcastSignals();
+ masterMaintainKidRevivalSchedule();
if (!TheKids.someRunning() && !TheKids.shouldRestartSome()) {
leave_suid();
// RegisteredRunner::startShutdown which promises a loop iteration.
RunRegisteredHere(RegisteredRunner::finishShutdown);
enter_suid();
-
- if (TheKids.someSignaled(SIGINT) || TheKids.someSignaled(SIGTERM)) {
- syslog(LOG_ALERT, "Exiting due to unexpected forced shutdown");
- exit(EXIT_FAILURE);
- }
-
- if (TheKids.allHopeless()) {
- syslog(LOG_ALERT, "Exiting due to repeated, frequent failures");
- exit(EXIT_FAILURE);
- }
-
- exit(EXIT_SUCCESS);
+ masterExit();
}
-
- masterCheckAndBroadcastSignals();
}
/* NOTREACHED */