3 * $Id: cache_manager.cc,v 1.49 2008/02/26 21:49:34 amosjeffries Exp $
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 registerAction(new CacheManagerActionLegacy(action
,desc
,pw_req_flag
,atomic
,handler
));
81 \ingroup CacheManagerAPI
82 * Registers a C++-style action, via a poiner to a subclass of
83 * a CacheManagerAction object, whose run() method will be invoked when
84 * CacheManager identifies that the user has requested the action.
87 CacheManager::registerAction(CacheManagerAction
*anAction
)
89 char *action
= anAction
->action
;
90 if (findAction(action
) != NULL
) {
91 debugs(16, 2, "CacheManager::registerAction: Duplicate '" << action
<< "'. Skipping.");
95 assert (strstr (" ", action
) == NULL
);
97 ActionsList
+= anAction
;
99 debugs(16, 3, "CacheManager::registerAction: registered " << action
);
104 \ingroup CacheManagerInternal
105 * Locates an action in the actions registry ActionsList.
106 \retval NULL if Action not found
107 \retval CacheManagerAction* if the action was found
110 CacheManager::findAction(char const * action
)
112 CacheManagerActionList::iterator a
;
114 debugs(16, 5, "CacheManager::findAction: looking for action " << action
);
115 for ( a
= ActionsList
.begin(); a
!= ActionsList
.end(); a
++) {
116 if (0 == strcmp((*a
)->action
, action
)) {
117 debugs(16, 6, " found");
122 debugs(16, 6, "Action not found.");
127 \ingroup CacheManagerInternal
128 * define whether the URL is a cache-manager URL and parse the action
129 * requested by the user. Checks via CacheManager::ActionProtection() that the
130 * item is accessible by the user.
131 \retval CacheManager::cachemgrStateData state object for the following handling
132 \retval NULL if the action can't be found or can't be accessed by the user
134 CacheManager::cachemgrStateData
*
135 CacheManager::ParseUrl(const char *url
)
138 LOCAL_ARRAY(char, host
, MAX_URL
);
139 LOCAL_ARRAY(char, request
, MAX_URL
);
140 LOCAL_ARRAY(char, password
, MAX_URL
);
141 CacheManagerAction
*a
;
142 cachemgrStateData
*mgr
= NULL
;
144 t
= sscanf(url
, "cache_object://%[^/]/%[^@]@%s", host
, request
, password
);
147 xstrncpy(request
, "menu", MAX_URL
);
150 * emx's sscanf insists of returning 2 because it sets request
153 } else if (request
[0] == '\0') {
154 xstrncpy(request
, "menu", MAX_URL
);
157 } else if ((a
= findAction(request
)) == NULL
) {
158 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' not found");
161 prot
= ActionProtection(a
);
163 if (!strcmp(prot
, "disabled") || !strcmp(prot
, "hidden")) {
164 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseUrl: action '" << request
<< "' is " << prot
);
169 /* set absent entries to NULL so we can test if they are present later */
170 mgr
= (cachemgrStateData
*)xcalloc(1, sizeof(cachemgrStateData
));
172 mgr
->user_name
= NULL
;
174 mgr
->passwd
= t
== 3 ? xstrdup(password
) : NULL
;
176 mgr
->action
= xstrdup(request
);
181 /// \ingroup CacheManagerInternal
183 \ingroup CacheManagerInternal
184 * Decodes the headers needed to perform user authentication and fills
185 * the details into the cachemgrStateData argument
188 CacheManager::ParseHeaders(cachemgrStateData
* mgr
, const HttpRequest
* request
)
190 const char *basic_cookie
; /* base 64 _decoded_ user:passwd pair */
191 const char *passwd_del
;
192 assert(mgr
&& request
);
193 basic_cookie
= request
->header
.getAuth(HDR_AUTHORIZATION
, "Basic");
198 if (!(passwd_del
= strchr(basic_cookie
, ':'))) {
199 debugs(16, DBG_IMPORTANT
, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie
<< "'");
203 /* found user:password pair, reset old values */
204 safe_free(mgr
->user_name
);
206 safe_free(mgr
->passwd
);
208 mgr
->user_name
= xstrdup(basic_cookie
);
210 mgr
->user_name
[passwd_del
- basic_cookie
] = '\0';
212 mgr
->passwd
= xstrdup(passwd_del
+ 1);
214 /* warning: this prints decoded password which maybe not what you want to do @?@ @?@ */
215 debugs(16, 9, "CacheManager::ParseHeaders: got user: '" << mgr
->user_name
<< "' passwd: '" << mgr
->passwd
<< "'");
219 \ingroup CacheManagerInternal
221 \retval 0 if mgr->password is good or "none"
222 \retval 1 if mgr->password is "disable"
223 \retval !0 if mgr->password does not match configured password
226 CacheManager::CheckPassword(cachemgrStateData
* mgr
)
228 char *pwd
= PasswdGet(Config
.passwd_list
, mgr
->action
);
229 CacheManagerAction
*a
= findAction(mgr
->action
);
231 debugs(16, 4, "CacheManager::CheckPassword for action " << mgr
->action
);
235 return a
->flags
.pw_req
;
237 if (strcmp(pwd
, "disable") == 0)
240 if (strcmp(pwd
, "none") == 0)
246 return strcmp(pwd
, mgr
->passwd
);
249 /// \ingroup CacheManagerInternal
251 CacheManager::StateFree(cachemgrStateData
* mgr
)
253 safe_free(mgr
->action
);
254 safe_free(mgr
->user_name
);
255 safe_free(mgr
->passwd
);
256 mgr
->entry
->unlock();
261 \ingroup CacheManagerAPI
262 * Main entry point in the Cache Manager's activity. Gets called as part
263 * of the forward chain if the right URL is detected there. Initiates
264 * all needed internal work and renders the response.
267 CacheManager::Start(int fd
, HttpRequest
* request
, StoreEntry
* entry
)
269 cachemgrStateData
*mgr
= NULL
;
270 ErrorState
*err
= NULL
;
271 CacheManagerAction
*a
;
272 debugs(16, 3, "CacheManager::Start: '" << entry
->url() << "'" );
274 if ((mgr
= ParseUrl(entry
->url())) == NULL
) {
275 err
= errorCon(ERR_INVALID_URL
, HTTP_NOT_FOUND
, request
);
276 err
->url
= xstrdup(entry
->url());
277 errorAppendEntry(entry
, err
);
278 entry
->expires
= squid_curtime
;
285 entry
->expires
= squid_curtime
;
287 debugs(16, 5, "CacheManager: " << fd_table
[fd
].ipaddr
<< " requesting '" << mgr
->action
<< "'");
289 /* get additional info from request headers */
290 ParseHeaders(mgr
, request
);
294 if (CheckPassword(mgr
) != 0) {
295 /* build error message */
298 err
= errorCon(ERR_CACHE_MGR_ACCESS_DENIED
, HTTP_UNAUTHORIZED
, request
);
299 /* warn if user specified incorrect password */
302 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
303 (mgr
->user_name
? mgr
->user_name
: "<unknown>") << "@" <<
304 fd_table
[fd
].ipaddr
<< ": incorrect password for '" <<
305 mgr
->action
<< "'" );
307 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
308 (mgr
->user_name
? mgr
->user_name
: "<unknown>") << "@" <<
309 fd_table
[fd
].ipaddr
<< ": password needed for '" <<
310 mgr
->action
<< "'" );
312 rep
= err
->BuildHttpReply();
317 * add Authenticate header, use 'action' as a realm because
318 * password depends on action
320 rep
->header
.putAuth("Basic", mgr
->action
);
322 /* store the reply */
323 entry
->replaceHttpReply(rep
);
325 entry
->expires
= squid_curtime
;
334 debugs(16, DBG_IMPORTANT
, "CacheManager: " <<
335 (mgr
->user_name
? mgr
->user_name
: "<unknown>") << "@" <<
336 fd_table
[fd
].ipaddr
<< " requesting '" <<
337 mgr
->action
<< "'" );
338 /* retrieve object requested */
339 a
= findAction(mgr
->action
);
345 HttpVersion
version(1,0);
346 HttpReply
*rep
= new HttpReply
;
347 rep
->setHeaders(version
,
352 squid_curtime
, /* LMT */
354 entry
->replaceHttpReply(rep
);
367 /// \ingroup CacheManagerInternal
368 void CacheManager::ShutdownAction::run(StoreEntry
*sentry
)
370 debugs(16, DBG_CRITICAL
, "Shutdown by Cache Manager command.");
373 /// \ingroup CacheManagerInternal
374 CacheManager::ShutdownAction::ShutdownAction() : CacheManagerAction("shutdown","Shut Down the Squid Process", 1, 1) { }
376 /// \ingroup CacheManagerInternal
378 CacheManager::ReconfigureAction::run(StoreEntry
* sentry
)
380 debugs(16, DBG_IMPORTANT
, "Reconfigure by Cache Manager command.");
381 storeAppendPrintf(sentry
, "Reconfiguring Squid Process ....");
384 /// \ingroup CacheManagerInternal
385 CacheManager::ReconfigureAction::ReconfigureAction() : CacheManagerAction("reconfigure","Reconfigure Squid", 1, 1) { }
387 /// \ingroup CacheManagerInternal
389 CacheManager::OfflineToggleAction::run(StoreEntry
* sentry
)
391 Config
.onoff
.offline
= !Config
.onoff
.offline
;
392 debugs(16, DBG_IMPORTANT
, "offline_mode now " << (Config
.onoff
.offline
? "ON" : "OFF") << " by Cache Manager request.");
394 storeAppendPrintf(sentry
, "offline_mode is now %s\n",
395 Config
.onoff
.offline
? "ON" : "OFF");
397 /// \ingroup CacheManagerInternal
398 CacheManager::OfflineToggleAction::OfflineToggleAction() : CacheManagerAction ("offline_toggle", "Toggle offline_mode setting", 1, 1) { }
401 \ingroup CacheManagerInternal
402 * Renders the protection level text for an action.
403 * Also doubles as a check for the protection level.
406 CacheManager::ActionProtection(const CacheManagerAction
* at
)
410 pwd
= PasswdGet(Config
.passwd_list
, at
->action
);
413 return at
->flags
.pw_req
? "hidden" : "public";
415 if (!strcmp(pwd
, "disable"))
418 if (strcmp(pwd
, "none") == 0)
424 /// \ingroup CacheManagerInternal
426 CacheManager::MenuAction::run(StoreEntry
* sentry
)
428 CacheManagerActionList::iterator a
;
430 debugs(16, 4, "CacheManager::MenuCommand invoked");
431 for (a
= cmgr
->ActionsList
.begin(); a
!= cmgr
->ActionsList
.end(); ++a
) {
432 debugs(16, 5, " showing action " << (*a
)->action
);
433 storeAppendPrintf(sentry
, " %-22s\t%-32s\t%s\n",
434 (*a
)->action
, (*a
)->desc
, cmgr
->ActionProtection(*a
));
437 /// \ingroup CacheManagerInternal
438 CacheManager::MenuAction::MenuAction(CacheManager
*aMgr
) : CacheManagerAction ("menu", "Cache Manager Menu", 0, 1), cmgr(aMgr
) { }
441 \ingroup CacheManagerInternal
442 * gets from the global Config the password the user would need to supply
443 * for the action she queried
446 CacheManager::PasswdGet(cachemgr_passwd
* a
, const char *action
)
451 for (w
= a
->actions
; w
!= NULL
; w
= w
->next
) {
452 if (0 == strcmp(w
->key
, action
))
455 if (0 == strcmp(w
->key
, "all"))
465 CacheManager
* CacheManager::instance
=0;
468 \ingroup CacheManagerAPI
469 * Singleton accessor method.
472 CacheManager::GetInstance() {
474 debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up");
475 instance
= new CacheManager
;
481 /// \ingroup CacheManagerInternal
482 void CacheManagerActionLegacy::run(StoreEntry
*sentry
)
486 /// \ingroup CacheManagerInternal
487 CacheManagerAction::CacheManagerAction(char const *anAction
, char const *aDesc
, unsigned int isPwReq
, unsigned int isAtomic
)
489 flags
.pw_req
= isPwReq
;
490 flags
.atomic
= isAtomic
;
491 action
= xstrdup (anAction
);
492 desc
= xstrdup (aDesc
);
494 /// \ingroup CacheManagerInternal
495 CacheManagerAction::~CacheManagerAction()
501 /// \ingroup CacheManagerInternal
502 CacheManagerActionLegacy::CacheManagerActionLegacy(char const *anAction
, char const *aDesc
, unsigned int isPwReq
, unsigned int isAtomic
, OBJH
*aHandler
) : CacheManagerAction(anAction
, aDesc
, isPwReq
, isAtomic
), handler(aHandler
)