]>
Commit | Line | Data |
---|---|---|
22f3fd98 | 1 | |
2 | /* | |
63be0a78 | 3 | * $Id: cache_manager.cc,v 1.49 2008/02/26 21:49:34 amosjeffries Exp $ |
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. | |
24 | * | |
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. | |
29 | * | |
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" |
22f3fd98 | 44 | |
63be0a78 | 45 | /** |
46 | \defgroup CacheManagerInternal Cache Manager Internals | |
47 | \ingroup CacheManagerAPI | |
48 | */ | |
49 | ||
50 | /// \ingroup CacheManagerInternal | |
22f3fd98 | 51 | #define MGR_PASSWD_SZ 128 |
52 | ||
c83f0bd5 | 53 | |
22f3fd98 | 54 | |
63be0a78 | 55 | /// \ingroup CacheManagerInternal |
03c4599f | 56 | CacheManagerActionList *ActionsList = NULL; |
62ee09ca | 57 | |
58 | CacheManager::CacheManager() | |
59 | { | |
03c4599f K |
60 | if (ActionsList != NULL) |
61 | delete(ActionsList); //TODO: Laaazy. Will be moved to class member | |
62 | ActionsList = new CacheManagerActionList; | |
c83f0bd5 | 63 | registerAction("menu", "This Cachemanager Menu", MenuCommand, 0, 1); |
62ee09ca | 64 | registerAction("shutdown", |
65 | "Shut Down the Squid Process", | |
c83f0bd5 | 66 | ShutdownCommand, 1, 1); |
757a2291 GS |
67 | registerAction("reconfigure", |
68 | "Reconfigure the Squid Process", | |
c83f0bd5 | 69 | ReconfigureCommand, 1, 1); |
62ee09ca | 70 | registerAction("offline_toggle", |
71 | "Toggle offline_mode setting", | |
c83f0bd5 | 72 | OfflineToggleCommand, 1, 1); |
62ee09ca | 73 | } |
22f3fd98 | 74 | |
75 | void | |
62ee09ca | 76 | CacheManager::registerAction(char const * action, char const * desc, OBJH * handler, int pw_req_flag, int atomic) |
22f3fd98 | 77 | { |
c83f0bd5 | 78 | CacheManagerActionLegacy *a; |
62e76326 | 79 | |
62ee09ca | 80 | if (findAction(action) != NULL) { |
03c4599f | 81 | debugs(16, 2, "CacheManager::registerAction: Duplicate '" << action << "'. Skipping."); |
62e76326 | 82 | return; |
e45867bd | 83 | } |
62e76326 | 84 | |
528b2c61 | 85 | assert (strstr (" ", action) == NULL); |
2f53e904 | 86 | a = new CacheManagerActionLegacy(action,desc,pw_req_flag,atomic,handler); |
62e76326 | 87 | |
03c4599f | 88 | *ActionsList += a; |
62e76326 | 89 | |
62ee09ca | 90 | debugs(16, 3, "CacheManager::registerAction: registered " << action); |
91 | } | |
92 | ||
c83f0bd5 | 93 | /// \ingroup CacheManagerInternal |
62ee09ca | 94 | CacheManagerAction * |
95 | CacheManager::findAction(char const * action) | |
22f3fd98 | 96 | { |
03c4599f K |
97 | CacheManagerActionList::iterator a; |
98 | ||
99 | debugs(16, 5, "CacheManager::findAction: looking for action " << action); | |
100 | for ( a = ActionsList->begin(); a != ActionsList->end(); a++) { | |
101 | //debugs(16, 6, " checking against '" << (*a)->action << "'"); | |
102 | if (0 == strcmp((*a)->action, action)) { | |
103 | debugs(16, 6, " found"); | |
104 | return *a; | |
105 | } | |
22f3fd98 | 106 | } |
62e76326 | 107 | |
03c4599f | 108 | debugs(16, 6, "Action not found."); |
22f3fd98 | 109 | return NULL; |
110 | } | |
111 | ||
63be0a78 | 112 | /// \ingroup CacheManagerInternal |
c83f0bd5 K |
113 | cachemgrStateData * |
114 | CacheManager::ParseUrl(const char *url) | |
22f3fd98 | 115 | { |
116 | int t; | |
117 | LOCAL_ARRAY(char, host, MAX_URL); | |
118 | LOCAL_ARRAY(char, request, MAX_URL); | |
119 | LOCAL_ARRAY(char, password, MAX_URL); | |
62ee09ca | 120 | CacheManagerAction *a; |
22f3fd98 | 121 | cachemgrStateData *mgr = NULL; |
7395afb8 | 122 | const char *prot; |
22f3fd98 | 123 | t = sscanf(url, "cache_object://%[^/]/%[^@]@%s", host, request, password); |
62e76326 | 124 | |
22f3fd98 | 125 | if (t < 2) { |
62e76326 | 126 | xstrncpy(request, "menu", MAX_URL); |
cd377065 | 127 | #ifdef _SQUID_OS2_ |
62e76326 | 128 | /* |
129 | * emx's sscanf insists of returning 2 because it sets request | |
130 | * to null | |
131 | */ | |
cd377065 | 132 | } else if (request[0] == '\0') { |
62e76326 | 133 | xstrncpy(request, "menu", MAX_URL); |
cd377065 | 134 | #endif |
62e76326 | 135 | |
c83f0bd5 K |
136 | } else if ((a = findAction(request)) == NULL) { |
137 | debugs(16, 1, "CacheManager::ParseUrl: action '" << request << "' not found"); | |
62e76326 | 138 | return NULL; |
7395afb8 | 139 | } else { |
c83f0bd5 | 140 | prot = ActionProtection(a); |
62e76326 | 141 | |
142 | if (!strcmp(prot, "disabled") || !strcmp(prot, "hidden")) { | |
c83f0bd5 | 143 | debugs(16, 1, "CacheManager::ParseUrl: action '" << request << "' is " << prot); |
62e76326 | 144 | return NULL; |
145 | } | |
22f3fd98 | 146 | } |
62e76326 | 147 | |
63259c34 | 148 | /* set absent entries to NULL so we can test if they are present later */ |
e6ccf245 | 149 | mgr = (cachemgrStateData *)xcalloc(1, sizeof(cachemgrStateData)); |
62e76326 | 150 | |
63259c34 | 151 | mgr->user_name = NULL; |
62e76326 | 152 | |
63259c34 | 153 | mgr->passwd = t == 3 ? xstrdup(password) : NULL; |
62e76326 | 154 | |
22f3fd98 | 155 | mgr->action = xstrdup(request); |
62e76326 | 156 | |
22f3fd98 | 157 | return mgr; |
158 | } | |
159 | ||
63be0a78 | 160 | /// \ingroup CacheManagerInternal |
c83f0bd5 K |
161 | void |
162 | CacheManager::ParseHeaders(cachemgrStateData * mgr, const HttpRequest * request) | |
63259c34 | 163 | { |
2ac76861 | 164 | const char *basic_cookie; /* base 64 _decoded_ user:passwd pair */ |
63259c34 | 165 | const char *passwd_del; |
166 | assert(mgr && request); | |
a9925b40 | 167 | basic_cookie = request->header.getAuth(HDR_AUTHORIZATION, "Basic"); |
62e76326 | 168 | |
99edd1c3 | 169 | if (!basic_cookie) |
62e76326 | 170 | return; |
171 | ||
63259c34 | 172 | if (!(passwd_del = strchr(basic_cookie, ':'))) { |
c83f0bd5 | 173 | debugs(16, 1, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie << "'"); |
62e76326 | 174 | return; |
63259c34 | 175 | } |
62e76326 | 176 | |
63259c34 | 177 | /* found user:password pair, reset old values */ |
178 | safe_free(mgr->user_name); | |
62e76326 | 179 | |
63259c34 | 180 | safe_free(mgr->passwd); |
62e76326 | 181 | |
2ac76861 | 182 | mgr->user_name = xstrdup(basic_cookie); |
62e76326 | 183 | |
63259c34 | 184 | mgr->user_name[passwd_del - basic_cookie] = '\0'; |
62e76326 | 185 | |
2ac76861 | 186 | mgr->passwd = xstrdup(passwd_del + 1); |
62e76326 | 187 | |
63259c34 | 188 | /* warning: this prints decoded password which maybe not what you want to do @?@ @?@ */ |
c83f0bd5 | 189 | debugs(16, 9, "CacheManager::ParseHeaders: got user: '" << mgr->user_name << "' passwd: '" << mgr->passwd << "'"); |
63259c34 | 190 | } |
191 | ||
63be0a78 | 192 | /** |
193 | \ingroup CacheManagerInternal | |
194 | * | |
195 | \retval 0 if mgr->password is good or "none" | |
196 | \retval 1 if mgr->password is "disable" | |
197 | \retval !0 if mgr->password does not match configured password | |
22f3fd98 | 198 | */ |
c83f0bd5 K |
199 | int |
200 | CacheManager::CheckPassword(cachemgrStateData * mgr) | |
22f3fd98 | 201 | { |
c83f0bd5 K |
202 | char *pwd = PasswdGet(Config.passwd_list, mgr->action); |
203 | CacheManagerAction *a = findAction(mgr->action); | |
03c4599f K |
204 | |
205 | debugs(16, 4, "CacheManager::CheckPassword for action " << mgr->action); | |
22f3fd98 | 206 | assert(a != NULL); |
62e76326 | 207 | |
22f3fd98 | 208 | if (pwd == NULL) |
62e76326 | 209 | return a->flags.pw_req; |
210 | ||
22f3fd98 | 211 | if (strcmp(pwd, "disable") == 0) |
62e76326 | 212 | return 1; |
213 | ||
22f3fd98 | 214 | if (strcmp(pwd, "none") == 0) |
62e76326 | 215 | return 0; |
216 | ||
63259c34 | 217 | if (!mgr->passwd) |
62e76326 | 218 | return 1; |
219 | ||
22f3fd98 | 220 | return strcmp(pwd, mgr->passwd); |
221 | } | |
222 | ||
63be0a78 | 223 | /// \ingroup CacheManagerInternal |
c83f0bd5 K |
224 | void |
225 | CacheManager::StateFree(cachemgrStateData * mgr) | |
22f3fd98 | 226 | { |
2ac76861 | 227 | safe_free(mgr->action); |
228 | safe_free(mgr->user_name); | |
229 | safe_free(mgr->passwd); | |
97b5e68f | 230 | mgr->entry->unlock(); |
2ac76861 | 231 | xfree(mgr); |
22f3fd98 | 232 | } |
233 | ||
63be0a78 | 234 | // API |
22f3fd98 | 235 | void |
c83f0bd5 | 236 | CacheManager::Start(int fd, HttpRequest * request, StoreEntry * entry) |
22f3fd98 | 237 | { |
238 | cachemgrStateData *mgr = NULL; | |
239 | ErrorState *err = NULL; | |
62ee09ca | 240 | CacheManagerAction *a; |
bf8fe701 | 241 | debugs(16, 3, "objectcacheStart: '" << entry->url() << "'" ); |
62e76326 | 242 | |
c83f0bd5 | 243 | if ((mgr = ParseUrl(entry->url())) == NULL) { |
2cc81f1f | 244 | err = errorCon(ERR_INVALID_URL, HTTP_NOT_FOUND, request); |
3900307b | 245 | err->url = xstrdup(entry->url()); |
62e76326 | 246 | errorAppendEntry(entry, err); |
247 | entry->expires = squid_curtime; | |
248 | return; | |
22f3fd98 | 249 | } |
62e76326 | 250 | |
22f3fd98 | 251 | mgr->entry = entry; |
34266cde | 252 | |
3d0ac046 | 253 | entry->lock(); |
22f3fd98 | 254 | entry->expires = squid_curtime; |
34266cde | 255 | |
bf8fe701 | 256 | debugs(16, 5, "CACHEMGR: " << fd_table[fd].ipaddr << " requesting '" << mgr->action << "'"); |
34266cde | 257 | |
63259c34 | 258 | /* get additional info from request headers */ |
c83f0bd5 | 259 | ParseHeaders(mgr, request); |
34266cde | 260 | |
22f3fd98 | 261 | /* Check password */ |
62e76326 | 262 | |
c83f0bd5 | 263 | if (CheckPassword(mgr) != 0) { |
62e76326 | 264 | /* build error message */ |
265 | ErrorState *err; | |
266 | HttpReply *rep; | |
2cc81f1f | 267 | err = errorCon(ERR_CACHE_MGR_ACCESS_DENIED, HTTP_UNAUTHORIZED, request); |
62e76326 | 268 | /* warn if user specified incorrect password */ |
269 | ||
270 | if (mgr->passwd) | |
bf8fe701 | 271 | debugs(16, 1, "CACHEMGR: " << |
272 | (mgr->user_name ? mgr->user_name : "<unknown>") << "@" << | |
273 | fd_table[fd].ipaddr << ": incorrect password for '" << | |
274 | mgr->action << "'" ); | |
62e76326 | 275 | else |
bf8fe701 | 276 | debugs(16, 1, "CACHEMGR: " << |
277 | (mgr->user_name ? mgr->user_name : "<unknown>") << "@" << | |
278 | fd_table[fd].ipaddr << ": password needed for '" << | |
279 | mgr->action << "'" ); | |
62e76326 | 280 | |
62e76326 | 281 | rep = errorBuildReply(err); |
282 | ||
283 | errorStateFree(err); | |
284 | ||
285 | /* | |
286 | * add Authenticate header, use 'action' as a realm because | |
287 | * password depends on action | |
288 | */ | |
a9925b40 | 289 | rep->header.putAuth("Basic", mgr->action); |
62e76326 | 290 | |
291 | /* store the reply */ | |
db237875 | 292 | entry->replaceHttpReply(rep); |
62e76326 | 293 | |
294 | entry->expires = squid_curtime; | |
295 | ||
296 | entry->complete(); | |
297 | ||
c83f0bd5 | 298 | StateFree(mgr); |
62e76326 | 299 | |
300 | return; | |
22f3fd98 | 301 | } |
62e76326 | 302 | |
bf8fe701 | 303 | debugs(16, 1, "CACHEMGR: " << |
304 | (mgr->user_name ? mgr->user_name : "<unknown>") << "@" << | |
305 | fd_table[fd].ipaddr << " requesting '" << | |
306 | mgr->action << "'" ); | |
22f3fd98 | 307 | /* retrieve object requested */ |
c83f0bd5 | 308 | a = findAction(mgr->action); |
22f3fd98 | 309 | assert(a != NULL); |
62e76326 | 310 | |
3900307b | 311 | entry->buffer(); |
62e76326 | 312 | |
cb69b4c7 | 313 | { |
450e0c10 | 314 | HttpVersion version(1,0); |
06a5ae20 | 315 | HttpReply *rep = new HttpReply; |
316 | rep->setHeaders(version, | |
317 | HTTP_OK, | |
318 | NULL, | |
319 | "text/plain", | |
320 | -1, /* C-Len */ | |
321 | squid_curtime, /* LMT */ | |
322 | squid_curtime); | |
db237875 | 323 | entry->replaceHttpReply(rep); |
cb69b4c7 | 324 | } |
62e76326 | 325 | |
c83f0bd5 | 326 | a->run(entry); |
62e76326 | 327 | |
3900307b | 328 | entry->flush(); |
b66315e4 | 329 | |
330 | if (a->flags.atomic) | |
62e76326 | 331 | entry->complete(); |
62e76326 | 332 | |
c83f0bd5 | 333 | StateFree(mgr); |
22f3fd98 | 334 | } |
335 | ||
63be0a78 | 336 | /// \ingroup CacheManagerInternal |
c83f0bd5 K |
337 | void |
338 | CacheManager::ShutdownCommand(StoreEntry *unused) | |
339 | { | |
340 | debugs(16, 0, "Shutdown by command."); | |
341 | shut_down(0); | |
342 | } | |
343 | void CacheManagerShutdownAction::run(StoreEntry *sentry) | |
22f3fd98 | 344 | { |
bf8fe701 | 345 | debugs(16, 0, "Shutdown by command."); |
22f3fd98 | 346 | shut_down(0); |
347 | } | |
348 | ||
757a2291 | 349 | /// \ingroup CacheManagerInternal |
c83f0bd5 K |
350 | void |
351 | CacheManager::ReconfigureCommand(StoreEntry * sentry) | |
757a2291 GS |
352 | { |
353 | debug(16, 0) ("Reconfigure by command.\n"); | |
354 | storeAppendPrintf(sentry, "Reconfiguring Squid Process ...."); | |
355 | reconfigure(SIGHUP); | |
356 | } | |
357 | ||
63be0a78 | 358 | /// \ingroup CacheManagerInternal |
c83f0bd5 K |
359 | void |
360 | CacheManager::OfflineToggleCommand(StoreEntry * sentry) | |
d20b1cd0 | 361 | { |
362 | Config.onoff.offline = !Config.onoff.offline; | |
bf8fe701 | 363 | debugs(16, 0, "offline_mode now " << (Config.onoff.offline ? "ON" : "OFF") << "."); |
364 | ||
d20b1cd0 | 365 | storeAppendPrintf(sentry, "offline_mode is now %s\n", |
62e76326 | 366 | Config.onoff.offline ? "ON" : "OFF"); |
d20b1cd0 | 367 | } |
368 | ||
63be0a78 | 369 | /// \ingroup CacheManagerInternal |
c83f0bd5 K |
370 | const char * |
371 | CacheManager::ActionProtection(const CacheManagerAction * at) | |
7395afb8 | 372 | { |
373 | char *pwd; | |
374 | assert(at); | |
c83f0bd5 | 375 | pwd = PasswdGet(Config.passwd_list, at->action); |
62e76326 | 376 | |
7395afb8 | 377 | if (!pwd) |
62e76326 | 378 | return at->flags.pw_req ? "hidden" : "public"; |
379 | ||
7395afb8 | 380 | if (!strcmp(pwd, "disable")) |
62e76326 | 381 | return "disabled"; |
382 | ||
7395afb8 | 383 | if (strcmp(pwd, "none") == 0) |
62e76326 | 384 | return "public"; |
385 | ||
7395afb8 | 386 | return "protected"; |
387 | } | |
388 | ||
63be0a78 | 389 | /// \ingroup CacheManagerInternal |
c83f0bd5 K |
390 | void |
391 | CacheManager::MenuCommand(StoreEntry * sentry) | |
22f3fd98 | 392 | { |
03c4599f | 393 | CacheManagerActionList::iterator a; |
62e76326 | 394 | |
03c4599f K |
395 | debugs(16, 4, "CacheManager::MenuCommand invoked"); |
396 | for (a = ActionsList->begin(); a != ActionsList->end(); ++a) { | |
397 | debugs(16, 5, " showing action " << (*a)->action); | |
e1a88700 | 398 | storeAppendPrintf(sentry, " %-22s\t%-32s\t%s\n", |
03c4599f | 399 | (*a)->action, (*a)->desc, CacheManager::GetInstance()->ActionProtection(*a)); |
7395afb8 | 400 | } |
22f3fd98 | 401 | } |
402 | ||
63be0a78 | 403 | /// \ingroup CacheManagerInternal |
c83f0bd5 K |
404 | char * |
405 | CacheManager::PasswdGet(cachemgr_passwd * a, const char *action) | |
22f3fd98 | 406 | { |
407 | wordlist *w; | |
62e76326 | 408 | |
22f3fd98 | 409 | while (a != NULL) { |
62e76326 | 410 | for (w = a->actions; w != NULL; w = w->next) { |
411 | if (0 == strcmp(w->key, action)) | |
412 | return a->passwd; | |
413 | ||
414 | if (0 == strcmp(w->key, "all")) | |
415 | return a->passwd; | |
416 | } | |
417 | ||
418 | a = a->next; | |
22f3fd98 | 419 | } |
62e76326 | 420 | |
22f3fd98 | 421 | return NULL; |
422 | } | |
c83f0bd5 K |
423 | |
424 | CacheManager* CacheManager::instance=0; | |
425 | ||
426 | CacheManager* | |
427 | CacheManager::GetInstance() { | |
03c4599f K |
428 | if (instance == 0) { |
429 | debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up"); | |
430 | instance = new CacheManager; | |
431 | } | |
432 | return instance; | |
c83f0bd5 K |
433 | } |
434 | ||
435 | ||
436 | void CacheManagerActionLegacy::run(StoreEntry *sentry) | |
437 | { | |
438 | handler(sentry); | |
439 | } | |
440 | ||
2f53e904 K |
441 | CacheManagerAction::CacheManagerAction(char const *anAction, char const *aDesc, unsigned int isPwReq, unsigned int isAtomic) |
442 | { | |
443 | flags.pw_req = isPwReq; | |
444 | flags.atomic = isAtomic; | |
445 | action = xstrdup (anAction); | |
446 | desc = xstrdup (aDesc); | |
447 | } | |
448 | CacheManagerAction::~CacheManagerAction() { | |
449 | xfree(action); | |
450 | xfree(desc); | |
451 | } | |
452 | ||
453 | CacheManagerActionLegacy::CacheManagerActionLegacy(char const *anAction, char const *aDesc, unsigned int isPwReq, unsigned int isAtomic, OBJH *aHandler) : CacheManagerAction(anAction, aDesc, isPwReq, isAtomic), handler(aHandler) | |
454 | { | |
455 | } |