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