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