The attached patch add support for parameterized Cache Manager queries. Currently, one sends mgr queries to the "whole" Squid. Kids responses may get aggregated by Coordinator, and we, in general, want to aggregate all responses that can be aggregated. This patch allow us to aggregate stats for a subset of kids. For example, the following query aggregates stats for just the first and the third workers: mgr:info?workers=1,3 When query response information cannot be aggregated (or at least is not aggregated right now), then a parameterized query will result in several matching "byKid { ..." blocks. This patch support the following scope variants: * raw interface with access to any kid process or groups of kids; similar to ${process_number} macro we already support in squid.conf: mgr:foo?processes=id,id,id... * higher-level interface to isolate workers by their numbers, starting with 1 for the first worker: mgr:foo?workers=num,num,num... Currently, all kids except Coordinator are workers, but that will change in the future as we get more kinds of kids.Currently, one sends mgr queries to the "whole" Squid. Kids responses may get aggregated by Coordinator, and we, in general, want to aggregate all responses that can be aggregated. === modified file 'src/cache_manager.cc' --- src/cache_manager.cc 2011-01-14 14:10:21 +0000 +++ src/cache_manager.cc 2011-01-24 17:25:19 +0000 @@ -31,40 +31,41 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "config.h" #include "base/TextException.h" #include "CacheManager.h" #include "Debug.h" #include "errorpage.h" #include "fde.h" #include "HttpReply.h" #include "HttpRequest.h" #include "mgr/ActionCreator.h" #include "mgr/Action.h" #include "mgr/ActionProfile.h" #include "mgr/BasicActions.h" #include "mgr/Command.h" #include "mgr/Forwarder.h" #include "mgr/FunAction.h" +#include "mgr/QueryParams.h" #include "protos.h" /* rotate_logs() */ #include "SquidTime.h" #include "Store.h" #include "wordlist.h" #include /// \ingroup CacheManagerInternal #define MGR_PASSWD_SZ 128 /// creates Action using supplied Action::Create method and command class ClassActionCreator: public Mgr::ActionCreator { public: typedef Mgr::Action::Pointer Handler(const Mgr::Command::Pointer &cmd); public: ClassActionCreator(Handler *aHandler): handler(aHandler) {} virtual Mgr::Action::Pointer create(const Mgr::Command::Pointer &cmd) const { @@ -159,72 +160,88 @@ cmd->profile = findAction(params.actionName.termedBuf()); Must(cmd->profile != NULL); return cmd->profile->creator->create(cmd); } /** \ingroup CacheManagerInternal * define whether the URL is a cache-manager URL and parse the action * requested by the user. Checks via CacheManager::ActionProtection() that the * item is accessible by the user. \retval CacheManager::cachemgrStateData state object for the following handling \retval NULL if the action can't be found or can't be accessed by the user */ Mgr::Command::Pointer CacheManager::ParseUrl(const char *url) { int t; LOCAL_ARRAY(char, host, MAX_URL); LOCAL_ARRAY(char, request, MAX_URL); LOCAL_ARRAY(char, password, MAX_URL); - t = sscanf(url, "cache_object://%[^/]/%[^@]@%s", host, request, password); + LOCAL_ARRAY(char, params, MAX_URL); + host[0] = 0; + request[0] = 0; + password[0] = 0; + params[0] = 0; + int pos = -1; + int len = strlen(url); + Must(len > 0); + t = sscanf(url, "cache_object://%[^/]/%[^@?]%n@%[^?]?%s", host, request, &pos, password, params); + + if (pos >0 && url[pos] == '?') { + ++pos; + if (pos < len) + xstrncpy(params, url + pos, sizeof(params)); + } if (t < 2) xstrncpy(request, "menu", MAX_URL); #ifdef _SQUID_OS2_ if (t == 2 && request[0] == '\0') { /* * emx's sscanf insists of returning 2 because it sets request * to null */ xstrncpy(request, "menu", MAX_URL); } #endif Mgr::ActionProfile::Pointer profile = findAction(request); if (!profile) { debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' not found"); return NULL; } const char *prot = ActionProtection(profile); if (!strcmp(prot, "disabled") || !strcmp(prot, "hidden")) { debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' is " << prot); return NULL; } Mgr::Command::Pointer cmd = new Mgr::Command; + if (!Mgr::QueryParams::Parse(params, cmd->params.queryParams)) + return NULL; cmd->profile = profile; cmd->params.httpUri = url; cmd->params.userName = String(); - cmd->params.password = t == 3 ? String(password) : String(); + cmd->params.password = password; cmd->params.actionName = request; return cmd; } /// \ingroup CacheManagerInternal /* \ingroup CacheManagerInternal * Decodes the headers needed to perform user authentication and fills * the details into the cachemgrStateData argument */ void CacheManager::ParseHeaders(const HttpRequest * request, Mgr::ActionParams ¶ms) { assert(request); params.httpMethod = request->method.id(); params.httpFlags = request->flags; #if HAVE_AUTH_MODULE_BASIC // TODO: use the authentication system decode to retrieve these details properly. === modified file 'src/mgr/ActionParams.cc' --- src/mgr/ActionParams.cc 2010-10-29 00:12:28 +0000 +++ src/mgr/ActionParams.cc 2011-01-24 13:39:06 +0000 @@ -10,33 +10,35 @@ #include "ipc/TypedMsgHdr.h" #include "mgr/ActionParams.h" Mgr::ActionParams::ActionParams(): httpMethod(METHOD_NONE) { } Mgr::ActionParams::ActionParams(const Ipc::TypedMsgHdr &msg) { msg.getString(httpUri); const int m = msg.getInt(); Must(METHOD_NONE <= m && m < METHOD_ENUM_END); httpMethod = static_cast<_method_t>(m); msg.getPod(httpFlags); msg.getString(actionName); msg.getString(userName); msg.getString(password); + queryParams.unpack(msg); } void Mgr::ActionParams::pack(Ipc::TypedMsgHdr &msg) const { msg.putString(httpUri); msg.putInt(httpMethod); msg.putPod(httpFlags); msg.putString(actionName); msg.putString(userName); msg.putString(password); + queryParams.pack(msg); } === modified file 'src/mgr/ActionParams.h' --- src/mgr/ActionParams.h 2010-10-28 18:52:59 +0000 +++ src/mgr/ActionParams.h 2011-01-24 13:39:06 +0000 @@ -1,43 +1,44 @@ /* * $Id$ * * DEBUG: section 16 Cache Manager API * */ #ifndef SQUID_MGR_ACTION_PARAMS_H #define SQUID_MGR_ACTION_PARAMS_H #include "HttpRequestMethod.h" #include "ipc/forward.h" +#include "mgr/QueryParams.h" namespace Mgr { /// Cache Manager Action parameters extracted from the user request class ActionParams { public: ActionParams(); explicit ActionParams(const Ipc::TypedMsgHdr &msg); ///< load from msg void pack(Ipc::TypedMsgHdr &msg) const; ///< store into msg public: /* details of the client HTTP request that caused the action */ String httpUri; ///< HTTP request URI _method_t httpMethod; ///< HTTP request method request_flags httpFlags; ///< HTTP request flags /* action parameters extracted from the client HTTP request */ String actionName; ///< action name (and credentials realm) String userName; ///< user login name; currently only used for logging String password; ///< user password; used for acceptance check and cleared - + QueryParams queryParams; }; } // namespace Mgr std::ostream &operator <<(std::ostream &os, const Mgr::ActionParams ¶ms); #endif /* SQUID_MGR_ACTION_PARAMS_H */ === modified file 'src/mgr/Inquirer.cc' --- src/mgr/Inquirer.cc 2010-11-27 01:46:22 +0000 +++ src/mgr/Inquirer.cc 2011-01-27 14:49:37 +0000 @@ -1,111 +1,123 @@ /* * $Id$ * * DEBUG: section 16 Cache Manager API * */ #include "config.h" #include "base/TextException.h" #include "comm/Write.h" #include "CommCalls.h" #include "HttpReply.h" +#include "HttpRequest.h" #include "ipc/Coordinator.h" #include "mgr/ActionWriter.h" #include "mgr/Command.h" +#include "mgr/IntParam.h" #include "mgr/Inquirer.h" #include "mgr/Request.h" #include "mgr/Response.h" #include "SquidTime.h" +#include "errorpage.h" #include #include CBDATA_NAMESPACED_CLASS_INIT(Mgr, Inquirer); Mgr::Inquirer::RequestsMap Mgr::Inquirer::TheRequestsMap; unsigned int Mgr::Inquirer::LastRequestId = 0; /// compare Ipc::StrandCoord using kidId, for std::sort() below static bool LesserStrandByKidId(const Ipc::StrandCoord &c1, const Ipc::StrandCoord &c2) { return c1.kidId < c2.kidId; } Mgr::Inquirer::Inquirer(Action::Pointer anAction, int aFd, const Request &aCause, const Ipc::StrandCoords &coords): AsyncJob("Mgr::Inquirer"), aggrAction(anAction), cause(aCause), fd(aFd), - strands(coords), pos(strands.begin()), + strands(applyQueryParams(coords, aCause.params.queryParams)), pos(strands.begin()), requestId(0), closer(NULL), timeout(aggrAction->atomic() ? 10 : 100) { debugs(16, 5, HERE << "FD " << aFd << " action: " << aggrAction); - // order by ascending kid IDs; useful for non-aggregatable stats - std::sort(strands.begin(), strands.end(), LesserStrandByKidId); - closer = asyncCall(16, 5, "Mgr::Inquirer::noteCommClosed", CommCbMemFunT(this, &Inquirer::noteCommClosed)); comm_add_close_handler(fd, closer); } Mgr::Inquirer::~Inquirer() { debugs(16, 5, HERE); close(); } /// closes our copy of the client HTTP connection socket void Mgr::Inquirer::close() { if (fd >= 0) { removeCloseHandler(); comm_close(fd); fd = -1; } } void Mgr::Inquirer::removeCloseHandler() { if (closer != NULL) { comm_remove_close_handler(fd, closer); closer = NULL; } } void Mgr::Inquirer::start() { debugs(16, 5, HERE); Must(fd >= 0); Must(aggrAction != NULL); - - std::auto_ptr reply(new HttpReply); - reply->setHeaders(HTTP_OK, NULL, "text/plain", -1, squid_curtime, squid_curtime); - reply->header.putStr(HDR_CONNECTION, "close"); // until we chunk response - std::auto_ptr replyBuf(reply->pack()); + + std::auto_ptr replyBuf; + if (strands.empty()) { + LOCAL_ARRAY(char, url, MAX_URL); + snprintf(url, MAX_URL, "%s", aggrAction->command().params.httpUri.termedBuf()); + HttpRequest *req = HttpRequest::CreateFromUrl(url); + ErrorState *err = errorCon(ERR_INVALID_URL, HTTP_NOT_FOUND, req); + std::auto_ptr reply(err->BuildHttpReply()); + replyBuf.reset(reply->pack()); + errorStateFree(err); + } + else { + std::auto_ptr reply(new HttpReply); + reply->setHeaders(HTTP_OK, NULL, "text/plain", -1, squid_curtime, squid_curtime); + reply->header.putStr(HDR_CONNECTION, "close"); // until we chunk response + replyBuf.reset(reply->pack()); + } writer = asyncCall(16, 5, "Mgr::Inquirer::noteWroteHeader", CommCbMemFunT(this, &Inquirer::noteWroteHeader)); Comm::Write(fd, replyBuf.get(), writer); } /// called when we wrote the response header void Mgr::Inquirer::noteWroteHeader(const CommIoCbParams& params) { debugs(16, 5, HERE); writer = NULL; Must(params.flag == COMM_OK); Must(params.fd == fd); Must(params.size != 0); // start inquiries at the initial pos inquire(); } void Mgr::Inquirer::inquire() @@ -149,41 +161,41 @@ /// called when the HTTP client or some external force closed our socket void Mgr::Inquirer::noteCommClosed(const CommCloseCbParams& params) { debugs(16, 5, HERE); Must(fd < 0 || fd == params.fd); fd = -1; mustStop("commClosed"); } void Mgr::Inquirer::swanSong() { debugs(16, 5, HERE); removeTimeoutEvent(); if (requestId > 0) { DequeueRequest(requestId); requestId = 0; } - if (aggrAction->aggregatable()) { + if (!strands.empty() && aggrAction->aggregatable()) { removeCloseHandler(); AsyncJob::Start(new ActionWriter(aggrAction, fd)); fd = -1; // should not close fd because we passed it to ActionWriter } close(); } bool Mgr::Inquirer::doneAll() const { return !writer && pos == strands.end(); } /// returns and forgets the right Inquirer callback for strand request AsyncCall::Pointer Mgr::Inquirer::DequeueRequest(unsigned int requestId) { debugs(16, 3, HERE << " requestId " << requestId); Must(requestId != 0); AsyncCall::Pointer call; @@ -234,20 +246,66 @@ { debugs(16, 3, HERE); if (requestId != 0) { DequeueRequest(requestId); requestId = 0; Must(!done()); // or we should not be called ++pos; // advance after a failed inquiry inquire(); } } const char* Mgr::Inquirer::status() const { static MemBuf buf; buf.reset(); buf.Printf(" [FD %d, requestId %u]", fd, requestId); buf.terminate(); return buf.content(); } + +Ipc::StrandCoords +Mgr::Inquirer::applyQueryParams(const Ipc::StrandCoords& aStrands, const QueryParams& aParams) +{ + Ipc::StrandCoords strands; + + QueryParam::Pointer processesParam = cause.params.queryParams.get("processes"); + QueryParam::Pointer workersParam = cause.params.queryParams.get("workers"); + + if (processesParam == NULL || workersParam == NULL) { + if (processesParam != NULL) { + IntParam* param = dynamic_cast(processesParam.getRaw()); + if (param != NULL && param->type == QueryParam::ptInt) { + const std::vector& processes = param->value(); + for (Ipc::StrandCoords::const_iterator iter = aStrands.begin(); + iter != aStrands.end(); ++iter) + { + if (std::find(processes.begin(), processes.end(), iter->kidId) != processes.end()) + strands.push_back(*iter); + } + } + } else if (workersParam != NULL) { + IntParam* param = dynamic_cast(workersParam.getRaw()); + if (param != NULL && param->type == QueryParam::ptInt) { + const std::vector& workers = param->value(); + for (size_t i = 0; i < aStrands.size(); ++i) + { + if (std::find(workers.begin(), workers.end(), i + 1) != workers.end()) + strands.push_back(aStrands[i]); + } + } + } else { + strands = aStrands; + } + + // order by ascending kid IDs; useful for non-aggregatable stats + std::sort(strands.begin(), strands.end(), LesserStrandByKidId); + } + + debugs(0, 0, HERE << "strands kid IDs = "); + for (Ipc::StrandCoords::const_iterator iter = strands.begin(); iter != strands.end(); ++iter) { + debugs(0, 0, HERE << iter->kidId); + } + + return strands; +} === modified file 'src/mgr/Inquirer.h' --- src/mgr/Inquirer.h 2010-10-29 00:12:28 +0000 +++ src/mgr/Inquirer.h 2011-01-24 13:39:06 +0000 @@ -41,40 +41,42 @@ virtual bool doneAll() const; virtual const char *status() const; private: typedef UnaryMemFunT HandleAckDialer; void inquire(); void noteWroteHeader(const CommIoCbParams& params); void noteCommClosed(const CommCloseCbParams& params); void handleRemoteAck(const Response& response); static AsyncCall::Pointer DequeueRequest(unsigned int requestId); static void RequestTimedOut(void* param); void requestTimedOut(); void removeTimeoutEvent(); void close(); void removeCloseHandler(); + Ipc::StrandCoords applyQueryParams(const Ipc::StrandCoords& aStrands, + const QueryParams& aParams); private: Action::Pointer aggrAction; //< action to aggregate Request cause; ///< cache manager request received from HTTP client int fd; ///< HTTP client socket descriptor Ipc::StrandCoords strands; ///< all strands we want to query, in order Ipc::StrandCoords::const_iterator pos; ///< strand we should query now unsigned int requestId; ///< ID of our outstanding request to strand AsyncCall::Pointer writer; ///< comm_write callback AsyncCall::Pointer closer; ///< comm_close handler const double timeout; ///< number of seconds to wait for strand response /// maps requestId to Inquirer::handleRemoteAck callback typedef std::map RequestsMap; static RequestsMap TheRequestsMap; ///< pending strand requests static unsigned int LastRequestId; ///< last requestId used === added file 'src/mgr/IntParam.cc' --- src/mgr/IntParam.cc 1970-01-01 00:00:00 +0000 +++ src/mgr/IntParam.cc 2011-01-24 13:44:39 +0000 @@ -0,0 +1,48 @@ +/* + * $Id$ + * + * DEBUG: section 16 Cache Manager API + * + */ + +#include "config.h" +#include "base/TextException.h" +#include "ipc/TypedMsgHdr.h" +#include "mgr/IntParam.h" + + +Mgr::IntParam::IntParam(): + QueryParam(QueryParam::ptInt), array() +{ +} + +Mgr::IntParam::IntParam(const std::vector& anArray): + QueryParam(QueryParam::ptInt), array(anArray) +{ +} + +void +Mgr::IntParam::pack(Ipc::TypedMsgHdr& msg) const +{ + msg.putPod(type); + msg.putInt(array.size()); + typedef std::vector::const_iterator Iterator; + for (Iterator iter = array.begin(); iter != array.end(); ++iter) + msg.putInt(*iter); +} + +void +Mgr::IntParam::unpackValue(const Ipc::TypedMsgHdr& msg) +{ + array.clear(); + int count = msg.getInt(); + Must(count >= 0); + for ( ; count > 0; --count) + array.push_back(msg.getInt()); +} + +const std::vector& +Mgr::IntParam::value() const +{ + return array; +} === added file 'src/mgr/IntParam.h' --- src/mgr/IntParam.h 1970-01-01 00:00:00 +0000 +++ src/mgr/IntParam.h 2011-01-24 13:39:06 +0000 @@ -0,0 +1,35 @@ +/* + * $Id$ + * + * DEBUG: section 16 Cache Manager API + * + */ + +#ifndef SQUID_MGR_INT_PARAM_H +#define SQUID_MGR_INT_PARAM_H + +#include "ipc/forward.h" +#include "mgr/forward.h" +#include "mgr/QueryParam.h" +#include + + +namespace Mgr +{ + +class IntParam: public QueryParam +{ +public: + IntParam(); + IntParam(const std::vector& anArray); + virtual void pack(Ipc::TypedMsgHdr& msg) const; + virtual void unpackValue(const Ipc::TypedMsgHdr& msg); + const std::vector& value() const; + +private: + std::vector array; +}; + +} // namespace Mgr + +#endif /* SQUID_MGR_INT_PARAM_H */ === modified file 'src/mgr/Makefile.am' --- src/mgr/Makefile.am 2010-10-28 18:52:59 +0000 +++ src/mgr/Makefile.am 2011-01-24 13:39:06 +0000 @@ -27,21 +27,28 @@ FunAction.h \ InfoAction.cc \ InfoAction.h \ Inquirer.cc \ Inquirer.h \ IntervalAction.cc \ IntervalAction.h \ IoAction.cc \ IoAction.h \ Registration.cc \ Registration.h \ Request.cc \ Request.h \ Response.cc \ Response.h \ ServiceTimesAction.cc \ ServiceTimesAction.h \ StoreIoAction.cc \ StoreIoAction.h \ StoreToCommWriter.cc \ - StoreToCommWriter.h + StoreToCommWriter.h \ + QueryParam.h \ + QueryParams.cc \ + QueryParams.h \ + IntParam.cc \ + IntParam.h \ + StringParam.cc \ + StringParam.h === added file 'src/mgr/QueryParam.h' --- src/mgr/QueryParam.h 1970-01-01 00:00:00 +0000 +++ src/mgr/QueryParam.h 2011-01-24 13:39:06 +0000 @@ -0,0 +1,40 @@ +/* + * $Id$ + * + * DEBUG: section 16 Cache Manager API + * + */ + +#ifndef SQUID_MGR_QUERY_PARAM_H +#define SQUID_MGR_QUERY_PARAM_H + +#include "ipc/forward.h" +#include "RefCount.h" + + +namespace Mgr +{ + +class QueryParam: public RefCountable +{ +public: + typedef enum {ptInt = 1, ptString} Type; + typedef RefCount Pointer; + +public: + QueryParam(Type aType): type(aType) {} + virtual ~QueryParam() {} + virtual void pack(Ipc::TypedMsgHdr& msg) const = 0; ///< store parameter into msg + virtual void unpackValue(const Ipc::TypedMsgHdr& msg) = 0; ///< load parameter value from msg + +private: + QueryParam(const QueryParam&); // not implemented + QueryParam& operator= (const QueryParam&); // not implemented + +public: + Type type; +}; + +} // namespace Mgr + +#endif /* SQUID_MGR_QUERY_PARAM_H */ === added file 'src/mgr/QueryParams.cc' --- src/mgr/QueryParams.cc 1970-01-01 00:00:00 +0000 +++ src/mgr/QueryParams.cc 2011-01-24 13:44:14 +0000 @@ -0,0 +1,139 @@ +/* + * $Id$ + * + * DEBUG: section 16 Cache Manager API + * + */ + +#include "config.h" +#include "base/TextException.h" +#include "ipc/TypedMsgHdr.h" +#include "mgr/IntParam.h" +#include "mgr/StringParam.h" +#include "mgr/QueryParams.h" +#include + + +Mgr::QueryParam::Pointer +Mgr::QueryParams::get(const String& name) +{ + Must(name.size() != 0); + Params::iterator pos = find(name); + return (pos == params.end() ? NULL : pos->second); +} + +void +Mgr::QueryParams::pack(Ipc::TypedMsgHdr& msg) const +{ + msg.putInt(params.size()); + for (Params::const_iterator iter = params.begin(); iter != params.end(); ++iter) { + Must(iter->first.size() != 0); + msg.putString(iter->first); + Must(iter->second != NULL); + iter->second->pack(msg); + } +} + +void +Mgr::QueryParams::unpack(const Ipc::TypedMsgHdr& msg) +{ + int count = msg.getInt(); + Must(count >= 0); + params.clear(); + for ( ; count > 0; --count) { + String name; + msg.getString(name); + Must(name.size() != 0); + QueryParam::Type type; + msg.getPod(type); + QueryParam::Pointer value = CreateParam(type); + value->unpackValue(msg); + params.push_back(Param(name, value)); + } +} + +Mgr::QueryParams::Params::iterator +Mgr::QueryParams::find(const String& name) +{ + Must(name.size() != 0); + Params::iterator iter = params.begin(); + for ( ; iter != params.end(); ++iter) { + if (name.caseCmp(iter->first) == 0) + break; + } + return iter; +} + +bool +Mgr::QueryParams::ParseParam(const String& paramStr, Param& param) +{ + bool parsed = false; + regmatch_t pmatch[3]; + regex_t intExpr; + regcomp(&intExpr, "^([a-z][a-z0-9_]*)=([0-9]+((,[0-9]+))*)$", REG_EXTENDED | REG_ICASE); + regex_t stringExpr; + regcomp(&stringExpr, "^([a-z][a-z0-9_]*)=([^&= ]+)$", REG_EXTENDED | REG_ICASE); + if (regexec(&intExpr, paramStr.termedBuf(), 3, pmatch, 0) == 0) { + param.first = paramStr.substr(pmatch[1].rm_so, pmatch[1].rm_eo); + std::vector array; + int n = pmatch[2].rm_so; + for (int i = n; i < pmatch[2].rm_eo; ++i) { + if (paramStr[i] == ',') { + array.push_back(atoi(paramStr.substr(n, i).termedBuf())); + n = i + 1; + } + } + if (n < pmatch[2].rm_eo) + array.push_back(atoi(paramStr.substr(n, pmatch[2].rm_eo).termedBuf())); + param.second = new IntParam(array); + parsed = true; + } else if (regexec(&stringExpr, paramStr.termedBuf(), 3, pmatch, 0) == 0) { + param.first = paramStr.substr(pmatch[1].rm_so, pmatch[1].rm_eo); + param.second = new StringParam(paramStr.substr(pmatch[2].rm_so, pmatch[2].rm_eo)); + parsed = true; + } + regfree(&stringExpr); + regfree(&intExpr); + return parsed; +} + +bool +Mgr::QueryParams::Parse(const String& aParamsStr, QueryParams& aParams) +{ + if (aParamsStr.size() != 0) { + Param param; + size_t n = 0; + size_t len = aParamsStr.size(); + for (size_t i = n; i < len; ++i) { + if (aParamsStr[i] == '&') { + if (!ParseParam(aParamsStr.substr(n, i), param)) + return false; + aParams.params.push_back(param); + n = i + 1; + } + } + if (n < len) { + if (!ParseParam(aParamsStr.substr(n, len), param)) + return false; + aParams.params.push_back(param); + } + } + return true; +} + +Mgr::QueryParam::Pointer +Mgr::QueryParams::CreateParam(QueryParam::Type aType) +{ + switch(aType) { + case QueryParam::ptInt: + return new IntParam(); + + case QueryParam::ptString: + return new StringParam(); + + default: + throw TexcHere("unknown parameter type"); + break; + } + return NULL; +} === added file 'src/mgr/QueryParams.h' --- src/mgr/QueryParams.h 1970-01-01 00:00:00 +0000 +++ src/mgr/QueryParams.h 2011-01-24 13:39:06 +0000 @@ -0,0 +1,49 @@ +/* + * $Id$ + * + * DEBUG: section 16 Cache Manager API + * + */ + +#ifndef SQUID_MGR_QUERY_PARAMS_H +#define SQUID_MGR_QUERY_PARAMS_H + +#include "ipc/forward.h" +#include "mgr/QueryParam.h" +#include "SquidString.h" +#include +#include + + +namespace Mgr +{ + +class QueryParams +{ +public: + typedef std::pair Param; + typedef std::vector Params; + +public: + /// returns query parameter by name + QueryParam::Pointer get(const String& name); + void pack(Ipc::TypedMsgHdr& msg) const; ///< store params into msg + void unpack(const Ipc::TypedMsgHdr& msg); ///< load params from msg + /// parses the query string parameters + static bool Parse(const String& aParamsStr, QueryParams& aParams); + +private: + /// find query parameter by name + Params::iterator find(const String& name); + /// creates a parameter of the specified type + static QueryParam::Pointer CreateParam(QueryParam::Type aType); + /// parses string like "param=value"; returns true if success + static bool ParseParam(const String& paramStr, Param& param); + +private: + Params params; +}; + +} // namespace Mgr + +#endif /* SQUID_MGR_QUERY_PARAMS_H */ === added file 'src/mgr/StringParam.cc' --- src/mgr/StringParam.cc 1970-01-01 00:00:00 +0000 +++ src/mgr/StringParam.cc 2011-01-24 13:45:03 +0000 @@ -0,0 +1,40 @@ +/* + * $Id$ + * + * DEBUG: section 16 Cache Manager API + * + */ + +#include "config.h" +#include "ipc/TypedMsgHdr.h" +#include "mgr/StringParam.h" + + +Mgr::StringParam::StringParam(): + QueryParam(QueryParam::ptString), str() +{ +} + +Mgr::StringParam::StringParam(const String& aString): + QueryParam(QueryParam::ptString), str(aString) +{ +} + +void +Mgr::StringParam::pack(Ipc::TypedMsgHdr& msg) const +{ + msg.putPod(type); + msg.putString(str); +} + +void +Mgr::StringParam::unpackValue(const Ipc::TypedMsgHdr& msg) +{ + msg.getString(str); +} + +const String& +Mgr::StringParam::value() const +{ + return str; +} === added file 'src/mgr/StringParam.h' --- src/mgr/StringParam.h 1970-01-01 00:00:00 +0000 +++ src/mgr/StringParam.h 2011-01-24 13:39:06 +0000 @@ -0,0 +1,35 @@ +/* + * $Id$ + * + * DEBUG: section 16 Cache Manager API + * + */ + +#ifndef SQUID_MGR_STRING_PARAM_H +#define SQUID_MGR_STRING_PARAM_H + +#include "ipc/forward.h" +#include "mgr/forward.h" +#include "mgr/QueryParam.h" +#include "SquidString.h" + + +namespace Mgr +{ + +class StringParam: public QueryParam +{ +public: + StringParam(); + StringParam(const String& aString); + virtual void pack(Ipc::TypedMsgHdr& msg) const; + virtual void unpackValue(const Ipc::TypedMsgHdr& msg); + const String& value() const; + +private: + String str; +}; + +} // namespace Mgr + +#endif /* SQUID_MGR_STRING_PARAM_H */ === modified file 'src/mgr/forward.h' --- src/mgr/forward.h 2010-10-28 18:52:59 +0000 +++ src/mgr/forward.h 2011-01-24 13:39:06 +0000 @@ -3,31 +3,33 @@ * * DEBUG: section 16 Cache Manager API * */ #ifndef SQUID_MGR_FORWARD_H #define SQUID_MGR_FORWARD_H #include "RefCount.h" namespace Mgr { class Action; class ActionCreator; class ActionProfile; class ActionWriter; class Command; class Request; class Response; +class QueryParam; +class QueryParams; typedef RefCount ActionPointer; typedef RefCount ActionProfilePointer; typedef RefCount ActionCreatorPointer; typedef RefCount CommandPointer; typedef ActionPointer (ClassActionCreationHandler)(const CommandPointer &cmd); } // namespace Mgr #endif /* SQUID_MGR_FORWARD_H */ === modified file 'tools/cachemgr.cc' --- tools/cachemgr.cc 2010-11-01 05:44:28 +0000 +++ tools/cachemgr.cc 2011-01-26 15:48:34 +0000 @@ -115,40 +115,42 @@ #endif #if HAVE_FNMATCH_H extern "C" { #include } #endif #ifndef DEFAULT_CACHEMGR_CONFIG #define DEFAULT_CACHEMGR_CONFIG "/etc/squid/cachemgr.conf" #endif typedef struct { char *server; char *hostname; int port; char *action; char *user_name; char *passwd; char *pub_auth; + char *workers; + char *processes; } cachemgr_request; /* * Static variables and constants */ static const time_t passwd_ttl = 60 * 60 * 3; /* in sec */ static const char *script_name = "/cgi-bin/cachemgr.cgi"; static const char *progname = NULL; static time_t now; /* * Function prototypes */ static const char *safe_str(const char *str); static const char *xstrtok(char **str, char del); static void print_trailer(void); static void auth_html(const char *host, int port, const char *user_name); static void error_html(const char *msg); static char *menu_url(cachemgr_request * req, const char *action); static int parse_status_line(const char *sline, const char **statusStr); @@ -822,46 +824,48 @@ #else if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) { #endif snprintf(buf, 1024, "socket: %s\n", xstrerror()); error_html(buf); return 1; } if (connect(s, AI->ai_addr, AI->ai_addrlen) < 0) { snprintf(buf, 1024, "connect %s: %s\n", S.ToURL(ipbuf,MAX_IPSTRLEN), xstrerror()); error_html(buf); S.FreeAddrInfo(AI); return 1; } S.FreeAddrInfo(AI); l = snprintf(buf, sizeof(buf), - "GET cache_object://%s/%s HTTP/1.0\r\n" + "GET cache_object://%s/%s%s%s HTTP/1.0\r\n" "Accept: */*\r\n" "%s" /* Authentication info or nothing */ "\r\n", req->hostname, req->action, + req->workers? "?workers=" : (req->processes ? "?processes=" : ""), + req->workers? req->workers : (req->processes ? req->processes: ""), make_auth_header(req)); if (write(s, buf, l) < 0) { fprintf(stderr,"ERROR: (%d) writing request: '%s'\n", errno, buf); } else { debug("wrote request: '%s'\n", buf); } return read_reply(s, req); } int main(int argc, char *argv[]) { char *s; cachemgr_request *req; now = time(NULL); #ifdef _SQUID_MSWIN_ Win32SockInit(); atexit(Win32SockCleanup); @@ -992,53 +996,57 @@ *q++ = '\0'; rfc1738_unescape(t); rfc1738_unescape(q); if (0 == strcasecmp(t, "server") && strlen(q)) req->server = xstrdup(q); else if (0 == strcasecmp(t, "host") && strlen(q)) req->hostname = xstrdup(q); else if (0 == strcasecmp(t, "port") && strlen(q)) req->port = atoi(q); else if (0 == strcasecmp(t, "user_name") && strlen(q)) req->user_name = xstrdup(q); else if (0 == strcasecmp(t, "passwd") && strlen(q)) req->passwd = xstrdup(q); else if (0 == strcasecmp(t, "auth") && strlen(q)) req->pub_auth = xstrdup(q), decode_pub_auth(req); else if (0 == strcasecmp(t, "operation")) req->action = xstrdup(q); + else if(0 == strcasecmp(t, "workers") && strlen(q)) + req->workers = xstrdup(q); + else if(0 == strcasecmp(t, "processes") && strlen(q)) + req->processes = xstrdup(q); } if (req->server && !req->hostname) { char *p; req->hostname = strtok(req->server, ":"); if ((p = strtok(NULL, ":"))) req->port = atoi(p); } make_pub_auth(req); - debug("cmgr: got req: host: '%s' port: %d uname: '%s' passwd: '%s' auth: '%s' oper: '%s'\n", - safe_str(req->hostname), req->port, safe_str(req->user_name), safe_str(req->passwd), safe_str(req->pub_auth), safe_str(req->action)); + debug("cmgr: got req: host: '%s' port: %d uname: '%s' passwd: '%s' auth: '%s' oper: '%s' workers: '%s' processes: '%s'\n", + safe_str(req->hostname), req->port, safe_str(req->user_name), safe_str(req->passwd), safe_str(req->pub_auth), safe_str(req->action), safe_str(req->workers), safe_str(req->processes)); return req; } /* Routines to support authentication */ /* * Encodes auth info into a "public" form. * Currently no powerful encryption is used. */ static void make_pub_auth(cachemgr_request * req) { static char buf[1024]; safe_free(req->pub_auth); debug("cmgr: encoding for pub...\n"); if (!req->passwd || !strlen(req->passwd)) return;