]> git.ipfire.org Git - thirdparty/squid.git/blame - src/cache_manager.cc
Raised some noncritical debug messages' debug-level.
[thirdparty/squid.git] / src / cache_manager.cc
CommitLineData
22f3fd98 1
2/*
262a0e14 3 * $Id$
22f3fd98 4 *
5 * DEBUG: section 16 Cache Manager Objects
6 * AUTHOR: Duane Wessels
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
22f3fd98 10 *
2b6662ba 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.
22f3fd98 19 *
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.
26ac0430 24 *
22f3fd98 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.
26ac0430 29 *
22f3fd98 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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
22f3fd98 34 */
35
62ee09ca 36#include "CacheManager.h"
aa839030 37#include "errorpage.h"
528b2c61 38#include "HttpReply.h"
39#include "HttpRequest.h"
e6ccf245 40#include "Store.h"
528b2c61 41#include "fde.h"
985c86bc 42#include "SquidTime.h"
d295d770 43#include "wordlist.h"
3e1da049 44#include "Debug.h"
22f3fd98 45
63be0a78 46/// \ingroup CacheManagerInternal
22f3fd98 47#define MGR_PASSWD_SZ 128
48
c83f0bd5 49
d154d3ec
FC
50/**
51 \ingroup CacheManagerInternals
52 * Constructor. Its purpose is to register internal commands
53 */
62ee09ca 54CacheManager::CacheManager()
55{
dce760db
FC
56 registerAction(new OfflineToggleAction);
57 registerAction(new ShutdownAction);
58 registerAction(new ReconfigureAction);
afea465e 59 registerAction(new MenuAction(this));
62ee09ca 60}
22f3fd98 61
d154d3ec
FC
62/**
63 \ingroup CacheManagerAPI
64 * Registers a C-style action, which is implemented as a pointer to a function
65 * taking as argument a pointer to a StoreEntry and returning void.
66 * Implemented via CacheManagerActionLegacy.
67 */
22f3fd98 68void
62ee09ca 69CacheManager::registerAction(char const * action, char const * desc, OBJH * handler, int pw_req_flag, int atomic)
22f3fd98 70{
1d8395bd
K
71 debugs(16, 3, "CacheManager::registerAction: registering legacy " << action);
72 registerAction(new CacheManagerActionLegacy(action,desc,pw_req_flag,atomic,handler));
73}
62e76326 74
d154d3ec
FC
75/**
76 \ingroup CacheManagerAPI
26ac0430 77 * Registers a C++-style action, via a poiner to a subclass of
d154d3ec
FC
78 * a CacheManagerAction object, whose run() method will be invoked when
79 * CacheManager identifies that the user has requested the action.
80 */
1d8395bd
K
81void
82CacheManager::registerAction(CacheManagerAction *anAction)
83{
84 char *action = anAction->action;
62ee09ca 85 if (findAction(action) != NULL) {
03c4599f 86 debugs(16, 2, "CacheManager::registerAction: Duplicate '" << action << "'. Skipping.");
62e76326 87 return;
e45867bd 88 }
62e76326 89
528b2c61 90 assert (strstr (" ", action) == NULL);
62e76326 91
afea465e 92 ActionsList += anAction;
62e76326 93
62ee09ca 94 debugs(16, 3, "CacheManager::registerAction: registered " << action);
95}
96
1d8395bd 97
d154d3ec
FC
98/**
99 \ingroup CacheManagerInternal
100 * Locates an action in the actions registry ActionsList.
101\retval NULL if Action not found
102\retval CacheManagerAction* if the action was found
103 */
62ee09ca 104CacheManagerAction *
105CacheManager::findAction(char const * action)
22f3fd98 106{
03c4599f
K
107 CacheManagerActionList::iterator a;
108
109 debugs(16, 5, "CacheManager::findAction: looking for action " << action);
afea465e 110 for ( a = ActionsList.begin(); a != ActionsList.end(); a++) {
03c4599f
K
111 if (0 == strcmp((*a)->action, action)) {
112 debugs(16, 6, " found");
113 return *a;
114 }
22f3fd98 115 }
62e76326 116
03c4599f 117 debugs(16, 6, "Action not found.");
22f3fd98 118 return NULL;
119}
120
832c08ab
FC
121/**
122 \ingroup CacheManagerInternal
123 * define whether the URL is a cache-manager URL and parse the action
124 * requested by the user. Checks via CacheManager::ActionProtection() that the
125 * item is accessible by the user.
126 \retval CacheManager::cachemgrStateData state object for the following handling
127 \retval NULL if the action can't be found or can't be accessed by the user
128 */
bdaaf1a1 129CacheManager::cachemgrStateData *
c83f0bd5 130CacheManager::ParseUrl(const char *url)
22f3fd98 131{
132 int t;
133 LOCAL_ARRAY(char, host, MAX_URL);
134 LOCAL_ARRAY(char, request, MAX_URL);
135 LOCAL_ARRAY(char, password, MAX_URL);
62ee09ca 136 CacheManagerAction *a;
22f3fd98 137 cachemgrStateData *mgr = NULL;
7395afb8 138 const char *prot;
22f3fd98 139 t = sscanf(url, "cache_object://%[^/]/%[^@]@%s", host, request, password);
62e76326 140
22f3fd98 141 if (t < 2) {
62e76326 142 xstrncpy(request, "menu", MAX_URL);
cd377065 143#ifdef _SQUID_OS2_
62e76326 144 /*
145 * emx's sscanf insists of returning 2 because it sets request
146 * to null
147 */
cd377065 148 } else if (request[0] == '\0') {
62e76326 149 xstrncpy(request, "menu", MAX_URL);
cd377065 150#endif
62e76326 151
c83f0bd5 152 } else if ((a = findAction(request)) == NULL) {
3e1da049 153 debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' not found");
62e76326 154 return NULL;
7395afb8 155 } else {
c83f0bd5 156 prot = ActionProtection(a);
62e76326 157
158 if (!strcmp(prot, "disabled") || !strcmp(prot, "hidden")) {
3e1da049 159 debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' is " << prot);
62e76326 160 return NULL;
161 }
22f3fd98 162 }
62e76326 163
63259c34 164 /* set absent entries to NULL so we can test if they are present later */
e6ccf245 165 mgr = (cachemgrStateData *)xcalloc(1, sizeof(cachemgrStateData));
62e76326 166
63259c34 167 mgr->user_name = NULL;
62e76326 168
63259c34 169 mgr->passwd = t == 3 ? xstrdup(password) : NULL;
62e76326 170
22f3fd98 171 mgr->action = xstrdup(request);
62e76326 172
22f3fd98 173 return mgr;
174}
175
63be0a78 176/// \ingroup CacheManagerInternal
832c08ab
FC
177/*
178 \ingroup CacheManagerInternal
179 * Decodes the headers needed to perform user authentication and fills
180 * the details into the cachemgrStateData argument
181 */
c83f0bd5
K
182void
183CacheManager::ParseHeaders(cachemgrStateData * mgr, const HttpRequest * request)
63259c34 184{
2ac76861 185 const char *basic_cookie; /* base 64 _decoded_ user:passwd pair */
63259c34 186 const char *passwd_del;
187 assert(mgr && request);
a9925b40 188 basic_cookie = request->header.getAuth(HDR_AUTHORIZATION, "Basic");
62e76326 189
99edd1c3 190 if (!basic_cookie)
62e76326 191 return;
192
63259c34 193 if (!(passwd_del = strchr(basic_cookie, ':'))) {
3e1da049 194 debugs(16, DBG_IMPORTANT, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie << "'");
62e76326 195 return;
63259c34 196 }
62e76326 197
63259c34 198 /* found user:password pair, reset old values */
199 safe_free(mgr->user_name);
62e76326 200
63259c34 201 safe_free(mgr->passwd);
62e76326 202
2ac76861 203 mgr->user_name = xstrdup(basic_cookie);
62e76326 204
63259c34 205 mgr->user_name[passwd_del - basic_cookie] = '\0';
62e76326 206
2ac76861 207 mgr->passwd = xstrdup(passwd_del + 1);
62e76326 208
63259c34 209 /* warning: this prints decoded password which maybe not what you want to do @?@ @?@ */
c83f0bd5 210 debugs(16, 9, "CacheManager::ParseHeaders: got user: '" << mgr->user_name << "' passwd: '" << mgr->passwd << "'");
63259c34 211}
212
63be0a78 213/**
214 \ingroup CacheManagerInternal
215 *
216 \retval 0 if mgr->password is good or "none"
217 \retval 1 if mgr->password is "disable"
218 \retval !0 if mgr->password does not match configured password
22f3fd98 219 */
c83f0bd5
K
220int
221CacheManager::CheckPassword(cachemgrStateData * mgr)
22f3fd98 222{
c83f0bd5
K
223 char *pwd = PasswdGet(Config.passwd_list, mgr->action);
224 CacheManagerAction *a = findAction(mgr->action);
03c4599f
K
225
226 debugs(16, 4, "CacheManager::CheckPassword for action " << mgr->action);
22f3fd98 227 assert(a != NULL);
62e76326 228
22f3fd98 229 if (pwd == NULL)
62e76326 230 return a->flags.pw_req;
231
22f3fd98 232 if (strcmp(pwd, "disable") == 0)
62e76326 233 return 1;
234
22f3fd98 235 if (strcmp(pwd, "none") == 0)
62e76326 236 return 0;
237
63259c34 238 if (!mgr->passwd)
62e76326 239 return 1;
240
22f3fd98 241 return strcmp(pwd, mgr->passwd);
242}
243
63be0a78 244/// \ingroup CacheManagerInternal
c83f0bd5
K
245void
246CacheManager::StateFree(cachemgrStateData * mgr)
22f3fd98 247{
2ac76861 248 safe_free(mgr->action);
249 safe_free(mgr->user_name);
250 safe_free(mgr->passwd);
97b5e68f 251 mgr->entry->unlock();
2ac76861 252 xfree(mgr);
22f3fd98 253}
254
832c08ab
FC
255/**
256 \ingroup CacheManagerAPI
257 * Main entry point in the Cache Manager's activity. Gets called as part
258 * of the forward chain if the right URL is detected there. Initiates
259 * all needed internal work and renders the response.
260 */
22f3fd98 261void
c83f0bd5 262CacheManager::Start(int fd, HttpRequest * request, StoreEntry * entry)
22f3fd98 263{
264 cachemgrStateData *mgr = NULL;
265 ErrorState *err = NULL;
62ee09ca 266 CacheManagerAction *a;
832c08ab 267 debugs(16, 3, "CacheManager::Start: '" << entry->url() << "'" );
62e76326 268
c83f0bd5 269 if ((mgr = ParseUrl(entry->url())) == NULL) {
2cc81f1f 270 err = errorCon(ERR_INVALID_URL, HTTP_NOT_FOUND, request);
3900307b 271 err->url = xstrdup(entry->url());
62e76326 272 errorAppendEntry(entry, err);
273 entry->expires = squid_curtime;
274 return;
22f3fd98 275 }
62e76326 276
22f3fd98 277 mgr->entry = entry;
34266cde 278
3d0ac046 279 entry->lock();
22f3fd98 280 entry->expires = squid_curtime;
34266cde 281
3e1da049 282 debugs(16, 5, "CacheManager: " << fd_table[fd].ipaddr << " requesting '" << mgr->action << "'");
34266cde 283
63259c34 284 /* get additional info from request headers */
c83f0bd5 285 ParseHeaders(mgr, request);
34266cde 286
22f3fd98 287 /* Check password */
62e76326 288
c83f0bd5 289 if (CheckPassword(mgr) != 0) {
62e76326 290 /* build error message */
076df709 291 ErrorState *errState;
62e76326 292 HttpReply *rep;
076df709 293 errState = errorCon(ERR_CACHE_MGR_ACCESS_DENIED, HTTP_UNAUTHORIZED, request);
62e76326 294 /* warn if user specified incorrect password */
295
296 if (mgr->passwd)
26ac0430
AJ
297 debugs(16, DBG_IMPORTANT, "CacheManager: " <<
298 (mgr->user_name ? mgr->user_name : "<unknown>") << "@" <<
299 fd_table[fd].ipaddr << ": incorrect password for '" <<
bf8fe701 300 mgr->action << "'" );
62e76326 301 else
26ac0430
AJ
302 debugs(16, DBG_IMPORTANT, "CacheManager: " <<
303 (mgr->user_name ? mgr->user_name : "<unknown>") << "@" <<
304 fd_table[fd].ipaddr << ": password needed for '" <<
bf8fe701 305 mgr->action << "'" );
62e76326 306
076df709 307 rep = errState->BuildHttpReply();
62e76326 308
076df709 309 errorStateFree(errState);
62e76326 310
311 /*
312 * add Authenticate header, use 'action' as a realm because
313 * password depends on action
314 */
a9925b40 315 rep->header.putAuth("Basic", mgr->action);
62e76326 316
317 /* store the reply */
db237875 318 entry->replaceHttpReply(rep);
62e76326 319
320 entry->expires = squid_curtime;
321
322 entry->complete();
323
c83f0bd5 324 StateFree(mgr);
62e76326 325
326 return;
22f3fd98 327 }
62e76326 328
26ac0430
AJ
329 debugs(16, DBG_IMPORTANT, "CacheManager: " <<
330 (mgr->user_name ? mgr->user_name : "<unknown>") << "@" <<
331 fd_table[fd].ipaddr << " requesting '" <<
bf8fe701 332 mgr->action << "'" );
22f3fd98 333 /* retrieve object requested */
c83f0bd5 334 a = findAction(mgr->action);
22f3fd98 335 assert(a != NULL);
62e76326 336
3900307b 337 entry->buffer();
62e76326 338
cb69b4c7 339 {
06a5ae20 340 HttpReply *rep = new HttpReply;
11992b6f 341 rep->setHeaders(HTTP_OK, NULL, "text/plain", -1, squid_curtime, squid_curtime);
db237875 342 entry->replaceHttpReply(rep);
cb69b4c7 343 }
62e76326 344
c83f0bd5 345 a->run(entry);
62e76326 346
3900307b 347 entry->flush();
b66315e4 348
349 if (a->flags.atomic)
62e76326 350 entry->complete();
62e76326 351
c83f0bd5 352 StateFree(mgr);
22f3fd98 353}
354
832c08ab 355/// \ingroup CacheManagerInternal
dce760db 356void CacheManager::ShutdownAction::run(StoreEntry *sentry)
22f3fd98 357{
3e1da049 358 debugs(16, DBG_CRITICAL, "Shutdown by Cache Manager command.");
22f3fd98 359 shut_down(0);
360}
832c08ab 361/// \ingroup CacheManagerInternal
dce760db 362CacheManager::ShutdownAction::ShutdownAction() : CacheManagerAction("shutdown","Shut Down the Squid Process", 1, 1) { }
22f3fd98 363
832c08ab 364/// \ingroup CacheManagerInternal
c83f0bd5 365void
dce760db 366CacheManager::ReconfigureAction::run(StoreEntry * sentry)
757a2291 367{
e680134c 368 debugs(16, DBG_IMPORTANT, "Reconfigure by Cache Manager command.");
757a2291
GS
369 storeAppendPrintf(sentry, "Reconfiguring Squid Process ....");
370 reconfigure(SIGHUP);
371}
832c08ab 372/// \ingroup CacheManagerInternal
dce760db 373CacheManager::ReconfigureAction::ReconfigureAction() : CacheManagerAction("reconfigure","Reconfigure Squid", 1, 1) { }
757a2291 374
63be0a78 375/// \ingroup CacheManagerInternal
c83f0bd5 376void
dce760db 377CacheManager::OfflineToggleAction::run(StoreEntry * sentry)
d20b1cd0 378{
379 Config.onoff.offline = !Config.onoff.offline;
3e1da049 380 debugs(16, DBG_IMPORTANT, "offline_mode now " << (Config.onoff.offline ? "ON" : "OFF") << " by Cache Manager request.");
bf8fe701 381
d20b1cd0 382 storeAppendPrintf(sentry, "offline_mode is now %s\n",
62e76326 383 Config.onoff.offline ? "ON" : "OFF");
d20b1cd0 384}
832c08ab 385/// \ingroup CacheManagerInternal
dce760db 386CacheManager::OfflineToggleAction::OfflineToggleAction() : CacheManagerAction ("offline_toggle", "Toggle offline_mode setting", 1, 1) { }
d20b1cd0 387
832c08ab
FC
388/*
389 \ingroup CacheManagerInternal
390 * Renders the protection level text for an action.
391 * Also doubles as a check for the protection level.
832c08ab 392 */
c83f0bd5
K
393const char *
394CacheManager::ActionProtection(const CacheManagerAction * at)
7395afb8 395{
396 char *pwd;
397 assert(at);
c83f0bd5 398 pwd = PasswdGet(Config.passwd_list, at->action);
62e76326 399
7395afb8 400 if (!pwd)
62e76326 401 return at->flags.pw_req ? "hidden" : "public";
402
7395afb8 403 if (!strcmp(pwd, "disable"))
62e76326 404 return "disabled";
405
7395afb8 406 if (strcmp(pwd, "none") == 0)
62e76326 407 return "public";
408
7395afb8 409 return "protected";
410}
411
63be0a78 412/// \ingroup CacheManagerInternal
c83f0bd5 413void
bdaaf1a1 414CacheManager::MenuAction::run(StoreEntry * sentry)
22f3fd98 415{
03c4599f 416 CacheManagerActionList::iterator a;
62e76326 417
03c4599f 418 debugs(16, 4, "CacheManager::MenuCommand invoked");
afea465e 419 for (a = cmgr->ActionsList.begin(); a != cmgr->ActionsList.end(); ++a) {
03c4599f 420 debugs(16, 5, " showing action " << (*a)->action);
e1a88700 421 storeAppendPrintf(sentry, " %-22s\t%-32s\t%s\n",
26ac0430 422 (*a)->action, (*a)->desc, cmgr->ActionProtection(*a));
7395afb8 423 }
22f3fd98 424}
832c08ab 425/// \ingroup CacheManagerInternal
e5d1c7ec 426CacheManager::MenuAction::MenuAction(CacheManager *aMgr) : CacheManagerAction ("menu", "Cache Manager Menu", 0, 1), cmgr(aMgr) { }
22f3fd98 427
832c08ab
FC
428/*
429 \ingroup CacheManagerInternal
430 * gets from the global Config the password the user would need to supply
431 * for the action she queried
432 */
c83f0bd5
K
433char *
434CacheManager::PasswdGet(cachemgr_passwd * a, const char *action)
22f3fd98 435{
436 wordlist *w;
62e76326 437
22f3fd98 438 while (a != NULL) {
62e76326 439 for (w = a->actions; w != NULL; w = w->next) {
440 if (0 == strcmp(w->key, action))
441 return a->passwd;
442
443 if (0 == strcmp(w->key, "all"))
444 return a->passwd;
445 }
446
447 a = a->next;
22f3fd98 448 }
62e76326 449
22f3fd98 450 return NULL;
451}
c83f0bd5
K
452
453CacheManager* CacheManager::instance=0;
454
832c08ab
FC
455/**
456 \ingroup CacheManagerAPI
457 * Singleton accessor method.
458 */
c83f0bd5 459CacheManager*
26ac0430
AJ
460CacheManager::GetInstance()
461{
462 if (instance == 0) {
463 debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up");
464 instance = new CacheManager;
465 }
466 return instance;
c83f0bd5
K
467}
468
469
832c08ab 470/// \ingroup CacheManagerInternal
c83f0bd5
K
471void CacheManagerActionLegacy::run(StoreEntry *sentry)
472{
26ac0430 473 handler(sentry);
c83f0bd5 474}
832c08ab 475/// \ingroup CacheManagerInternal
2f53e904
K
476CacheManagerAction::CacheManagerAction(char const *anAction, char const *aDesc, unsigned int isPwReq, unsigned int isAtomic)
477{
478 flags.pw_req = isPwReq;
479 flags.atomic = isAtomic;
480 action = xstrdup (anAction);
481 desc = xstrdup (aDesc);
482}
832c08ab
FC
483/// \ingroup CacheManagerInternal
484CacheManagerAction::~CacheManagerAction()
485{
2f53e904
K
486 xfree(action);
487 xfree(desc);
488}
489
832c08ab 490/// \ingroup CacheManagerInternal
2f53e904
K
491CacheManagerActionLegacy::CacheManagerActionLegacy(char const *anAction, char const *aDesc, unsigned int isPwReq, unsigned int isAtomic, OBJH *aHandler) : CacheManagerAction(anAction, aDesc, isPwReq, isAtomic), handler(aHandler)
492{
493}