5 * DEBUG: section 16 Cache Manager Objects
6 * AUTHOR: Duane Wessels
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
36 #include "CacheManager.h"
37 #include "errorpage.h"
38 #include "HttpReply.h"
39 #include "HttpRequest.h"
42 #include "SquidTime.h"
47 \defgroup CacheManagerInternal Cache Manager Internals
48 \ingroup CacheManagerAPI
51 /// \ingroup CacheManagerInternal
52 #define MGR_PASSWD_SZ 128
56 \ingroup CacheManagerInternals
57 * Constructor. Its purpose is to register internal commands
59 CacheManager::CacheManager()
61 registerAction(new OfflineToggleAction
);
62 registerAction(new ShutdownAction
);
63 registerAction(new ReconfigureAction
);
64 registerAction(new MenuAction(this));
68 \ingroup CacheManagerAPI
69 * Registers a C-style action, which is implemented as a pointer to a function
70 * taking as argument a pointer to a StoreEntry and returning void.
71 * Implemented via CacheManagerActionLegacy.
74 CacheManager::registerAction(char const * action
, char const * desc
, OBJH
* handler
, int pw_req_flag
, int atomic
)
76 debugs(16, 3, "CacheManager::registerAction: registering legacy " << action
);
77 if (findAction(action
) != NULL
) {
78 debugs(16, 2, "CacheManager::registerAction: Duplicate '" << action
<< "'. Skipping.");
81 registerAction(new CacheManagerActionLegacy(action
,desc
,pw_req_flag
,atomic
,handler
));
85 \ingroup CacheManagerAPI
86 * Registers a C++-style action, via a poiner to a subclass of
87 * a CacheManagerAction object, whose run() method will be invoked when
88 * CacheManager identifies that the user has requested the action.
91 CacheManager::registerAction(CacheManagerAction
*anAction
)
93 char *action
= anAction
->action
;
94 if (findAction(action
) != NULL
) {
95 debugs(16, 2, "CacheManager::registerAction: Duplicate '" << action
<< "'. Skipping.");
99 assert (strstr (" ", action
) == NULL
);
101 ActionsList
+= anAction
;
103 debugs(16, 3, "CacheManager::registerAction: registered " << action
);
108 \ingroup CacheManagerInternal
109 * Locates an action in the actions registry ActionsList.
110 \retval NULL if Action not found
111 \retval CacheManagerAction* if the action was found
114 CacheManager::findAction(char const * action
)
116 CacheManagerActionList::iterator a
;
118 debugs(16, 5, "CacheManager::findAction: looking for action " << action
);
119 for ( a
= ActionsList
.begin(); a
!= ActionsList
.end(); a
++) {
120 if (0 == strcmp((*a
)->action
, action
)) {
121 debugs(16, 6, " found");
126 debugs(16, 6, "Action not found.");
131 \ingroup CacheManagerInternal
132 * define whether the URL is a cache-manager URL and parse the action
133 * requested by the user. Checks via CacheManager::ActionProtection() that the
134 * item is accessible by the user.
135 \retval CacheManager::cachemgrStateData state object for the following handling
136 \retval NULL if the action can't be found or can't be accessed by the user
138 CacheManager::cachemgrStateData
*
139 CacheManager::ParseUrl(const char *url
)
142 LOCAL_ARRAY(char, host
, MAX_URL
);
143 LOCAL_ARRAY(char, request
, MAX_URL
);
144 LOCAL_ARRAY(char, password
, MAX_URL
);
145 CacheManagerAction
*a
;
146 cachemgrStateData
*mgr
= NULL
;
148 t
= sscanf(url
, "cache_object://%[^/]/%[^@]@%s", host
, request
, password
);
151 xstrncpy(request
, "menu", MAX_URL
);
154 * emx's sscanf insists of returning 2 because it sets request
157 } else if (request
[0] == '\0') {
158 xstrncpy(request
, "menu", MAX_URL
);
161 } else if ((a
= findAction(request
)) == NULL
) {
162 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' not found");
165 prot
= ActionProtection(a
);
167 if (!strcmp(prot
, "disabled") || !strcmp(prot
, "hidden")) {
168 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' is " << prot
);
173 /* set absent entries to NULL so we can test if they are present later */
174 mgr
= (cachemgrStateData
*)xcalloc(1, sizeof(cachemgrStateData
));
176 mgr
->user_name
= NULL
;
178 mgr
->passwd
= t
== 3 ? xstrdup(password
) : NULL
;
180 mgr
->action
= xstrdup(request
);
185 /// \ingroup CacheManagerInternal
187 \ingroup CacheManagerInternal
188 * Decodes the headers needed to perform user authentication and fills
189 * the details into the cachemgrStateData argument
192 CacheManager::ParseHeaders(cachemgrStateData
* mgr
, const HttpRequest
* request
)
194 const char *basic_cookie
; /* base 64 _decoded_ user:passwd pair */
195 const char *passwd_del
;
196 assert(mgr
&& request
);
197 basic_cookie
= request
->header
.getAuth(HDR_AUTHORIZATION
, "Basic");
202 if (!(passwd_del
= strchr(basic_cookie
, ':'))) {
203 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie
<< "'");
207 /* found user:password pair, reset old values */
208 safe_free(mgr
->user_name
);
210 safe_free(mgr
->passwd
);
212 mgr
->user_name
= xstrdup(basic_cookie
);
214 mgr
->user_name
[passwd_del
- basic_cookie
] = '\0';
216 mgr
->passwd
= xstrdup(passwd_del
+ 1);
218 /* warning: this prints decoded password which maybe not what you want to do @?@ @?@ */
219 debugs(16, 9, "CacheManager::ParseHeaders: got user: '" << mgr
->user_name
<< "' passwd: '" << mgr
->passwd
<< "'");
223 \ingroup CacheManagerInternal
225 \retval 0 if mgr->password is good or "none"
226 \retval 1 if mgr->password is "disable"
227 \retval !0 if mgr->password does not match configured password
230 CacheManager::CheckPassword(cachemgrStateData
* mgr
)
232 char *pwd
= PasswdGet(Config
.passwd_list
, mgr
->action
);
233 CacheManagerAction
*a
= findAction(mgr
->action
);
235 debugs(16, 4, "CacheManager::CheckPassword for action " << mgr
->action
);
239 return a
->flags
.pw_req
;
241 if (strcmp(pwd
, "disable") == 0)
244 if (strcmp(pwd
, "none") == 0)
250 return strcmp(pwd
, mgr
->passwd
);
253 /// \ingroup CacheManagerInternal
255 CacheManager::StateFree(cachemgrStateData
* mgr
)
257 safe_free(mgr
->action
);
258 safe_free(mgr
->user_name
);
259 safe_free(mgr
->passwd
);
260 mgr
->entry
->unlock();
265 \ingroup CacheManagerAPI
266 * Main entry point in the Cache Manager's activity. Gets called as part
267 * of the forward chain if the right URL is detected there. Initiates
268 * all needed internal work and renders the response.
271 CacheManager::Start(int fd
, HttpRequest
* request
, StoreEntry
* entry
)
273 cachemgrStateData
*mgr
= NULL
;
274 ErrorState
*err
= NULL
;
275 CacheManagerAction
*a
;
276 debugs(16, 3, "CacheManager::Start: '" << entry
->url() << "'" );
278 if ((mgr
= ParseUrl(entry
->url())) == NULL
) {
279 err
= errorCon(ERR_INVALID_URL
, HTTP_NOT_FOUND
, request
);
280 err
->url
= xstrdup(entry
->url());
281 errorAppendEntry(entry
, err
);
282 entry
->expires
= squid_curtime
;
289 entry
->expires
= squid_curtime
;
291 debugs(16, 5, "CacheManager: " << fd_table
[fd
].ipaddr
<< " requesting '" << mgr
->action
<< "'");
293 /* get additional info from request headers */
294 ParseHeaders(mgr
, request
);
298 if (CheckPassword(mgr
) != 0) {
299 /* build error message */
302 err
= errorCon(ERR_CACHE_MGR_ACCESS_DENIED
, HTTP_UNAUTHORIZED
, request
);
303 /* warn if user specified incorrect password */
306 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
307 (mgr
->user_name
? mgr
->user_name
: "<unknown>") << "@" <<
308 fd_table
[fd
].ipaddr
<< ": incorrect password for '" <<
309 mgr
->action
<< "'" );
311 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
312 (mgr
->user_name
? mgr
->user_name
: "<unknown>") << "@" <<
313 fd_table
[fd
].ipaddr
<< ": password needed for '" <<
314 mgr
->action
<< "'" );
316 rep
= err
->BuildHttpReply();
321 * add Authenticate header, use 'action' as a realm because
322 * password depends on action
324 rep
->header
.putAuth("Basic", mgr
->action
);
326 /* store the reply */
327 entry
->replaceHttpReply(rep
);
329 entry
->expires
= squid_curtime
;
338 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
339 (mgr
->user_name
? mgr
->user_name
: "<unknown>") << "@" <<
340 fd_table
[fd
].ipaddr
<< " requesting '" <<
341 mgr
->action
<< "'" );
342 /* retrieve object requested */
343 a
= findAction(mgr
->action
);
349 HttpVersion
version(1,0);
350 HttpReply
*rep
= new HttpReply
;
351 rep
->setHeaders(version
,
356 squid_curtime
, /* LMT */
358 entry
->replaceHttpReply(rep
);
371 /// \ingroup CacheManagerInternal
372 void CacheManager::ShutdownAction::run(StoreEntry
*sentry
)
374 debugs(16, DBG_CRITICAL
, "Shutdown by Cache Manager command.");
377 /// \ingroup CacheManagerInternal
378 CacheManager::ShutdownAction::ShutdownAction() : CacheManagerAction("shutdown","Shut Down the Squid Process", 1, 1) { }
380 /// \ingroup CacheManagerInternal
382 CacheManager::ReconfigureAction::run(StoreEntry
* sentry
)
384 debugs(16, DBG_IMPORTANT
, "Reconfigure by Cache Manager command.");
385 storeAppendPrintf(sentry
, "Reconfiguring Squid Process ....");
388 /// \ingroup CacheManagerInternal
389 CacheManager::ReconfigureAction::ReconfigureAction() : CacheManagerAction("reconfigure","Reconfigure Squid", 1, 1) { }
391 /// \ingroup CacheManagerInternal
393 CacheManager::OfflineToggleAction::run(StoreEntry
* sentry
)
395 Config
.onoff
.offline
= !Config
.onoff
.offline
;
396 debugs(16, DBG_IMPORTANT
, "offline_mode now " << (Config
.onoff
.offline
? "ON" : "OFF") << " by Cache Manager request.");
398 storeAppendPrintf(sentry
, "offline_mode is now %s\n",
399 Config
.onoff
.offline
? "ON" : "OFF");
401 /// \ingroup CacheManagerInternal
402 CacheManager::OfflineToggleAction::OfflineToggleAction() : CacheManagerAction ("offline_toggle", "Toggle offline_mode setting", 1, 1) { }
405 \ingroup CacheManagerInternal
406 * Renders the protection level text for an action.
407 * Also doubles as a check for the protection level.
410 CacheManager::ActionProtection(const CacheManagerAction
* at
)
414 pwd
= PasswdGet(Config
.passwd_list
, at
->action
);
417 return at
->flags
.pw_req
? "hidden" : "public";
419 if (!strcmp(pwd
, "disable"))
422 if (strcmp(pwd
, "none") == 0)
428 /// \ingroup CacheManagerInternal
430 CacheManager::MenuAction::run(StoreEntry
* sentry
)
432 CacheManagerActionList::iterator a
;
434 debugs(16, 4, "CacheManager::MenuCommand invoked");
435 for (a
= cmgr
->ActionsList
.begin(); a
!= cmgr
->ActionsList
.end(); ++a
) {
436 debugs(16, 5, " showing action " << (*a
)->action
);
437 storeAppendPrintf(sentry
, " %-22s\t%-32s\t%s\n",
438 (*a
)->action
, (*a
)->desc
, cmgr
->ActionProtection(*a
));
441 /// \ingroup CacheManagerInternal
442 CacheManager::MenuAction::MenuAction(CacheManager
*aMgr
) : CacheManagerAction ("menu", "Cache Manager Menu", 0, 1), cmgr(aMgr
) { }
445 \ingroup CacheManagerInternal
446 * gets from the global Config the password the user would need to supply
447 * for the action she queried
450 CacheManager::PasswdGet(cachemgr_passwd
* a
, const char *action
)
455 for (w
= a
->actions
; w
!= NULL
; w
= w
->next
) {
456 if (0 == strcmp(w
->key
, action
))
459 if (0 == strcmp(w
->key
, "all"))
469 CacheManager
* CacheManager::instance
=0;
472 \ingroup CacheManagerAPI
473 * Singleton accessor method.
476 CacheManager::GetInstance()
479 debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up");
480 instance
= new CacheManager
;
486 /// \ingroup CacheManagerInternal
487 void CacheManagerActionLegacy::run(StoreEntry
*sentry
)
491 /// \ingroup CacheManagerInternal
492 CacheManagerAction::CacheManagerAction(char const *anAction
, char const *aDesc
, unsigned int isPwReq
, unsigned int isAtomic
)
494 flags
.pw_req
= isPwReq
;
495 flags
.atomic
= isAtomic
;
496 action
= xstrdup (anAction
);
497 desc
= xstrdup (aDesc
);
499 /// \ingroup CacheManagerInternal
500 CacheManagerAction::~CacheManagerAction()
506 /// \ingroup CacheManagerInternal
507 CacheManagerActionLegacy::CacheManagerActionLegacy(char const *anAction
, char const *aDesc
, unsigned int isPwReq
, unsigned int isAtomic
, OBJH
*aHandler
) : CacheManagerAction(anAction
, aDesc
, isPwReq
, isAtomic
), handler(aHandler
)