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