Instead of exiting, disable an optional eCAP service that fails initialization. Report all service initialization failures but mark optional services down instead of killing Squid by propagating the failure to the Squid core. An initialization failure of an optional (bypass=1) service should not lead to Squid quitting because such a service may be replaced by other services (using adaptation sets) or simply ignored. Initialization failures of essential services still lead to fatal errors, but they are now reported better. Ecap services should indicate failures by throwing an exception. Squid reports exception details using std::exception::what() method if possible. === modified file 'src/adaptation/ecap/ServiceRep.cc' --- src/adaptation/ecap/ServiceRep.cc 2011-03-11 21:41:33 +0000 +++ src/adaptation/ecap/ServiceRep.cc 2011-03-16 16:04:59 +0000 @@ -82,52 +82,93 @@ /*AsyncJob("Adaptation::Ecap::ServiceRep"),*/ Adaptation::Service(cfg), isDetached(false) { } Adaptation::Ecap::ServiceRep::~ServiceRep() { } void Adaptation::Ecap::ServiceRep::noteFailure() { assert(false); // XXX: should this be ICAP-specific? } void Adaptation::Ecap::ServiceRep::finalize() { Adaptation::Service::finalize(); theService = FindAdapterService(cfg().uri); if (theService) { - debugs(93,2, HERE << "configuring eCAP service: " << theService->uri()); - const ConfigRep cfgRep(dynamic_cast(cfg())); - theService->configure(cfgRep); - - debugs(93,DBG_IMPORTANT, HERE << "starting eCAP service: " << theService->uri()); - theService->start(); + try { + tryConfigureAndStart(); + Must(up()); + } + catch (const std::exception &e) { // standardized exceptions + if (!handleFinalizeFailure(e.what())) + throw; // rethrow for upper layers to handle + } + catch (...) { // all other exceptions + if (!handleFinalizeFailure("unrecognized exception")) + throw; // rethrow for upper layers to handle + } + return; // success or handled exception } else { debugs(93,1, "Warning: configured ecap_service was not loaded: " << cfg().uri); } } +/// attempts to configure and start eCAP service; the caller handles exceptions +void +Adaptation::Ecap::ServiceRep::tryConfigureAndStart() +{ + debugs(93,2, HERE << "configuring eCAP service: " << theService->uri()); + const ConfigRep cfgRep(dynamic_cast(cfg())); + theService->configure(cfgRep); + + debugs(93,DBG_IMPORTANT, "starting eCAP service: " << theService->uri()); + theService->start(); +} + +/// handles failures while configuring or starting an eCAP service; +/// returns false if the error must be propagated to higher levels +bool +Adaptation::Ecap::ServiceRep::handleFinalizeFailure(const char *error) +{ + const bool salvage = cfg().bypass; + const int level = salvage ? DBG_IMPORTANT : DBG_CRITICAL; + const char *kind = salvage ? "optional" : "essential"; + debugs(93, level, "ERROR: failed to start " << kind << " eCAP service: " << + cfg().uri << ":\n" << error); + + if (!salvage) + return false; // we cannot handle the problem; the caller may escalate + + // make up() false, preventing new adaptation requests and enabling bypass + theService.reset(); + debugs(93, level, "WARNING: " << kind << " eCAP service is " << + "down after initialization failure: " << cfg().uri); + + return true; // tell the caller to ignore the problem because we handled it +} + bool Adaptation::Ecap::ServiceRep::probed() const { return true; // we "probe" the adapter in finalize(). } bool Adaptation::Ecap::ServiceRep::up() const { return theService != NULL; } bool Adaptation::Ecap::ServiceRep::wantsUrl(const String &urlPath) const { Must(up()); return theService->wantsUrl(urlPath.termedBuf()); } Adaptation::Initiate * Adaptation::Ecap::ServiceRep::makeXactLauncher(HttpMsg *virgin, HttpRequest *cause) { === modified file 'src/adaptation/ecap/ServiceRep.h' --- src/adaptation/ecap/ServiceRep.h 2011-03-11 21:57:27 +0000 +++ src/adaptation/ecap/ServiceRep.h 2011-03-15 23:31:44 +0000 @@ -22,39 +22,43 @@ class ServiceRep : public Adaptation::Service { public: explicit ServiceRep(const ServiceConfigPointer &aConfig); virtual ~ServiceRep(); typedef libecap::shared_ptr AdapterService; /* Adaptation::Service API */ virtual void finalize(); virtual bool probed() const; virtual bool up() const; virtual Adaptation::Initiate *makeXactLauncher(HttpMsg *virginHeader, HttpRequest *virginCause); virtual bool wantsUrl(const String &urlPath) const; virtual void noteFailure(); virtual const char *status() const; virtual void detach(); virtual bool detached() const; +protected: + void tryConfigureAndStart(); + bool handleFinalizeFailure(const char *error); + private: AdapterService theService; // the actual adaptation service we represent bool isDetached; }; /// register loaded eCAP module service extern void RegisterAdapterService(const ServiceRep::AdapterService& adapterService); /// unregister loaded eCAP module service by service uri extern void UnregisterAdapterService(const String& serviceUri); /// returns loaded eCAP module service by service uri extern ServiceRep::AdapterService FindAdapterService(const String& serviceUri); /// check for loaded eCAP services without matching ecap_service in squid.conf extern void CheckUnusedAdapterServices(const Services& services); } // namespace Ecap } // namespace Adaptation #endif /* SQUID_ECAP_SERVICE_REP_H */