]> git.ipfire.org Git - thirdparty/squid.git/blob - src/cache_manager.cc
Cleanup: zap CVS Id tags
[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 /**
47 \defgroup CacheManagerInternal Cache Manager Internals
48 \ingroup CacheManagerAPI
49 */
50
51 /// \ingroup CacheManagerInternal
52 #define MGR_PASSWD_SZ 128
53
54
55 /**
56 \ingroup CacheManagerInternals
57 * Constructor. Its purpose is to register internal commands
58 */
59 CacheManager::CacheManager()
60 {
61 registerAction(new OfflineToggleAction);
62 registerAction(new ShutdownAction);
63 registerAction(new ReconfigureAction);
64 registerAction(new MenuAction(this));
65 }
66
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 */
73 void
74 CacheManager::registerAction(char const * action, char const * desc, OBJH * handler, int pw_req_flag, int atomic)
75 {
76 debugs(16, 3, "CacheManager::registerAction: registering legacy " << action);
77 registerAction(new CacheManagerActionLegacy(action,desc,pw_req_flag,atomic,handler));
78 }
79
80 /**
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.
85 */
86 void
87 CacheManager::registerAction(CacheManagerAction *anAction)
88 {
89 char *action = anAction->action;
90 if (findAction(action) != NULL) {
91 debugs(16, 2, "CacheManager::registerAction: Duplicate '" << action << "'. Skipping.");
92 return;
93 }
94
95 assert (strstr (" ", action) == NULL);
96
97 ActionsList += anAction;
98
99 debugs(16, 3, "CacheManager::registerAction: registered " << action);
100 }
101
102
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 */
109 CacheManagerAction *
110 CacheManager::findAction(char const * action)
111 {
112 CacheManagerActionList::iterator a;
113
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");
118 return *a;
119 }
120 }
121
122 debugs(16, 6, "Action not found.");
123 return NULL;
124 }
125
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 */
134 CacheManager::cachemgrStateData *
135 CacheManager::ParseUrl(const char *url)
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);
141 CacheManagerAction *a;
142 cachemgrStateData *mgr = NULL;
143 const char *prot;
144 t = sscanf(url, "cache_object://%[^/]/%[^@]@%s", host, request, password);
145
146 if (t < 2) {
147 xstrncpy(request, "menu", MAX_URL);
148 #ifdef _SQUID_OS2_
149 /*
150 * emx's sscanf insists of returning 2 because it sets request
151 * to null
152 */
153 } else if (request[0] == '\0') {
154 xstrncpy(request, "menu", MAX_URL);
155 #endif
156
157 } else if ((a = findAction(request)) == NULL) {
158 debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' not found");
159 return NULL;
160 } else {
161 prot = ActionProtection(a);
162
163 if (!strcmp(prot, "disabled") || !strcmp(prot, "hidden")) {
164 debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' is " << prot);
165 return NULL;
166 }
167 }
168
169 /* set absent entries to NULL so we can test if they are present later */
170 mgr = (cachemgrStateData *)xcalloc(1, sizeof(cachemgrStateData));
171
172 mgr->user_name = NULL;
173
174 mgr->passwd = t == 3 ? xstrdup(password) : NULL;
175
176 mgr->action = xstrdup(request);
177
178 return mgr;
179 }
180
181 /// \ingroup CacheManagerInternal
182 /*
183 \ingroup CacheManagerInternal
184 * Decodes the headers needed to perform user authentication and fills
185 * the details into the cachemgrStateData argument
186 */
187 void
188 CacheManager::ParseHeaders(cachemgrStateData * mgr, const HttpRequest * request)
189 {
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");
194
195 if (!basic_cookie)
196 return;
197
198 if (!(passwd_del = strchr(basic_cookie, ':'))) {
199 debugs(16, DBG_IMPORTANT, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie << "'");
200 return;
201 }
202
203 /* found user:password pair, reset old values */
204 safe_free(mgr->user_name);
205
206 safe_free(mgr->passwd);
207
208 mgr->user_name = xstrdup(basic_cookie);
209
210 mgr->user_name[passwd_del - basic_cookie] = '\0';
211
212 mgr->passwd = xstrdup(passwd_del + 1);
213
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 << "'");
216 }
217
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
224 */
225 int
226 CacheManager::CheckPassword(cachemgrStateData * mgr)
227 {
228 char *pwd = PasswdGet(Config.passwd_list, mgr->action);
229 CacheManagerAction *a = findAction(mgr->action);
230
231 debugs(16, 4, "CacheManager::CheckPassword for action " << mgr->action);
232 assert(a != NULL);
233
234 if (pwd == NULL)
235 return a->flags.pw_req;
236
237 if (strcmp(pwd, "disable") == 0)
238 return 1;
239
240 if (strcmp(pwd, "none") == 0)
241 return 0;
242
243 if (!mgr->passwd)
244 return 1;
245
246 return strcmp(pwd, mgr->passwd);
247 }
248
249 /// \ingroup CacheManagerInternal
250 void
251 CacheManager::StateFree(cachemgrStateData * mgr)
252 {
253 safe_free(mgr->action);
254 safe_free(mgr->user_name);
255 safe_free(mgr->passwd);
256 mgr->entry->unlock();
257 xfree(mgr);
258 }
259
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 */
266 void
267 CacheManager::Start(int fd, HttpRequest * request, StoreEntry * entry)
268 {
269 cachemgrStateData *mgr = NULL;
270 ErrorState *err = NULL;
271 CacheManagerAction *a;
272 debugs(16, 3, "CacheManager::Start: '" << entry->url() << "'" );
273
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;
279 return;
280 }
281
282 mgr->entry = entry;
283
284 entry->lock();
285 entry->expires = squid_curtime;
286
287 debugs(16, 5, "CacheManager: " << fd_table[fd].ipaddr << " requesting '" << mgr->action << "'");
288
289 /* get additional info from request headers */
290 ParseHeaders(mgr, request);
291
292 /* Check password */
293
294 if (CheckPassword(mgr) != 0) {
295 /* build error message */
296 ErrorState *err;
297 HttpReply *rep;
298 err = errorCon(ERR_CACHE_MGR_ACCESS_DENIED, HTTP_UNAUTHORIZED, request);
299 /* warn if user specified incorrect password */
300
301 if (mgr->passwd)
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 << "'" );
306 else
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 << "'" );
311
312 rep = err->BuildHttpReply();
313
314 errorStateFree(err);
315
316 /*
317 * add Authenticate header, use 'action' as a realm because
318 * password depends on action
319 */
320 rep->header.putAuth("Basic", mgr->action);
321
322 /* store the reply */
323 entry->replaceHttpReply(rep);
324
325 entry->expires = squid_curtime;
326
327 entry->complete();
328
329 StateFree(mgr);
330
331 return;
332 }
333
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);
340 assert(a != NULL);
341
342 entry->buffer();
343
344 {
345 HttpVersion version(1,0);
346 HttpReply *rep = new HttpReply;
347 rep->setHeaders(version,
348 HTTP_OK,
349 NULL,
350 "text/plain",
351 -1, /* C-Len */
352 squid_curtime, /* LMT */
353 squid_curtime);
354 entry->replaceHttpReply(rep);
355 }
356
357 a->run(entry);
358
359 entry->flush();
360
361 if (a->flags.atomic)
362 entry->complete();
363
364 StateFree(mgr);
365 }
366
367 /// \ingroup CacheManagerInternal
368 void CacheManager::ShutdownAction::run(StoreEntry *sentry)
369 {
370 debugs(16, DBG_CRITICAL, "Shutdown by Cache Manager command.");
371 shut_down(0);
372 }
373 /// \ingroup CacheManagerInternal
374 CacheManager::ShutdownAction::ShutdownAction() : CacheManagerAction("shutdown","Shut Down the Squid Process", 1, 1) { }
375
376 /// \ingroup CacheManagerInternal
377 void
378 CacheManager::ReconfigureAction::run(StoreEntry * sentry)
379 {
380 debugs(16, DBG_IMPORTANT, "Reconfigure by Cache Manager command.");
381 storeAppendPrintf(sentry, "Reconfiguring Squid Process ....");
382 reconfigure(SIGHUP);
383 }
384 /// \ingroup CacheManagerInternal
385 CacheManager::ReconfigureAction::ReconfigureAction() : CacheManagerAction("reconfigure","Reconfigure Squid", 1, 1) { }
386
387 /// \ingroup CacheManagerInternal
388 void
389 CacheManager::OfflineToggleAction::run(StoreEntry * sentry)
390 {
391 Config.onoff.offline = !Config.onoff.offline;
392 debugs(16, DBG_IMPORTANT, "offline_mode now " << (Config.onoff.offline ? "ON" : "OFF") << " by Cache Manager request.");
393
394 storeAppendPrintf(sentry, "offline_mode is now %s\n",
395 Config.onoff.offline ? "ON" : "OFF");
396 }
397 /// \ingroup CacheManagerInternal
398 CacheManager::OfflineToggleAction::OfflineToggleAction() : CacheManagerAction ("offline_toggle", "Toggle offline_mode setting", 1, 1) { }
399
400 /*
401 \ingroup CacheManagerInternal
402 * Renders the protection level text for an action.
403 * Also doubles as a check for the protection level.
404 */
405 const char *
406 CacheManager::ActionProtection(const CacheManagerAction * at)
407 {
408 char *pwd;
409 assert(at);
410 pwd = PasswdGet(Config.passwd_list, at->action);
411
412 if (!pwd)
413 return at->flags.pw_req ? "hidden" : "public";
414
415 if (!strcmp(pwd, "disable"))
416 return "disabled";
417
418 if (strcmp(pwd, "none") == 0)
419 return "public";
420
421 return "protected";
422 }
423
424 /// \ingroup CacheManagerInternal
425 void
426 CacheManager::MenuAction::run(StoreEntry * sentry)
427 {
428 CacheManagerActionList::iterator a;
429
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));
435 }
436 }
437 /// \ingroup CacheManagerInternal
438 CacheManager::MenuAction::MenuAction(CacheManager *aMgr) : CacheManagerAction ("menu", "Cache Manager Menu", 0, 1), cmgr(aMgr) { }
439
440 /*
441 \ingroup CacheManagerInternal
442 * gets from the global Config the password the user would need to supply
443 * for the action she queried
444 */
445 char *
446 CacheManager::PasswdGet(cachemgr_passwd * a, const char *action)
447 {
448 wordlist *w;
449
450 while (a != NULL) {
451 for (w = a->actions; w != NULL; w = w->next) {
452 if (0 == strcmp(w->key, action))
453 return a->passwd;
454
455 if (0 == strcmp(w->key, "all"))
456 return a->passwd;
457 }
458
459 a = a->next;
460 }
461
462 return NULL;
463 }
464
465 CacheManager* CacheManager::instance=0;
466
467 /**
468 \ingroup CacheManagerAPI
469 * Singleton accessor method.
470 */
471 CacheManager*
472 CacheManager::GetInstance()
473 {
474 if (instance == 0) {
475 debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up");
476 instance = new CacheManager;
477 }
478 return instance;
479 }
480
481
482 /// \ingroup CacheManagerInternal
483 void CacheManagerActionLegacy::run(StoreEntry *sentry)
484 {
485 handler(sentry);
486 }
487 /// \ingroup CacheManagerInternal
488 CacheManagerAction::CacheManagerAction(char const *anAction, char const *aDesc, unsigned int isPwReq, unsigned int isAtomic)
489 {
490 flags.pw_req = isPwReq;
491 flags.atomic = isAtomic;
492 action = xstrdup (anAction);
493 desc = xstrdup (aDesc);
494 }
495 /// \ingroup CacheManagerInternal
496 CacheManagerAction::~CacheManagerAction()
497 {
498 xfree(action);
499 xfree(desc);
500 }
501
502 /// \ingroup CacheManagerInternal
503 CacheManagerActionLegacy::CacheManagerActionLegacy(char const *anAction, char const *aDesc, unsigned int isPwReq, unsigned int isAtomic, OBJH *aHandler) : CacheManagerAction(anAction, aDesc, isPwReq, isAtomic), handler(aHandler)
504 {
505 }